Subversion Repositories eFlore/Applications.coel

Rev

Rev 1934 | Go to most recent revision | Blame | Compare with Previous | Last modification | View Log | RSS feed

<?php
/** Paramêtres indiquant que l'on est en français pour permettre l'affichage des dates en français. */
setlocale(LC_TIME, 'fr_FR.UTF-8');
/** Définition de l'encodage interne des fichiers. */
mb_internal_encoding('UTF-8');

/**
 * Classe mère abstraite contenant les méthodes génériques des services.
 * Encodage en entrée : utf8
 * Encodage en sortie : utf8
 * 
 * @author Jean-Pascal MILCENT <jpm@tela-botanica.org>
 * @license GPL v3 <http://www.gnu.org/licenses/gpl.txt>
 * @license CECILL v2 <http://www.cecill.info/licences/Licence_CeCILL_V2-en.txt>
 * @version $Id$
 * @copyright 2009
 */
abstract class Coel {
        
        const ETAT_AJOUTER = 1;
        const ETAT_MODIFIER = 2;
        const ETAT_SUPPRIMER = 3;
        
        protected $sessionName = 'CoelSession';
        protected $sessionExpireTime = 8640000;// 60*60*24*100 = 100 jours
        protected static $cookieChemin = '';
        public $config;
        protected $bdd;
        protected $log = array();
        protected $messages = array();
        protected $debug = array();
        protected $distinct = false;
        protected $orderby = null;
        protected $formatRetour = 'objet';
        protected $start = 0; 
        protected $limit = 150;
        
        public function __construct($config, $demarrer_session = true) {
                // Tableau contenant la config de Jrest
                $this->config = $config;
                
                // Connection à la base de données
                $this->bdd = $this->connecterPDO($this->config, 'coel');
                
                $this->gererSession($demarrer_session);
                $this->gererIdentificationPermanente();

        if(isset($_GET['start'])) $this->start = intval($_GET['start']);
        if(isset($_GET['limit'])) $this->limit = intval($_GET['limit']);
                
                // Nettoyage du $_GET (non-sécurisé)
                if (isset($_GET)) {
                        $get_params = array('orderby', 'distinct', 'formatRetour', 'searchCity');
                        foreach ($get_params as $get) {
                                $verifier = array('NULL', "\n", "\r", "\\", "'", '"', "\x00", "\x1a", ';');
                                if (isset($_GET[$get]) && $_GET[$get] != '') {
                                        $_GET[$get] = str_replace($verifier, '', $_GET[$get]);
                                        $this->$get = $_GET[$get];
                                } else {
                                        $_GET[$get] = null;
                                }
                        }
                }
        }

        /**
         * Méthode appelée quand aucun paramètre n'est passé dans l'url et avec une requête de type GET.
         */
        public function getRessource() {
                $this->getElement(array());
        }
        
        
        protected function creerXmlHisto($donnees) {
                // Création du xml de l'enregistrement à historiser
                $info = null;
                $xml_tpl = '<?xml version="1.0" encoding="UTF-8" ?>'."\n<resultset>\n<row>\n%s</row>\n</resultset>";
                $xml = null;
                 
                foreach ($donnees as $cle => $valeur) {
                        if (!is_null($valeur) && $valeur != '') {
                                $xml .= "<$cle>$valeur</$cle>\n";
                        }
                }
                if ($xml !== null) {
                        $info = sprintf($xml_tpl, $xml);
                        $info = $this->bdd->quote($info);
                }
                
                return $info;
        }
        
        protected function getTableauDepuisXmlHisto($xml) {
                // Création du xml de l'enregistrement à historiser
                $info = array();
                
                if (!empty($xml) && preg_match_all('|<([^>]+)>(.*)</[^>]+>|U', $xml, $matches, PREG_SET_ORDER)) {
                        foreach ($matches as $val) {
                                $info[$val[1]] = $val[2];
                        }
                }
                
                return $info;
        }
                
