Subversion Repositories eFlore/Applications.cel

Rev

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

Rev 1649 Rev 1650
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
			//$this->bdd->commit();
-
 
272
			$dernier_autoinc = $this->bdd->lastInsertId();
271
			$dernier_autoinc = $this->bdd->lastInsertId();
273
			var_dump($dernier_autoinc);
272
			$this->bdd->commit();
274
			die;
-
 
275
 
-
 
-
 
273
 
276
 
274
			if(! $dernier_autoinc) trigger_error("l'insertion semble avoir échoué", E_USER_NOTICE);
277
 
275
 
278
			$obs_ajouts += count($enregistrements);
276
			$obs_ajouts += count($enregistrements);
279
			// $obs_ajouts += count($enregistrements['insert']);
277
			// $obs_ajouts += count($enregistrements['insert']);
280
			// $obs_maj += count($enregistrements['update']);
278
			// $obs_maj += count($enregistrements['update']);
281
			self::stockerImages($this, $enregistrements, $images, $dernier_autoinc);
279
			$nb_images_ajoutees = self::stockerImages($this, $enregistrements, $images, $dernier_autoinc);
282
		}
280
		}
283
 
281
 
284
		restore_error_handler();
282
		restore_error_handler();
285
 
283
 
286
		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)",
287
		// fin: renvoi summary
286
						   $obs_ajouts,
288
		die("$obs_ajouts observations ajoutées");
287
						   $nb_images_ajoutees);
-
 
288
 
-
 
289
		die("$summary");
289
	}
290
	}
290
 
291
 
291
	static function detectionEntete($entete) {
292
	static function detectionEntete($entete) {
292
		$colonnes_reconnues = Array();
293
		$colonnes_reconnues = Array();
293
		$cols = ExportXLS::nomEnsembleVersListeColonnes('standard');
294
		$cols = ExportXLS::nomEnsembleVersListeColonnes('standard');
294
		foreach($entete as $k => $v) {
295
		foreach($entete as $k => $v) {
295
			$entete_simple = iconv('UTF-8', 'ASCII//TRANSLIT', strtolower(trim($v)));
296
			$entete_simple = iconv('UTF-8', 'ASCII//TRANSLIT', strtolower(trim($v)));
296
			foreach($cols as $col) {
297
			foreach($cols as $col) {
297
				$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'])));
298
				$entete_officiel_abbrev = $col['abbrev'];
299
				$entete_officiel_abbrev = $col['abbrev'];
299
				if($entete_simple == $entete_officiel_simple || $entete_simple == $entete_officiel_abbrev) {
300
				if($entete_simple == $entete_officiel_simple || $entete_simple == $entete_officiel_abbrev) {
300
					// debug echo "define C_" . strtoupper($entete_officiel_abbrev) . ", $k ($v)\n";
301
					// debug echo "define C_" . strtoupper($entete_officiel_abbrev) . ", $k ($v)\n";
301
					define("C_" . strtoupper($entete_officiel_abbrev), $k);
302
					define("C_" . strtoupper($entete_officiel_abbrev), $k);
302
					$colonnes_reconnues[$k] = 1;
303
					$colonnes_reconnues[$k] = 1;
303
					break;
304
					break;
304
				}
305
				}
305
			}
306
			}
306
		}
307
		}
307
 
308
 
308
		// 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
309
 
310
 
310
		// 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 )
311
		// ==> Array( I => rien )
312
		// ==> Array( I => rien )
312
		$colonnesID_non_reconnues = array_diff_key($entete, $colonnes_reconnues);
313
		$colonnesID_non_reconnues = array_diff_key($entete, $colonnes_reconnues);
313
 
314
 
314
		// des colonnes de ExportXLS::nomEnsembleVersListeColonnes()
315
		// des colonnes de ExportXLS::nomEnsembleVersListeColonnes()
315
		// ne retient que celles marquées "importables"
316
		// ne retient que celles marquées "importables"
316
		$colonnes_automatiques = array_filter($cols, function($v) {	return !$v['importable']; });
317
		$colonnes_automatiques = array_filter($cols, function($v) {	return !$v['importable']; });
317
 
318
 
318
		// 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
319
		array_walk($colonnes_automatiques, function(&$v) {	$v = $v['nom']; });
320
		array_walk($colonnes_automatiques, function(&$v) {	$v = $v['nom']; });
320
 
321
 
321
		// 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 ) )
322
		// ==> Array ( S => Ordre, AA => Phénologie )
323
		// ==> Array ( S => Ordre, AA => Phénologie )
323
		$colonnesID_a_exclure = array_intersect($entete, $colonnes_automatiques);
324
		$colonnesID_a_exclure = array_intersect($entete, $colonnes_automatiques);
324
 
325
 
325
		// TODO: pourquoi ne pas comparer avec les abbrevs aussi ?
326
		// TODO: pourquoi ne pas comparer avec les abbrevs aussi ?
326
		// merge ( Array( I => rien ) , Array ( S => Ordre, AA => Phénologie ) )
327
		// merge ( Array( I => rien ) , Array ( S => Ordre, AA => Phénologie ) )
327
		// ==> Array ( I => rien, AA => Phénologie )
328
		// ==> Array ( I => rien, AA => Phénologie )
328
		return array_merge($colonnesID_non_reconnues, $colonnesID_a_exclure);
329
		return array_merge($colonnesID_non_reconnues, $colonnesID_a_exclure);
329
	}
330
	}
