Subversion Repositories eFlore/Applications.cel

Rev

Rev 1650 | Rev 1656 | Go to most recent revision | Only display areas with differences | Regard whitespace | Details | Blame | Last modification | View Log | RSS feed

Rev 1650 Rev 1651
1
<?php
1
<?php
2
/**
2
/**
3
* @category  PHP
3
* @category  PHP
4
* @package   jrest
4
* @package   jrest
5
* @author    Raphaël Droz <raphael@tela-botania.org>
5
* @author    Raphaël Droz <raphael@tela-botania.org>
6
* @copyright 2013 Tela-Botanica
6
* @copyright 2013 Tela-Botanica
7
* @license   http://www.cecill.info/licences/Licence_CeCILL_V2-fr.txt Licence CECILL
7
* @license   http://www.cecill.info/licences/Licence_CeCILL_V2-fr.txt Licence CECILL
8
* @license GPL v3 <http://www.gnu.org/licenses/gpl.txt>
8
* @license GPL v3 <http://www.gnu.org/licenses/gpl.txt>
9
*/
9
*/
10
 
10
 
11
/**
11
/**
12
 * Service d'import de données d'observation du CEL au format XLS
12
 * Service d'import de données d'observation du CEL au format XLS
13
 *
13
 *
14
 * Sont define()'d commme n° de colonne tous les abbrevs retournés par
14
 * Sont define()'d commme n° de colonne tous les abbrevs retournés par
15
 * ExportXLS::nomEnsembleVersListeColonnes() préfixés par C_  cf: detectionEntete()
15
 * ExportXLS::nomEnsembleVersListeColonnes() préfixés par C_  cf: detectionEntete()
16
 *
16
 *
17
 * Exemple d'un test:
17
 * Exemple d'un test:
18
 * $ GET "/jrest/ExportXLS/22506?format=csv&range=*&limite=13" \
18
 * $ GET "/jrest/ExportXLS/22506?format=csv&range=*&limite=13" \
19
 *   | curl -F "upload=@-" -F utilisateur=22506 "/jrest/ImportXLS"
19
 *   | curl -F "upload=@-" -F utilisateur=22506 "/jrest/ImportXLS"
20
 * # 13 observations importées
20
 * # 13 observations importées
21
 * + cf MySQL general_log = 1
21
 * + cf MySQL general_log = 1
22
 *  
22
 *  
23
 **/
23
 **/
24
 
24
 
25
set_include_path(get_include_path() . PATH_SEPARATOR . dirname(dirname(realpath(__FILE__))) . '/lib');
25
set_include_path(get_include_path() . PATH_SEPARATOR . dirname(dirname(realpath(__FILE__))) . '/lib');
26
// TERM
26
// TERM
27
error_reporting(-1);
27
error_reporting(-1);
28
ini_set('html_errors', 0);
28
ini_set('html_errors', 0);
29
ini_set('xdebug.cli_color', 2);
29
ini_set('xdebug.cli_color', 2);
30
require_once('lib/PHPExcel/Classes/PHPExcel.php');
30
require_once('lib/PHPExcel/Classes/PHPExcel.php');
31
require_once('ExportXLS.php');
31
require_once('ExportXLS.php');
32
 
32
 
33
 
33
 
34
date_default_timezone_set("Europe/Paris");
34
date_default_timezone_set("Europe/Paris");
35
 
35
 
36
// nombre d'INSERT à cumuler par requête SQL
36
// nombre d'INSERT à cumuler par requête SQL
37
// (= nombre de lignes XLS à bufferiser)
37
// (= nombre de lignes XLS à bufferiser)
38
//define('NB_LIRE_LIGNE_SIMUL', 30);
38
//define('NB_LIRE_LIGNE_SIMUL', 30);
39
define('NB_LIRE_LIGNE_SIMUL', 5);
39
define('NB_LIRE_LIGNE_SIMUL', 5);
40
 
40
 
41
// Numbers of days between January 1, 1900 and 1970 (including 19 leap years)
41
// Numbers of days between January 1, 1900 and 1970 (including 19 leap years)
42
// see traiterDateObs()
42
// see traiterDateObs()
43
define("MIN_DATES_DIFF", 25569);
43
define("MIN_DATES_DIFF", 25569);
44
 
44
 
45
 
45
 
46
class MyReadFilter implements PHPExcel_Reader_IReadFilter {
46
class MyReadFilter implements PHPExcel_Reader_IReadFilter {
47
	// exclusion de colonnes
47
	// exclusion de colonnes
48
	public $exclues = array();
48
	public $exclues = array();
49
 
49
 
50
	// lecture par morceaux
50
	// lecture par morceaux
51
    public $ligne_debut = 0; 
51
    public $ligne_debut = 0; 
52
    public $ligne_fin = 0;
52
    public $ligne_fin = 0;
53
 
53
 
54
	public function __construct() {}
54
	public function __construct() {}
55
	public function def_interval($debut, $nb) {
55
	public function def_interval($debut, $nb) {
56
		$this->ligne_debut = $debut;
56
		$this->ligne_debut = $debut;
57
		$this->ligne_fin = $debut + $nb;
57
		$this->ligne_fin = $debut + $nb;
58
	}
58
	}
59
    public function readCell($colonne, $ligne, $worksheetName = '') {
59
    public function readCell($colonne, $ligne, $worksheetName = '') {
60
		if(@$this->exclues[$colonne]) return false;
60
		if(@$this->exclues[$colonne]) return false;
61
		// si des n° de morceaux ont été initialisés, on filtre...
61
		// si des n° de morceaux ont été initialisés, on filtre...
62
		if($this->ligne_debut && ($ligne < $this->ligne_debut || $ligne >= $this->ligne_fin)) return false;
62
		if($this->ligne_debut && ($ligne < $this->ligne_debut || $ligne >= $this->ligne_fin)) return false;
63
		return true;
63
		return true;
64
    } 
64
    } 
65
} 
65
} 
66
 
66
 
