Subversion Repositories eFlore/Projets.eflore-projets

Rev

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

Rev Author Line No. Line
3 jpm 1
<?php
2
/**
3
* Classe Commun.php est une classe abstraite qui contient les méthodes de base communes à tous les
4
* sous-services des projets.
5
*
6
* Encodage en entrée : utf8
7
* Encodage en sortie : utf8
8
* @package eflore-projets
9
* @author Jennifer DHÉ <jennifer.dhe@tela-botanica.org>
10
* @author Delphine CAUQUIL <delphine@tela-botanica.org>
11
* @author Jean-Pascal MILCENT <jpm@tela-botanica.org>
12
* @license GPL v3 <http://www.gnu.org/licenses/gpl.txt>
13
* @license CECILL v2 <http://www.cecill.info/licences/Licence_CeCILL_V2-en.txt>
14
* @version 1.0
15
* @copyright 1999-2011 Tela Botanica (accueil@tela-botanica.org)
16
*/
17
 
18
abstract class Commun {
19
 
20
	/** Objet Bdd. */
21
	private $Bdd = null;
22
	/** Objet Rest Client. */
23
	private $RestClient = null;
24
 
25
	/** Contients les paramètres. Doit remplacer table_param. */
26
	protected $parametres = array();
27
	/** Contients les ressources. Doit remplacer table_ressources. */
28
	protected $ressources = array();
29
	/** Le nom du service courrant. */
30
	protected $serviceNom = null;
31
 
32
	//Classe commune à tous les services web d'un projet. Contient par exemple les fonctions permettant de
33
	//renvoyer la réponse http...
34
	protected $entete_http; // Entete de la réponse correspondant au code de réponse de la requete http */
35
	protected $corps_http; // Tableau de résultat à retourner au format json ou la description de l'erreur si elle existe */
36
	protected $service; // Nom du service appelé
37
	/** Stocke la version du projet demandée dans la requete
38
	 *  - "*" : (/#projet/* /meta-donnees) Renvoi les meta-données de toutes les versions du projet
39
	 *  - "numero de la version" : (/#projet/2.00/meta-donnees) Renvoi les meta-données de la version 2.00 du projet */
40
	protected $version_projet = '+';
41
	protected $table_version; //Stocke les noms des tables de toutes les versions du projet disponibles
42
	/** tableau contenant tous les champs d'une table (est rempli par la fonction Commun::recupererNomChamp($table)) */
43
	protected $champs_table = array();
44
	private static $tri_multi_dimension = array();
45
	private static $tri_type = '';
46
 
563 aurelien 47
	public function __construct($config = null) {
536 gduche 48
		$this->config = is_null($config) ? Config::get($this->serviceNom) : $config;
49
	}
654 jpm 50
 
3 jpm 51
	public function consulter($ressources, $parametres) {
236 delphine 52
		$this->ressources = $ressources;
53
		$this->parametres = $parametres;
54
		$this->chargerNomDuService();
3 jpm 55
 
236 delphine 56
		$this->traiterParametres();
57
		$this->traiterVersionProjet();
3 jpm 58
 
236 delphine 59
		$resultats = '';
60
		foreach ($this->table_version as $version) {
61
			$this->table = $version; //on stocke le nom de la table correspondant à la version du projet en cours
62
			$this->recupererNomChamp($this->table); //on récupère les noms des champs disponibles (Ds Commun.php)
63
			$this->traiterRessources(); //dans CommunNomsTaxons.php
654 jpm 64
			$requete = $this->assemblerLaRequete();// dans Noms ou Taxons...
767 raphael 65
			$resultat = $this->getBdd()->recupererTous($requete . ' -- ' . __FILE__ . ':' . __LINE__ . ' (' .$this->table . ')');
236 delphine 66
			$versionResultat = $this->traiterResultat($resultat, $version, $requete);
67
			if (count($this->table_version) > 1) {
68
				$resultats[$version] = $versionResultat;
69
			} else {
70
				$resultats = $versionResultat;
3 jpm 71
			}
236 delphine 72
		}
3 jpm 73
 
74
		return $resultats;
75
	}
76
 
77
	private function chargerNomDuService() {
78
		$this->serviceNom = get_class($this);
79
	}
80
 
654 jpm 81
	public function traiterResultat($resultat, $version) {
3 jpm 82
		$versionResultat = null;
83
		if ($resultat == '') {
84
			//cas ou la requete comporte des erreurs
85
			$message = 'La requête SQL formée comporte une erreur!';
86
			$code = RestServeur::HTTP_CODE_RESSOURCE_INTROUVABLE;
87
			throw new Exception($message, $code);
88
		} elseif ($resultat) {
89
			$versionResultat = $this->retournerResultatFormate($resultat, $version);
90
		} else {
91
			$message = 'Les données recherchées sont introuvables.';
92
			$code = RestServeur::HTTP_CODE_RESSOURCE_INTROUVABLE;
93
			throw new Exception($message, $code);
94
		}
95
		return $versionResultat;
96
	}
97
 
98
	//+------------------------------------------------------------------------------------------------------+
99
	// Méthodes concernant les paramètres
100
	/**
101
	 *  Permet de récupérer une liste des noms des champs de la table passée en paramètre
102
	 *  @param $table : Nom de la table dont on souhaite connaitre les champs
103
	 */
104
	public function recupererNomChamp($table) {
105
		$requete = 'SHOW FIELDS FROM '.$table;
106
		$resultat = $this->getBdd()->recupererTous($requete);
107
		if ($resultat == '') {
108
			$e = 'La requête SQL formée comporte une erreur!';
109
			$this->renvoyerErreur(RestServeur::HTTP_CODE_RESSOURCE_INTROUVABLE, $e);
110
		} elseif ($resultat) {
111
			foreach ($resultat as $info) {
112
				$this->champs_table[] = $info['Field'];
113
			}
114
		} else {
115
			$m = "La table recherchée n'existe pas";
116
			$this->renvoyerErreur(RestServeur::HTTP_CODE_RESSOURCE_INTROUVABLE, $m);
117
		}
118
	}
119
 
120
	/**
121
	 * Fonction permettant de creer la table dont le nom est passé en paramètre (champs_api, champs_bdtfx,
122
	 * correspondance_champs...). Les données de chaque table sont présentes dans le fichier de configuration config.ini
123
	 * @param String $table : Peut contenir plusieurs nom de table dont on souhaite récupérer les données : table,table,table.
124
	 * 	Ex : recupererTableConfig('champs_api,champs_bdtfx')
125
	 */
126
	public function recupererTableConfig($table) {
127
		$tables = explode(',', $table);
128
		foreach ($tables as $tab) {
129
			$tableau = explode(',', Config::get($tab));
130
			$tableau = array_map('trim', $tableau);
131
			foreach ($tableau as $champ) {
132
				list($code, $rang) = explode('=', $champ);
133
				$tab_tampon[$code] = $rang;
134
			}
135
			$this->$tab = $tab_tampon;
136
			$tab_tampon = array();
137
		}
138
	}
139
 
140
	public function renvoyerErreur($entete, $message) {
141
		throw new Exception($message, $entete);
142
	}
143
 
144
	/**
145
	 * Permet de remplir la variable version_projet et de retirer cette donnée du tableau des ressources
146
	 * @param $ressources
147
	 */
148
	public function traiterVersionProjet() {
81 delphine 149
		if (isset($this->parametres['version.projet'])) {
92 jpm 150
			if (preg_match('/^[0-9]+(?:[._][0-9]+|)$/', $this->parametres['version.projet'])) {
81 delphine 151
				$this->version_projet = $this->parametres['version.projet'];
92 jpm 152
				$this->version_projet = 'v'.str_replace('.', '_', $this->version_projet);
153
			} else  {
154
				$this->version_projet = $this->parametres['version.projet'];
3 jpm 155
			}
156
		}
157
		//si la liste des noms est demandée pr toutes les versions, on affiche seulement la dernière version :
158
		if ($this->version_projet == '*' && $this->ressources == array()) {
92 jpm 159
			$message = "L'affichage de plusieurs versions ne fonctionne que pour les ressources de type /ressources/#id";
160
			$code = RestServeur::HTTP_CODE_MAUVAISE_REQUETE;
161
			throw new Exception($message, $code);
3 jpm 162
		}
163
		//on recupère les versions du projet disponible dans la table des meta-donnees (utilisation service MetaDonnees)
164
		$table_num_version = $this->recupererVersionDisponible();
165
		//on recupere la liste des noms des tables de la bdd correspondant aux differentes versions du projet en fct de la ou les versions demandées
166
		$this->recupererListeNomTablePrChaqueVersion($table_num_version);
167
	}
168
 
169
	/**
170
	 * Recupération des versions disponibles par appel du service metaDonnees
171
	 * Verification de l'existance du service recherché dans la requete (si précisé : hors *)
172
	 * @return array  : tableau contenant le numéro de chaque version disponible
173
	 */
174
	public function recupererVersionDisponible() {
175
		$versions_dispo = '';
176
		$req_version = 'SELECT version FROM '.Config::get('bdd_table_meta');
177
		$res_version = $this->getBdd()->recupererTous($req_version);
178
		if ($res_version == '') { //cas ou la requete comporte des erreurs
179
			$e = "La requête SQL de versionnage formée comporte une erreur : $req_version";
180
			$this->renvoyerErreur(RestServeur::HTTP_CODE_RESSOURCE_INTROUVABLE, $e);
181
		} elseif ($res_version) {
182
			foreach ($res_version as $version) {
183
				$versions_dispo[] = $version['version'];
184
			}
185
		} else {
186
			$m = 'Versions introuvables dans la table des méta-données';
187
			$this->renvoyerErreur(RestServeur::HTTP_CODE_RESSOURCE_INTROUVABLE, $m);
188
		}
189
		return $versions_dispo;
190
	}
191
 
192
	public function recupererListeNomTablePrChaqueVersion($table_num_version) {
193
		switch ($this->serviceNom) {
194
			case 'Ontologies' :
195
				$prefixe_table = 'bdd_table_ontologies';
196
				break;
197
			default:
198
				$prefixe_table = 'bdd_table';
199
		}
200
		switch ($this->version_projet) {
201
			case '+' :
202
				$derniere_version = $table_num_version[count($table_num_version) - 1];
203
				$this->table_version[] = Config::get($prefixe_table).'_v'.str_replace('.', '_', $derniere_version);
204
				break;
205
			case '*' :
206
				foreach ($table_num_version as $num_version) {
207
					$this->table_version[] = Config::get($prefixe_table).'_v'.str_replace('.', '_', $num_version);
208
				}
209
				break;
210
			default  :
211
				$this->table_version[] = Config::get($prefixe_table).'_'.$this->version_projet;
212
				break;
213
		}
214
	}
215
 
593 mathilde 216
	//valeur * signifie pas de limites
217
	public function definirNavigationLimite($valeur){
218
		if (isset($this->parametres['navigation.limite'])  ) {
219
			if ((preg_match('/^([0-9]+)$/', $valeur) && $valeur != 0 ) || $valeur == '*' ){
220
				$this->limite_requete['limite'] = $valeur;
221
			} else {
222
				$e = "Erreur : valeur erronnée pour le paramètre navigation.limite.";
223
				throw new Exception($e, RestServeur::HTTP_CODE_MAUVAISE_REQUETE);
224
			}
654 jpm 225
		}
593 mathilde 226
	}
654 jpm 227
 
593 mathilde 228
	public function definirNavigationDepart($valeur){
229
		if (isset($this->parametres['navigation.depart'])) {
230
			if(preg_match('/^([0-9]+)$/', $valeur)){
231
				$this->limite_requete['depart'] = $valeur;
232
			} else {
233
				$e = "Erreur : valeur erronnée pour le paramètre navigation.depart.";
234
				throw new Exception($e, RestServeur::HTTP_CODE_MAUVAISE_REQUETE);
235
			}
236
		} else {
237
			$e = "indiquez également la valeur pour le paramètre navigation.limite.";
238
			throw new Exception($e, RestServeur::HTTP_CODE_MAUVAISE_REQUETE);
239
		}
240
	}
3 jpm 241
	/**
242
	 * Est appelée pour former l'url complete des resultats precedants ou suivants.
243
	 * @param int : Permet de connaitre le nombre de noms obtenus par la requete
244
	 * @return string Retourne l'url complete des resultats precedents ou suivant sous la forme d'un tableau
245
	 */
246
	public function formulerUrl($nb_resultat, $id = null) {
247
		$url = array();
248
		$debut_url = Config::get('url_service').$id.'?';
597 mathilde 249
		//on recréé l'url sans les parametres de navigation qui seront rajoutés ci-apres. On les enlève dc de la table des parametres
3 jpm 250
		foreach($this->parametres as $cle => $val) {
251
			$param_url[str_replace('_', '.', $cle)] = $val;
252
		}
253
 
254
		$this->recupererLesLimitesSuivantes($nb_resultat, $param_url);
255
		if (isset($param_url['navigation.depart']) && isset($param_url['navigation.limite'])) {
256
			$url['suivant'] = $debut_url.http_build_query($param_url);
257
		}
258
 
259
		$this->recupererLesLimitesPrecedentes($param_url);
260
		if (isset($param_url['navigation.depart']) && isset($param_url['navigation.limite'])) {
261
			$url['precedent'] = $debut_url.http_build_query($param_url);
262
		}
263
		return $url;
264
	}
265
 
266
	public function supprimerNavigation(&$param_url) {
267
		unset($param_url['navigation.depart']);
268
		unset($param_url['navigation.limite']);
269
	}
270
 
271
	/**
272
	 * Description :
273
	 * Permet de former les limites de la requete retournant les résultats suivants.
274
	 * Cette url sera afficher dans l'entete de la reponse retournée en format JSON (retour.format=defaut).
275
	 * @param int : $nb_resultat : Permet de connaitre le nombre de résultats obtenus par la requete
276
	 * @return string : la fin de l'url decrivant les limites des resultats suivants. Si aucun résultats ne suient,
277
	 * une chaine de caractère vide est retournée
278
	 */
279
	public function recupererLesLimitesSuivantes($nb_resultat, &$param_url_suiv) {
280
		$this->supprimerNavigation($param_url);
281
		$depart = $this->limite_requete['depart'];
282
		$limite = $this->limite_requete['limite'];
283
		$depart_suivant = $depart + $limite;
284
		$limite_suivant = $limite;
285
		if ($nb_resultat > $depart_suivant) {
286
			$param_url_suiv['navigation.depart'] = $depart_suivant;
287
			$param_url_suiv['navigation.limite'] = $limite_suivant;
288
		} else {
289
			$param_url_suiv['navigation.depart'] = null;
290
			$param_url_suiv['navigation.limite'] = null;
291
		}
292
	}
293
 
294
	/**
295
	 * Description :
296
	 * Permet de former les limites de la requete retournant les résultats precedents.
297
	 * Cette url sera afficher dans l'entete de la taxons/105reponse retournée en format JSON (retour.format=defaut)
298
	 * @return string : la fin de l'url decrivant les limites des resultats precedents.
299
	 * Si aucun résultats ne precedent, une chaine de caractère vide est retournée
300
	 */
301
	public function recupererLesLimitesPrecedentes(&$param_url) {
302
		$this->supprimerNavigation($param_url);
303
		$depart = $this->limite_requete['depart'];
304
		$limite = $this->limite_requete['limite'];
305
		if ($depart == 0) {
306
			$url_precedente = '';
307
		} else {
308
			if (($depart - $limite) < 0) {
309
				$depart_precedent = 0;
310
			} else {
311
				$depart_precedent = $depart - $limite;
312
			}
313
			$param_url['navigation.depart'] = $depart_precedent;
314
			$param_url['navigation.limite'] = $limite;
315
		}
316
	}
317
 
801 raphael 318
	static function getDureeCache() {
536 gduche 319
		$dureecache = 0;
320
		$dureecache = Config::get('dureecache');
321
		if ($dureecache == null || !is_numeric($dureecache) || $dureecache < 0) {
322
			$dureecache = 0;
323
		}
324
		return (int) $dureecache;
325
	}
654 jpm 326
 
3 jpm 327
	public function ajouterHref($service, $val) {
328
		// http://tela-botanica.org/service:eflore:0.1/[projet]/[version_projet]/[service]/[ressource]:[valeur]
329
		if ($this->version_projet == '+') {
330
			$url = Config::get('url_service_base').Config::get('nom_projet').'/'.$service.'/'.$val;
331
		} else {
151 delphine 332
			$url = Config::get('url_service_base').Config::get('nom_projet').'/'.$service.'/'.$val.'?version.projet='.ltrim($this->version_projet, 'v');
3 jpm 333
		}
334
		return $url;
335
	}
336
 
337
	public function ajouterHrefAutreProjet($service, $ressource, $valeur, $projet = null, $param = null) {
338
		//on enleve les GA et Co, les meta ou les "_"
339
		$this->transliterer($service, $valeur);
340
		//on définit les nom des projets, des services et des ressources de l'url (dans les méta-donnees)
341
		$tab = array(
342
			'langue' 			  => array('service' => 'langues', 'projet' => 'iso-639-1', 'ressource' => ''),
343
			'couverture_spatiale' => array('service' => 'zone-geo', 'projet' => 'iso-3166-1', 'ressource' => ''),
265 jpm 344
			'type' 				  => array('service' => 'ontologies', 'projet' => 'eflore', 'ressource' => 'contactType:'),
345
			'datum' 			  => array('service' => 'ontologies', 'projet' => 'eflore', 'ressource' => 'datum:')
3 jpm 346
		);
347
		if (array_key_exists($service, $tab)) {
348
			extract($tab[$service]);
349
		} else {
350
			if (strpos(Config::get('nom_projet'), 'bd') === 0 && $projet == null) {
351
				$projet 		= 'bdnt';
352
				$service 		= 'ontologies';
353
				$ressource 		= '';
354
			}
355
		}
88 delphine 356
		$param = ($param) ? "?".$param : "";
3 jpm 357
		$url = Config::get('url_service_base').$projet.'/'.$service.'/'.$ressource.$valeur.$param;
358
		return $url;
359
	}
360
 
361
	/**Permet de consulter une url et retourne le résultat ou une erreur
362
	 * @param $url	 */
363
	public function consulterHref($url) {
364
		$res = $this->getRestClient()->consulter($url);
365
		$entete = $this->getRestClient()->getReponseEntetes();
366
		//Si le service meta-donnees fonctionne correctement, l'entete comprend la clé wrapper_data
367
		if (isset($entete['wrapper_data'])) {
368
			$res = json_decode($res);
369
			return $res;
370
		} else {
371
			$u = 'L\'url <a href="'.$url.'">'.$url.'</a> lancée via RestClient renvoie une erreur';
372
			$this->renvoyerErreur(RestServeur::HTTP_CODE_RESSOURCE_INTROUVABLE, $u);
373
		}
374
	}
375
 
376
	public function transliterer(&$service, &$val) {
377
		if (preg_match('/^.+:(.+)$/', $val, $match)) {
378
			$val = $match[1];
379
		}
380
		$service = str_replace(array('_Ga','_Co','_meta'), '', $service);
381
		if ($service == 'rang') {
382
			$ressource = 'rangTaxo';
383
		} elseif (preg_match('/^(statut)(?:_|-)([^_-]+)$/', $service, $match)) {
384
			$service = $match[1].ucfirst($match[2]);
385
		} elseif (strrpos($service, 'datum') !== false) {
386
			$service = 'datum';
387
		}
388
	}
389
 
609 mathilde 390
	// prend en arguments la valeur de la recherche, les résultats approchés, le paramétre recherche
391
	// retourne le tableau trié en fonction de la ressemblance entre le résultat approché et la valeur recherchée
3 jpm 392
	public function trierRechercheFloue($nom_demande, $tab_approchee, $nom) {
393
		$trie = '';
394
		$resultat = array();
395
		foreach ($tab_approchee as $id => $tab) {
396
			$nom_demande_ss = strtolower(Chaine::supprimerAccents($nom_demande));
397
			$nom_flou_ss = strtolower(Chaine::supprimerAccents($tab[$nom]));
398
			$stat = array();
399
			// Prime pour la ressemblance globale :
400
			$score = 500 - levenshtein($nom_flou_ss, $nom_demande_ss);
401
			// On affine
402
			$score = $score + (similar_text($nom_demande_ss, $nom_flou_ss) * 3);
403
			$stat['score'] = $score;
404
			foreach ($tab as $key => $valeur) {
405
				$stat[$key] = $valeur;
406
			}
407
			$resultat[] = $stat;
408
		}
409
 
410
 
411
		// Vérification que nous avons bien trouvé un nom approché
412
		if (count($resultat) > 0) {
413
			$trie = Tableau::trierMD($resultat, array('score' => SORT_DESC));
414
		}
415
		return $trie;
416
	}
417
 
418
	protected function recupererTableauConfig($param) {
419
		$tableau = array();
420
		$tableauPartiel = explode(',', Config::get($param));
421
		$tableauPartiel = array_map('trim', $tableauPartiel);
422
		foreach ($tableauPartiel as $champ) {
423
			if (strpos($champ, '=') === false) {
424
				$tableau[] = $champ;
425
			} else {
426
				list($cle, $val) = explode('=', $champ);
427
				$tableau[$cle] = $val;
428
			}
429
		}
430
		return $tableau;
431
	}
432
 
433
	//+------------------------------------------------------------------------------------------------------+
434
	// Méthodes d'accès aux objets du Framework
435
	/**
436
	 * Méthode de connection à la base de données sur demande.
437
	 * Tous les services web n'ont pas besoin de s'y connecter.
438
	 */
439
	protected function getBdd() {
440
		if (! isset($this->Bdd)) {
441
			$this->Bdd = new Bdd();
442
		}
443
		return $this->Bdd;
444
	}
445
 
446
	/**
447
	 * Méthode permettant de faire appel à un client REST en fonction des besoins du service.
448
	 */
449
	protected function getRestClient() {
450
		if (! isset($this->RestClient)) {
451
			$this->RestClient = new RestClient();
452
		}
453
		return $this->RestClient;
454
	}
807 raphael 455
 
456
 
457
	/**
458
	 * Génération de fichiers pour les cartes
459
	 */
460
	static function convertirEnPNGAvecRsvg($idFichier, $chemin, $svg) {
461
		// test répertoire de cache
462
		if(!is_dir($chemin)) {
463
			mkdir($chemin, 0777, true);
464
		}
465
		if(!is_dir($chemin)) {
466
			error_log(__FILE__ . ": can't create cache {$chemin}");
467
			return NULL;
468
		}
469
 
470
		// test présence du binaire de conversion (rsvg)
471
		/*
472
		  // `which` no possible using safe-mode...
473
		$i = $s = NULL;
474
		exec('which rsvg-convert', $s, $i);
475
		if($i != 0) {
476
			error_log(__FILE__ . ": no rsvg-convert binary");
477
			return NULL;
478
		}
479
		*/
480
		// conversion svg => png
847 aurelien 481
		// troncage du nom de fichier si celui-ci est trop long
482
		// (passé 255 caractères, le risque de collision est très faible)
483
		$cheminReduit = substr($chemin.$idFichier, 0, 240);
484
 
485
		$fichierPng = $cheminReduit.'.png';
486
		$fichierSvg = $cheminReduit.'.svg';
487
 
488
 
807 raphael 489
		file_put_contents($fichierSvg, $svg);
490
		$i = $s = NULL;
491
		$rsvg = exec("rsvg-convert $fichierSvg -d 75 -p 75 -o $fichierPng", $s, $i);
492
		if($i != 0) {
493
			error_log(__FILE__ . ": `rsvg-convert $fichierSvg -o $fichierPng` returned $i: " . implode(', ', $s));
494
			return NULL;
495
		}
496
 
497
		self::indexerFichierPng($fichierPng);
498
		return file_get_contents($fichierPng);
499
	}
500
 
501
	static function indexerFichierPng($fichierPng) {
502
		$img = imagecreatefrompng($fichierPng);
503
		imagetruecolortopalette($img, false, 32);
504
		$blanc = imagecolorallocate($img, 255, 255, 255);
505
		imagefill($img, 0, 0, $blanc);
506
		imagepng($img, $fichierPng, 9, PNG_ALL_FILTERS);
507
	}
3 jpm 508
}
509
?>