330
 
331
 
331
	/*
332
	/*
332
	 * charge un groupe de lignes
333
	 * charge un groupe de lignes
333
	 */
334
	 */
334
	static function chargerLignes($cel, $lignes, $colonnes_statiques, &$dernier_ordre) {
335
	static function chargerLignes($cel, $lignes, $colonnes_statiques, &$dernier_ordre) {
335
		$enregistrement = NULL;
336
		$enregistrement = NULL;
336
		$enregistrements = Array();
337
		$enregistrements = Array();
337
		$toutes_images = Array();
338
		$toutes_images = Array();
338
 
339
 
339
		foreach($lignes as $ligne) {
340
		foreach($lignes as $ligne) {
340
			//$ligne = array_filter($ligne, function($cell) { return !is_null($cell); });
341
			//$ligne = array_filter($ligne, function($cell) { return !is_null($cell); });
341
			//if(!$ligne) continue;
342
			//if(!$ligne) continue;
342
			// 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
343
			if(! array_filter($ligne, function($cell) { return !is_null($cell); })) continue;
344
			if(! array_filter($ligne, function($cell) { return !is_null($cell); })) continue;
344
 
345
 
345
			if( ($enregistrement = self::chargerLigne($ligne, $dernier_ordre, $cel)) ) {
346
			if( ($enregistrement = self::chargerLigne($ligne, $dernier_ordre, $cel)) ) {
346
				// $enregistrements[] = array_merge($colonnes_statiques, $enregistrement);
347
				// $enregistrements[] = array_merge($colonnes_statiques, $enregistrement);
347
				$enregistrements[] = $enregistrement;
348
				$enregistrements[] = $enregistrement;
348
 
349
 
349
				if(isset($enregistrement['_images'])) {
350
				if(isset($enregistrement['_images'])) {
350
					$pos = count($enregistrements) - 1;
351
					$pos = count($enregistrements) - 1;
351
					$last = &$enregistrements[$pos];
352
					$last = &$enregistrements[$pos];
352
					// 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
353
					// 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
354
					$toutes_images[] = Array("images" => $last['_images'],
355
					$toutes_images[] = Array("images" => $last['_images'],
355
											 "obs_pos" => $pos);
356
											 "obs_pos" => $pos);
356
					// 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,
357
					// mais est utile pour cel_obs_images
358
					// mais est utile pour cel_obs_images
358
					unset($last['_images']);
359
					unset($last['_images']);
359
				}
360
				}
360
 
361
 
361
				$dernier_ordre++;
362
				$dernier_ordre++;
362
			}
363
			}
363
		}
364
		}
364
 
365
 
365
		// 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);
366
		return Array($enregistrements, $toutes_images);
367
		return Array($enregistrements, $toutes_images);
367
	}
368
	}
368
 
369
 
369
 
370
 
370
	static function trierColonnes(&$enregistrements) {
371
	static function trierColonnes(&$enregistrements) {
371
		foreach($enregistrements as &$enregistrement) {
372
		foreach($enregistrements as &$enregistrement) {
372
			$enregistrement = self::sortArrayByArray($enregistrement, self::$ordre_BDD);
373
			$enregistrement = self::sortArrayByArray($enregistrement, self::$ordre_BDD);
373
			//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; });
374
			//$req .= implode(', ', $enregistrement) . "\n";
375
			//$req .= implode(', ', $enregistrement) . "\n";
375
		}
376
		}
376
	}
377
	}
377
 
378
 
378
 
379
 
379
	static function stockerImages($cel, $enregistrements, $toutes_images, $lastid) {
380
	static function stockerImages($cel, $enregistrements, $toutes_images, $lastid) {
380
		if(! $lastid) $lastid = rand();
-
 
381
 
-
 
382
		$images_insert =
-
 
383
			'INSERT INTO cel_obs_images (id_image, id_observation) VALUES'
381
		$images_insert = 'INSERT INTO cel_obs_images (id_image, id_observation) VALUES %s ON DUPLICATE KEY UPDATE id_image = id_image';
384
			.' %s '
-
 
385
			.'ON DUPLICATE KEY UPDATE id_image = id_image';
-
 
386
		$images_obs_assoc = Array();
382
		$images_obs_assoc = Array();
387
 
383
 
388
		foreach($toutes_images as $images_pour_obs) {
384
		foreach($toutes_images as $images_pour_obs) {
389
			$obs = $enregistrements[$images_pour_obs["obs_pos"]];
385
			$obs = $enregistrements[$images_pour_obs["obs_pos"]];
390
			$id_obs = $lastid // dernier autoinc inséré
386
			$id_obs = $lastid // dernier autoinc inséré
391
				- count($enregistrements) - 1 // correspondrait au premier autoinc
387
				- count($enregistrements) + 1 // correspondrait au premier autoinc
392
				+ $images_pour_obs["obs_pos"]; // ordre d'insertion = ordre dans le tableau $enregistrements
388
				+ $images_pour_obs["obs_pos"]; // ordre d'insertion = ordre dans le tableau $enregistrements (commence à 0)
393
			foreach($images_pour_obs['images'] as $image) {
389
			foreach($images_pour_obs['images'] as $image) {
394
				$images_obs_assoc[] = sprintf('(%d,%d)',
390
				$images_obs_assoc[] = sprintf('(%d,%d)',
395
											  $image['id_image'], // intval() useless
391
											  $image['id_image'], // intval() useless
396
											  $id_obs); // intval() useless
392
											  $id_obs); // intval() useless
397
			}
393
			}
398
		}
394
		}
399
 
395
 
400
		if($images_obs_assoc) {
396
		if($images_obs_assoc) {
401
			$requete = sprintf($images_insert, implode(', ', $images_obs_assoc));
397
			$requete = sprintf($images_insert, implode(', ', $images_obs_assoc));
402
			echo "$requete\n";
398
			// debug echo "$requete\n";
-
 
399
			$cel->requeter($requete);
403
		}
400
		}
-
 
401
 
-
 
402
		return count($images_obs_assoc);
404
	}
403
	}
