Subversion Repositories eFlore/Applications.cel

Rev

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

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