        protected function historiser($table_id, $ligne_cle, $info, $id_utilisateur, $etat, $id_session) {
                date_default_timezone_set('Europe/Paris');
                // Pré-traitement des variables
                $info = (is_null($info)) ? 'NULL' : $info;
                
                // Historisation (Ajout des méta-données)
                $requete =      'INSERT INTO coel_meta_historique_ligne '. 
                                        '       (cmhl_ce_table, cmhl_cle_ligne, cmhl_enregistrement, cmhl_date_modification, cmhl_ce_modifier_par, cmhl_ce_etat, cmhl_ip, cmhl_session_id) '.
                                        'VALUES ('.
                                        "               $table_id, ".
                                        '               "'.$ligne_cle.'", '.
                                        "               $info, ".
                                        '               "'.date('Y-m-d H:i:s', time()).'", '.
                                        "               $id_utilisateur, ".
                                        "               $etat, ".
                                        '               "'.$_SERVER['REMOTE_ADDR'].'", '.
                                        '               "'.$id_session.'") ';
                
                // Exécution de la requêtre SQL et test d'éventuelles erreurs
                $resultat = $this->bdd->exec($requete);
                if ($resultat === false ) {
                        $this->log[] = "Des données de la table '$table_id' (enrg. $ligne_cle) n'ont pas été historisées car la requête a échouée.";
                }
                return $this->bdd->lastInsertId();
        }
        
        protected function envoyer($donnees = null, $mime = 'application/json', $encodage = 'utf-8', $json = true) {
                // Traitements des messages d'erreurs et données
                if (count($this->messages) != 0) {
                        header('HTTP/1.1 500 Internal Server Error');
                        $mime = 'text/html';
                        $encodage = 'utf-8';
                        $json = true;
                        $sortie = $this->messages;
                } else {
                        $sortie = $donnees;
                        if (is_null($donnees)) {
                                $sortie = 'OK';
                        }
                }

                // Gestion de l'envoie du déboguage
                $this->envoyerDebogage();
                
                // Encodage au format et JSON et envoie sur la sortie standard
                $contenu = $json ? json_encode($sortie) : $sortie;
                $this->envoyerContenu($encodage, $mime, $contenu);
        }
        
        protected function envoyerHTML($donnees = null) {
                // Traitements des messages d'erreurs et données
                $contenu = '';
                if (count($this->messages) > 0) {
                        $contenu = '<p>'.implode('<br />', $this->messages).'</p>';
                } else {
                        $contenu = $donnees;
                        if (is_null($donnees)) {
                                $contenu = 'OK';
                        }
                }

                // Gestion de l'envoie du déboguage
                $this->envoyerDebogage();
                
                // Envoie sur la sortie standard
                $this->envoyerContenu(null, null, $contenu);
        }
        
        protected function envoyerEnteteMessage() {
                if (!is_array($this->messages)) {
                        $this->messages[] = $this->messages;
                }
                if (count($this->messages) != 0) {
                        foreach ($this->messages as $cle => $val) {
                                if (is_array($val)) {
                                        $this->messages[$cle] = print_r($val, true);
                                }
                        }
                        header('X-MessageJrest-Data: '.json_encode($this->messages));
                }
        }

        protected function envoyerDebogage() {
                $this->envoyerEnteteDebogage();
        }
        
        protected function envoyerEnteteDebogage() {
                if (!is_array($this->debug)) {
                        $this->debug[] = $this->debug;
                }
                if (count($this->debug) != 0) {
                        foreach ($this->debug as $cle => $val) {
                                if (is_array($val)) {
                                        $this->debug[$cle] = print_r($val, true);
                                }
                        }
                        header('X-DebugJrest-Data: '.json_encode($this->debug));
                }
        }
        
        protected function envoyerContenu($encodage, $mime, $contenu) {
                if (!is_null($mime) && !is_null($encodage)) {
                        header("Content-Type: $mime; charset=$encodage");
                } else if (!is_null($mime) && is_null($encodage)) {
                        header("Content-Type: $mime");
                }
                print $contenu;
        }
        