405
 
404
 
406
	/*
405
	/*
407
	  Aucune des valeurs présentes dans $enregistrement n'est quotée
406
	  Aucune des valeurs présentes dans $enregistrement n'est quotée
408
	  cad aucune des valeurs retournée par traiter{Espece|Localisation}()
407
	  cad aucune des valeurs retournée par traiter{Espece|Localisation}()
409
	  car ce tableau est passé à un PDO::preparedStatement() qui applique
408
	  car ce tableau est passé à un PDO::preparedStatement() qui applique
410
	  proprement les règle d'échappement.
409
	  proprement les règle d'échappement.
411
	 */
410
	 */
412
	static function chargerLigne($ligne, $dernier_ordre, $cel) {
411
	static function chargerLigne($ligne, $dernier_ordre, $cel) {
413
		// en premier car le résultat est utile pour
412
		// en premier car le résultat est utile pour
414
		// traiter longitude et latitude (traiterLonLat())
413
		// traiter longitude et latitude (traiterLonLat())
415
		$referentiel = self::identReferentiel($ligne[C_NOM_REFERENTIEL]);
414
		$referentiel = self::identReferentiel($ligne[C_NOM_REFERENTIEL]);
416
 
415
 
417
		// $espece est rempli de plusieurs informations
416
		// $espece est rempli de plusieurs informations
418
		$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,
419
						C_NOM_RET_NN => NULL, C_NT => NULL, C_FAMILLE => NULL);
418
						C_NOM_RET_NN => NULL, C_NT => NULL, C_FAMILLE => NULL);
420
		self::traiterEspece($ligne, $espece, $cel);
419
		self::traiterEspece($ligne, $espece, $cel);
421
 
420
 
422
		// $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
423
		$localisation = Array(C_ZONE_GEO => NULL, C_CE_ZONE_GEO => NULL);
422
		$localisation = Array(C_ZONE_GEO => NULL, C_CE_ZONE_GEO => NULL);
424
		self::traiterLocalisation($ligne, $localisation, $cel);
423
		self::traiterLocalisation($ligne, $localisation, $cel);
425
 
424
 
426
		// $transmission est utilisé pour date_transmission
425
		// $transmission est utilisé pour date_transmission
427
		$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;
428
 
427
 
429
 
428
 
430
		// 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.
431
		// 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())
432
		$enregistrement = Array(
431
		$enregistrement = Array(
433
			"ordre" => $dernier_ordre,
432
			"ordre" => $dernier_ordre,
434
 
433
 
435
			"nom_sel" => $espece[C_NOM_SEL],
434
			"nom_sel" => $espece[C_NOM_SEL],
436
			"nom_sel_nn" => $espece[C_NOM_SEL_NN],
435
			"nom_sel_nn" => $espece[C_NOM_SEL_NN],
437
			"nom_ret" => $espece[C_NOM_RET],
436
			"nom_ret" => $espece[C_NOM_RET],
438
			"nom_ret_nn" => $espece[C_NOM_RET_NN],
437
			"nom_ret_nn" => $espece[C_NOM_RET_NN],
439
			"nt" => $espece[C_NT],
438
			"nt" => $espece[C_NT],
440
			"famille" => $espece[C_FAMILLE],
439
			"famille" => $espece[C_FAMILLE],
441
 
440
 
442
			"nom_referentiel" => $referentiel,
441
			"nom_referentiel" => $referentiel,
443
 
442
 
444
			"zone_geo" => $localisation[C_ZONE_GEO],
443
			"zone_geo" => $localisation[C_ZONE_GEO],
445
			"ce_zone_geo" => $localisation[C_CE_ZONE_GEO],
444
			"ce_zone_geo" => $localisation[C_CE_ZONE_GEO],
446
 
445
 
447
			// $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)
448
			"date_observation" => self::traiterDateObs($ligne[C_DATE_OBSERVATION], $ligne),
447
			"date_observation" => self::traiterDateObs($ligne[C_DATE_OBSERVATION], $ligne),
449
 
448
 
450
			"lieudit" => trim($ligne[C_LIEUDIT]),
449
			"lieudit" => trim($ligne[C_LIEUDIT]),
451
			"station" => trim($ligne[C_STATION]),
450
			"station" => trim($ligne[C_STATION]),
452
			"milieu" => trim($ligne[C_MILIEU]),
451
			"milieu" => trim($ligne[C_MILIEU]),
453
 
452
 
454
			"mots_cles_texte" => NULL, // TODO: foreign-key
453
			"mots_cles_texte" => NULL, // TODO: foreign-key
455
			"commentaire" => trim($ligne[C_COMMENTAIRE]),
454
			"commentaire" => trim($ligne[C_COMMENTAIRE]),
456
 
455
 
457
			"transmission" => $transmission,
456
			"transmission" => $transmission,
458
			"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()
459
 
458
 
460
			// $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)
461
			"latitude" => self::traiterLonLat(NULL, $ligne[C_LATITUDE], $referentiel, $ligne),
460
			"latitude" => self::traiterLonLat(NULL, $ligne[C_LATITUDE], $referentiel, $ligne),
462
			"longitude" => self::traiterLonLat($ligne[C_LONGITUDE], NULL, $referentiel, $ligne),
461
			"longitude" => self::traiterLonLat($ligne[C_LONGITUDE], NULL, $referentiel, $ligne),
463
 
462
 
464
			// @ 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é)
465
			"abondance" => @$ligne[C_ABONDANCE],
464
			"abondance" => @$ligne[C_ABONDANCE],
466
			"certitude" => @$ligne[C_CERTITUDE],
465
			"certitude" => @$ligne[C_CERTITUDE],
467
			"phenologie" => @$ligne[C_PHENOLOGIE],
466
			"phenologie" => @$ligne[C_PHENOLOGIE],
468
 
467
 
469
			"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)
470
		);
469
		);
