Subversion Repositories eFlore/Applications.cel

Rev

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

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