        private function connecterPDO($config, $base = 'database') {
        $cfg = $config[$base];
                $dsn = $cfg['phptype'].':dbname='.$cfg['database'].';host='.$cfg['hostspec'];
                try {
                $PDO = new PDO($dsn, $cfg['username'], $cfg['password']);
                } catch (PDOException $e) {
                echo 'La connexion à la base de donnée via PDO a échouée : ' . $e->getMessage();
                }
                // Passe en UTF-8 la connexion à la BDD
                $PDO->exec("SET NAMES 'utf8'");
                // Affiche les erreurs détectées par PDO (sinon mode silencieux => aucune erreur affiché)
                $PDO->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
        return $PDO;
        }
        
        protected function getTxt($id) {
                $sortie = '';
                switch ($id) {
                        case 'sql_erreur' : $sortie = 'Requête echec. Fichier : "%s". Ligne : "%s". Message : %s'; break;
                        case 'sql_erreur_requete' : $sortie = "Requête echec.\nFichier : %s.\nLigne : %s.\nMessage : %s.\nRequête : %s"; break;
                        default : $sortie = $id; 
                }
                return $sortie;
        }
        
        protected function concatenerChamps($champs, $prefixe = null) {
                $chaine = '';
                foreach ($champs as $cle => $valeur) {
                        if (is_null($prefixe) || (!is_null($prefixe) && stristr($cle, $prefixe.'_'))) {
                                $chaine .= ", $cle = $valeur";
                        }
                }
                return ltrim($chaine, ', ');
        }

        protected function separerChampsValeurs($donnees, $prefixe = null) {
                $sortie = array('', '');
                // Concaténation des champs et des valeurs dans des chaines séparées
                foreach ($donnees as $cle => $valeur) {
                        if (is_null($prefixe) || (!is_null($prefixe) && stristr($cle, $prefixe.'_'))) {
                                $sortie[0] .= "$cle, ";
                                $sortie[1] .= "$valeur, ";
                        }
                }
                // Suppression de la virgule finale
                $sortie[0] = rtrim($sortie[0], ', ');
                $sortie[1] = rtrim($sortie[1], ', ');
                return $sortie;
        }
        
        
        protected function traiterParametresUrl($params_attendu, $params, $pourBDD = true) {
                $sortie = array();
                foreach ($params_attendu as $num => $nom) {
                        if (isset($params[$num]) && $params[$num] != '*') {
                                if ($pourBDD) {
                                        $params[$num] = $this->bdd->quote($params[$num]);
                                }
                                $sortie[$nom] = $params[$num];
                        }
                }
                return $sortie;
        }
        
        protected function traiterBddClauseIn($chaine) {
                $sortie = '';
                $chaine = trim($chaine, " '");
                if ($chaine != '') {
                        $valeurs = explode(',', $chaine);
                        foreach ($valeurs as $id => $valeur) {
                                $valeurs[$id] = $this->bdd->quote($valeur);
                        }
                        $sortie = implode(',', $valeurs);
                }
                return $sortie;
        }
        
        protected function traiterParametresPost($params) {
                $sortie = array();
                foreach ($params as $cle => $valeur) {
                        $sortie[$cle] = $this->bdd->quote($valeur);
                }
                return $sortie;
        }
        
        protected function getIdentification(&$params) {
                // Initialisation des variables
                $utilisateur = array(0, session_id());
                
                // L'id utilisateur est soit passé par le POST soit dans l'url
                if (is_array($params) && isset($params['cmhl_ce_modifier_par'])) {
                        $utilisateur[0] = $params['cmhl_ce_modifier_par'];
                        unset($params['cmhl_ce_modifier_par']);
                } else if (is_string($params)) {
                        $utilisateur[0] = $params;
                }
                
                return $utilisateur;
        }
        
        protected function etreAutorise($id_utilisateur) {
                $autorisation = false;
                if (!array_key_exists('coel_utilisateur', $_SESSION) || (($_SESSION['coel_utilisateur'] != '') && $_SESSION['coel_utilisateur']['id'] != $id_utilisateur)) {
                        $this->messages[] = 'Accès interdit.';
                } else if ($_SESSION['coel_utilisateur'] == '') {         
                        $this->messages[] = 'Veuillez vous identifiez pour accéder à cette fonction.';
                } else {
                        $autorisation = true;
                }
                return $autorisation;
        }
        