471
 
470
 
472
		// 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
473
		// que si des résultats sont trouvés
472
		// que si des résultats sont trouvés
474
		// "@" 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)
475
		if(@$ligne[C_IMAGES]) self::traiterImage($ligne[C_IMAGES], $cel, $enregistrement);
474
		if(@$ligne[C_IMAGES]) self::traiterImage($ligne[C_IMAGES], $cel, $enregistrement);
476
 
475
 
477
		return $enregistrement;
476
		return $enregistrement;
478
	}
477
	}
479
 
478
 
480
	static function traiterImage($str, $cel, &$enregistrement) {
479
	static function traiterImage($str, $cel, &$enregistrement) {
481
		$liste_images = array_filter(explode("/", $str));
480
		$liste_images = array_filter(explode("/", $str));
482
		array_walk($liste_images,
481
		array_walk($liste_images,
483
				   function($item, $key, $obj) { $item = $obj->quoteNonNull(trim($item)); },
482
				   function($item, $key, $obj) { $item = $obj->quoteNonNull(trim($item)); },
484
				   $cel);
483
				   $cel);
485
		$requete = sprintf(
484
		$requete = sprintf(
486
			"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\")",
487
			$cel->id_utilisateur,
486
			$cel->id_utilisateur,
488
			implode('","', $liste_images));
487
			implode('","', $liste_images));
489
 
488
 
490
		$resultat = $cel->requeter($requete);
489
		$resultat = $cel->requeter($requete);
491
 
490
 
492
		if($resultat) $enregistrement['_images'] = $resultat;
491
		if($resultat) $enregistrement['_images'] = $resultat;
493
	}
492
	}
494
 
493
 
495
 
494
 
496
	/* FONCTIONS de TRANSFORMATION de VALEUR DE CELLULE */
495
	/* FONCTIONS de TRANSFORMATION de VALEUR DE CELLULE */
497
 
496
 
498
	// TODO: PHP 5.3, utiliser date_parse_from_format()
497
	// TODO: PHP 5.3, utiliser date_parse_from_format()
499
	// TODO: parser les heures (cf product-owner)
498
	// TODO: parser les heures (cf product-owner)
500
	// TODO: passer par le timestamp pour s'assurer de la validité
499
	// TODO: passer par le timestamp pour s'assurer de la validité
501
	static function traiterDateObs($date, $ligne) {
500
	static function traiterDateObs($date, $ligne) {
502
		// TODO: see https://github.com/PHPOffice/PHPExcel/issues/208
501
		// TODO: see https://github.com/PHPOffice/PHPExcel/issues/208
503
		if(is_double($date)) {
502
		if(is_double($date)) {
504
			if($date > 0)
503
			if($date > 0)
505
				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";
506
			trigger_error("ligne \"{$ligne[C_NOM_SEL]}\": " .
505
			trigger_error("ligne \"{$ligne[C_NOM_SEL]}\": " .
507
						  "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",
508
						  E_USER_NOTICE);
507
						  E_USER_NOTICE);
509
			
508
			
510
			// 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");
511
 
510
 
512
			// attention, UNIX timestamp, car Excel les décompte depuis 1900
511
			// attention, UNIX timestamp, car Excel les décompte depuis 1900
513
			// 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
514
			// $timestamp = ($date - MIN_DATES_DIFF) * 60 * 60 * 24 - time(); // NON
513
			// $timestamp = ($date - MIN_DATES_DIFF) * 60 * 60 * 24 - time(); // NON
515
 
514
 
516
			// $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
517
 
516
 
518
			// echo strftime("%Y/%m/%d 00:00:00", $timestamp); // NON
517
			// echo strftime("%Y/%m/%d 00:00:00", $timestamp); // NON
519
		}
518
		}
520
		else {
519
		else {
521
			$timestamp = strtotime($date);
520
			$timestamp = strtotime($date);
522
			if(! $timestamp) {
521
			if(! $timestamp) {
523
				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);
524
				return NULL;
523
				return NULL;
525
			}
524
			}
526
			return strftime("%Y-%m-%d 00:00:00", strtotime($date));
525
			return strftime("%Y-%m-%d 00:00:00", strtotime($date));
527
		}
526
		}
528
	}
