Subversion Repositories eFlore/Applications.moissonnage

Rev

Rev 31 | Details | Compare with Previous | Last modification | View Log | RSS feed

Rev Author Line No. Line
26 alex 1
<?php
2
 
3
 
4
/**
5
 * Classe commune a tous les services de ce projet qui va analyser et rechercher des erreurs
6
 * sur les valeurs passees en parametres lors d'un appel a ces web services.
7
 *
8
 * Les parametres suivants sont traites :
9
 *   - zoom : le niveau de zoom sur la carte (API cartographique client)
10
 *     On va verifier que c'est un nombre entier compris entre une valeur minimale et une valeur maximale
11
 *
12
 *   - bbox : rectangle dont les bords delimitent en coordonnees l'espace de recherche de donnees spatiales
13
 *     L'ordre des valeurs est le suivant : ouest, sud, est, nord
14
 *     On va verifier que c'est une serie de quatre nombre decimaux delimites par des virgules
15
 *     et compris dans l'espace representant le monde a partir du systeme de projection WGS84 (EPSG:4326)
16
 *
34 alex 17
 *   - stations : liste de points d'observations. C'est une serie de stations qui est passee en parametres
18
 *     de cette maniere, separees entre elles par un seperateur vertical |. Chaque station est decrite
19
 *     par sa source de donnees, le type de site et ses coordonnees longitude et latitude.
26 alex 20
 *
21
 *   - referentiel : referentiel taxonomique a utiliser. On peut passer aussi bien en parametres
22
 *     son nom court que son nom complet (incluant le numero de version)
23
 *     On va verifier la disponibilite du referentiel pour ce service
24
 *
25
 *   - num_taxon : numero taxonomique d'une espece
31 alex 26
 *   - nn : numero nomenclatural d'une espece
26 alex 27
 *     On va rechercher sa presence dans les referentiels disponibles
28
 *     (les informations principales sur ce taxon seront renvoyees)
29
 *
30
 *   - dept : une liste de numeros de departements separees entre eux par des virgules
31
 *     On va verifier que chaque numero est un nombre entier et represente un code de departement valide
32
 *
33
 *   - auteur : l'auteur de l'observation
34
 *
31 alex 35
 *   - date_debut et date_fin : intervalle de dates d'observation. On verifie que ce sont des dates valides
36
 *     et que date_debut <= date_fin. Le programme peut accepter la presence d'un seul de ces deux
37
 *     parametres : soit une date de debut, soit une date de fin
34 alex 38
 *
39
 *   - nb_jours : valeur entiere qui indique de recuperer les observations effectuees ou transmises
40
 *     sur les derniers jours avant la date d'execution du script
31 alex 41
 *
26 alex 42
 * La fonction principale de verification des parametres va parcourir tous les parametres, et verifier
43
 * pour chacun d'eux la validite de leurs valeurs. Dans le cas ou une valeur anormale est detectee
44
 * ou qu'elle se trouve en dehors de l'intervalle des resultats acceptes, une liste de messages d'erreur
45
 * sera generee et mise a jour.
46
 *
47
 * Le resultat final de la verfication peut entrainer deux cas d'utilisation possibles. Si des messages
48
 * d'erreurs ont ete generes durant la verification, ils sont regroupes en un seul message pour lever
49
 * une exception qui sera interpretee par la classe appelante. Dans le cas ou la verification n'a rien
50
 * rencontre d'anormal, une fonction renverra la liste des parametres valides utilisables
51
 * pour des recherches de donnees pour la suite du traitement pour le service.
52
 *
53
 * @package framework-0.3
54
 * @author Alexandre GALIBERT <alexandre.galibert@tela-botanica.org>
55
 * @license GPL v3 <http://www.gnu.org/licenses/gpl.txt>
56
 * @license CECILL v2 <http://www.cecill.info/licences/Licence_CeCILL_V2-en.txt>
57
 * @version $Id$
58
 * @copyright 2013 Tela Botanica (accueil@tela-botanica.org)
59
 *
60
 */
