Subversion Repositories eFlore/Applications.moissonnage

Rev

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