527
	}
529
 
528
 
530
	static function identReferentiel($referentiel) {
529
	static function identReferentiel($referentiel) {
531
		// 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;
532
		if(strpos(strtolower($referentiel), 'bdtfx') !== FALSE) return 'bdtfx:v1.01';
531
		if(strpos(strtolower($referentiel), 'bdtfx') !== FALSE) return 'bdtfx:v1.01';
533
		if(strpos(strtolower($referentiel), 'bdtxa') !== FALSE) return 'bdtxa:v1.00';
532
		if(strpos(strtolower($referentiel), 'bdtxa') !== FALSE) return 'bdtxa:v1.00';
534
		if(strpos(strtolower($referentiel), 'bdnff') !== FALSE) return 'bdnff:4.02';
533
		if(strpos(strtolower($referentiel), 'bdnff') !== FALSE) return 'bdnff:4.02';
535
		if(strpos(strtolower($referentiel), 'isfan') !== FALSE) return 'isfan:v1.00';
534
		if(strpos(strtolower($referentiel), 'isfan') !== FALSE) return 'isfan:v1.00';
536
 
535
 
537
		if($referentiel) {
536
		if($referentiel) {
538
			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);
539
		}
538
		}
540
		return NULL;
539
		return NULL;
541
		/* TODO: cf story,
540
		/* TODO: cf story,
542
		   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
543
		   + accepter les n° de version */
542
		   + accepter les n° de version */
544
	}
543
	}
545
 
544
 
546
	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) {
547
		// 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)
548
		if($lon && is_string($lon)) $lon = str_replace(',', '.', $lon);
547
		if($lon && is_string($lon)) $lon = str_replace(',', '.', $lon);
549
		if($lat && is_string($lat)) $lat = str_replace(',', '.', $lat);
548
		if($lat && is_string($lat)) $lat = str_replace(',', '.', $lat);
550
 
549
 
551
		// 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)
552
		// 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 ".")
553
		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);
554
		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);
555
 
554
 
556
		if($lon || $lat) {
555
		if($lon || $lat) {
557
			trigger_error("ligne \"{$ligne[C_NOM_SEL]}\": " .
556
			trigger_error("ligne \"{$ligne[C_NOM_SEL]}\": " .
558
						  "Attention: longitude ou latitude erronée",
557
						  "Attention: longitude ou latitude erronée",
559
						  E_USER_NOTICE);
558
						  E_USER_NOTICE);
560
		}
559
		}
561
		return NULL;
560
		return NULL;
562
 
561
 
563
		/* limite france métropole si bdtfx ? ou bdtxa ? ...
562
		/* limite france métropole si bdtfx ? ou bdtxa ? ...
564
		   NON!
563
		   NON!
565
		   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.
566
		   Il n'y a pas lieu d'effectuer des restriction ici.
565
		   Il n'y a pas lieu d'effectuer des restriction ici.
567
		   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.
568
		   TODO */
567
		   TODO */
569
		$bbox = self::getReferentielBBox($referentiel);
568
		$bbox = self::getReferentielBBox($referentiel);
570
		if(!$bbox) return NULL;
569
		if(!$bbox) return NULL;
571
 
570
 
572
		if($lon) {
571
		if($lon) {
573
			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;
574
			else return NULL;
573
			else return NULL;
575
		}
574
		}
576
		if($lat) {
575
		if($lat) {
577
			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;
578
			return NULL;
577
			return NULL;
579
		}
578
		}
580
	}
579
	}
581
 
580
 
582
 
581
 
