* @author Delphine CAUQUIL * @author Jean-Pascal MILCENT * @license GPL v3 * @license CECILL v2 * @version 1.0 * @copyright 1999-2011 Tela Botanica (accueil@tela-botanica.org) */ abstract class Commun { /** Objet Bdd. */ private $Bdd = null; /** Objet Rest Client. */ private $RestClient = null; /** Contients les paramètres. Doit remplacer table_param. */ protected $parametres = array(); /** Contients les ressources. Doit remplacer table_ressources. */ protected $ressources = array(); /** Le nom du service courrant. */ protected $serviceNom = null; //Classe commune à tous les services web d'un projet. Contient par exemple les fonctions permettant de //renvoyer la réponse http... protected $entete_http; // Entete de la réponse correspondant au code de réponse de la requete http */ protected $corps_http; // Tableau de résultat à retourner au format json ou la description de l'erreur si elle existe */ protected $service; // Nom du service appelé /** Stocke la version du projet demandée dans la requete * - "*" : (/#projet/* /meta-donnees) Renvoi les meta-données de toutes les versions du projet * - "numero de la version" : (/#projet/2.00/meta-donnees) Renvoi les meta-données de la version 2.00 du projet */ protected $version_projet = '+'; protected $table_version; //Stocke les noms des tables de toutes les versions du projet disponibles /** tableau contenant tous les champs d'une table (est rempli par la fonction Commun::recupererNomChamp($table)) */ protected $champs_table = array(); private static $tri_multi_dimension = array(); private static $tri_type = ''; public function consulter($ressources, $parametres) { $this->ressources = $ressources; $this->parametres = $parametres; $this->chargerNomDuService(); $this->traiterParametres(); $this->traiterVersionProjet(); $resultats = ''; foreach ($this->table_version as $version) { $this->table = $version; //on stocke le nom de la table correspondant à la version du projet en cours $this->recupererNomChamp($this->table); //on récupère les noms des champs disponibles (Ds Commun.php) $this->traiterRessources(); //dans CommunNomsTaxons.php $requete = $this->assemblerLaRequete(); $resultat = $this->getBdd()->recupererTous($requete); $versionResultat = $this->traiterResultat($resultat, $version, $requete); if (count($this->table_version) > 1) { $resultats[$version] = $versionResultat; } else { $resultats = $versionResultat; } } return $resultats; } private function chargerNomDuService() { $this->serviceNom = get_class($this); } public function traiterResultat($resultat, $version, $requete) { $versionResultat = null; if ($resultat == '') { //cas ou la requete comporte des erreurs $message = 'La requête SQL formée comporte une erreur!'; $code = RestServeur::HTTP_CODE_RESSOURCE_INTROUVABLE; throw new Exception($message, $code); } elseif ($resultat) { $versionResultat = $this->retournerResultatFormate($resultat, $version); } else { $message = 'Les données recherchées sont introuvables.'; $code = RestServeur::HTTP_CODE_RESSOURCE_INTROUVABLE; throw new Exception($message, $code); } return $versionResultat; } //+------------------------------------------------------------------------------------------------------+ // Méthodes concernant les paramètres /** * Permet de récupérer une liste des noms des champs de la table passée en paramètre * @param $table : Nom de la table dont on souhaite connaitre les champs */ public function recupererNomChamp($table) { $requete = 'SHOW FIELDS FROM '.$table; $resultat = $this->getBdd()->recupererTous($requete); if ($resultat == '') { $e = 'La requête SQL formée comporte une erreur!'; $this->renvoyerErreur(RestServeur::HTTP_CODE_RESSOURCE_INTROUVABLE, $e); } elseif ($resultat) { foreach ($resultat as $info) { $this->champs_table[] = $info['Field']; } } else { $m = "La table recherchée n'existe pas"; $this->renvoyerErreur(RestServeur::HTTP_CODE_RESSOURCE_INTROUVABLE, $m); } } /** * Fonction permettant de creer la table dont le nom est passé en paramètre (champs_api, champs_bdtfx, * correspondance_champs...). Les données de chaque table sont présentes dans le fichier de configuration config.ini * @param String $table : Peut contenir plusieurs nom de table dont on souhaite récupérer les données : table,table,table. * Ex : recupererTableConfig('champs_api,champs_bdtfx') */ public function recupererTableConfig($table) { $tables = explode(',', $table); foreach ($tables as $tab) { $tableau = explode(',', Config::get($tab)); $tableau = array_map('trim', $tableau); foreach ($tableau as $champ) { list($code, $rang) = explode('=', $champ); $tab_tampon[$code] = $rang; } $this->$tab = $tab_tampon; $tab_tampon = array(); } } public function renvoyerErreur($entete, $message) { throw new Exception($message, $entete); } /** * Permet de remplir la variable version_projet et de retirer cette donnée du tableau des ressources * @param $ressources */ public function traiterVersionProjet() { if (isset($this->parametres['version.projet'])) { if (preg_match('/^[0-9]+(?:[._][0-9]+|)$/', $this->parametres['version.projet'])) { $this->version_projet = $this->parametres['version.projet']; $this->version_projet = 'v'.str_replace('.', '_', $this->version_projet); } else { $this->version_projet = $this->parametres['version.projet']; } } //si la liste des noms est demandée pr toutes les versions, on affiche seulement la dernière version : if ($this->version_projet == '*' && $this->ressources == array()) { $message = "L'affichage de plusieurs versions ne fonctionne que pour les ressources de type /ressources/#id"; $code = RestServeur::HTTP_CODE_MAUVAISE_REQUETE; throw new Exception($message, $code); } //on recupère les versions du projet disponible dans la table des meta-donnees (utilisation service MetaDonnees) $table_num_version = $this->recupererVersionDisponible(); //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 $this->recupererListeNomTablePrChaqueVersion($table_num_version); } /** * Recupération des versions disponibles par appel du service metaDonnees * Verification de l'existance du service recherché dans la requete (si précisé : hors *) * @return array : tableau contenant le numéro de chaque version disponible */ public function recupererVersionDisponible() { $versions_dispo = ''; $req_version = 'SELECT version FROM '.Config::get('bdd_table_meta'); $res_version = $this->getBdd()->recupererTous($req_version); if ($res_version == '') { //cas ou la requete comporte des erreurs $e = "La requête SQL de versionnage formée comporte une erreur : $req_version"; $this->renvoyerErreur(RestServeur::HTTP_CODE_RESSOURCE_INTROUVABLE, $e); } elseif ($res_version) { foreach ($res_version as $version) { $versions_dispo[] = $version['version']; } } else { $m = 'Versions introuvables dans la table des méta-données'; $this->renvoyerErreur(RestServeur::HTTP_CODE_RESSOURCE_INTROUVABLE, $m); } return $versions_dispo; } public function recupererListeNomTablePrChaqueVersion($table_num_version) { switch ($this->serviceNom) { case 'Ontologies' : $prefixe_table = 'bdd_table_ontologies'; break; default: $prefixe_table = 'bdd_table'; } switch ($this->version_projet) { case '+' : $derniere_version = $table_num_version[count($table_num_version) - 1]; $this->table_version[] = Config::get($prefixe_table).'_v'.str_replace('.', '_', $derniere_version); break; case '*' : foreach ($table_num_version as $num_version) { $this->table_version[] = Config::get($prefixe_table).'_v'.str_replace('.', '_', $num_version); } break; default : $this->table_version[] = Config::get($prefixe_table).'_'.$this->version_projet; break; } } /** * Est appelée pour former l'url complete des resultats precedants ou suivants. * @param int : Permet de connaitre le nombre de noms obtenus par la requete * @return string Retourne l'url complete des resultats precedents ou suivant sous la forme d'un tableau */ public function formulerUrl($nb_resultat, $id = null) { $url = array(); $debut_url = Config::get('url_service').$id.'?'; //on recré l'url sans les parametres de navigation qui seront rajouter ci-apres. On les enlève dc de la table des parametres foreach($this->parametres as $cle => $val) { $param_url[str_replace('_', '.', $cle)] = $val; } $this->recupererLesLimitesSuivantes($nb_resultat, $param_url); if (isset($param_url['navigation.depart']) && isset($param_url['navigation.limite'])) { $url['suivant'] = $debut_url.http_build_query($param_url); } $this->recupererLesLimitesPrecedentes($param_url); if (isset($param_url['navigation.depart']) && isset($param_url['navigation.limite'])) { $url['precedent'] = $debut_url.http_build_query($param_url); } return $url; } public function supprimerNavigation(&$param_url) { unset($param_url['navigation.depart']); unset($param_url['navigation.limite']); } /** * Description : * Permet de former les limites de la requete retournant les résultats suivants. * Cette url sera afficher dans l'entete de la reponse retournée en format JSON (retour.format=defaut). * @param int : $nb_resultat : Permet de connaitre le nombre de résultats obtenus par la requete * @return string : la fin de l'url decrivant les limites des resultats suivants. Si aucun résultats ne suient, * une chaine de caractère vide est retournée */ public function recupererLesLimitesSuivantes($nb_resultat, &$param_url_suiv) { $this->supprimerNavigation($param_url); $depart = $this->limite_requete['depart']; $limite = $this->limite_requete['limite']; $depart_suivant = $depart + $limite; $limite_suivant = $limite; if ($nb_resultat > $depart_suivant) { $param_url_suiv['navigation.depart'] = $depart_suivant; $param_url_suiv['navigation.limite'] = $limite_suivant; } else { $param_url_suiv['navigation.depart'] = null; $param_url_suiv['navigation.limite'] = null; } } /** * Description : * Permet de former les limites de la requete retournant les résultats precedents. * Cette url sera afficher dans l'entete de la taxons/105reponse retournée en format JSON (retour.format=defaut) * @return string : la fin de l'url decrivant les limites des resultats precedents. * Si aucun résultats ne precedent, une chaine de caractère vide est retournée */ public function recupererLesLimitesPrecedentes(&$param_url) { $this->supprimerNavigation($param_url); $depart = $this->limite_requete['depart']; $limite = $this->limite_requete['limite']; if ($depart == 0) { $url_precedente = ''; } else { if (($depart - $limite) < 0) { $depart_precedent = 0; } else { $depart_precedent = $depart - $limite; } $param_url['navigation.depart'] = $depart_precedent; $param_url['navigation.limite'] = $limite; } } public function ajouterHref($service, $val) { // http://tela-botanica.org/service:eflore:0.1/[projet]/[version_projet]/[service]/[ressource]:[valeur] if ($this->version_projet == '+') { $url = Config::get('url_service_base').Config::get('nom_projet').'/'.$service.'/'.$val; } else { $url = Config::get('url_service_base').Config::get('nom_projet').'/'.$service.'/'.$val.'?version.projet='.ltrim($this->version_projet, 'v'); } return $url; } public function ajouterHrefAutreProjet($service, $ressource, $valeur, $projet = null, $param = null) { //on enleve les GA et Co, les meta ou les "_" $this->transliterer($service, $valeur); //on définit les nom des projets, des services et des ressources de l'url (dans les méta-donnees) $tab = array( 'langue' => array('service' => 'langues', 'projet' => 'iso-639-1', 'ressource' => ''), 'couverture_spatiale' => array('service' => 'zone-geo', 'projet' => 'iso-3166-1', 'ressource' => ''), 'type' => array('service' => 'ontologies', 'projet' => 'eflore', 'ressource' => 'contactType:'), 'datum' => array('service' => 'ontologies', 'projet' => 'eflore', 'ressource' => 'datum:') ); if (array_key_exists($service, $tab)) { extract($tab[$service]); } else { if (strpos(Config::get('nom_projet'), 'bd') === 0 && $projet == null) { $projet = 'bdnt'; $service = 'ontologies'; $ressource = ''; } } $param = ($param) ? "?".$param : ""; $url = Config::get('url_service_base').$projet.'/'.$service.'/'.$ressource.$valeur.$param; return $url; } /**Permet de consulter une url et retourne le résultat ou une erreur * @param $url */ public function consulterHref($url) { $res = $this->getRestClient()->consulter($url); $entete = $this->getRestClient()->getReponseEntetes(); //Si le service meta-donnees fonctionne correctement, l'entete comprend la clé wrapper_data if (isset($entete['wrapper_data'])) { $res = json_decode($res); return $res; } else { $u = 'L\'url '.$url.' lancée via RestClient renvoie une erreur'; $this->renvoyerErreur(RestServeur::HTTP_CODE_RESSOURCE_INTROUVABLE, $u); } } public function transliterer(&$service, &$val) { if (preg_match('/^.+:(.+)$/', $val, $match)) { $val = $match[1]; } $service = str_replace(array('_Ga','_Co','_meta'), '', $service); if ($service == 'rang') { $ressource = 'rangTaxo'; } elseif (preg_match('/^(statut)(?:_|-)([^_-]+)$/', $service, $match)) { $service = $match[1].ucfirst($match[2]); } elseif (strrpos($service, 'datum') !== false) { $service = 'datum'; } } // prend en valeur la valeur de la recherche, les résultats approchés, le paramétre recherche // retourne le tableau trié en fonction de la ressemble entre le résultat approché et la valeur recherchée public function trierRechercheFloue($nom_demande, $tab_approchee, $nom) { $trie = ''; $resultat = array(); foreach ($tab_approchee as $id => $tab) { $nom_demande_ss = strtolower(Chaine::supprimerAccents($nom_demande)); $nom_flou_ss = strtolower(Chaine::supprimerAccents($tab[$nom])); $stat = array(); // Prime pour la ressemblance globale : $score = 500 - levenshtein($nom_flou_ss, $nom_demande_ss); // On affine $score = $score + (similar_text($nom_demande_ss, $nom_flou_ss) * 3); $stat['score'] = $score; foreach ($tab as $key => $valeur) { $stat[$key] = $valeur; } $resultat[] = $stat; } // Vérification que nous avons bien trouvé un nom approché if (count($resultat) > 0) { $trie = Tableau::trierMD($resultat, array('score' => SORT_DESC)); } return $trie; } protected function recupererTableauConfig($param) { $tableau = array(); $tableauPartiel = explode(',', Config::get($param)); $tableauPartiel = array_map('trim', $tableauPartiel); foreach ($tableauPartiel as $champ) { if (strpos($champ, '=') === false) { $tableau[] = $champ; } else { list($cle, $val) = explode('=', $champ); $tableau[$cle] = $val; } } return $tableau; } //+------------------------------------------------------------------------------------------------------+ // Méthodes d'accès aux objets du Framework /** * Méthode de connection à la base de données sur demande. * Tous les services web n'ont pas besoin de s'y connecter. */ protected function getBdd() { if (! isset($this->Bdd)) { $this->Bdd = new Bdd(); } return $this->Bdd; } /** * Méthode permettant de faire appel à un client REST en fonction des besoins du service. */ protected function getRestClient() { if (! isset($this->RestClient)) { $this->RestClient = new RestClient(); } return $this->RestClient; } } ?>