        protected function recupererCle($table) {
                $cle = null;
                if (isset($table['champs_valeurs_id'])) {
                        $identifiants = array();
                        // Trie des clés primaire pour avoir toujours le même ordre dans la table historique pour les clés multiples
                        ksort($table['champs_valeurs_id']);
                        foreach ($table['champs_valeurs_id'] as $id) {
                                $identifiants[] = $id;
                        }
                        $cle = implode('-', $identifiants);
                }
                return $cle;
        }
        
        protected function avoirCleComplete($table) {
                $cle_complete = false;
                if (isset($table['champs_valeurs_id'])) {
                        $ok = true;
                        foreach ($table['id'] as $id) {
                                if (!isset($table['champs_valeurs_id'][$id]) || $table['champs_valeurs_id'][$id] == '') {
                                        $ok = false;
                                        break;
                                }
                        }
                        $cle_complete = $ok;
                }
                return $cle_complete;
        }
        
        protected function recupererEtat($table) {
                $etat = 1;// Ajout
                if ($this->avoirEnregistrement($table) === true) {
                        $etat = 2;// Modification
                }
                return $etat;
        }
        
        protected function avoirEnregistrement($table) {
                $avoir_enregistrement = false;
                $requete =      'SELECT * '.
                                        "FROM {$table['nom']} ".
                                        "WHERE %s ";
                
                $where = $this->construireWhere($table['champs_valeurs_id']);
                $requete = sprintf($requete, $where);
                
                // Exécution de la requêtre SQL et test d'éventuelles erreurs
                $resultat = $this->bdd->query($requete, PDO::FETCH_ASSOC)->fetch();
                
                if ($resultat !== false) {
                        $avoir_enregistrement = true;
                }
                return $avoir_enregistrement;
        }
        
        protected function contenirDonnees($requete) {
                $nbre = $this->bdd->query($requete)->fetchColumn();
                
                $presence = false;
                if ($nbre != 0) {
                        $presence = true;
                }
                return $presence;
        }
        
        protected function construireWhere($table) {
                $where = '1';
                if (is_array($table) && count($table) > 0) {
                        $table_where = array();
                        foreach ($table as $chp => $id) {
                                $table_where[] = "$chp = '$id'";
                        }
                        $where = implode(' AND ', $table_where);
                }
                return $where;
        }
        
        protected function recupererTablesAModifier($parametres) {
                $tables_a_modifier = $this->tables;
                foreach ($this->tables as $table_id => $table) {
                        $tables_a_modifier[$table_id]['champs'] = null;
                        foreach ($parametres as $cle => $valeur) {
                                if (preg_match('/^'.$table['prefixe'].'_/', $cle)) {
                                        // Contient les noms des champs seulements
                                        $tables_a_modifier[$table_id]['champs'][] = $cle;
                                        // Contient un tableau de résultats protégé pour l'inclusion en bdd
                                        $tables_a_modifier[$table_id]['champs_valeurs_protege'][$cle] = is_null($valeur) ? 'NULL' : $this->bdd->quote($valeur);
                                        // Contient un tableau de résultats non protégé
                                        $tables_a_modifier[$table_id]['champs_valeurs_brut'][$cle] = $valeur;
                                        if (preg_match('/_id_/', $cle)) {
                                                $tables_a_modifier[$table_id]['champs_valeurs_id'][$cle] = $valeur;
                                        }
                                }
                        }  
                }
                return $tables_a_modifier;
        }
        

        protected function mettreAJourAvecCle($id_utilisateur, $id_session, $table_id, $table) {
                if (!is_null($table['champs'])) {
                        // Historisation (Ajout des méta-données)
                        $cle = $this->recupererCle($table);
                        $etat = $this->recupererEtat($table);
                        $info = $this->creerXmlHisto($table['champs_valeurs_brut']);
                        $id_meta = $this->historiser($table_id, $cle, $info, $id_utilisateur, $etat, $id_session);
                        $champ_meta = "{$table['prefixe']}_ce_meta";
                        $table['champs_valeurs_protege'][$champ_meta] = $this->bdd->quote($id_meta);
                        
                        // Mise à jour des données ou ajout en fonction de l'état
                        if ($etat == 1) { // Ajout
                                $this->ajouter($table);
                        } else if ($etat == 2) { // Modif
                                $this->modifier($table);
                        }
                }
        }
        