583
	static function traiterEspece($ligne, Array &$espece, $cel) {
582
	static function traiterEspece($ligne, Array &$espece, $cel) {
584
		if(!$ligne[C_NOM_SEL]) return;
583
		if(!$ligne[C_NOM_SEL]) return;
585
 
584
 
586
		$taxon_info_webservice = new RechercheInfosTaxonBeta($cel->config);
585
		$taxon_info_webservice = new RechercheInfosTaxonBeta($cel->config);
587
 
586
 
588
		$ascii = iconv('UTF-8', 'ASCII//TRANSLIT', $ligne[C_NOM_SEL]);
587
		$ascii = iconv('UTF-8', 'ASCII//TRANSLIT', $ligne[C_NOM_SEL]);
589
		// FALSE = recherche étendue (LIKE x%)
588
		// FALSE = recherche étendue (LIKE x%)
590
		$resultat_recherche_espece = $taxon_info_webservice->rechercherInfosSurTexteCodeOuNumTax($ligne[C_NOM_SEL]);
589
		$resultat_recherche_espece = $taxon_info_webservice->rechercherInfosSurTexteCodeOuNumTax($ligne[C_NOM_SEL]);
591
 
590
 
592
		// on supprime les noms retenus et renvoi tel quel
591
		// on supprime les noms retenus et renvoi tel quel
593
		// on réutilise les define pour les noms d'indexes, tant qu'à faire
592
		// on réutilise les define pour les noms d'indexes, tant qu'à faire
594
		if (empty($resultat_recherche_espece['en_id_nom'])) {
593
		if (empty($resultat_recherche_espece['en_id_nom'])) {
595
			$espece[C_NOM_SEL] = trim($ligne[C_NOM_SEL]);
594
			$espece[C_NOM_SEL] = trim($ligne[C_NOM_SEL]);
596
 
595
 
597
			// le reste reste à NULL
596
			// le reste reste à NULL
598
			// TODO: si empty(C_NOM_SEL) et !empty(C_NOM_SEL_NN) : recherche info à partir de C_NOM_SEL_NN
597
			// TODO: si empty(C_NOM_SEL) et !empty(C_NOM_SEL_NN) : recherche info à partir de C_NOM_SEL_NN
599
			$espece[C_NOM_SEL_NN] = $ligne[C_NOM_SEL_NN];
598
			$espece[C_NOM_SEL_NN] = $ligne[C_NOM_SEL_NN];
600
			$espece[C_NOM_RET] = $ligne[C_NOM_RET];
599
			$espece[C_NOM_RET] = $ligne[C_NOM_RET];
601
			$espece[C_NOM_RET_NN] = $ligne[C_NOM_RET_NN];
600
			$espece[C_NOM_RET_NN] = $ligne[C_NOM_RET_NN];
602
			$espece[C_NT] = $ligne[C_NT];
601
			$espece[C_NT] = $ligne[C_NT];
603
			$espece[C_FAMILLE] = $ligne[C_FAMILLE];
602
			$espece[C_FAMILLE] = $ligne[C_FAMILLE];
604
 
603
 
605
			return;
604
			return;
606
		}
605
		}
607
 
606
 
608
		// succès de la détection, récupération des infos
607
		// succès de la détection, récupération des infos
609
		$espece[C_NOM_SEL] = $resultat_recherche_espece['nom_sel'];
608
		$espece[C_NOM_SEL] = $resultat_recherche_espece['nom_sel'];
610
		$espece[C_NOM_SEL_NN] = $resultat_recherche_espece['en_id_nom'];
609
		$espece[C_NOM_SEL_NN] = $resultat_recherche_espece['en_id_nom'];
611
 
610
 
612
		$complement = $taxon_info_webservice->rechercherInformationsComplementairesSurNumNom($resultat_recherche_espece['en_id_nom']);
611
		$complement = $taxon_info_webservice->rechercherInformationsComplementairesSurNumNom($resultat_recherche_espece['en_id_nom']);
613
		$espece[C_NOM_RET] = $complement['Nom_Retenu'];
612
		$espece[C_NOM_RET] = $complement['Nom_Retenu'];
614
		$espece[C_NOM_RET_NN] = $complement['Num_Nom_Retenu'];
613
		$espece[C_NOM_RET_NN] = $complement['Num_Nom_Retenu'];
615
		$espece[C_NT] = $complement['Num_Taxon'];
614
		$espece[C_NT] = $complement['Num_Taxon'];
616
		$espece[C_FAMILLE] = $complement['Famille'];
615
		$espece[C_FAMILLE] = $complement['Famille'];
617
	}
616
	}
618
 
617
 
619
 
618
 
620
	static function traiterLocalisation($ligne, Array &$localisation, $cel) {
619
	static function traiterLocalisation($ligne, Array &$localisation, $cel) {
621
	    $identifiant_commune = trim($ligne[C_ZONE_GEO]);
620
	    $identifiant_commune = trim($ligne[C_ZONE_GEO]);
622
		if(!$identifiant_commune) {
621
		if(!$identifiant_commune) {
623
			$departement = trim($ligne[C_CE_ZONE_GEO]);
622
			$departement = trim($ligne[C_CE_ZONE_GEO]);
624
			goto testdepartement;
623
			goto testdepartement;
625
		}
624
		}
626
 
625
 
627
 
626
 
628
		$select = "SELECT DISTINCT nom, code  FROM cel_zones_geo";
627
		$select = "SELECT DISTINCT nom, code  FROM cel_zones_geo";
629
	
628
	
630
		if (preg_match('/(.*) \((\d+)\)/', $identifiant_commune, $elements)) {
629
		if (preg_match('/(.*) \((\d+)\)/', $identifiant_commune, $elements)) {
631
			// commune + departement : montpellier (34)
630
			// commune + departement : montpellier (34)
632
			$nom_commune=$elements[1];
631
			$nom_commune=$elements[1];
633
			$code_commune=$elements[2];
632
			$code_commune=$elements[2];
634
	 	    $requete = sprintf("%s WHERE nom = %s AND code LIKE %s",
633
	 	    $requete = sprintf("%s WHERE nom = %s AND code LIKE %s",
635
							   $select, $cel->quoteNonNull($nom_commune), $cel->quoteNonNull($code_commune.'%'));
634
							   $select, $cel->quoteNonNull($nom_commune), $cel->quoteNonNull($code_commune.'%'));
636
		}
635
		}
637
		elseif (preg_match('/^(\d+|(2[ab]\d+))$/i', $identifiant_commune, $elements)) {
636
		elseif (preg_match('/^(\d+|(2[ab]\d+))$/i', $identifiant_commune, $elements)) {
638
			// Code insee seul  
637
			// Code insee seul  
639
			$code_insee_commune=$elements[1];
638
			$code_insee_commune=$elements[1];
640
	 	    $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));
641
		}
640
		}
