Rev 1528 | Rev 1578 | 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');
/**
* 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();
// Nettoyage du $_GET (sécurité)
if (isset($_GET)) {
$get_params = array('orderby', 'distinct', 'start', 'limit', 'formatRetour');
foreach ($get_params as $get) {
$verifier = array('NULL', "\n", "\r", "\\", "'", '"', "\x00", "\x1a", ';');
$_GET[$get] = str_replace($verifier, '', $_GET[$get]);
if (isset($_GET[$get]) && $_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) {
// 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 = 'text/html', $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($encodage, $mime, $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) {
header("Content-Type: $mime; charset=$encodage");
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 (($_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] = $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) {
$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']);
$this->cookieChemin = $this->reparerCheminCookie($url_morceaux['path']);
unset($url_morceaux);
session_set_cookie_params($this->sessionExpireTime, $this->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 getUtilisateur() {
return (isset($_SESSION['coel_utilisateur']) ? $_SESSION['coel_utilisateur'] : '');
}
protected function chargerUtilisateur($login, $mot_de_passe = null) {
$sortie = false;
$cp_ce_projet = $this->config['coel']['idProjetUtilisateurs'];
$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)} ".
" AND cp_ce_projet = '$cp_ce_projet' ".
((!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;
}
protected function setUtilisateur($utilisateur, $permanence = 1) {
$_SESSION['coel_utilisateur'] = $utilisateur;
$this->debug[] = $_SESSION['coel_utilisateur'];
$this->setCookiePersistant('coel_login', $utilisateur['login'], $permanence);
$this->setCookiePersistant('coel_mot_de_passe', $utilisateur['mot_de_passe'], $permanence);
$this->setCookiePersistant('coel_licence', $utilisateur['licence'], $permanence);
$this->setCookiePersistant('coel_permanence', $permanence, $permanence);
}
protected function setCookiePersistant($cookie_nom, $valeur, $permanence = 1) {
setCookie($cookie_nom, $valeur, time() + ($permanence ? 100*24*60*60 : 60*60), $this->cookieChemin);
$_COOKIE[$cookie_nom] = $valeur;
}
protected function supprimerCookie($cookie_nom) {
setCookie($cookie_nom, '', 1, $this->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>';
}
}
?>