        protected function ajouter($table) {
                $requete =      "INSERT INTO {$table['nom']} ".
                                        ' (%s) '.
                                        ' VALUES (%s) ';
                
                $champs = $valeurs = '';
                foreach ($table['champs_valeurs_protege'] as $chp => $val) {
                        $champs .= "$chp, ";
                        $valeurs .= "$val, ";
                }
                $requete = sprintf($requete, rtrim($champs, ', '), rtrim($valeurs, ', '));
                
                $resultat = $this->bdd->exec($requete);
                $dernier_id = false;
                if ($resultat === false) {
                        $cle = $this->recupererCle($table);
                        $this->log[] = "L'enregistrement '$cle' de la table {$table['nom']} n'a pas été ajouté car la requête a échouée.";
                } else {
                        $dernier_id = $this->bdd->lastInsertId();
                }
                return $dernier_id;
        }
        
        protected function modifier($table) {
        // nous ne voulons pas qu'une requête malheureuse affecte toute la base de données. Par exemple:
        // POST "/jrest/CoelStructure/1"<<<"var=val&" // écraserait tous les enregistrements
        // car on ne peut garantir que tout le code existant wrap'era ses appels dans un if(avoirCleComplete())
        if(!array_key_exists('champs_valeurs_id', $table)) die('erreur: ' . __FILE__ . ':' . __LINE__);
                $requete =      "UPDATE {$table['nom']} ".
                                        'SET %s '.
                                        'WHERE %s ';
                
                $champs_requete = '';
                foreach ($table['champs_valeurs_protege'] as $chp => $val) {
                        $champs_requete .= "$chp = $val, ";
                }
                $where = $this->construireWhere($table['champs_valeurs_id']);
                $requete = sprintf($requete, rtrim($champs_requete, ', '), $where);
                $resultat = $this->bdd->exec($requete);
                if ($resultat === false) {
                        $cle = $this->recupererCle($table);
                        $this->log[] = "L'enregistrement '$cle' de la table {$table['nom']} n'a pas été mis à jour car la requête a échouée.";
                } else {
                        $resultat = true;
                }
                return $resultat;
        }
        
        protected function supprimer($table) {
                $requete =      "DELETE FROM {$table['nom']} ".
                                        'WHERE %s ';
                
                $where = $this->construireWhere($table['champs_valeurs_id']);
                $requete = sprintf($requete, $where);
                
                $resultat = $this->bdd->exec($requete);
                if ($resultat === false) {
                        $cle = $this->recupererCle($table);
                        $this->messages[] = "L'enregistrement '$cle' de la table {$table['nom']} n'a pas été supprimé car la requête a échouée.";
                } else {
                        $resultat = true;
                }
                return $resultat;
        }
        
        private function gererSession($demarrage) {
                session_name($this->sessionName);

                // Configuration du cookie de session
                // Détermination du chemin pour les cookies nécessaire à Jrest
                /// ATTENTION : comme les cookies sont passés à Jrest, il faut utiliser l'url de Jrest pour établir le chemin
                $url_morceaux = parse_url($this->config['coel']['urlBaseJrest']);
                self::$cookieChemin = $this->reparerCheminCookie($url_morceaux['path']);
                unset($url_morceaux);
                session_set_cookie_params($this->sessionExpireTime, self::$cookieChemin);
                
                // Démarrage de la session
                if ($demarrage) {
                        session_start();
                }
        }
        