642
		else {
641
		else {
643
			// 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)
644
			// on prend le risque ici de retourner une mauvaise Commune
643
			// on prend le risque ici de retourner une mauvaise Commune
645
			$nom_commune = str_replace(" ", "%", iconv('UTF-8', 'ASCII//TRANSLIT', $identifiant_commune));
644
			$nom_commune = str_replace(" ", "%", iconv('UTF-8', 'ASCII//TRANSLIT', $identifiant_commune));
646
	 	    $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.'%'));
647
		}
646
		}
648
	
647
	
649
		$resultat_commune = $cel->requeter($requete);
648
		$resultat_commune = $cel->requeter($requete);
650
		// TODO: levenstein sort ?
649
		// TODO: levenstein sort ?
651
 
650
 
652
		// cas de la commune introuvable dans le référentiel
651
		// cas de la commune introuvable dans le référentiel
653
		// réinitialisation aux valeurs du fichier XLS
652
		// réinitialisation aux valeurs du fichier XLS
654
		if(! $resultat_commune) {
653
		if(! $resultat_commune) {
655
			$localisation[C_ZONE_GEO] = trim($ligne[C_ZONE_GEO]);
654
			$localisation[C_ZONE_GEO] = trim($ligne[C_ZONE_GEO]);
656
			$localisation[C_CE_ZONE_GEO] = trim($ligne[C_CE_ZONE_GEO]);
655
			$localisation[C_CE_ZONE_GEO] = trim($ligne[C_CE_ZONE_GEO]);
657
		} else {
656
		} else {
658
			$localisation[C_ZONE_GEO] = $resultat_commune[0]['nom'];
657
			$localisation[C_ZONE_GEO] = $resultat_commune[0]['nom'];
659
			$localisation[C_CE_ZONE_GEO] = $resultat_commune[0]['code'];
658
			$localisation[C_CE_ZONE_GEO] = $resultat_commune[0]['code'];
660
		}
659
		}
661
 
660
 
662
		$departement = &$localisation[C_CE_ZONE_GEO];
661
		$departement = &$localisation[C_CE_ZONE_GEO];
663
 
662
 
664
	testdepartement:
663
	testdepartement:
665
		if(strpos($departement, "INSEE-C:", 0) === 0) goto protectloc;
664
		if(strpos($departement, "INSEE-C:", 0) === 0) goto protectloc;
666
 
665
 
667
		if(!is_numeric($departement)) goto protectloc; // TODO ?
666
		if(!is_numeric($departement)) goto protectloc; // TODO ?
668
		if(strlen($departement) == 4) $departement = "INSEE-C:0" . $departement;
667
		if(strlen($departement) == 4) $departement = "INSEE-C:0" . $departement;
669
		if(strlen($departement) == 5) $departement = "INSEE-C:" . $departement;
668
		if(strlen($departement) == 5) $departement = "INSEE-C:" . $departement;
670
		// if(strlen($departement) <= 9) return "INSEE-C:0" . $departement; // ? ... TODO
669
		// if(strlen($departement) <= 9) return "INSEE-C:0" . $departement; // ? ... TODO
671
 
670
 
672
		$departement = trim($departement); // TODO
671
		$departement = trim($departement); // TODO
673
 
672
 
674
	protectloc:
673
	protectloc:
675
		$localisation[C_ZONE_GEO] = $localisation[C_ZONE_GEO];
674
		$localisation[C_ZONE_GEO] = $localisation[C_ZONE_GEO];
676
		$localisation[C_CE_ZONE_GEO] = $localisation[C_CE_ZONE_GEO];
675
		$localisation[C_CE_ZONE_GEO] = $localisation[C_CE_ZONE_GEO];
677
	}
676
	}
678
 
677
 
679
 
678
 
680
 
679
 
681
	/* HELPERS */
680
	/* HELPERS */
682
 
681
 
683
	// 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
684
	static function sortArrayByArray($array, $orderArray) {
683
	static function sortArrayByArray($array, $orderArray) {
685
		$ordered = array();
684
		$ordered = array();
686
		foreach($orderArray as $key) {
685
		foreach($orderArray as $key) {
687
			if(array_key_exists($key, $array)) {
686
			if(array_key_exists($key, $array)) {
688
				$ordered[$key] = $array[$key];
687
				$ordered[$key] = $array[$key];
689
				unset($array[$key]);
688
				unset($array[$key]);
690
			}
689
			}
691
		}
690
		}
692
		return $ordered + $array;
691
		return $ordered + $array;
693
	}
692
	}
694
 
693
 
695
	// 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é
696
	static function getReferentielBBox($referentiel) {
695
	static function getReferentielBBox($referentiel) {
697
		if($referentiel == 'bdtfx:v1.01') return Array(
696
		if($referentiel == 'bdtfx:v1.01') return Array(
698
			'NORD' => 51.2, // Dunkerque
697
			'NORD' => 51.2, // Dunkerque
699
			'SUD' => 41.3, // Bonifacio
698
			'SUD' => 41.3, // Bonifacio
700
			'EST' => 9.7, // Corse
699
			'EST' => 9.7, // Corse
701
			'OUEST' => -5.2); // Ouessan
700
			'OUEST' => -5.2); // Ouessan
702
		return FALSE;
701
		return FALSE;
703
	}
702
	}