67
class ImportXLS extends Cel  {
67
class ImportXLS extends Cel  {
68
 
68
 
69
	static $ordre_BDD = Array(
69
	static $ordre_BDD = Array(
70
		"ce_utilisateur",
70
		"ce_utilisateur",
71
		"prenom_utilisateur",
71
		"prenom_utilisateur",
72
		"nom_utilisateur",
72
		"nom_utilisateur",
73
		"courriel_utilisateur",
73
		"courriel_utilisateur",
74
		"ordre",
74
		"ordre",
75
		"nom_sel",
75
		"nom_sel",
76
		"nom_sel_nn",
76
		"nom_sel_nn",
77
		"nom_ret",
77
		"nom_ret",
78
		"nom_ret_nn",
78
		"nom_ret_nn",
79
		"nt",
79
		"nt",
80
		"famille",
80
		"famille",
81
		"nom_referentiel",
81
		"nom_referentiel",
82
		"zone_geo",
82
		"zone_geo",
83
		"ce_zone_geo",
83
		"ce_zone_geo",
84
		"date_observation",
84
		"date_observation",
85
		"lieudit",
85
		"lieudit",
86
		"station",
86
		"station",
87
		"milieu",
87
		"milieu",
88
		"mots_cles_texte",
88
		"mots_cles_texte",
89
		"commentaire",
89
		"commentaire",
90
		"transmission",
90
		"transmission",
91
		"date_creation",
91
		"date_creation",
92
		"date_modification",
92
		"date_modification",
93
		"date_transmission",
93
		"date_transmission",
94
		"latitude",
94
		"latitude",
95
		"longitude",
95
		"longitude",
96
		"abondance",
96
		"abondance",
97
		"certitude",
97
		"certitude",
98
		"phenologie",
98
		"phenologie",
99
		"code_insee_calcule"
99
		"code_insee_calcule"
100
	);
100
	);
101
 
101
 
102
	// cf: initialiser_pdo_ordered_statements()
102
	// cf: initialiser_pdo_ordered_statements()
103
	// eg: "INSERT INTO cel_obs (ce_utilisateur, ..., phenologie, code_insee_calcule) VALUES"
103
	// eg: "INSERT INTO cel_obs (ce_utilisateur, ..., phenologie, code_insee_calcule) VALUES"
104
	// colonnes statiques d'abord, les autres ensuite, dans l'ordre de $ordre_BDD
104
	// colonnes statiques d'abord, les autres ensuite, dans l'ordre de $ordre_BDD
105
	static $insert_prefix_ordre;
105
	static $insert_prefix_ordre;
106
	// eg: "(<id>, <prenom>, <nom>, <email>, now(), now(), ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)"
106
	// eg: "(<id>, <prenom>, <nom>, <email>, now(), now(), ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)"
107
	// dont le nombre de placeholder dépend du nombre de colonnes non-statiques
107
	// dont le nombre de placeholder dépend du nombre de colonnes non-statiques
108
	// colonnes statiques d'abord, les autres ensuite, dans l'ordre de $ordre_BDD
108
	// colonnes statiques d'abord, les autres ensuite, dans l'ordre de $ordre_BDD
109
	static $insert_ligne_pattern_ordre;
109
	static $insert_ligne_pattern_ordre;
110
 
110
 
111
	// seconde (meilleure) possibilité
111
	// seconde (meilleure) possibilité
112
	// cf: initialiser_pdo_statements()
112
	// cf: initialiser_pdo_statements()
113
	// eg: "INSERT INTO cel_obs (ce_utilisateur, ..., date_creation, ...phenologie, code_insee_calcule) VALUES"
113
	// eg: "INSERT INTO cel_obs (ce_utilisateur, ..., date_creation, ...phenologie, code_insee_calcule) VALUES"
114
	static $insert_prefix;
114
	static $insert_prefix;
115
	// eg: "(<id>, <prenom>, <nom>, <email>, ?, ?, ?, now(), now(), ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)"
115
	// eg: "(<id>, <prenom>, <nom>, <email>, ?, ?, ?, now(), now(), ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)"
116
	// dont le nombre de placeholder dépend du nombre de colonnes non-statiques
116
	// dont le nombre de placeholder dépend du nombre de colonnes non-statiques
117
	static $insert_ligne_pattern;
117
	static $insert_ligne_pattern;
118
 
118
 
119
	/*
119
	/*
120
	  Ces colonnes:
120
	  Ces colonnes:
121
	  - sont propres à l'ensemble des enregistrements uploadés
121
	  - sont propres à l'ensemble des enregistrements uploadés
122
	  - sont indépendantes du numéro de lignes
122
	  - sont indépendantes du numéro de lignes
123
	  - n'ont pas de valeur par défaut dans la structure de la table
123
	  - n'ont pas de valeur par défaut dans la structure de la table
124
	  - nécessitent une initialisation dans le cadre de l'upload
124
	  - nécessitent une initialisation dans le cadre de l'upload
125
 
125
 
126
	  initialiser_colonnes_statiques() y merge les données d'identification utilisateur
126
	  initialiser_colonnes_statiques() y merge les données d'identification utilisateur
127
	*/
127
	*/
128
	public $colonnes_statiques = Array(
128
	public $colonnes_statiques = Array(
129
		"ce_utilisateur" => NULL,
129
		"ce_utilisateur" => NULL,
130
		"prenom_utilisateur" => NULL,
130
		"prenom_utilisateur" => NULL,
131
		"nom_utilisateur" => NULL,
131
		"nom_utilisateur" => NULL,
132
		"courriel_utilisateur" => NULL,
132
		"courriel_utilisateur" => NULL,
133
 
133
 
134
		// fixes (fonction SQL)
134
		// fixes (fonction SQL)
135
		// XXX future: mais pourraient varier dans le futur si la mise-à-jour
135
		// XXX future: mais pourraient varier dans le futur si la mise-à-jour
136
		// d'observation est implémentée
136
		// d'observation est implémentée
137
		"date_creation" => "now()",
137
		"date_creation" => "now()",
138
		"date_modification" => "now()",
138
		"date_modification" => "now()",
139
	);
139
	);
140
 
140
 
141
	public $id_utilisateur = NULL;
141
	public $id_utilisateur = NULL;
142
 
142
 
143
	// erreurs d'import
143
	// erreurs d'import
144
	public $bilan = Array();
144
	public $bilan = Array();
145
 
145
 
146
 
146
 
147
	function ExportXLS($config) {
147
	function ExportXLS($config) {
148
		parent::__construct($config);
148
		parent::__construct($config);
149
	}
149
	}
150
 
150
 
151
	function createElement($pairs) {
151
	function createElement($pairs) {
152
		if(!isset($pairs['utilisateur']) || trim($pairs['utilisateur']) == '') {
152
		if(!isset($pairs['utilisateur']) || trim($pairs['utilisateur']) == '') {
153
			echo '0'; exit;
153
			echo '0'; exit;
154
		}
154
		}
155
 
155
 
156
		$id_utilisateur = intval($pairs['utilisateur']);
156
		$id_utilisateur = intval($pairs['utilisateur']);
157
		$this->id_utilisateur = $id_utilisateur; // pour traiterImage();
157
		$this->id_utilisateur = $id_utilisateur; // pour traiterImage();
158
 
158
 
159
		if(!isset($_SESSION)) session_start();
159
		if(!isset($_SESSION)) session_start();
160
        $this->controleUtilisateur($id_utilisateur);
160
        $this->controleUtilisateur($id_utilisateur);
161
 
161
 
162
        $this->utilisateur = $this->getInfosComplementairesUtilisateur($id_utilisateur);
162
        $this->utilisateur = $this->getInfosComplementairesUtilisateur($id_utilisateur);
163
 
163
 
164
		$this->initialiser_colonnes_statiques($id_utilisateur);
164
		$this->initialiser_colonnes_statiques($id_utilisateur);
165
 
165
 
166
		// initialisation du statement PDO/MySQL
166
		// initialisation du statement PDO/MySQL
167
		// première version, pattern de requête pas génial
167
		// première version, pattern de requête pas génial
168
		/* list(self;;$insert_prefix_ordre, self::$insert_ligne_pattern_ordre) =
168
		/* list(self;;$insert_prefix_ordre, self::$insert_ligne_pattern_ordre) =
169
		   $this->initialiser_pdo_ordered_statements($this->colonnes_statiques); */
169
		   $this->initialiser_pdo_ordered_statements($this->colonnes_statiques); */
170
		list(self::$insert_prefix, self::$insert_ligne_pattern) =
170
		list(self::$insert_prefix, self::$insert_ligne_pattern) =
171
			$this->initialiser_pdo_statements($this->colonnes_statiques);
171
			$this->initialiser_pdo_statements($this->colonnes_statiques);
172
 
172
 
173
 
173
 
174
		$infos_fichier = array_pop($_FILES);
174
		$infos_fichier = array_pop($_FILES);
175
		
175
		
176
		/*$objPHPExcel = PHPExcel_IOFactory::load($infos_fichier['tmp_name']);
176
		/*$objPHPExcel = PHPExcel_IOFactory::load($infos_fichier['tmp_name']);
177
		  $donnees = $objPHPExcel->getActiveSheet()->toArray(NULL,FALSE,FALSE,TRUE);*/
177
		  $donnees = $objPHPExcel->getActiveSheet()->toArray(NULL,FALSE,FALSE,TRUE);*/
178
 
178
 
179
		/*$objReader = PHPExcel_IOFactory::createReader("Excel5");
179
		/*$objReader = PHPExcel_IOFactory::createReader("Excel5");
180
		$objReader->setReadDataOnly(true);
180
		$objReader->setReadDataOnly(true);
181
		$objPHPExcel = $objReader->load($infos_fichier['tmp_name']);*/
181
		$objPHPExcel = $objReader->load($infos_fichier['tmp_name']);*/
182
 
182
 
183
		//var_dump($donnees);
183
		//var_dump($donnees);
184
 
184
 
185
		// renomme le fichier pour lui ajouter son extension initiale, ce qui
185
		// renomme le fichier pour lui ajouter son extension initiale, ce qui
186
		// permet (une sorte) d'autodétection du format.
186
		// permet (une sorte) d'autodétection du format.
187
		$fichier = $infos_fichier['tmp_name'];
187
		$fichier = $infos_fichier['tmp_name'];
188
		$extension = pathinfo($infos_fichier['name'], PATHINFO_EXTENSION);
188
		$extension = pathinfo($infos_fichier['name'], PATHINFO_EXTENSION);
189
		if( (strlen($extension) == 3 || strlen($extension) == 4) &&
189
		if( (strlen($extension) == 3 || strlen($extension) == 4) &&
190
			(rename($fichier, $fichier . '.' . $extension))) {
190
			(rename($fichier, $fichier . '.' . $extension))) {
191
			$fichier = $fichier . '.' . $extension;
191
			$fichier = $fichier . '.' . $extension;
192
		}
192
		}
193
 
193
 
194
		$objReader = PHPExcel_IOFactory::createReaderForFile($fichier);
194
		$objReader = PHPExcel_IOFactory::createReaderForFile($fichier);
195
		$objReader->setReadDataOnly(true);
195
		$objReader->setReadDataOnly(true);
196
 
196
 
197
		if(is_a($objReader, 'PHPExcel_Reader_CSV')) {
197
		if(is_a($objReader, 'PHPExcel_Reader_CSV')) {
198
			$objReader->setDelimiter(',')
198
			$objReader->setDelimiter(',')
199
				->setEnclosure('"')
199
				->setEnclosure('"')
200
				->setLineEnding("\n")
200
				->setLineEnding("\n")
201
				->setSheetIndex(0);
201
				->setSheetIndex(0);
202
		}
202
		}
203
 
203
 
204
		// on ne conserve que l'en-tête
204
		// on ne conserve que l'en-tête
205
		$filtre = new MyReadFilter();
205
		$filtre = new MyReadFilter();
206
		$filtre->def_interval(1, 2);
206
		$filtre->def_interval(1, 2);
207
		$objReader->setReadFilter($filtre);
207
		$objReader->setReadFilter($filtre);
208
 
208
 
209
		$objPHPExcel = $objReader->load($fichier);
209
		$objPHPExcel = $objReader->load($fichier);
210
		$obj_infos = $objReader->listWorksheetInfo($fichier);
210
		$obj_infos = $objReader->listWorksheetInfo($fichier);
211
		// XXX: indépendant du readFilter ?
211
		// XXX: indépendant du readFilter ?
212
		$nb_lignes = $obj_infos[0]['totalRows'];
212
		$nb_lignes = $obj_infos[0]['totalRows'];
213
 
213
 
214
		$donnees = $objPHPExcel->getActiveSheet()->toArray(NULL, FALSE, FALSE, TRUE);
214
		$donnees = $objPHPExcel->getActiveSheet()->toArray(NULL, FALSE, FALSE, TRUE);
215
		$filtre->exclues = self::detectionEntete($donnees[1]);
215
		$filtre->exclues = self::detectionEntete($donnees[1]);
216
 
216
 
217
		$obs_ajouts = 0;
217
		$obs_ajouts = 0;
218
		$obs_maj = 0;
218
		$obs_maj = 0;
219
		$dernier_ordre = $this->requeter("SELECT MAX(ordre) AS ordre FROM cel_obs WHERE ce_utilisateur = $id_utilisateur");
219
		$dernier_ordre = $this->requeter("SELECT MAX(ordre) AS ordre FROM cel_obs WHERE ce_utilisateur = $id_utilisateur");
220
		$dernier_ordre = intval($dernier_ordre[0]['ordre']) + 1;
220
		$dernier_ordre = intval($dernier_ordre[0]['ordre']) + 1;
221
		if(! $dernier_ordre) $dernier_ordre = 0;
221
		if(! $dernier_ordre) $dernier_ordre = 0;
222
 
222
 
223
		// on catch to les trigger_error(E_USER_NOTICE);
223
		// on catch to les trigger_error(E_USER_NOTICE);
224
		set_error_handler(array($this, 'erreurs_stock'), E_USER_NOTICE);
224
		set_error_handler(array($this, 'erreurs_stock'), E_USER_NOTICE);
225
 
225
 
226
		// lecture par morceaux (chunks), NB_LIRE_LIGNE_SIMUL lignes à fois
226
		// lecture par morceaux (chunks), NB_LIRE_LIGNE_SIMUL lignes à fois
227
		// pour aboutir des requêtes SQL d'insert groupés.
227
		// pour aboutir des requêtes SQL d'insert groupés.
228
		for($ligne = 2; $ligne < $nb_lignes + NB_LIRE_LIGNE_SIMUL; $ligne += NB_LIRE_LIGNE_SIMUL) {
228
		for($ligne = 2; $ligne < $nb_lignes + NB_LIRE_LIGNE_SIMUL; $ligne += NB_LIRE_LIGNE_SIMUL) {
229
			$filtre->def_interval($ligne, NB_LIRE_LIGNE_SIMUL);
229
			$filtre->def_interval($ligne, NB_LIRE_LIGNE_SIMUL);
230
			$objReader->setReadFilter($filtre);
230
			$objReader->setReadFilter($filtre);
231
 
231
 
232
			/* recharge avec $filtre actif (filtre sur lignes colonnes):
232
			/* recharge avec $filtre actif (filtre sur lignes colonnes):
233
			   - exclue les colonnes inutiles/inutilisables)
233
			   - exclue les colonnes inutiles/inutilisables)
234
			   - ne selectionne que les lignes dans le range [$ligne - $ligne + NB_LIRE_LIGNE_SIMUL] */
234
			   - ne selectionne que les lignes dans le range [$ligne - $ligne + NB_LIRE_LIGNE_SIMUL] */
235
			$objPHPExcel = $objReader->load($fichier);
235
			$objPHPExcel = $objReader->load($fichier);
236
			$donnees = $objPHPExcel->getActiveSheet()->toArray(NULL, FALSE, FALSE, TRUE);
236
			$donnees = $objPHPExcel->getActiveSheet()->toArray(NULL, FALSE, FALSE, TRUE);
237
 
237
 
238
			// ici on appel la fonction qui fera effectivement l'insertion multiple
238
			// ici on appel la fonction qui fera effectivement l'insertion multiple
239
			// à partir des (au plus) NB_LIRE_LIGNE_SIMUL lignes
239
			// à partir des (au plus) NB_LIRE_LIGNE_SIMUL lignes
240
 
240
 
241
			// TODO: passer $this, ne sert que pour appeler des méthodes public qui pourraient être statiques
241
			// TODO: passer $this, ne sert que pour appeler des méthodes public qui pourraient être statiques
242
			// notamment dans RechercheInfosTaxonBeta.php
242
			// notamment dans RechercheInfosTaxonBeta.php
243
			list($enregistrements, $images) =
243
			list($enregistrements, $images) =
244
				self::chargerLignes($this, $donnees, $this->colonnes_statiques, $dernier_ordre);
244
				self::chargerLignes($this, $donnees, $this->colonnes_statiques, $dernier_ordre);
245
			if(! $enregistrements) break;
245
			if(! $enregistrements) break;
246
 
246
 
247
			echo "===chunk\n";
247
			echo "===chunk\n";
248
			self::trierColonnes($enregistrements);
248
			self::trierColonnes($enregistrements);
249
			// normalement: NB_LIRE_LIGNE_SIMUL, sauf si une enregistrement ne semble pas valide
249
			// normalement: NB_LIRE_LIGNE_SIMUL, sauf si une enregistrement ne semble pas valide
250
			// ou bien lors du dernier chunk
250
			// ou bien lors du dernier chunk
251
 
251
 
252
			$nb_rec = count($enregistrements);
252
			$nb_rec = count($enregistrements);
253
			$sql_pattern = self::$insert_prefix .
253
			$sql_pattern = self::$insert_prefix .
254
				str_repeat(self::$insert_ligne_pattern_ordre . ', ', $nb_rec - 1) .
254
				str_repeat(self::$insert_ligne_pattern_ordre . ', ', $nb_rec - 1) .
255
				self::$insert_ligne_pattern_ordre;
255
				self::$insert_ligne_pattern_ordre;
256
 
256
 
257
			$sql_pattern = self::$insert_prefix .
257
			$sql_pattern = self::$insert_prefix .
258
				str_repeat(self::$insert_ligne_pattern . ', ', $nb_rec - 1) .
258
				str_repeat(self::$insert_ligne_pattern . ', ', $nb_rec - 1) .
259
				self::$insert_ligne_pattern;
259
				self::$insert_ligne_pattern;
260
 
260
 
261
			$this->bdd->beginTransaction();
261
			$this->bdd->beginTransaction();
262
			$stmt = $this->bdd->prepare($sql_pattern);
262
			$stmt = $this->bdd->prepare($sql_pattern);
263
			$donnees = array();
263
			$donnees = array();
264
			foreach($enregistrements as $e) $donnees = array_merge($donnees, array_values($e));
264
			foreach($enregistrements as $e) $donnees = array_merge($donnees, array_values($e));
265
 
265
 
266
			/* debug ici: echo $sql_pattern . "\n"; var_dump($enregistrements, $donnees); die;*/
266
			/* debug ici: echo $sql_pattern . "\n"; var_dump($enregistrements, $donnees); die;*/
267
 
267
 
268
			$stmt->execute($donnees);
268
			$stmt->execute($donnees);
269
 
269
 
270
			// $stmt->debugDumpParams(); // https://bugs.php.net/bug.php?id=52384
270
			// $stmt->debugDumpParams(); // https://bugs.php.net/bug.php?id=52384
271
			$dernier_autoinc = $this->bdd->lastInsertId();
271
			$dernier_autoinc = $this->bdd->lastInsertId();
272
			$this->bdd->commit();
272
			$this->bdd->commit();
273
 
273
 
274
			if(! $dernier_autoinc) trigger_error("l'insertion semble avoir échoué", E_USER_NOTICE);
274
			if(! $dernier_autoinc) trigger_error("l'insertion semble avoir échoué", E_USER_NOTICE);
275
 
275
 
276
			$obs_ajouts += count($enregistrements);
276
			$obs_ajouts += count($enregistrements);
277
			// $obs_ajouts += count($enregistrements['insert']);
277
			// $obs_ajouts += count($enregistrements['insert']);
278
			// $obs_maj += count($enregistrements['update']);
278
			// $obs_maj += count($enregistrements['update']);
279
			$nb_images_ajoutees = self::stockerImages($this, $enregistrements, $images, $dernier_autoinc);
279
			$nb_images_ajoutees = self::stockerImages($this, $enregistrements, $images, $dernier_autoinc);
280
		}
280
		}
281
 
281
 
282
		restore_error_handler();
282
		restore_error_handler();
283
 
283
 
284
		if($this->bilan) echo implode("\n", $this->bilan) . "\n";
284
		if($this->bilan) echo implode("\n", $this->bilan) . "\n";
285
 		$summary = sprintf("%d observation(s) ajoutée(s)\n%d image(s) attachée(s)",
285
 		$summary = sprintf("%d observation(s) ajoutée(s)\n%d image(s) attachée(s)",
286
						   $obs_ajouts,
286
						   $obs_ajouts,
287
						   $nb_images_ajoutees);
287
						   $nb_images_ajoutees);
288
 
288
 
289
		die("$summary");
289
		die("$summary");
290
	}
290
	}
291
 
291
 
292
	static function detectionEntete($entete) {
292
	static function detectionEntete($entete) {
293
		$colonnes_reconnues = Array();
293
		$colonnes_reconnues = Array();
294
		$cols = ExportXLS::nomEnsembleVersListeColonnes('standard');
294
		$cols = ExportXLS::nomEnsembleVersListeColonnes('standard');
295
		foreach($entete as $k => $v) {
295
		foreach($entete as $k => $v) {
296
			$entete_simple = iconv('UTF-8', 'ASCII//TRANSLIT', strtolower(trim($v)));
296
			$entete_simple = iconv('UTF-8', 'ASCII//TRANSLIT', strtolower(trim($v)));
297
			foreach($cols as $col) {
297
			foreach($cols as $col) {
298
				$entete_officiel_simple = iconv('UTF-8', 'ASCII//TRANSLIT', strtolower(trim($col['nom'])));
298
				$entete_officiel_simple = iconv('UTF-8', 'ASCII//TRANSLIT', strtolower(trim($col['nom'])));
299
				$entete_officiel_abbrev = $col['abbrev'];
299
				$entete_officiel_abbrev = $col['abbrev'];
300
				if($entete_simple == $entete_officiel_simple || $entete_simple == $entete_officiel_abbrev) {
300
				if($entete_simple == $entete_officiel_simple || $entete_simple == $entete_officiel_abbrev) {
301
					// debug echo "define C_" . strtoupper($entete_officiel_abbrev) . ", $k ($v)\n";
301
					// debug echo "define C_" . strtoupper($entete_officiel_abbrev) . ", $k ($v)\n";
302
					define("C_" . strtoupper($entete_officiel_abbrev), $k);
302
					define("C_" . strtoupper($entete_officiel_abbrev), $k);
303
					$colonnes_reconnues[$k] = 1;
303
					$colonnes_reconnues[$k] = 1;
304
					break;
304
					break;
305
				}
305
				}
306
			}
306
			}
307
		}
307
		}
308
 
308
 
309
		// prépare le filtre de PHPExcel qui évitera le traitement de toutes les colonnes superflues
309
		// prépare le filtre de PHPExcel qui évitera le traitement de toutes les colonnes superflues
310
 
310
 
311
		// eg: diff ( Array( H => Commune, I => rien ) , Array( H => 1, K => 1 )
311
		// eg: diff ( Array( H => Commune, I => rien ) , Array( H => 1, K => 1 )
312
		// ==> Array( I => rien )
312
		// ==> Array( I => rien )
313
		$colonnesID_non_reconnues = array_diff_key($entete, $colonnes_reconnues);
313
		$colonnesID_non_reconnues = array_diff_key($entete, $colonnes_reconnues);
314
 
314
 
315
		// des colonnes de ExportXLS::nomEnsembleVersListeColonnes()
315
		// des colonnes de ExportXLS::nomEnsembleVersListeColonnes()
316
		// ne retient que celles marquées "importables"
316
		// ne retient que celles marquées "importables"
317
		$colonnes_automatiques = array_filter($cols, function($v) {	return !$v['importable']; });
317
		$colonnes_automatiques = array_filter($cols, function($v) {	return !$v['importable']; });
318
 
318
 
319
		// ne conserve que le nom long pour matcher avec la ligne XLS d'entête
319
		// ne conserve que le nom long pour matcher avec la ligne XLS d'entête
320
		array_walk($colonnes_automatiques, function(&$v) {	$v = $v['nom']; });
320
		array_walk($colonnes_automatiques, function(&$v) {	$v = $v['nom']; });
321
 
321
 
322
		// intersect ( Array ( N => Milieu, S => Ordre ), Array ( ordre => Ordre, phenologie => Phénologie ) )
322
		// intersect ( Array ( N => Milieu, S => Ordre ), Array ( ordre => Ordre, phenologie => Phénologie ) )
323
		// ==> Array ( S => Ordre, AA => Phénologie )
323
		// ==> Array ( S => Ordre, AA => Phénologie )
324
		$colonnesID_a_exclure = array_intersect($entete, $colonnes_automatiques);
324
		$colonnesID_a_exclure = array_intersect($entete, $colonnes_automatiques);
325
 
325
 
326
		// TODO: pourquoi ne pas comparer avec les abbrevs aussi ?
326
		// TODO: pourquoi ne pas comparer avec les abbrevs aussi ?
327
		// merge ( Array( I => rien ) , Array ( S => Ordre, AA => Phénologie ) )
327
		// merge ( Array( I => rien ) , Array ( S => Ordre, AA => Phénologie ) )
328
		// ==> Array ( I => rien, AA => Phénologie )
328
		// ==> Array ( I => rien, AA => Phénologie )
329
		return array_merge($colonnesID_non_reconnues, $colonnesID_a_exclure);
329
		return array_merge($colonnesID_non_reconnues, $colonnesID_a_exclure);
330
	}
330
	}
331
 
331
 
332
	/*
332
	/*
333
	 * charge un groupe de lignes
333
	 * charge un groupe de lignes
334
	 */
334
	 */
335
	static function chargerLignes($cel, $lignes, $colonnes_statiques, &$dernier_ordre) {
335
	static function chargerLignes($cel, $lignes, $colonnes_statiques, &$dernier_ordre) {
336
		$enregistrement = NULL;
336
		$enregistrement = NULL;
337
		$enregistrements = Array();
337
		$enregistrements = Array();
338
		$toutes_images = Array();
338
		$toutes_images = Array();
339
 
339
 
340
		foreach($lignes as $ligne) {
340
		foreach($lignes as $ligne) {
341
			//$ligne = array_filter($ligne, function($cell) { return !is_null($cell); });
341
			//$ligne = array_filter($ligne, function($cell) { return !is_null($cell); });
342
			//if(!$ligne) continue;
342
			//if(!$ligne) continue;
343
			// on a besoin des NULL pour éviter des notice d'index indéfini
343
			// on a besoin des NULL pour éviter des notice d'index indéfini
344
			if(! array_filter($ligne, function($cell) { return !is_null($cell); })) continue;
344
			if(! array_filter($ligne, function($cell) { return !is_null($cell); })) continue;
345
 
345
 
346
			if( ($enregistrement = self::chargerLigne($ligne, $dernier_ordre, $cel)) ) {
346
			if( ($enregistrement = self::chargerLigne($ligne, $dernier_ordre, $cel)) ) {
347
				// $enregistrements[] = array_merge($colonnes_statiques, $enregistrement);
347
				// $enregistrements[] = array_merge($colonnes_statiques, $enregistrement);
348
				$enregistrements[] = $enregistrement;
348
				$enregistrements[] = $enregistrement;
349
 
349
 
350
				if(isset($enregistrement['_images'])) {
350
				if(isset($enregistrement['_images'])) {
351
					$pos = count($enregistrements) - 1;
351
					$pos = count($enregistrements) - 1;
352
					$last = &$enregistrements[$pos];
352
					$last = &$enregistrements[$pos];
353
					// ne dépend pas de cel_obs, et seront insérées *après* les enregistrements
353
					// ne dépend pas de cel_obs, et seront insérées *après* les enregistrements
354
					// mais nous ne voulons pas nous priver de faire des INSERT multiples pour autant
354
					// mais nous ne voulons pas nous priver de faire des INSERT multiples pour autant
355
					$toutes_images[] = Array("images" => $last['_images'],
355
					$toutes_images[] = Array("images" => $last['_images'],
356
											 "obs_pos" => $pos);
356
											 "obs_pos" => $pos);
357
					// ce champ n'a pas a faire partie de l'insertion dans cel_obs,
357
					// ce champ n'a pas a faire partie de l'insertion dans cel_obs,
358
					// mais est utile pour cel_obs_images
358
					// mais est utile pour cel_obs_images
359
					unset($last['_images']);
359
					unset($last['_images']);
360
				}
360
				}
361
 
361
 
362
				$dernier_ordre++;
362
				$dernier_ordre++;
363
			}
363
			}
364
		}
364
		}
365
 
365
 
366
		// XXX future: return Array($enregistrements_a_inserer, $enregistrements_a_MAJ, $toutes_images);
366
		// XXX future: return Array($enregistrements_a_inserer, $enregistrements_a_MAJ, $toutes_images);
367
		return Array($enregistrements, $toutes_images);
367
		return Array($enregistrements, $toutes_images);
368
	}
368
	}
369
 
369
 
370
 
370
 
371
	static function trierColonnes(&$enregistrements) {
371
	static function trierColonnes(&$enregistrements) {
372
		foreach($enregistrements as &$enregistrement) {
372
		foreach($enregistrements as &$enregistrement) {
373
			$enregistrement = self::sortArrayByArray($enregistrement, self::$ordre_BDD);
373
			$enregistrement = self::sortArrayByArray($enregistrement, self::$ordre_BDD);
374
			//array_walk($enregistrement, function(&$item, $k) { $item = is_null($item) ? "NULL" : $item; });
374
			//array_walk($enregistrement, function(&$item, $k) { $item = is_null($item) ? "NULL" : $item; });
375
			//$req .= implode(', ', $enregistrement) . "\n";
375
			//$req .= implode(', ', $enregistrement) . "\n";
376
		}
376
		}
377
	}
377
	}
378
 
378
 
379
 
379
 
380
	static function stockerImages($cel, $enregistrements, $toutes_images, $lastid) {
380
	static function stockerImages($cel, $enregistrements, $toutes_images, $lastid) {
381
		$images_insert = 'INSERT INTO cel_obs_images (id_image, id_observation) VALUES %s ON DUPLICATE KEY UPDATE id_image = id_image';
381
		$images_insert = 'INSERT INTO cel_obs_images (id_image, id_observation) VALUES %s ON DUPLICATE KEY UPDATE id_image = id_image';
382
		$images_obs_assoc = Array();
382
		$images_obs_assoc = Array();
383
 
383
 
384
		foreach($toutes_images as $images_pour_obs) {
384
		foreach($toutes_images as $images_pour_obs) {
385
			$obs = $enregistrements[$images_pour_obs["obs_pos"]];
385
			$obs = $enregistrements[$images_pour_obs["obs_pos"]];
386
			$id_obs = $lastid // dernier autoinc inséré
386
			$id_obs = $lastid // dernier autoinc inséré
387
				- count($enregistrements) + 1 // correspondrait au premier autoinc
387
				- count($enregistrements) + 1 // correspondrait au premier autoinc
388
				+ $images_pour_obs["obs_pos"]; // ordre d'insertion = ordre dans le tableau $enregistrements (commence à 0)
388
				+ $images_pour_obs["obs_pos"]; // ordre d'insertion = ordre dans le tableau $enregistrements (commence à 0)
389
			foreach($images_pour_obs['images'] as $image) {
389
			foreach($images_pour_obs['images'] as $image) {
390
				$images_obs_assoc[] = sprintf('(%d,%d)',
390
				$images_obs_assoc[] = sprintf('(%d,%d)',
391
											  $image['id_image'], // intval() useless
391
											  $image['id_image'], // intval() useless
392
											  $id_obs); // intval() useless
392
											  $id_obs); // intval() useless
393
			}
393
			}
394
		}
394
		}
395
 
395
 
396
		if($images_obs_assoc) {
396
		if($images_obs_assoc) {
397
			$requete = sprintf($images_insert, implode(', ', $images_obs_assoc));
397
			$requete = sprintf($images_insert, implode(', ', $images_obs_assoc));
398
			// debug echo "$requete\n";
398
			// debug echo "$requete\n";
399
			$cel->requeter($requete);
399
			$cel->requeter($requete);
400
		}
400
		}
401
 
401
 
402
		return count($images_obs_assoc);
402
		return count($images_obs_assoc);
403
	}
403
	}
404
 
404
 
405
	/*
405
	/*
406
	  Aucune des valeurs présentes dans $enregistrement n'est quotée
406
	  Aucune des valeurs présentes dans $enregistrement n'est quotée
407
	  cad aucune des valeurs retournée par traiter{Espece|Localisation}()
407
	  cad aucune des valeurs retournée par traiter{Espece|Localisation}()
408
	  car ce tableau est passé à un PDO::preparedStatement() qui applique
408
	  car ce tableau est passé à un PDO::preparedStatement() qui applique
409
	  proprement les règle d'échappement.
409
	  proprement les règle d'échappement.
410
	 */
410
	 */
411
	static function chargerLigne($ligne, $dernier_ordre, $cel) {
411
	static function chargerLigne($ligne, $dernier_ordre, $cel) {
412
		// en premier car le résultat est utile pour
412
		// en premier car le résultat est utile pour
413
		// traiter longitude et latitude (traiterLonLat())
413
		// traiter longitude et latitude (traiterLonLat())
414
		$referentiel = self::identReferentiel($ligne[C_NOM_REFERENTIEL]);
414
		$referentiel = self::identReferentiel($ligne[C_NOM_REFERENTIEL]);
415
 
415
 
416
		// $espece est rempli de plusieurs informations
416
		// $espece est rempli de plusieurs informations
417
		$espece = Array(C_NOM_SEL => NULL, C_NOM_SEL_NN => NULL, C_NOM_RET => NULL,
417
		$espece = Array(C_NOM_SEL => NULL, C_NOM_SEL_NN => NULL, C_NOM_RET => NULL,
418
						C_NOM_RET_NN => NULL, C_NT => NULL, C_FAMILLE => NULL);
418
						C_NOM_RET_NN => NULL, C_NT => NULL, C_FAMILLE => NULL);
419
		self::traiterEspece($ligne, $espece, $cel);
419
		self::traiterEspece($ligne, $espece, $cel);
420
 
420
 
421
		// $localisation est rempli à partir de plusieurs champs: C_ZONE_GEO et C_CE_ZONE_GEO
421
		// $localisation est rempli à partir de plusieurs champs: C_ZONE_GEO et C_CE_ZONE_GEO
422
		$localisation = Array(C_ZONE_GEO => NULL, C_CE_ZONE_GEO => NULL);
422
		$localisation = Array(C_ZONE_GEO => NULL, C_CE_ZONE_GEO => NULL);
423
		self::traiterLocalisation($ligne, $localisation, $cel);
423
		self::traiterLocalisation($ligne, $localisation, $cel);
424
 
424
 
425
		// $transmission est utilisé pour date_transmission
425
		// $transmission est utilisé pour date_transmission
426
		$transmission = in_array(strtolower(trim($ligne[C_TRANSMISSION])), array(1, 'oui')) ? 1 : 0;
426
		$transmission = in_array(strtolower(trim($ligne[C_TRANSMISSION])), array(1, 'oui')) ? 1 : 0;
427
 
427
 
428
 
428
 
429
		// Dans ce tableau, seules devraient apparaître les données variable pour chaque ligne.
429
		// Dans ce tableau, seules devraient apparaître les données variable pour chaque ligne.
430
		// Dans ce tableau, l'ordre des clefs n'importe pas (cf: self::sortArrayByArray())
430
		// Dans ce tableau, l'ordre des clefs n'importe pas (cf: self::sortArrayByArray())
431
		$enregistrement = Array(
431
		$enregistrement = Array(
432
			"ordre" => $dernier_ordre,
432
			"ordre" => $dernier_ordre,
433
 
433
 
434
			"nom_sel" => $espece[C_NOM_SEL],
434
			"nom_sel" => $espece[C_NOM_SEL],
435
			"nom_sel_nn" => $espece[C_NOM_SEL_NN],
435
			"nom_sel_nn" => $espece[C_NOM_SEL_NN],
436
			"nom_ret" => $espece[C_NOM_RET],
436
			"nom_ret" => $espece[C_NOM_RET],
437
			"nom_ret_nn" => $espece[C_NOM_RET_NN],
437
			"nom_ret_nn" => $espece[C_NOM_RET_NN],
438
			"nt" => $espece[C_NT],
438
			"nt" => $espece[C_NT],
439
			"famille" => $espece[C_FAMILLE],
439
			"famille" => $espece[C_FAMILLE],
440
 
440
 
441
			"nom_referentiel" => $referentiel,
441
			"nom_referentiel" => $referentiel,
442
 
442
 
443
			"zone_geo" => $localisation[C_ZONE_GEO],
443
			"zone_geo" => $localisation[C_ZONE_GEO],
444
			"ce_zone_geo" => $localisation[C_CE_ZONE_GEO],
444
			"ce_zone_geo" => $localisation[C_CE_ZONE_GEO],
445
 
445
 
446
			// $ligne: uniquement pour les infos en cas de gestion d'erreurs (date incompréhensible)
446
			// $ligne: uniquement pour les infos en cas de gestion d'erreurs (date incompréhensible)
447
			"date_observation" => self::traiterDateObs($ligne[C_DATE_OBSERVATION], $ligne),
447
			"date_observation" => self::traiterDateObs($ligne[C_DATE_OBSERVATION], $ligne),
448
 
448
 
449
			"lieudit" => trim($ligne[C_LIEUDIT]),
449
			"lieudit" => trim($ligne[C_LIEUDIT]),
450
			"station" => trim($ligne[C_STATION]),
450
			"station" => trim($ligne[C_STATION]),
451
			"milieu" => trim($ligne[C_MILIEU]),
451
			"milieu" => trim($ligne[C_MILIEU]),
452
 
452
 
453
			"mots_cles_texte" => NULL, // TODO: foreign-key
453
			"mots_cles_texte" => NULL, // TODO: foreign-key
454
			"commentaire" => trim($ligne[C_COMMENTAIRE]),
454
			"commentaire" => trim($ligne[C_COMMENTAIRE]),
455
 
455
 
456
			"transmission" => $transmission,
456
			"transmission" => $transmission,
457
			"date_transmission" => $transmission ? date("Y-m-d H:i:s") : NULL, // pas de fonction SQL dans un PDO statement, <=> now()
457
			"date_transmission" => $transmission ? date("Y-m-d H:i:s") : NULL, // pas de fonction SQL dans un PDO statement, <=> now()
458
 
458
 
459
			// $ligne: uniquement pour les infos en cas de gestion d'erreurs (lon/lat incompréhensible)
459
			// $ligne: uniquement pour les infos en cas de gestion d'erreurs (lon/lat incompréhensible)
460
			"latitude" => self::traiterLonLat(NULL, $ligne[C_LATITUDE], $referentiel, $ligne),
460
			"latitude" => self::traiterLonLat(NULL, $ligne[C_LATITUDE], $referentiel, $ligne),
461
			"longitude" => self::traiterLonLat($ligne[C_LONGITUDE], NULL, $referentiel, $ligne),
461
			"longitude" => self::traiterLonLat($ligne[C_LONGITUDE], NULL, $referentiel, $ligne),
462
 
462
 
463
			// @ car potentiellement optionnelles ou toutes vides => pas d'index dans PHPExcel (tableau optimisé)
463
			// @ car potentiellement optionnelles ou toutes vides => pas d'index dans PHPExcel (tableau optimisé)
464
			"abondance" => @$ligne[C_ABONDANCE],
464
			"abondance" => @$ligne[C_ABONDANCE],
465
			"certitude" => @$ligne[C_CERTITUDE],
465
			"certitude" => @$ligne[C_CERTITUDE],
466
			"phenologie" => @$ligne[C_PHENOLOGIE],
466
			"phenologie" => @$ligne[C_PHENOLOGIE],
467
 
467
 
468
			"code_insee_calcule" => substr($localisation[C_CE_ZONE_GEO], -5) // varchar(5)
468
			"code_insee_calcule" => substr($localisation[C_CE_ZONE_GEO], -5) // varchar(5)
469
		);
469
		);
470
 
470
 
471
		// passage de $enregistrement par référence, ainsi ['_images'] n'est défini
471
		// passage de $enregistrement par référence, ainsi ['_images'] n'est défini
472
		// que si des résultats sont trouvés
472
		// que si des résultats sont trouvés
473
		// "@" car PHPExcel supprime les colonnes null sur toute la feuille (ou tout le chunk)
473
		// "@" car PHPExcel supprime les colonnes null sur toute la feuille (ou tout le chunk)
474
		if(@$ligne[C_IMAGES]) self::traiterImage($ligne[C_IMAGES], $cel, $enregistrement);
474
		if(@$ligne[C_IMAGES]) self::traiterImage($ligne[C_IMAGES], $cel, $enregistrement);
475
 
475
 
476
		return $enregistrement;
476
		return $enregistrement;
477
	}
477
	}
478
 
478
 
479
	static function traiterImage($str, $cel, &$enregistrement) {
479
	static function traiterImage($str, $cel, &$enregistrement) {
480
		$liste_images = array_filter(explode("/", $str));
480
		$liste_images = array_filter(explode("/", $str));
481
		array_walk($liste_images,
481
		array_walk($liste_images,
482
				   function($item, $key, $obj) { $item = $obj->quoteNonNull(trim($item)); },
482
				   function($item, $key, $obj) { $item = $obj->quoteNonNull(trim($item)); },
483
				   $cel);
483
				   $cel);
484
		$requete = sprintf(
484
		$requete = sprintf(
485
			"SELECT id_image, nom_original FROM cel_images WHERE ce_utilisateur = %d AND nom_original IN (\"%s\")",
485
			"SELECT id_image, nom_original FROM cel_images WHERE ce_utilisateur = %d AND nom_original IN (\"%s\")",
486
			$cel->id_utilisateur,
486
			$cel->id_utilisateur,
487
			implode('","', $liste_images));
487
			implode('","', $liste_images));
488
 
488
 
489
		$resultat = $cel->requeter($requete);
489
		$resultat = $cel->requeter($requete);
490
 
490
 
491
		if($resultat) $enregistrement['_images'] = $resultat;
491
		if($resultat) $enregistrement['_images'] = $resultat;
492
	}
492
	}
493
 
493
 
494
 
494
 
495
	/* FONCTIONS de TRANSFORMATION de VALEUR DE CELLULE */
495
	/* FONCTIONS de TRANSFORMATION de VALEUR DE CELLULE */
496
 
496
 
497
	// TODO: PHP 5.3, utiliser date_parse_from_format()
497
	// TODO: PHP 5.3, utiliser date_parse_from_format()
498
	// TODO: parser les heures (cf product-owner)
498
	// TODO: parser les heures (cf product-owner)
499
	// TODO: passer par le timestamp pour s'assurer de la validité
499
	// TODO: passer par le timestamp pour s'assurer de la validité
500
	static function traiterDateObs($date, $ligne) {
500
	static function traiterDateObs($date, $ligne) {
501
		// TODO: see https://github.com/PHPOffice/PHPExcel/issues/208
501
		// TODO: see https://github.com/PHPOffice/PHPExcel/issues/208
502
		if(is_double($date)) {
502
		if(is_double($date)) {
503
			if($date > 0)
503
			if($date > 0)
504
				return PHPExcel_Style_NumberFormat::toFormattedString($date, PHPExcel_Style_NumberFormat::FORMAT_DATE_YYYYMMDD2) . " 00:00:00";
504
				return PHPExcel_Style_NumberFormat::toFormattedString($date, PHPExcel_Style_NumberFormat::FORMAT_DATE_YYYYMMDD2) . " 00:00:00";
505
			trigger_error("ligne \"{$ligne[C_NOM_SEL]}\": " .
505
			trigger_error("ligne \"{$ligne[C_NOM_SEL]}\": " .
506
						  "Attention: date antérieure à 1970 et format de cellule \"DATE\" utilisés ensemble",
506
						  "Attention: date antérieure à 1970 et format de cellule \"DATE\" utilisés ensemble",
507
						  E_USER_NOTICE);
507
						  E_USER_NOTICE);
508
			
508
			
509
			// throw new Exception("erreur: date antérieure à 1970 et format de cellule \"DATE\" utilisés ensemble");
509
			// throw new Exception("erreur: date antérieure à 1970 et format de cellule \"DATE\" utilisés ensemble");
510
 
510
 
511
			// attention, UNIX timestamp, car Excel les décompte depuis 1900
511
			// attention, UNIX timestamp, car Excel les décompte depuis 1900
512
			// cf http://fczaja.blogspot.fr/2011/06/convert-excel-date-into-timestamp.html
512
			// cf http://fczaja.blogspot.fr/2011/06/convert-excel-date-into-timestamp.html
513
			// $timestamp = ($date - MIN_DATES_DIFF) * 60 * 60 * 24 - time(); // NON
513
			// $timestamp = ($date - MIN_DATES_DIFF) * 60 * 60 * 24 - time(); // NON
514
 
514
 
515
			// $timestamp = PHPExcel_Calculation::getInstance()->calculateFormula("=" . $date . "-DATE(1970,1,1)*60*60*24"); // NON
515
			// $timestamp = PHPExcel_Calculation::getInstance()->calculateFormula("=" . $date . "-DATE(1970,1,1)*60*60*24"); // NON
516
 
516
 
517
			// echo strftime("%Y/%m/%d 00:00:00", $timestamp); // NON
517
			// echo strftime("%Y/%m/%d 00:00:00", $timestamp); // NON
518
		}
518
		}
519
		else {
519
		else {
520
			$timestamp = strtotime($date);
520
			$timestamp = strtotime($date);
521
			if(! $timestamp) {
521
			if(! $timestamp) {
522
				if($date) trigger_error("ligne \"{$ligne[C_NOM_SEL]}\": Attention: date erronée ($date)", E_USER_NOTICE);
522
				if($date) trigger_error("ligne \"{$ligne[C_NOM_SEL]}\": Attention: date erronée ($date)", E_USER_NOTICE);
523
				return NULL;
523
				return NULL;
524
			}
524
			}
525
			return strftime("%Y-%m-%d 00:00:00", strtotime($date));
525
			return strftime("%Y-%m-%d 00:00:00", strtotime($date));
526
		}
526
		}
527
	}
527
	}
528
 
528
 
529
	static function identReferentiel($referentiel) {
529
	static function identReferentiel($referentiel) {
530
		// SELECT DISTINCT nom_referentiel, COUNT(id_observation) AS count FROM cel_obs GROUP BY nom_referentiel ORDER BY count DESC;
530
		// SELECT DISTINCT nom_referentiel, COUNT(id_observation) AS count FROM cel_obs GROUP BY nom_referentiel ORDER BY count DESC;
531
		if(strpos(strtolower($referentiel), 'bdtfx') !== FALSE) return 'bdtfx:v1.01';
531
		if(strpos(strtolower($referentiel), 'bdtfx') !== FALSE) return 'bdtfx:v1.01';
532
		if(strpos(strtolower($referentiel), 'bdtxa') !== FALSE) return 'bdtxa:v1.00';
532
		if(strpos(strtolower($referentiel), 'bdtxa') !== FALSE) return 'bdtxa:v1.00';
533
		if(strpos(strtolower($referentiel), 'bdnff') !== FALSE) return 'bdnff:4.02';
533
		if(strpos(strtolower($referentiel), 'bdnff') !== FALSE) return 'bdnff:4.02';
534
		if(strpos(strtolower($referentiel), 'isfan') !== FALSE) return 'isfan:v1.00';
534
		if(strpos(strtolower($referentiel), 'isfan') !== FALSE) return 'isfan:v1.00';
535
 
535
 
536
		if($referentiel) {
536
		if($referentiel) {
537
			trigger_error("ligne \"{$ligne[C_NOM_SEL]}\": Attention: référentiel inconnu", E_USER_NOTICE);
537
			trigger_error("ligne \"{$ligne[C_NOM_SEL]}\": Attention: référentiel inconnu", E_USER_NOTICE);
538
		}
538
		}
539
		return NULL;
539
		return NULL;
540
		/* TODO: cf story,
540
		/* TODO: cf story,
541
		   En cas de NULL faire une seconde passe de détection à partir du nom saisi
541
		   En cas de NULL faire une seconde passe de détection à partir du nom saisi
542
		   + accepter les n° de version */
542
		   + accepter les n° de version */
543
	}
543
	}
544
 
544
 
545
	static function traiterLonLat($lon = NULL, $lat = NULL, $referentiel = 'bdtfx:v1.01', $ligne) {
545
	static function traiterLonLat($lon = NULL, $lat = NULL, $referentiel = 'bdtfx:v1.01', $ligne) {
546
		// en CSV ces valeurs sont des string, avec séparateur en français (","; cf défauts dans ExportXLS)
546
		// en CSV ces valeurs sont des string, avec séparateur en français (","; cf défauts dans ExportXLS)
547
		if($lon && is_string($lon)) $lon = str_replace(',', '.', $lon);
547
		if($lon && is_string($lon)) $lon = str_replace(',', '.', $lon);
548
		if($lat && is_string($lat)) $lat = str_replace(',', '.', $lat);
548
		if($lat && is_string($lat)) $lat = str_replace(',', '.', $lat);
549
 
549
 
550
		// sprintf applique une précision à 5 décimale (comme le ferait MySQL)
550
		// sprintf applique une précision à 5 décimale (comme le ferait MySQL)
551
		// tout en uniformisant le format de séparateur des décimales (le ".")
551
		// tout en uniformisant le format de séparateur des décimales (le ".")
552
		if($lon && is_numeric($lon) && $lon >= -180 && $lon <= 180) return sprintf('%.5F', $lon);
552
		if($lon && is_numeric($lon) && $lon >= -180 && $lon <= 180) return sprintf('%.5F', $lon);
553
		if($lat && is_numeric($lat) && $lat >= -90 && $lat <= 90) return sprintf('%.5F', $lat);
553
		if($lat && is_numeric($lat) && $lat >= -90 && $lat <= 90) return sprintf('%.5F', $lat);
554
 
554
 
555
		if($lon || $lat) {
555
		if($lon || $lat) {
556
			trigger_error("ligne \"{$ligne[C_NOM_SEL]}\": " .
556
			trigger_error("ligne \"{$ligne[C_NOM_SEL]}\": " .
557
						  "Attention: longitude ou latitude erronée",
557
						  "Attention: longitude ou latitude erronée",
558
						  E_USER_NOTICE);
558
						  E_USER_NOTICE);
559
		}
559
		}
560
		return NULL;
560
		return NULL;
561
 
561
 
562
		/* limite france métropole si bdtfx ? ou bdtxa ? ...
562
		/* limite france métropole si bdtfx ? ou bdtxa ? ...
563
		   NON!
563
		   NON!
564
		   Un taxon d'un référentiel donné peut être théoriquement observé n'importe où sur le globe.
564
		   Un taxon d'un référentiel donné peut être théoriquement observé n'importe où sur le globe.
565
		   Il n'y a pas lieu d'effectuer des restriction ici.
565
		   Il n'y a pas lieu d'effectuer des restriction ici.
566
		   Cependant des erreurs fréquentes (0,0 ou lon/lat inversées) peuvent être détectés ici.
566
		   Cependant des erreurs fréquentes (0,0 ou lon/lat inversées) peuvent être détectés ici.
567
		   TODO */
567
		   TODO */
568
		$bbox = self::getReferentielBBox($referentiel);
568
		$bbox = self::getReferentielBBox($referentiel);
569
		if(!$bbox) return NULL;
569
		if(!$bbox) return NULL;
570
 
570
 
571
		if($lon) {
571
		if($lon) {
572
			if($lon < $bbox['EST'] && $lon > $bbox['OUEST']) return is_numeric($lon) ? $lon : NULL;
572
			if($lon < $bbox['EST'] && $lon > $bbox['OUEST']) return is_numeric($lon) ? $lon : NULL;
573
			else return NULL;
573
			else return NULL;
574
		}
574
		}
575
		if($lat) {
575
		if($lat) {
576
			if($lat < $bbox['NORD'] && $lat > $bbox['SUD']) return is_numeric($lat) ? $lat : NULL;
576
			if($lat < $bbox['NORD'] && $lat > $bbox['SUD']) return is_numeric($lat) ? $lat : NULL;
577
			return NULL;
577
			return NULL;
578
		}
578
		}
579
	}
579
	}
580
 
580
 
581
 
581
 
582
	static function traiterEspece($ligne, Array &$espece, $cel) {
582
	static function traiterEspece($ligne, Array &$espece, $cel) {
583
		if(!$ligne[C_NOM_SEL]) return;
583
		if(!$ligne[C_NOM_SEL]) return;
-
 
584
 
-
 
585
		// nom_sel reste toujours celui de l'utilisateur
-
 
586
		$espece[C_NOM_SEL] = trim($ligne[C_NOM_SEL]);
584
 
587
 
585
		$taxon_info_webservice = new RechercheInfosTaxonBeta($cel->config);
588
		$taxon_info_webservice = new RechercheInfosTaxonBeta($cel->config);
586
 
589
 
587
		$ascii = iconv('UTF-8', 'ASCII//TRANSLIT', $ligne[C_NOM_SEL]);
590
		$ascii = iconv('UTF-8', 'ASCII//TRANSLIT', $ligne[C_NOM_SEL]);
-
 
591
 
588
		// FALSE = recherche étendue (LIKE x%)
592
		// TODO: si empty(C_NOM_SEL) et !empty(C_NOM_SEL_NN) : recherche info à partir de C_NOM_SEL_NN
589
		$resultat_recherche_espece = $taxon_info_webservice->rechercherInfosSurTexteCodeOuNumTax($ligne[C_NOM_SEL]);
593
		$resultat_recherche_espece = $taxon_info_webservice->rechercherInfosSurTexteCodeOuNumTax($ligne[C_NOM_SEL]);
590
 
594
 
591
		// on supprime les noms retenus et renvoi tel quel
595
		// on supprime les noms retenus et renvoi tel quel
592
		// on réutilise les define pour les noms d'indexes, tant qu'à faire
596
		// on réutilise les define pour les noms d'indexes, tant qu'à faire
593
		if (empty($resultat_recherche_espece['en_id_nom'])) {
597
		if (empty($resultat_recherche_espece['en_id_nom'])) {
594
			$espece[C_NOM_SEL] = trim($ligne[C_NOM_SEL]);
598
			// XXX; tout à NULL sauf C_NOM_SEL ci-dessus ?
595
 
-
 
596
			// le reste reste à NULL
-
 
597
			// TODO: si empty(C_NOM_SEL) et !empty(C_NOM_SEL_NN) : recherche info à partir de C_NOM_SEL_NN
-
 
598
			$espece[C_NOM_SEL_NN] = $ligne[C_NOM_SEL_NN];
599
			$espece[C_NOM_SEL_NN] = $ligne[C_NOM_SEL_NN];
599
			$espece[C_NOM_RET] = $ligne[C_NOM_RET];
600
			$espece[C_NOM_RET] = $ligne[C_NOM_RET];
600
			$espece[C_NOM_RET_NN] = $ligne[C_NOM_RET_NN];
601
			$espece[C_NOM_RET_NN] = $ligne[C_NOM_RET_NN];
601
			$espece[C_NT] = $ligne[C_NT];
602
			$espece[C_NT] = $ligne[C_NT];
602
			$espece[C_FAMILLE] = $ligne[C_FAMILLE];
603
			$espece[C_FAMILLE] = $ligne[C_FAMILLE];
603
 
604
 
604
			return;
605
			return;
605
		}
606
		}
606
 
607
 
607
		// succès de la détection, récupération des infos
-
 
608
		$espece[C_NOM_SEL] = $resultat_recherche_espece['nom_sel'];
608
		// succès de la détection = écrasement du numéro nomenclatural saisi...
609
		$espece[C_NOM_SEL_NN] = $resultat_recherche_espece['en_id_nom'];
-
 
-
 
609
		$espece[C_NOM_SEL_NN] = $resultat_recherche_espece['en_id_nom'];
610
 
610
		// et des info complémentaires
611
		$complement = $taxon_info_webservice->rechercherInformationsComplementairesSurNumNom($resultat_recherche_espece['en_id_nom']);
611
		$complement = $taxon_info_webservice->rechercherInformationsComplementairesSurNumNom($resultat_recherche_espece['en_id_nom']);
612
		$espece[C_NOM_RET] = $complement['Nom_Retenu'];
612
		$espece[C_NOM_RET] = $complement['Nom_Retenu'];
613
		$espece[C_NOM_RET_NN] = $complement['Num_Nom_Retenu'];
613
		$espece[C_NOM_RET_NN] = $complement['Num_Nom_Retenu'];
614
		$espece[C_NT] = $complement['Num_Taxon'];
614
		$espece[C_NT] = $complement['Num_Taxon'];
615
		$espece[C_FAMILLE] = $complement['Famille'];
615
		$espece[C_FAMILLE] = $complement['Famille'];
616
	}
616
	}
617
 
617
 
618
 
618
 
619
	static function traiterLocalisation($ligne, Array &$localisation, $cel) {
619
	static function traiterLocalisation($ligne, Array &$localisation, $cel) {
620
	    $identifiant_commune = trim($ligne[C_ZONE_GEO]);
620
	    $identifiant_commune = trim($ligne[C_ZONE_GEO]);
621
		if(!$identifiant_commune) {
621
		if(!$identifiant_commune) {
622
			$departement = trim($ligne[C_CE_ZONE_GEO]);
622
			$departement = trim($ligne[C_CE_ZONE_GEO]);
623
			goto testdepartement;
623
			goto testdepartement;
624
		}
624
		}
625
 
625
 
626
 
626
 
627
		$select = "SELECT DISTINCT nom, code  FROM cel_zones_geo";
627
		$select = "SELECT DISTINCT nom, code  FROM cel_zones_geo";
628
	
628
	
629
		if (preg_match('/(.*) \((\d+)\)/', $identifiant_commune, $elements)) {
629
		if (preg_match('/(.*) \((\d+)\)/', $identifiant_commune, $elements)) {
630
			// commune + departement : montpellier (34)
630
			// commune + departement : montpellier (34)
631
			$nom_commune=$elements[1];
631
			$nom_commune=$elements[1];
632
			$code_commune=$elements[2];
632
			$code_commune=$elements[2];
633
	 	    $requete = sprintf("%s WHERE nom = %s AND code LIKE %s",
633
	 	    $requete = sprintf("%s WHERE nom = %s AND code LIKE %s",
634
							   $select, $cel->quoteNonNull($nom_commune), $cel->quoteNonNull($code_commune.'%'));
634
							   $select, $cel->quoteNonNull($nom_commune), $cel->quoteNonNull($code_commune.'%'));
635
		}
635
		}
636
		elseif (preg_match('/^(\d+|(2[ab]\d+))$/i', $identifiant_commune, $elements)) {
636
		elseif (preg_match('/^(\d+|(2[ab]\d+))$/i', $identifiant_commune, $elements)) {
637
			// Code insee seul  
637
			// Code insee seul  
638
			$code_insee_commune=$elements[1];
638
			$code_insee_commune=$elements[1];
639
	 	    $requete = sprintf("%s WHERE code = %s", $select, $cel->quoteNonNull($code_insee_commune));
639
	 	    $requete = sprintf("%s WHERE code = %s", $select, $cel->quoteNonNull($code_insee_commune));
640
		}
640
		}
641
		else {
641
		else {
642
			// Commune seule (le departement sera recupere dans la colonne departement si elle est presente)
642
			// Commune seule (le departement sera recupere dans la colonne departement si elle est presente)
643
			// on prend le risque ici de retourner une mauvaise Commune
643
			// on prend le risque ici de retourner une mauvaise Commune
644
			$nom_commune = str_replace(" ", "%", iconv('UTF-8', 'ASCII//TRANSLIT', $identifiant_commune));
644
			$nom_commune = str_replace(" ", "%", iconv('UTF-8', 'ASCII//TRANSLIT', $identifiant_commune));
645
	 	    $requete = sprintf("%s WHERE nom LIKE %s", $select, $cel->quoteNonNull($nom_commune.'%'));
645
	 	    $requete = sprintf("%s WHERE nom LIKE %s", $select, $cel->quoteNonNull($nom_commune.'%'));
646
		}
646
		}
647
	
647
	
648
		$resultat_commune = $cel->requeter($requete);
648
		$resultat_commune = $cel->requeter($requete);
649
		// TODO: levenstein sort ?
649
		// TODO: levenstein sort ?
650
 
650
 
651
		// cas de la commune introuvable dans le référentiel
651
		// cas de la commune introuvable dans le référentiel
652
		// réinitialisation aux valeurs du fichier XLS
652
		// réinitialisation aux valeurs du fichier XLS
653
		if(! $resultat_commune) {
653
		if(! $resultat_commune) {
654
			$localisation[C_ZONE_GEO] = trim($ligne[C_ZONE_GEO]);
654
			$localisation[C_ZONE_GEO] = trim($ligne[C_ZONE_GEO]);
655
			$localisation[C_CE_ZONE_GEO] = trim($ligne[C_CE_ZONE_GEO]);
655
			$localisation[C_CE_ZONE_GEO] = trim($ligne[C_CE_ZONE_GEO]);
656
		} else {
656
		} else {
657
			$localisation[C_ZONE_GEO] = $resultat_commune[0]['nom'];
657
			$localisation[C_ZONE_GEO] = $resultat_commune[0]['nom'];
658
			$localisation[C_CE_ZONE_GEO] = $resultat_commune[0]['code'];
658
			$localisation[C_CE_ZONE_GEO] = $resultat_commune[0]['code'];
659
		}
659
		}
660
 
660
 
661
		$departement = &$localisation[C_CE_ZONE_GEO];
661
		$departement = &$localisation[C_CE_ZONE_GEO];
662
 
662
 
663
	testdepartement:
663
	testdepartement:
664
		if(strpos($departement, "INSEE-C:", 0) === 0) goto protectloc;
664
		if(strpos($departement, "INSEE-C:", 0) === 0) goto protectloc;
665
 
665
 
666
		if(!is_numeric($departement)) goto protectloc; // TODO ?
666
		if(!is_numeric($departement)) goto protectloc; // TODO ?
667
		if(strlen($departement) == 4) $departement = "INSEE-C:0" . $departement;
667
		if(strlen($departement) == 4) $departement = "INSEE-C:0" . $departement;
668
		if(strlen($departement) == 5) $departement = "INSEE-C:" . $departement;
668
		if(strlen($departement) == 5) $departement = "INSEE-C:" . $departement;
669
		// if(strlen($departement) <= 9) return "INSEE-C:0" . $departement; // ? ... TODO
669
		// if(strlen($departement) <= 9) return "INSEE-C:0" . $departement; // ? ... TODO
670
 
670
 
671
		$departement = trim($departement); // TODO
671
		$departement = trim($departement); // TODO
672
 
672
 
673
	protectloc:
673
	protectloc:
674
		$localisation[C_ZONE_GEO] = $localisation[C_ZONE_GEO];
674
		$localisation[C_ZONE_GEO] = $localisation[C_ZONE_GEO];
675
		$localisation[C_CE_ZONE_GEO] = $localisation[C_CE_ZONE_GEO];
675
		$localisation[C_CE_ZONE_GEO] = $localisation[C_CE_ZONE_GEO];
676
	}
676
	}
677
 
677
 
678
 
678
 
679
 
679
 
680
	/* HELPERS */
680
	/* HELPERS */
681
 
681
 
682
	// http://stackoverflow.com/questions/348410/sort-an-array-based-on-another-array
682
	// http://stackoverflow.com/questions/348410/sort-an-array-based-on-another-array
683
	static function sortArrayByArray($array, $orderArray) {
683
	static function sortArrayByArray($array, $orderArray) {
684
		$ordered = array();
684
		$ordered = array();
685
		foreach($orderArray as $key) {
685
		foreach($orderArray as $key) {
686
			if(array_key_exists($key, $array)) {
686
			if(array_key_exists($key, $array)) {
687
				$ordered[$key] = $array[$key];
687
				$ordered[$key] = $array[$key];
688
				unset($array[$key]);
688
				unset($array[$key]);
689
			}
689
			}
690
		}
690
		}
691
		return $ordered + $array;
691
		return $ordered + $array;
692
	}
692
	}
693
 
693
 
694
	// retourne une BBox [N,S,E,O) pour un référentiel donné
694
	// retourne une BBox [N,S,E,O) pour un référentiel donné
695
	static function getReferentielBBox($referentiel) {
695
	static function getReferentielBBox($referentiel) {
696
		if($referentiel == 'bdtfx:v1.01') return Array(
696
		if($referentiel == 'bdtfx:v1.01') return Array(
697
			'NORD' => 51.2, // Dunkerque
697
			'NORD' => 51.2, // Dunkerque
698
			'SUD' => 41.3, // Bonifacio
698
			'SUD' => 41.3, // Bonifacio
699
			'EST' => 9.7, // Corse
699
			'EST' => 9.7, // Corse
700
			'OUEST' => -5.2); // Ouessan
700
			'OUEST' => -5.2); // Ouessan
701
		return FALSE;
701
		return FALSE;
702
	}
702
	}
703
 
703
 
704
	// ces valeurs ne sont pas inséré via les placeholders du PDO::preparedStatement
704
	// ces valeurs ne sont pas inséré via les placeholders du PDO::preparedStatement
705
	// et doivent donc être échappées correctement.
705
	// et doivent donc être échappées correctement.
706
	public function initialiser_colonnes_statiques() {
706
	public function initialiser_colonnes_statiques() {
707
		$this->colonnes_statiques = array_merge($this->colonnes_statiques,
707
		$this->colonnes_statiques = array_merge($this->colonnes_statiques,
708
												Array(
708
												Array(
709
													"ce_utilisateur" => $this->id_utilisateur,
709
													"ce_utilisateur" => $this->id_utilisateur,
710
													"prenom_utilisateur" => $this->quoteNonNull($this->utilisateur['prenom']),
710
													"prenom_utilisateur" => $this->quoteNonNull($this->utilisateur['prenom']),
711
													"nom_utilisateur" => $this->quoteNonNull($this->utilisateur['nom']),
711
													"nom_utilisateur" => $this->quoteNonNull($this->utilisateur['nom']),
712
													"courriel_utilisateur" => $this->quoteNonNull($this->utilisateur['courriel']),
712
													"courriel_utilisateur" => $this->quoteNonNull($this->utilisateur['courriel']),
713
												));
713
												));
714
 
714
 
715
	}
715
	}
716
 
716
 
717
	static function initialiser_pdo_ordered_statements($colonnes_statiques) {
717
	static function initialiser_pdo_ordered_statements($colonnes_statiques) {
718
		return Array(
718
		return Array(
719
			// insert_ligne_pattern_ordre
719
			// insert_ligne_pattern_ordre
720
			sprintf('INSERT INTO cel_obs (%s, %s) VALUES',
720
			sprintf('INSERT INTO cel_obs (%s, %s) VALUES',
721
					implode(', ', array_keys($colonnes_statiques)),
721
					implode(', ', array_keys($colonnes_statiques)),
722
					implode(', ', array_diff(self::$ordre_BDD, array_keys($colonnes_statiques)))),
722
					implode(', ', array_diff(self::$ordre_BDD, array_keys($colonnes_statiques)))),
723
 
723
 
724
			// insert_ligne_pattern_ordre
724
			// insert_ligne_pattern_ordre
725
			sprintf('(%s, %s ?)',
725
			sprintf('(%s, %s ?)',
726
					implode(', ', $colonnes_statiques),
726
					implode(', ', $colonnes_statiques),
727
					str_repeat('?, ', count(self::$ordre_BDD) - count($colonnes_statiques) - 1))
727
					str_repeat('?, ', count(self::$ordre_BDD) - count($colonnes_statiques) - 1))
728
		);
728
		);
729
	}
729
	}
730
 
730
 
731
	static function initialiser_pdo_statements($colonnes_statiques) {
731
	static function initialiser_pdo_statements($colonnes_statiques) {
732
		return Array( 
732
		return Array( 
733
			// insert_prefix
733
			// insert_prefix
734
			sprintf('INSERT INTO cel_obs (%s) VALUES ',
734
			sprintf('INSERT INTO cel_obs (%s) VALUES ',
735
					implode(', ', self::$ordre_BDD)),
735
					implode(', ', self::$ordre_BDD)),
736
 
736
 
737
 
737
 
738
			// insert_ligne_pattern, cf: self::$insert_ligne_pattern
738
			// insert_ligne_pattern, cf: self::$insert_ligne_pattern
739
			'(' .
739
			'(' .
740
			// 3) créé une chaîne de liste de champ à inséré en DB
740
			// 3) créé une chaîne de liste de champ à inséré en DB
741
			implode(', ', array_values(
741
			implode(', ', array_values(
742
				// 2) garde les valeurs fixes (de $colonnes_statiques),
742
				// 2) garde les valeurs fixes (de $colonnes_statiques),
743
				// mais remplace les NULL par des "?"
743
				// mais remplace les NULL par des "?"
744
				array_map(function($item) { return is_null($item) ? '?' : $item; },
744
				array_map(function($item) { return is_null($item) ? '?' : $item; },
745
						  // 1) créé un tableau genre (nom_sel_nn => NULL) depuis self::$ordre_BDD
745
						  // 1) créé un tableau genre (nom_sel_nn => NULL) depuis self::$ordre_BDD
746
						  // et écrase certaines valeurs avec $colonnes_statiques (initilisé avec les données utilisateur)
746
						  // et écrase certaines valeurs avec $colonnes_statiques (initilisé avec les données utilisateur)
747
						  array_merge(array_map(function() { return NULL; }, array_flip(self::$ordre_BDD)), $colonnes_statiques
747
						  array_merge(array_map(function() { return NULL; }, array_flip(self::$ordre_BDD)), $colonnes_statiques
748
				)))) .
748
				)))) .
749
			')'
749
			')'
750
		);
750
		);
751
	}
751
	}
752
 
752
 
753
	// équivalent à CEL->Bdd->proteger() (qui wrap PDO::quote),
753
	// équivalent à CEL->Bdd->proteger() (qui wrap PDO::quote),
754
	// sans transformer NULL en ""
754
	// sans transformer NULL en ""
755
	private function quoteNonNull($chaine) {
755
	private function quoteNonNull($chaine) {
756
		if(is_null($chaine)) return "NULL";
756
		if(is_null($chaine)) return "NULL";
757
		if(!is_string($chaine)) die("erreur __FILE__, __LINE__");
757
		if(!is_string($chaine)) die("erreur __FILE__, __LINE__");
758
		return $this->bdd->quote($chaine);
758
		return $this->bdd->quote($chaine);
759
	}
759
	}
760
 
760
 
761
	public function erreurs_stock($errno, $errstr) {
761
	public function erreurs_stock($errno, $errstr) {
762
		$this->bilan[] = $errstr;
762
		$this->bilan[] = $errstr;
763
	}
763
	}
764
}
764
}