        private function reparerCheminCookie($chemin) {
                // dirname renvoit / si le chemin vaut seulement /dossier/, cela renvoit /dossier si le chemin vaut /dossier//.
                $chemin = dirname($chemin.'/.');
                // Fixe la gestion des chemins pour les cookies sous les OS utilisant le \ comme séparteur de chemin
                $chemin = str_replace('\\', '/', $chemin);
                // Ajoute un '/' terminal sauf si on est à la racine web
                $chemin = ($chemin != '/' ? $chemin.'/' : '');
                return $chemin;
        }
        
        private function gererIdentificationPermanente() {
                // Pour maintenir l'utilisateur tjrs réellement identifié nous sommes obligé de recréer une SESSION et de le recharger depuis la bdd
                if ($this->getUtilisateur() == '' 
                                && isset($_COOKIE['coel_login']) 
                                && ($utilisateur = $this->chargerUtilisateur($_COOKIE['coel_login'], $_COOKIE['coel_mot_de_passe']))) {
                        $this->setUtilisateur($utilisateur, $_COOKIE['coel_permanence']);
                }
        }
        
        /*protected function rechargerCookieUtilisateur() {
                $utilisateur = $this->chargerUtilisateur($_COOKIE['coel_login']);
                $this->setUtilisateur($utilisateur, $_COOKIE['coel_permanence']);
        }*/
        
        protected function getUtilisateur() {
                return (isset($_SESSION['coel_utilisateur']) ? $_SESSION['coel_utilisateur'] : '');
        }

        /**
         * Tente de trouver un utilisateur dans la table Personnes de CoeL, en fonction de son
         * courriel ($login); si le mot de passe est fournie, le vérifie, sinon l'ignore
         * 
         * MàJ 2017 : le mot de passe n'est plus stocké en local, tout se fait via l'annuaire
         */
        protected function chargerUtilisateur($login, $mot_de_passe = null) {
                $sortie = false;
                $requete =      'SELECT cp_id_personne AS id, cp_fmt_nom_complet AS nom_complet, cp_prenom AS prenom, '.
                                        '       cp_nom AS nom, cp_login AS login, cp_mot_de_passe AS mot_de_passe, cp_parametre AS parametre, '.
                                        '       cp_ville AS ville, cp_code_postal AS code_postal, '.
                                        '       cp_mark_licence AS licence '.
                                        'FROM coel_personne '.
                                        "WHERE cp_login = {$this->bdd->quote($login)} ";
                                        //((!is_null($mot_de_passe)) ? "AND cp_mot_de_passe = {$this->bdd->quote($mot_de_passe)} " : '');
                try {
                        $resultat = $this->bdd->query($requete)->fetch();
                        if ($resultat === false) {
                                $this->debug[] = "L'utilisateur n'est pas enregistré comme utilisateur de COEL.";
                        } else {
                                $sortie = array('id' => $resultat['id'], 
                                        'login' => $login, 
                                        'mot_de_passe' => $resultat['mot_de_passe'], 
                                        'nom_complet' => $resultat['nom_complet'], 
                                        'nom' => $resultat['nom'], 
                                        'prenom' => $resultat['prenom'], 
                                        'parametre' => $resultat['parametre'],
                                        'ville' => $resultat['ville'], 
                                        'code_postal' => $resultat['code_postal'],  
                                        'licence' => $resultat['licence']);
                        }
                } catch (PDOException $e) {
                        $this->messages[] = sprintf($this->getTxt('sql_erreur'), $e->getFile(), $e->getLine(), $e->getMessage());
                }
                
                return $sortie;
        }

        /**
         * Définit un utilisateur comme "actif" en plaçant un objet Utilisateur dans la session;
         * Ne pose plus de cookies depuis l'adaptation au SSO, sauf pour la licence (acceptée ou non)
         * @param unknown $utilisateur
         * @param number $permanence
         */
        protected function setUtilisateur($utilisateur, $permanence = 1) {
                $_SESSION['coel_utilisateur'] = $utilisateur;
                //$this->setCookiePersistant('coel_licence', $utilisateur['licence'], $permanence);
        }
        
        protected function setCookiePersistant($cookie_nom, $valeur, $permanence = 1) {
                setCookie($cookie_nom, $valeur, time() + ($permanence ? 100*24*60*60 : 60*60), self::$cookieChemin);
                $_COOKIE[$cookie_nom] = $valeur;
        }
        