704
 
703
 
705
	// 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
706
	// et doivent donc être échappées correctement.
705
	// et doivent donc être échappées correctement.
707
	public function initialiser_colonnes_statiques() {
706
	public function initialiser_colonnes_statiques() {
708
		$this->colonnes_statiques = array_merge($this->colonnes_statiques,
707
		$this->colonnes_statiques = array_merge($this->colonnes_statiques,
709
												Array(
708
												Array(
710
													"ce_utilisateur" => $this->id_utilisateur,
709
													"ce_utilisateur" => $this->id_utilisateur,
711
													"prenom_utilisateur" => $this->quoteNonNull($this->utilisateur['prenom']),
710
													"prenom_utilisateur" => $this->quoteNonNull($this->utilisateur['prenom']),
712
													"nom_utilisateur" => $this->quoteNonNull($this->utilisateur['nom']),
711
													"nom_utilisateur" => $this->quoteNonNull($this->utilisateur['nom']),
713
													"courriel_utilisateur" => $this->quoteNonNull($this->utilisateur['courriel']),
712
													"courriel_utilisateur" => $this->quoteNonNull($this->utilisateur['courriel']),
714
												));
713
												));
715
 
714
 
716
	}
715
	}
717
 
716
 
718
	static function initialiser_pdo_ordered_statements($colonnes_statiques) {
717
	static function initialiser_pdo_ordered_statements($colonnes_statiques) {
719
		return Array(
718
		return Array(
720
			// insert_ligne_pattern_ordre
719
			// insert_ligne_pattern_ordre
721
			sprintf('INSERT INTO cel_obs (%s, %s) VALUES',
720
			sprintf('INSERT INTO cel_obs (%s, %s) VALUES',
722
					implode(', ', array_keys($colonnes_statiques)),
721
					implode(', ', array_keys($colonnes_statiques)),
723
					implode(', ', array_diff(self::$ordre_BDD, array_keys($colonnes_statiques)))),
722
					implode(', ', array_diff(self::$ordre_BDD, array_keys($colonnes_statiques)))),
724
 
723
 
725
			// insert_ligne_pattern_ordre
724
			// insert_ligne_pattern_ordre
726
			sprintf('(%s, %s ?)',
725
			sprintf('(%s, %s ?)',
727
					implode(', ', $colonnes_statiques),
726
					implode(', ', $colonnes_statiques),
728
					str_repeat('?, ', count(self::$ordre_BDD) - count($colonnes_statiques) - 1))
727
					str_repeat('?, ', count(self::$ordre_BDD) - count($colonnes_statiques) - 1))
729
		);
728
		);
730
	}
729
	}
731
 
730
 
732
	static function initialiser_pdo_statements($colonnes_statiques) {
731
	static function initialiser_pdo_statements($colonnes_statiques) {
733
		return Array( 
732
		return Array( 
734
			// insert_prefix
733
			// insert_prefix
735
			sprintf('INSERT INTO cel_obs (%s) VALUES ',
734
			sprintf('INSERT INTO cel_obs (%s) VALUES ',
736
					implode(', ', self::$ordre_BDD)),
735
					implode(', ', self::$ordre_BDD)),
-
 
736
 
737
 
737
 
738
			// 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
-
 
741
			implode(', ', array_values(
-
 
742
				// 2) garde les valeurs fixes (de $colonnes_statiques),
740
			implode(', ', array_values(array_map(
743
				// mais remplace les NULL par des "?"
741
				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
742
				array_merge(
746
						  // et écrase certaines valeurs avec $colonnes_statiques (initilisé avec les données utilisateur)
743
					array_map(function() { return NULL; }, array_flip(self::$ordre_BDD)),
-
 
744
					$colonnes_statiques
747
						  array_merge(array_map(function() { return NULL; }, array_flip(self::$ordre_BDD)), $colonnes_statiques
745
				)))) .
748
				)))) .
746
			')'
749
			')'
747
		);
750
		);
748
	}
751
	}
749
 
752
 
750
	// équivalent à CEL->Bdd->proteger() (qui wrap PDO::quote),
753
	// équivalent à CEL->Bdd->proteger() (qui wrap PDO::quote),
751
	// sans transformer NULL en ""
754
	// sans transformer NULL en ""
752
	private function quoteNonNull($chaine) {
755
	private function quoteNonNull($chaine) {
753
		if(is_null($chaine)) return "NULL";
756
		if(is_null($chaine)) return "NULL";
754
		if(!is_string($chaine)) die("erreur __FILE__, __LINE__");
757
		if(!is_string($chaine)) die("erreur __FILE__, __LINE__");
755
		return $this->bdd->quote($chaine);
758
		return $this->bdd->quote($chaine);
756
	}
759
	}
757
 
760
 
758
	public function erreurs_stock($errno, $errstr) {
761
	public function erreurs_stock($errno, $errstr) {
759
		$this->bilan[] = $errstr;
762
		$this->bilan[] = $errstr;
760
	}
763
	}
761
}
764
}