61
 
62
class VerificateurParametres {
63
 
64
	private $parametres = array();
65
	private $validation = null;
66
	private $erreurs = array();
31 alex 67
	private $dateTraitee = false;
26 alex 68
 
69
	private $bboxMonde = null;
70
 
71
 
72
	public function __construct($parametres) {
73
		foreach ($parametres as $nomParametre => $valeur) {
34 alex 74
			$this->parametres[$nomParametre] = $valeur;
26 alex 75
		}
76
		$this->bboxMonde = array(
77
			'ouest' => floatval(Config::get('carte.limite_ouest')),
78
			'est'   => floatval(Config::get('carte.limite_est')),
79
			'sud'   => floatval(Config::get('carte.limite_sud')),
80
			'nord'  => floatval(Config::get('carte.limite_nord'))
81
		);
82
		$this->validation = new StdClass();
83
	}
84
 
85
	public function verifierParametres() {
86
		foreach ($this->parametres as $nomParametre => $valeur) {
87
			switch ($nomParametre) {
88
				case 'zoom' : $this->traiterParametreZoom($valeur);
89
					break;
90
				case 'bbox' : $this->traiterParametreBbox($valeur);
91
					break;
34 alex 92
				case 'stations'  : $this->traiterParametreStations($this->parametres['stations']);
26 alex 93
					break;
94
				case 'dept' : $this->traiterParametreDepartement($valeur);
95
					break;
96
				case 'auteur' : $this->traiterParametreAuteur($valeur);
97
					break;
98
				case 'referentiel' : $this->traiterParametreReferentiel($valeur);
99
					break;
100
				case 'num_taxon' : $this->traiterParametreTaxon($valeur);
101
					break;
31 alex 102
				case 'nn' : $this->traiterParametreNomenclatural($valeur);
103
					break;
104
				case 'type_site' : $this->traiterParametreTypeSite($valeur);
105
					break;
106
				case 'nb_jours' : $this->traiterParametreNbJours($valeur);
107
					break;
108
				case 'date_debut' :
109
				case 'date_fin'  : {
110
					$dateDebut = isset($this->parametres['date_debut']) ? $this->parametres['date_debut'] : null;
111
					$dateFin = isset($this->parametres['date_fin']) ? $this->parametres['date_fin'] : null;
112
					if (!$this->dateTraitee) {
113
						$this->traiterParametresDate($dateDebut, $dateFin);
114
						$this->dateTraitee = true;
115
					}
116
					break;
117
				}
34 alex 118
				case 'format' : $this->validation->format = strtolower($valeur); break;
26 alex 119
				// autres parametres ==> les ignorer
120
				default : break;
121
			}
122
		}
123
		$this->verifierPresenceParametreSpatial();
124
	}
125
 
126
	private function ajouterErreur($messageErreur) {
127
		$this->erreurs[] = $messageErreur;
128
	}
129
 
130
	public function renvoyerResultatVerification() {
131
		return $this->validation;
132
	}
133
 
134
	public function contienterreurs() {
135
		return count($this->erreurs);
136
	}
137
 
138
	public function leverException() {
139
		$messagesErreur = "Les erreurs suivantes ont été rencontrées : \n".
140
			implode("\n", $this->erreurs);
141
		throw new Exception($messagesErreur, RestServeur::HTTP_CODE_MAUVAISE_REQUETE);
142
	}
143
 
144
 
145
 
146
	// ------------------------------------------------------------------------- //
147
	// Fonctions de verification de parametres et detection de valeurs anormales //
148
	// ------------------------------------------------------------------------- //
149
 
150
	private function traiterParametreZoom($zoom) {
151
		$zoom = intval($zoom);
152
		$mondeZoom = array(
153
				'min' => intval(Config::get('carte.zoom_minimal')),
154
				'max' => intval(Config::get('carte.zoom_maximal'))
155
		);
156
		if ($zoom < $mondeZoom['min'] || $zoom > $mondeZoom['max']) {
157
			$message = 'Niveau de zoom non reconnu par le service. Il doit être compris entre '.
158
				$mondeZoom['min'].' et '.$mondeZoom['max'];
159
			$this->ajouterErreur($message);
160
		} else {
161
			$this->validation->zoom = $zoom;
162
		}
163
	}
164
 
34 alex 165
	private function traiterParametreBbox($chaineBbox) {
166
		$listeBbox = explode('|', trim($chaineBbox));
167
		$critereBbox = array();
168
		foreach ($listeBbox as $bbox) {
169
			$bboxVerifiee = $this->verifierCoordonneesBbox($bbox);
170
			if (!is_null($bboxVerifiee)) {
171
				$critereBbox[] = $bboxVerifiee;
172
			}
173
		}
174
		$this->validation->bbox = $critereBbox;
175
	}
176
 
177
	private function verifierCoordonneesBbox($bbox) {
178
		$bboxVerifiee = null;
26 alex 179
		// verifier que la chaine de caracteres $bbox est une serie de chaque nombre decimaux
180
		// separes entre eux par une virgule
181
		if (preg_match('/^(-?\d{1,3}(.\d+)?,){3}(-?\d{1,3}(.\d+)?)$/', $bbox) == 0) {
182
			$message = "Format de saisie des coordonnees de la bounding box non valide.  Le service ".
183
				"n'accepte seulement qu'une serie de 4 nombre décimaux séparés par des virgules.";
184
			$this->ajouterErreur($message);
185
		} else {
186
			$coordonnees = explode(',', $bbox);
187
			// index du tableau des coordonnees : ouest/sud/est/nord
188
			$nomsIndexBbox = array("ouest", "sud", "est", "nord");
189
			$bbox = array();
190
			for ($i = 0; $i < count($coordonnees); $i ++) {
191
				$bbox[$nomsIndexBbox[$i]] = $coordonnees[$i];
192
			}
193
			// verifier que les coordonnees de chaque bord de la bbox sont valides
194
			if ($this->estUneBboxValide($bbox)) {
34 alex 195
				$bboxVerifiee = $bbox;
26 alex 196
			} else {
197
				$message = "Certaines coordonnées de la bounding box sont situés en dehors des limites ".
198
					"de notre monde";
199
				$this->ajouterErreur($message);
200
			}
201
		}
34 alex 202
		return $bboxVerifiee;
26 alex 203
	}
204
 
205
	private function estUneBboxValide($bbox) {
206
		$monde = $this->bboxMonde;
207
		return (floatval($bbox['ouest']) >= $monde['ouest']  && floatval($bbox['ouest']) <= $monde['est']
208
		&& floatval($bbox['est'])   >= $monde['ouest']  && floatval($bbox['est'])   <= $monde['est']
209
		&& floatval($bbox['nord'])  >= $monde['sud']    && floatval($bbox['nord'])  <= $monde['nord']
210
		&& floatval($bbox['sud'])   >= $monde['sud']    && floatval($bbox['sud'])   <= $monde['nord']);
211
	}
212
 
34 alex 213
	private function traiterParametreStations($listeStations) {
214
		$stations = explode('|', trim($listeStations));
215
		$critereStations = array();
216
		foreach ($stations as $station) {
217
			preg_match("/([a-z]+):([a-z]+):(-?\d{1,3}.\d+),(-?\d{1,3}.\d+)/", $station, $matches);
218
			if (count($matches) < 5) {
219
				$message = "Les données transmises sur la station à interroger sont incomplètes. ".
220
					"Le format suivant doit être respecté : source:type_site:longitude:latitude";
221
				$this->ajouterErreur($message);
222
			} else {
223
				$matches = array_splice($matches, 1, count($matches));
224
				$this->verifierCoordonneesStation($matches);
225
				$this->verifierSourceStation($matches[0]);
226
				$this->verifierTypeStation($matches[1]);
227
				$critereStations[] = $matches;
228
			}
229
		}
230
		$this->validation->stations = $critereStations;
231
	}
232
 
233
	private function verifierCoordonneesStation(& $station) {
234
		$longitude = floatval($station[2]);
235
		$latitude  = floatval($station[3]);
26 alex 236
		if ($this->sontDesCoordonneesValides($longitude, $latitude)) {
34 alex 237
			$station[2] = $latitude;
238
			$station[3] = $longitude;
26 alex 239
		} else {
34 alex 240
			$message = "Les coordonnees du point passées en parametres sont en dehors des limites de notre monde";
26 alex 241
			$this->ajouterErreur($message);
242
		}
243
	}
244
 
245
	private function sontDesCoordonneesValides($longitude, $latitude) {
246
		$monde = $this->bboxMonde;
247
		return (floatval($longitude) >= $monde['ouest'] && floatval($longitude) <= $monde['est']
248
			&& floatval($latitude)  >= $monde['sud']   && floatval($latitude)  <= $monde['nord']);
249
	}
250
 
34 alex 251
	private function verifierTypeStation($typeStation) {
252
		$typeStationsValides = array('station', 'commune');
253
		if (!in_array($typeStation, $typeStationsValides)) {
254
			$message = "'$typeStation' n'est pas un type de station reconnu. Sont acceptés seulement 'station' et 'commune'.";
255
			$this->ajouterErreur($message);
256
		}
257
	}
258
 
259
	private function verifierSourceStation($source) {
260
		$sourcesARequeter = explode(',', trim($this->parametres['source']));
261
		if (!in_array($source, $sourcesARequeter)) {
262
			$message = "La source '$source' n'est pas listée dans le paramètre source de l'URL (sont disponibles ".
263
				implode(',', $sourcesARequeter).").";
264
			$this->ajouterErreur($message);
265
		}
266
	}
267
 
26 alex 268
	private function traiterParametreDepartement($numeroDepartement) {
269
		if ($numeroDepartement != '*') {
270
			$departements = explode(',', trim($numeroDepartement));
271
			foreach ($departements as $departement) {
272
				if($this->estUnCodeDepartementValide($departement)) {
273
					$this->validation->departement[] = $departement;
274
				} else {
275
					$message = "Code de département non valide";
276
					$this->ajouterErreur($message);
277
				}
278
			}
279
		}
280
	}
281
 
282
	private function estUnCodeDepartementValide($departement) {
283
		// expression reguliere pour verifier que c'est un nombre entier a 2 ou 3 chiffres
284
		$estUnDepartementValide = true;
31 alex 285
		if ($departement == '2A' || $departement == '2B') {
286
		 	$estUnDepartementValide = true;
287
		} elseif (preg_match('/^\d{2,3}$/', $departement) != 1) {
26 alex 288
			$estUnDepartementValide = false;
289
		} else {
290
			if ((intval($departement) < 1 || intval($departement) > 95)
291
				&& (intval($departement) < 971 || intval($departement) > 978)) {
292
				$estUnDepartementValide = false;
293
			}
31 alex 294
		}
26 alex 295
		return $estUnDepartementValide;
296
	}
297
 
298
	private function traiterParametreAuteur($auteur) {
299
		if ($auteur != '*') {
300
			$this->validation->auteur = trim($auteur);
301
		}
302
	}
303
 
31 alex 304
	private function traiterParametreTypeSite($typeSite) {
305
		$this->validation->typeSite = strtolower(trim($typeSite));
306
	}
307
 
26 alex 308
	private function traiterParametreReferentiel($referentiel)  {
309
		if (!isset($this->validation->referentiel) && $referentiel != '*') {
310
			$referentielAUtiliser = $this->affecterNomCompletReferentiel($referentiel);
311
			if (is_null($referentielAUtiliser)) {
312
				$message = "Le référentiel demandé n'est pas reconnu par le service.";
313
				$this->ajouterErreur($message);
314
			} else {
315
				$this->validation->referentiel = $referentielAUtiliser;
316
			}
317
		}
318
	}
319
 
320
	private function affecterNomCompletReferentiel($referentiel) {
321
		$referentielAUtiliser = null;
322
		$listeReferentiels = Referentiel::recupererListeReferentielsDisponibles();
323
		foreach ($listeReferentiels as $nomReferentiel) {
324
			$nomCourtReferentiel = current(explode('_', $nomReferentiel));
325
			if ($referentiel == $nomCourtReferentiel || $referentiel == $nomReferentiel) {
326
				$referentielAUtiliser = $nomReferentiel;
327
				break;
328
			}
329
		}
330
		return $referentielAUtiliser;
331
	}
332
 
333
	private function traiterParametreTaxon($numeroTaxon) {
334
		if ($numeroTaxon != '*') {
31 alex 335
			$listeTaxons = explode(',', $numeroTaxon);
336
			foreach ($listeTaxons as $nt) {
337
				$taxon = null;
338
				if (isset($this->validation->referentiel)) {
339
					$taxon = $this->renvoyerTaxonDansReferentiel($nt, 'nt', $this->validation->referentiel);
340
				} else {
341
					$taxon = $this->rechercherTaxonDansReferentiels($nt, 'nt');
342
				}
343
				if (is_null($taxon)) {
344
					$message = "Le numéro de taxon n'a pas permis de retrouver le taxon associé.";
345
					$this->ajouterErreur($message);
346
				} else {
347
					$this->ajouterTaxonAListe($taxon);
348
					$this->validation->referentiel = $taxon['referentiel'];
349
				}
26 alex 350
			}
31 alex 351
		}
352
	}
353
 
354
	private function traiterParametreNomenclatural($numeroNomenclatural) {
355
		if ($numeroNomenclatural != '*') {
356
			$listeTaxons = explode(',', $numeroNomenclatural);
357
			foreach ($listeTaxons as $nn) {
358
				$taxon = null;
359
				if (isset($this->validation->referentiel)) {
360
					$taxon = $this->renvoyerTaxonDansReferentiel($nn, 'nn', $this->validation->referentiel);
361
				} else {
362
					$taxon = $this->rechercherTaxonDansReferentiels($nn, 'nn');
363
				}
364
				if (is_null($taxon)) {
365
					$message = "Le numéro nomenclatural n'a pas permis de retrouver le taxon associé.";
366
					$this->ajouterErreur($message);
367
				} else {
368
					$this->ajouterTaxonAListe($taxon);
369
					$this->validation->referentiel = $taxon['referentiel'];
370
				}
26 alex 371
			}
372
		}
373
	}
374
 
31 alex 375
	private function rechercherTaxonDansReferentiels($numeroTaxon, $typeNumero) {
26 alex 376
		$taxon = null;
377
		$listeReferentiels = Referentiel::recupererListeReferentielsDisponibles();
378
		foreach ($listeReferentiels as $nomReferentiel) {
31 alex 379
			$taxon = $this->renvoyerTaxonDansReferentiel($numeroTaxon, $typeNumero, $nomReferentiel);
26 alex 380
			if (!is_null($taxon)) {
381
				break;
382
			}
383
		}
384
		return $taxon;
385
	}
386
 
31 alex 387
	private function renvoyerTaxonDansReferentiel($numeroTaxon, $typeNumero, $nomReferentiel) {
26 alex 388
		$referentiel = new Referentiel($nomReferentiel);
31 alex 389
		$referentiel->chargerTaxon($typeNumero, $numeroTaxon);
26 alex 390
		$taxon = $referentiel->renvoyerTaxon();
391
		return $taxon;
392
	}
393
 
31 alex 394
	private function ajouterTaxonAListe($taxon) {
395
		if (!isset($this->validation->taxon)) {
396
			$this->validation->taxon = array($taxon);
397
		} elseif (!in_array($taxon, $this->validation->taxon)) {
398
			$this->validation->taxon[] = $taxon;
399
		}
400
	}
401
 
402
	private function traiterParametresDate($dateDebut, $dateFin) {
403
		$statutValidite = $this->verifierValiditeDate($dateDebut, 'début')
404
			+ $this->verifierValiditeDate($dateFin, 'fin');
405
		if ($statutValidite == 2) {
406
			if (!is_null($dateDebut) && !is_null($dateFin) && $dateDebut > $dateFin) {
407
				$message = "Intervalle de dates incorrect : date de fin avant la date de début";
408
				$this->ajouterErreur($message);
409
			} else {
410
				$this->validation->dateDebut = $dateDebut;
411
				$this->validation->dateFin   = $dateFin;
412
			}
413
		}
414
	}
415
 
416
	private function verifierValiditeDate(& $date, $position) {
417
		$statutValidite = is_null($date) ? 1 : 0;
418
		$moisParDefaut = $position == 'début' ? 1 : 12;
419
		$jourParDefaut = $position == 'début' ? 1 : 31;
420
		if (!is_null($date)) {
421
			$split = explode("-", $date);
422
			$annee = intval($split[0]);
423
			$mois  = isset($split[1]) ? intval($split[1]) : $moisParDefaut;
424
			$jour  = isset($split[2]) ? intval($split[2]) : $jourParDefaut;
425
			if (!$this->estDateValide($jour, $mois, $annee)) {
426
				$message = "Date de {$position} saisie non valide";
427
				$this->ajouterErreur($message);
428
			} else {
429
				$date = $annee."-".(($mois<10)?"0".$mois:$mois)."-".(($jour<10)?"0".$jour:$jour);
430
				$dateDuJour = date('Y-m-d');
431
				if ($date > $dateDuJour) {
432
					$message = "La date de {$position} saisie postérieure à la date du jour : {$dateDuJour}";
433
					$this->ajouterErreur($message);
434
				} else {
435
					$statutValidite = 1;
436
				}
437
			}
438
		}
439
		return $statutValidite;
440
	}
441
 
442
	private function estDateValide($jour, $mois, $annee) {
443
		$estBissextile = (($annee % 4 == 0 && $annee % 100 != 0) || ($annee % 400 == 0));
444
		$dureeFevrier = $estBissextile ? 29 : 28;
445
		$dureeMois = array(1 => 31, 2 => $dureeFevrier, 3 => 31, 4 => 30, 5 => 31, 6 => 30, 7 => 31,
446
			8 => 31, 9 => 30, 10 => 31, 11 => 30, 12 => 31);
447
		return ($annee >= 1900 && $mois >= 1 && $mois <= 12 && $jour >= 1 && $jour <= $dureeMois[$mois]);
448
	}
449
 
26 alex 450
	private function verifierPresenceParametreSpatial() {
451
		$presenceParametreSpatial = false;
452
		if (isset($this->parametres['bbox'])
34 alex 453
			|| (isset($this->parametres['stations']))) {
26 alex 454
			$presenceParametreSpatial = true;
455
		}
456
		if ($presenceParametreSpatial == false) {
457
			$message = "Aucune coordonnée n'a été saisie";
458
			$this->ajouterErreur($message);
459
		}
460
	}
461
 
31 alex 462
	private function traiterParametreNbJours($nbJours) {
463
		// verifier que c'est un nombre entier positif (avec expression reguliere)
464
		if (preg_match('/^\d+$/', $nbJours) != 1) {
465
			$message = "La valeur passée pour le nombre de jours doit être un entier positif.";
466
			$this->ajouterErreur($message);
467
		} elseif ($nbJours > 0) {
468
			$this->validation->nbJours = intval($nbJours);
469
		}
470
	}
471
 
26 alex 472
}
473
 
474
?>