        protected function supprimerCookie($cookie_nom) {
                setCookie($cookie_nom, '', 1, self::$cookieChemin);
                $_COOKIE[$cookie_nom] = '';
        }
        
        /**
         * Méthode prenant en paramètre un chemin de fichier squelette et un tableau associatif de données,
         * en extrait les variables, charge le squelette et retourne le résultat des deux combinés.
         *
         * @param String $fichier       le chemin du fichier du squelette
         * @param Array  $donnees       un tableau associatif contenant les variables a injecter dans le squelette.
         *
         * @return boolean false si le squelette n'existe pas, sinon la chaine résultat.
         */
        public static function traiterSquelettePhp($fichier, Array $donnees = array()) {
                $sortie = false;
                if (file_exists($fichier)) {
                        // Extraction des variables du tableau de données
                        extract($donnees);
                        // Démarage de la bufferisation de sortie
                        ob_start();
                        // Si les tags courts sont activés
                        if ((bool) @ini_get('short_open_tag') === true) {
                                // Simple inclusion du squelette
                                include $fichier;
                        } else {
                                // Sinon, remplacement des tags courts par la syntaxe classique avec echo
                                $html_et_code_php = self::traiterTagsCourts($fichier);
                                // Pour évaluer du php mélangé dans du html il est nécessaire de fermer la balise php ouverte par eval
                                $html_et_code_php = '?>'.$html_et_code_php;
                                // Interprétation du html et du php dans le buffer
                                echo eval($html_et_code_php);
                        }
                        // Récupèration du contenu du buffer
                        $sortie = ob_get_contents();
                        // Suppression du buffer
                        @ob_end_clean();
                } else {
                        $msg = "Le fichier du squelette '$fichier' n'existe pas.";
                        trigger_error($msg, E_USER_WARNING);
                }
                // Retourne le contenu
                return $sortie;
        }
        
        /**
         * Fonction chargeant le contenu du squelette et remplaçant les tags court php (<?= ...) par un tag long avec echo.
         *
         * @param String $chemin_squelette le chemin du fichier du squelette
         *
         * @return string le contenu du fichier du squelette php avec les tags courts remplacés.
         */
        private static function traiterTagsCourts($chemin_squelette) {
                $contenu = file_get_contents($chemin_squelette);
                // Remplacement de tags courts par un tag long avec echo
                $contenu = str_replace('<?=', '<?php echo ',  $contenu);
                // Ajout systématique d'un point virgule avant la fermeture php
                $contenu = preg_replace("/;*\s*\?>/", "; ?>", $contenu);
                return $contenu;
        }
        
        public static function debug($var) {
                echo '<pre>'.print_r($var, true).'</pre>';
        }

        /*
         * Retourne une paire de coordonnée depuis un tableau représentant une adresse
         * ou une adresse partielle à l'aide du service Nominatim.
         * (http://wiki.openstreetmap.org/wiki/Nominatim)
         *
         * @param in data: un tableau associatif doté de *toutes* les clefs suivantes:
         *                                 'adresse', 'cp', 'ville', 'pays'
         * @param out lonlat: un tableau associatif contenant 'lon' et 'lat' si elles ont été déterminées
         *                                        Celles-ci sont sous la forme de deux chaînes de caractères numériques représentant
         *                                        un FLOAT() dont le séparateur des décimales est le "." (point).
         * @param in opts: un tableau associatif optionnel écrasant ou ajoutant des valeurs d'API
         * @return boolean: selon que la fonction à réussi ou non
         *
         * opts['force-q']: force l'utilisation d'une requête en texte libre
         *                                      quelque soit le remplissage des composants individuels
         *
         * Note: le code postal est contre-productif pour Nominatim:
         * https://lists.openstreetmap.org/pipermail/talk-fr/2013-April/057084.html
         */
        static function coordGuess(Array $data, Array &$lonlat, Array $opts = NULL) {
                if(!$data) return FALSE;
                $common_query = array('accept_language' => 'fr', 'format' => 'json', 'limit' => 1);
                if(! $data['adresse'] || (! $data['cp'] && ! $data['ville']) || @$opts['force-q']) {
                        unset($opts['force-q']);
                        $query = array_merge($common_query,
                                                                 array('q' => implode(', ', array_filter($data))),
                                                                 $opts ? $opts : array());
                }
                else {
                        $query = array_merge($common_query,
                                                                 array_filter(array('street' => $data['adresse'],
                                                                                                        'city' => $data['ville'],
                                                                                                        'postalcode' => $data['cp'],
                                                                                                        'country' => $data['pays'])),
                                                                 $opts ? $opts : array());
                }
                $fullquery = 'http://nominatim.openstreetmap.org/search.php?' . http_build_query($query);

        $ctx = stream_context_create(array('http' => array( 'timeout' => '4' )));
        $r = json_decode(file_get_contents($fullquery, FALSE, $ctx));
                error_log(sprintf("COEL: Nominatim request returned %d result(s) [%s]", count($r), $fullquery));
                if($r && isset($r[0])) {
                        $lonlat['lon'] = $r[0]->lon;
                        $lonlat['lat'] = $r[0]->lat;
                        return TRUE;
                }
                return FALSE;
        }

        /*
         * Retourne un tableau prêt à être utilisé par coordGuess(), c'est à dire initialisant toutes
         * les clefs nécessaires (même si NULL) et disposant systématiquement d'un pays.
         * Cela à partir d'un tableau au format de clefs prédéterminé.
         * S'occupe du filtrage basique des éléments pour maximiser les chances de détection.
         *
         * @param in: un tableau associatif contenant les clefs telles que nommées à l'issue du
         *                              du formulaire de saisie: 'cs_adresse_01', 'cs_code_postal', 'cs_ville'
         * @param db: une db optionnelle, utilisée si nécessaire pour converir les code pays
         *                              du formulaire de saisie: 'cs_adresse_01', 'cs_code_postal', 'cs_ville'
         * @return un tableau associatif contenant une ou plusieurs des clefs suivantes:
         *                      'adresse', 'cp', 'ville', 'pays'
         */
        static function addrReStruct(Array $in, $db = NULL) {
        $pays = FALSE;
                if(is_numeric(@$in['cs_ce_truk_pays'])) {
            if($db) {
                $pays= $db->query(sprintf(
                    "SELECT cmlv_nom FROM coel_meta_liste_valeur ".
                    "WHERE cmlv_ce_parent = 1074 AND cmlv_id_valeur = %d",
                    intval($in['cs_ce_truk_pays'])))->fetchColumn();
            }
        }
        else $pays = @trim($in['cs_ce_truk_pays']);

                // pas de précision au n°, mais Nominatim peine avec des préfixes trop élaborés
                // coupons jusqu'à un élément reconnaissable s'il existe
                $adresse = @trim($in['cs_adresse_01']);
                $adresse = substr($adresse, stripos($adresse, "ville de "));
                $adresse = substr($adresse, stripos($adresse, "impasse"));
                $adresse = substr($adresse, stripos($adresse, "chemin"));
                $adresse = substr($adresse, stripos($adresse, "route"));
                $adresse = substr($adresse, stripos($adresse, "rue"));
                $adresse = substr($adresse, stripos($adresse, "avenue"));
                $adresse = substr($adresse, stripos($adresse, "boulevard"));
                $adresse = substr($adresse, stripos($adresse, "place"));
                $adresse = substr($adresse, stripos($adresse, "promenade"));
                $adresse = substr($adresse, stripos($adresse, "allée"));
                $adresse = substr($adresse, stripos($adresse, "cours"));
                $adresse = preg_replace("/[\r\n].*/", "", $adresse);

                $ville = preg_replace('/cedex.*/i', '', @trim($in['cs_ville']));
                return array(
                        'adresse' => $adresse,
                        'cp' => @trim($in['cs_code_postal']) ? $in['cs_code_postal'] : '',
                        'ville' => $ville,
                        'pays' =>  $pays !== FALSE ? $pays : 'France'
                );
        }
}
?>