/trunk/jrest/services/ExempleService.php |
---|
New file |
0,0 → 1,163 |
<?php |
/** |
* Service Exemple pour apprendre à utiliser les classes JRest |
* Encodage en entrée : utf8 |
* Encodage en sortie : utf8 |
* |
* @author Aurélien PERONNET <aurelien@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$ |
*/ |
class ExempleService extends Del { |
/** |
* Méthode appelée avec une requête de type GET avec une url de la forme |
* http://localhost/jrest/ExempleService/ |
* |
* Sert normalement à renvoyer la description des possibilités du service |
* |
*/ |
public function getRessource() { |
$chaine = '<div>'. |
'<p> Bonjour, je suis un service web </p>'. |
'<p> Vous pouvez m\'appeler avec les méthodes GET, POST, PUT et DELETE </p>'. |
'</div>'; |
$this->envoyer($chaine); |
} |
/** |
* Méthode appelée avec une requête de type GET avec une url de la forme |
* http://localhost/jrest/ExempleService/uid[0]/$uid[1]/ etc... |
* |
* Sert normalement à ramener un élément précis indiqué par un identifiant |
* qui se situe dans l'url après le nom du service |
* Le filtrage, le format de retour, les paramètres optionnels ... sont normalement indiqués |
* dans le tableau $_GET |
* Pour obtenir l'élément 2501 dans le format HTML cela pourrait donner |
* http://localhost/jrest/ExempleService/2501?format=HTML |
* |
* @param $uid un tableau contenant les élements passés dans l'url après le nom du service |
* |
*/ |
public function getElement($uid) |
{ |
$format = 'html'; |
if(isset($_GET['format'])) { |
$format = strtolower($_GET['format']); |
} |
switch ($format) { |
case 'html': |
$retour = '<div id="element_'.$uid[0].'">'. |
'<p> Bonjour, je suis l\'élément '.$uid[0].' </p>'. |
'<span> Les filtres demandés sont : </span>'. |
'<ul>'; |
foreach($_GET as $cle => $valeur) { |
$retour .= '<li>'.$cle.' = '.$valeur.'</li>'; |
} |
$retour .= '</ul>'. |
'</div>'; |
$mime = 'text/html'; |
break; |
case 'json': |
$retour = array('id' => $uid[0], |
'message' => 'Bonjour, je suis l\'élément '.$uid[0], |
'filtres' => $_GET); |
$retour = json_encode($retour); |
$mime = 'application/json'; |
break; |
case 'xml': |
$retour = '<?xml version="1.0" encoding="utf-8"?>'; |
$retour .= '<element id="element_'.$uid[0].'"> '. |
'<message> Bonjour, je suis l\'élément '.$uid[0].' </message>'. |
'<filtres> Les filtres demandés sont : '; |
foreach($_GET as $cle => $valeur) { |
$retour .= '<filtre>'.$cle.' = '.$valeur.'</filtre>'; |
} |
$retour .= '</filtres>'. |
'</element>'; |
$mime = 'text/xml'; |
break; |
} |
$this->envoyer($retour,$mime); |
} |
/** |
* Méthode appelée avec une requête de type PUT, ou bien un POST sans identifiant après le nom |
* du service, donc une url de la forme : |
* http://localhost/jrest/ExempleService/ + du contenu dans le post |
* Cette méthode sert normalement a créer un nouvel élément avec les données fournies en post |
* |
* @param array $pairs Un tableau contenant les valeurs passées dans le post qui permettront de créer l'élement |
*/ |
function createElement($pairs) |
{ |
$chaine = '<div>'. |
'<p> Bonjour, vous venez de créer un nouvel élément </p>'. |
'<span> Les données suivantes ont été utilisées : </span>'. |
'<ul>'; |
foreach($pairs as $cle => $valeur) { |
$chaine .= '<li>'.$cle.' = '.$valeur.'</li>'; |
} |
$chaine .= '</ul>'. |
'</div>'; |
$this->envoyer($chaine); |
} |
/** |
* Méthode appelée avec une requête de type PUT, ou bien un POST avec un identifiant après le nom |
* du service, donc une url de la forme : |
* http://localhost/jrest/ExempleService/$uid[0] + du contenu dans le post |
* Cette méthode sert normalement a modifier un élément existant avec les données fournies |
* en post |
* |
* @param array $pairs Un tableau contenant les valeurs passées dans le post qui permettront |
* de modifier l'élement indiqué par ce qui suit le nom du service dans l'url |
*/ |
public function updateElement($uid,$pairs) |
{ |
$chaine = '<div>'. |
'<p> Bonjour, l\'élément '.$uid[0].' a été modifié </p>'. |
'<span> Les données suivantes ont été utilisées : </span>'. |
'<ul>'; |
foreach($pairs as $cle => $valeur) { |
$chaine .= '<li>'.$cle.' = '.$valeur.'</li>'; |
} |
$chaine .= '</ul>'. |
'</div>'; |
$this->envoyer($chaine); |
} |
/** |
* Méthode appelée avec une requête de type DELETE, en indiquant l'identifiant de l'élément à supprimer |
* ce qui donne une url de la forme : |
* http://localhost/jrest/ExempleService/$uid[0] |
* |
* ATTENTION : Safari ne sais pas envoyer les DELETE donc on triche en faisant un post qui contient |
* action=DELETE (les majuscules sont importantes) |
* On a donc http://localhost/jrest/ExempleService/$uid[0] + action=DELETE dans le post |
* |
* @param $uid un tableau contenant les élements passés dans l'url après le nom du service |
*/ |
function deleteElement($uid){ |
$chaine = '<div>'. |
'<p> Bonjour, l\'élément '.$uid[0].' a été supprimé </p>'. |
'</div>'; |
$this->envoyer($chaine); |
} |
} |
?> |
/trunk/jrest/services/Dao.php |
---|
New file |
0,0 → 1,220 |
<?php |
// declare(encoding='UTF-8'); |
/** |
* Classe modèle spécifique à l'application, donc d'accés au données, elle ne devrait pas être appelée de l'extérieur. |
* Elle est abstraite donc doit obligatoirement être étendue. |
* |
* @category Php5 |
* @package Referentiel |
* @author Jean-Pascal MILCENT <jpm@tela-botanica.org> |
* @copyright 2010 Tela-Botanica |
* @license http://www.cecill.info/licences/Licence_CeCILL_V2-fr.txt Licence CECILL |
* @license http://www.gnu.org/licenses/gpl.html Licence GNU-GPL |
* @version SVN: $Id$ |
*/ |
class CelDao { |
const ORDRE_ASCENDANT = 'ASC'; |
const ORDRE_DESCENDANT = 'DESC'; |
const HTTP_REQUETE_SEPARATEUR = '&'; |
protected $distinction = '0'; |
protected $limite_debut = null; |
protected $limite_nbre = null; |
protected $ordre = null; |
public $url_jrest = null; |
public function __construct($url_jrest_distant) { |
$this->url_jrest = $url_jrest_distant; |
} |
//+----------------------------------------------------------------------------------------------------------------+ |
// ACCESSEURS |
public function setDistinction($distinct) { |
$this->distinction = $distinct; |
} |
public function getDistinction() { |
return $this->distinction; |
} |
public function viderDistinction() { |
$this->distinction = null; |
} |
public function avoirLimitation() { |
$limitation = false; |
if (!is_null($this->limite_debut) && !is_null($this->limite_nbre)) { |
$limitation = true; |
} |
return $limitation; |
} |
public function setLimitation($limite_debut, $limite_nbre) { |
$this->limite_debut = $limite_debut; |
$this->limite_nbre = $limite_nbre; |
} |
public function getLimiteDebut() { |
return $this->limite_debut; |
} |
public function getLimiteNbre() { |
return $this->limite_nbre; |
} |
public function viderLimite() { |
$this->limite_debut = null; |
$this->limite_nbre = null; |
} |
public function addOrdre($champ, $trie = self::ORDRE_ASCENDANT) { |
if (!isset($this->ordre[$champ])) { |
if (self::ORDRE_ASCENDANT == $trie || self::ORDRE_DESCENDANT == $trie) { |
$this->ordre[$champ] = $trie; |
} else { |
$e = "La valeur pour le trie doit être : {self::ORDRE_ASCENDANT} ou {self::ORDRE_DESCENDANT}."; |
trigger_error($e, E_USER_WARNING); |
} |
} else { |
$e = "Le champ $champ existe déjà dans le tableau des ordres."; |
trigger_error($e, E_USER_WARNING); |
} |
} |
public function getOrdre() { |
$champs = array(); |
foreach ($this->ordre as $champ => $trie) { |
$champs[] = "$champ $trie"; |
} |
return implode(', ', $champs); |
} |
public function viderOrdre() { |
$this->ordre = null; |
} |
//+----------------------------------------------------------------------------------------------------------------+ |
// MÉTHODES |
public function envoyerRequeteConsultation($url) { |
$url = $this->traiterUrlParametres($url); |
$retour = $this->envoyerRequete($url, 'GET'); |
return $retour; |
} |
public function envoyerRequeteAjout($url, Array $donnees) { |
$retour = $this->envoyerRequete($url, 'PUT', $donnees); |
return $retour; |
} |
public function envoyerRequeteModif($url, Array $donnees) { |
$retour = $this->envoyerRequete($url, 'POST', $donnees); |
return $retour; |
} |
public function envoyerRequeteSuppression($url) { |
$retour = $this->envoyerRequete($url, 'DELETE'); |
return $retour; |
} |
private function envoyerRequete($url, $mode, Array $donnees = array()) { |
$contenu = false; |
if ($mode != 'GET' && $mode != 'PUT' && $mode != 'POST' && $mode != 'DELETE') { |
$e = "Le mode de requête '$mode' n'est pas accepté!"; |
trigger_error($e, E_USER_WARNING); |
} else { |
$contexte = stream_context_create(array( |
'http' => array( |
'method' => $mode, |
'header' => "Content-type: application/x-www-form-urlencoded\r\n", |
'content' => http_build_query($donnees, null, self::HTTP_REQUETE_SEPARATEUR)))); |
$flux = @fopen($url, 'r', false, $contexte); |
if (!$flux) { |
$this->traiterEntete($http_response_header, $url); |
$e = "L'ouverture de l'url '$url' par la méthode HTTP '$mode' a échoué!"; |
trigger_error($e, E_USER_WARNING); |
} else { |
// Informations sur les en-têtes et métadonnées du flux |
$entetes = stream_get_meta_data($flux); |
$this->traiterEntete($entetes, $url); |
// Contenu actuel de $url |
$contenu = stream_get_contents($flux); |
fclose($flux); |
} |
} |
$this->reinitialiser(); |
return $contenu; |
} |
private function traiterUrlParametres($url) { |
$parametres = array(); |
if (! is_null($this->getLimiteDebut())) { |
$parametres[] = 'start='.$this->getLimiteDebut(); |
} |
if (! is_null($this->getLimiteNbre())) { |
$parametres[] = 'limit='.$this->getLimiteNbre(); |
} |
if (! is_null($this->ordre)) { |
$parametres[] = 'orderby='.urlencode($this->getOrdre()); |
} |
if ($this->getDistinction() != 0) { |
$parametres[] = 'distinct='.$this->getDistinction(); |
} |
if (count($parametres) > 0) { |
$url_parametres = implode('&', $parametres); |
$url = $url.'?'.$url_parametres; |
} |
return $url; |
} |
private function traiterEntete($entetes, $uri) { |
$infos = $this->analyserEntete($entetes, $uri); |
$this->traiterEnteteDebug($infos); |
$this->traiterEnteteMessage($infos); |
} |
private function analyserEntete($entetes, $uri) { |
$infos = array('date' => null, 'uri' => $uri, 'debugs' => null, 'messages' => null); |
if (isset($entetes['wrapper_data'])) { |
$entetes = $entetes['wrapper_data']; |
} |
foreach ($entetes as $entete) { |
if (preg_match('/^X-DebugJrest-Data: (.+)$/', $entete, $match)) { |
$infos['debugs'] = json_decode($match[1]); |
} |
if (preg_match('/^X-MessageJrest-Data: (.+)$/', $entete, $match)) { |
$infos['messages'] = json_decode($match[1]); |
} |
if (preg_match('/^Date: .+ ([012][0-9]:[012345][0-9]:[012345][0-9]) .*$/', $entete, $match)) { |
$infos['date'] = $match[1]; |
} |
} |
return $infos; |
} |
private function traiterEnteteDebug($entetes) { |
if (isset($entetes['debugs'])) { |
$date = $entetes['date']; |
$uri = $entetes['uri']; |
$debugs = $entetes['debugs']; |
foreach ($debugs as $debug) { |
$e = "DEBUG : $date - $uri :\n$debug"; |
trigger_error($e, E_USER_NOTICE); |
} |
} |
} |
private function traiterEnteteMessage($entetes) { |
if (isset($entetes['messages'])) { |
$date = $entetes['date']; |
$uri = $entetes['uri']; |
$messages = $entetes['messages']; |
foreach ($messages as $message) { |
$e = "MESSAGE : $date - $uri :\n$message"; |
trigger_error($e, E_USER_NOTICE); |
} |
} |
} |
private function reinitialiser() { |
$this->viderDistinction(); |
$this->viderLimite(); |
$this->viderOrdre(); |
} |
} |
/trunk/jrest/services/Del.php |
---|
New file |
0,0 → 1,479 |
<?php |
/** |
* 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@clapas.org> |
* @author AUrélien PERONNET <aurelien@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 © 2010, Jean-Pascal MILCENT |
*/ |
abstract class Del { |
const SQL_MODE_ASSOC = PDO::FETCH_ASSOC; |
const SQL_RETOUR_COMPLET = 'All'; |
const SQL_RETOUR_LIGNE = 'Row'; |
const SQL_RETOUR_COLONNE = 'Column'; |
const SQL_RETOUR_BRUT = 'Raw'; |
public $config; |
private $ressources; |
protected $parametres = array(); |
protected $bdd; |
protected $messages = array(); |
protected $debug = array(); |
public function __construct($config) { |
// Tableau contenant la config de Jrest |
$this->config = $config; |
// Réglages de PHP |
setlocale(LC_ALL, $this->config['settings']['locale']); |
date_default_timezone_set($this->config['settings']['fuseauHoraire']); |
// Nettoyage du _GET (sécurité) |
$this->collecterParametres();// Récupération de tous les parametres de _GET, nettoyage et mise dans $this->parametres |
$this->recupererParametresUrl();// Vidage de _GET et création d'attribut de la classe |
$this->definirParametresUrlParDefaut(); |
// Définition de variable générale dans la config |
$this->config['settings']['baseURLAbsoluDyn'] = 'http://'.$_SERVER['SERVER_NAME'].$this->config['settings']['baseURL'].'%s'; |
} |
//+----------------------------------------------------------------------------------------------------------------+ |
// GESTION de la BASE de DONNÉES |
private function connecterPDOSiNecessaire() { |
if($this->bdd == null) { |
// Connection à la base de données |
$this->bdd = $this->connecterPDO($this->config, 'database'); |
} |
} |
private function connecterPDO($config, $base = 'database') { |
$cfg = $config[$base]; |
// ATTENTION : la connexin à la bdd peut échouer si l'host vaut localhost. Utiliser 127.0.0.1 à la place. |
$dsn = $cfg['phptype'].':dbname='.$cfg['database'].';host='.$cfg['hostspec']; |
try { |
// Création de la connexion en UTF-8 à la BDD |
$PDO = new PDO($dsn, $cfg['username'], $cfg['password'], array(PDO::MYSQL_ATTR_INIT_COMMAND => "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); |
} catch (PDOException $e) { |
echo 'La connexion à la base de donnée via PDO a échouée : ' .$dsn. $e->getMessage(); |
} |
return $PDO; |
} |
/** |
* Protège automatiquement toutes les chaines comprises entre deux caractères '|'. |
* Puis execute la requete. |
* @see protegerRequete() |
* @param unknown_type $requete |
*/ |
protected function requeter($requete, $retour = self::SQL_RETOUR_COMPLET, $mode = PDO::FETCH_ASSOC) { |
$requete = $this->protegerRequete($requete); |
return $this->executerRequete($requete, $retour, $mode); |
} |
/** |
* Protège automatiquement toutes les chaines comprises entre deux caractères '|'. |
* @see protegerRequete() |
* @param unknown_type $requete |
*/ |
protected function executer($requete) { |
$requete = $this->protegerRequete($requete); |
return $this->executerRequeteSimple($requete); |
} |
/** |
* Méthode permettant de rechercher dans une requete SQL sous forme de chaine (String) les chaines |
* à protéger. Cela évite de protéger chaque variable avant de l'insérer dans une requete SQL. |
* Par contre, il est important que les chaine à protéger ne contiennent pas le caractère '|'. |
* |
* @param $requete |
*/ |
protected function protegerRequete($requete) { |
$this->connecterPDOSiNecessaire(); |
if (substr_count($requete, '|') % 2 === 0) { |
if (preg_match_all('/\|([^|]*)\|/', $requete, $correspondances, PREG_SET_ORDER)) { |
foreach ($correspondances as $chaine) { |
$chaine_protegee = $this->bdd->quote($chaine[1]); |
$requete = str_replace($chaine[0], $chaine_protegee, $requete); |
} |
} |
} else { |
$this->messages[] = "La requête a protéger contient un nombre impair de caractère de protection '|'."; |
$requete = false; |
} |
return $requete; |
} |
protected function proteger($chaine) { |
$this->connecterPDOSiNecessaire(); |
return $this->bdd->quote($chaine); |
} |
protected function protegerTableau(Array $tableau) { |
foreach ($tableau as $id => $val) { |
$tableau[$id] = $this->proteger($val); |
} |
return $tableau; |
} |
/** |
* @deprecated utiliser executer() à la place |
* @see executer() |
*/ |
protected function executerRequeteSimple($requete) { |
$this->connecterPDOSiNecessaire(); |
$resultat = false; |
try { |
$resultat = $this->bdd->exec($requete); |
if ($resultat === false) { |
$this->debug[] = "La requête a échoué : $requete"; |
} |
} catch (PDOException $e) { |
$this->debug[] = sprintf($this->getTxt('sql_erreur_requete'), $e->getFile(), $e->getLine(), $e->getMessage(), $requete); |
} |
return $resultat; |
} |
/** |
* @deprecated utiliser requeter() à la place |
* @see requeter() |
*/ |
protected function executerRequete($requete, $retour = self::SQL_RETOUR_COMPLET, $mode = PDO::FETCH_ASSOC) { |
$this->connecterPDOSiNecessaire(); |
$resultat = false; |
try { |
switch ($retour) { |
case self::SQL_RETOUR_COMPLET : |
$resultat = $this->bdd->query($requete)->fetchAll($mode);// Retourne toutes les lignes |
break; |
case self::SQL_RETOUR_LIGNE : |
$resultat = $this->bdd->query($requete)->fetch($mode);// Retourne la première ligne |
break; |
case self::SQL_RETOUR_COLONNE : |
$resultat = $this->bdd->query($requete)->fetchColumn();// Retourne la première colonne de la première ligne |
break; |
case self::SQL_RETOUR_BRUT : |
$resultat = $this->bdd->query($requete);// Retourne l'objet brut pour être utilisé dans une boucle de type foreach |
break; |
default: |
$this->debug[] = "Le type de retour '$retour' est inconnu."; |
} |
if ($resultat === false) { |
$this->debug[] = "La requête a retourné aucun résultat : $requete"; |
} |
} catch (PDOException $e) { |
$this->debug[] = sprintf($this->getTxt('sql_erreur_requete'), $e->getFile(), $e->getLine(), $e->getMessage(), $requete); |
} |
return $resultat; |
} |
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; |
} |
//+----------------------------------------------------------------------------------------------------------------+ |
// TRAITEMENT des URLs et des PARAMÊTRES |
private function collecterParametres() { |
if (isset($_GET) && $_GET != '') { |
foreach ($_GET as $cle => $valeur) { |
$this->parametres[$cle] = $this->verifierSecuriteParametreUrl($valeur); |
} |
} |
} |
private function recupererParametresUrl() { |
if (isset($_GET)) { |
$get_params = array('orderby', 'distinct', 'start', 'limit', 'formatRetour'); |
foreach ($get_params as $get) { |
if (isset($_GET[$get])) { |
$_GET[$get] = $this->verifierSecuriteParametreUrl($_GET[$get]); |
if ($_GET[$get] != '') { |
if (!isset($this->$get)) { |
$this->$get = $_GET[$get]; |
} else { |
$e = "Impossible d'ajouter l'attribut $get à la classe du service car elle possède déjà un attribut nommé : $get"; |
trigger_error($e, E_USER_WARNING); |
} |
} else { |
$_GET[$get] = null; |
} |
} |
} |
} |
} |
protected function verifierSecuriteParametreUrl($param) { |
$verifier = array('NULL', "\n", "\r", "\\", "'", '"', "\x00", "\x1a", ';'); |
$param = strip_tags(str_replace($verifier, '', $param)); |
return $param; |
} |
private function definirParametresUrlParDefaut() { |
if (!isset($this->start)) { |
$this->start = 0; |
} |
if (!isset($this->limit)) { |
$this->limit = 150; |
} |
} |
protected function traiterParametres($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 traiterNomMethodeGet($nom) { |
$methode = 'get'; |
$methode .= str_replace(' ', '', ucwords(str_replace('-', ' ', strtolower($nom)))); |
return $methode; |
} |
//+----------------------------------------------------------------------------------------------------------------+ |
// GESTION de l'ENVOI au NAVIGATEUR |
protected function envoyerJson($donnees, $encodage = 'utf-8') { |
$encodage_json = true; |
$this->envoyer($donnees, 'application/json', $encodage, $encodage_json); |
} |
protected function envoyerJsonVar($variable, $donnees = null, $encodage = 'utf-8') { |
$contenu = "var $variable = ".json_encode($donnees); |
$this->envoyer($contenu, 'text/html', $encodage); |
} |
protected function envoyerJsonp($donnees = null, $encodage = 'utf-8') { |
$contenu = $this->parametres['callback'].'('.json_encode($donnees).');'; |
$this->envoyer($contenu, 'text/html', $encodage); |
} |
protected function envoyer($donnees = null, $mime = 'text/html', $encodage = 'utf-8', $json = false) { |
// Traitements des messages d'erreurs et données |
if (count($this->messages) != 0) { |
header('HTTP/1.1 500 Internal Server Error'); |
$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); |
} |
private function envoyerDebogage() { |
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)); |
} |
} |
private 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 envoyerAuth($message_accueil, $message_echec) { |
header('HTTP/1.0 401 Unauthorized'); |
header('WWW-Authenticate: Basic realm="'.mb_convert_encoding($message_accueil, 'ISO-8859-1', 'UTF-8').'"'); |
header('Content-type: text/plain; charset=UTF-8'); |
print $message_echec; |
exit(0); |
} |
//+----------------------------------------------------------------------------------------------------------------+ |
// GESTION DU DAO |
protected function getDao() { |
if (isset($this->dao)) { |
return $this->dao; |
} else { |
$e = "Le DAO n'a pas été initialisé. Utiliser la méthode initialiserDao()."; |
trigger_error($e, E_USER_WARNING); |
} |
} |
protected function initialiserDao($url_services_distant) { |
if (!isset($this->dao)) { |
$this->dao = new Dao($url_services_distant); |
} else { |
$this->dao->url_jrest = $url_services_distant; |
} |
} |
//+----------------------------------------------------------------------------------------------------------------+ |
// GESTION DU LOG |
public function logger($index,$chaine) { |
Log::getInstance(); |
Log::setCheminLog($this->config['log']['cheminlog']); |
Log::setTimeZone($this->config['log']['timezone']); |
Log::setTailleMax($this->config['log']['taillemax']); |
Log::ajouterEntree($index,$chaine); |
} |
//+----------------------------------------------------------------------------------------------------------------+ |
// GESTION DE MÉTHODES COMMUNES ENTRE LES SERVICES |
protected function nettoyerTableau(Array $tableau) { |
foreach ($tableau as $cle => $valeur) { |
if (is_array($valeur)) { |
$valeur = $this->nettoyerTableau($valeur); |
} else { |
$valeur = $this->nettoyerTexte($valeur); |
} |
$tableau[$cle] = $valeur; |
} |
return $tableau; |
} |
/** |
* Fonction nettoyant les caractères spéciaux (&,<) et les valeurs nulles du CEL dans un texte comprenant du HTML. |
*/ |
protected function nettoyerTexte($txt) { |
$txt = preg_replace('/&(?!([a-z]+|#[0-9]+|#x[0-9][a-f]+);)/i', '&', $txt); |
// TODO : trouver une regexp qui permet de remplacer les symboles < et > isolés |
//$txt = preg_replace('/<(?!([a-z][a-z0-9]*)\b[^>]*>(.*?)<\/\1>|\/\s*([a-z][a-z0-9]*)\s*>)/i', '<', $txt); |
//$txt = preg_replace('/(?!<([a-z][a-z0-9]*)\b[^>]*)>(?!(.*?)<\/\1>)/i', '>', $txt); |
$txt = preg_replace('/(?:000null|null)/i', '', $txt); |
return $txt; |
} |
protected function convertirDateHeureMysqlEnTimestamp($date_heure_mysql){ |
$val = explode(' ', $date_heure_mysql); |
$date = explode('-', $val[0]); |
$heure = explode(':', $val[1]); |
return mktime((int) $heure[0], (int) $heure[1], (int) $heure[2], (int) $date[1], (int) $date[2], (int) $date[0]); |
} |
protected function etreNull($valeur) { |
$etre_null = false; |
if ($valeur == '' || $valeur == null || $valeur == '000null' || $valeur == 'null' || $valeur == '*') { |
$etre_null = true; |
} |
return $etre_null; |
} |
protected function formaterDate($date_heure_mysql, $format = '%A %d %B %Y à %H:%M') { |
$date_formatee = ''; |
if (!$this->etreNull($date_heure_mysql)) { |
$timestamp = $this->convertirDateHeureMysqlEnTimestamp($date_heure_mysql); |
$date_formatee = strftime($format, $timestamp); |
} |
return $date_formatee; |
} |
protected function traiterValeursMultiples($valeurs, $separateur_entree = ',' , $separateur_sortie = ',') { |
if (! $this->etreNull($valeurs)) { |
$valeurs_a_proteger = explode($separateur_entree,trim(trim($valeurs), $separateur_entree)); |
foreach ($valeurs_a_proteger as $valeur) { |
$valeurs_protegees[] = $this->bdd->quote($valeur); |
} |
$valeurs = implode($separateur_sortie, $valeurs_protegees); |
} |
return ($this->etreNull($valeurs)) ? null : $valeurs; |
} |
//+----------------------------------------------------------------------------------------------------------------+ |
// GESTION DES SQUELETTES PHP |
/** |
* 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; |
} |
} |
?> |
/trunk/jrest/.htaccess |
---|
New file |
0,0 → 1,4 |
RewriteEngine On |
RewriteCond %{REQUEST_FILENAME} !-d |
RewriteCond %{REQUEST_FILENAME} !-f |
RewriteRule ^.*$ /del/jrest/index.php |
/trunk/jrest/jrest.ini.php |
---|
New file |
0,0 → 1,30 |
<? |
/* |
[settings] |
baseURL = "/del/jrest/" |
; URL de base absolue du Jrest |
baseURLAbsolu = "http://localhost/del/jrest/" |
; URL de base absolue du Jrest du DEL construit dynamiquement dans le fichier Del.php |
baseURLAbsoluDyn = "" |
; Url absolue du DEL |
delUrlAbsolu = "http://localhost/del/" |
; Indication du nom de l'éditeur pour les flux de syndication |
editeur = "Tela Botanica" |
; Format du Guid du CEL pour les flux de syndication principalement |
guidObsTpl = "urn:lsid:tela-botanica.org:del:%s" |
;Format du Guid des images pour les flux de syndication |
guidImgTpl = "%s" |
; Indication de la locale (setLocale(LC_ALL, ?)) pour les classes héritant de Cel |
locale = "fr_FR.UTF-8" |
; Indication du fuseau horraire par défaut date_default_timezone_set(?)pour les classes héritant de Cel |
fuseauHoraire = "Europe/Paris" |
; Determination en ligne |
[database] |
phptype = mysql |
username = |
password = |
hostspec = |
database = del |
;*/?> |
/trunk/jrest/bibliotheque/Log.php |
---|
New file |
0,0 → 1,195 |
<?php |
//declare(encoding='UTF-8'); |
/** |
* Classe permettant de logger des messages dans les fichier situés dans le dossier de log |
* |
* PHP Version 5 |
* |
* @category PHP |
* @package Framework |
* @author aurelien <aurelien@tela-botanica.org> |
* @copyright 2009 Tela-Botanica |
* @license http://www.cecill.info/licences/Licence_CeCILL_V2-fr.txt Licence CECILL |
* @version SVN: <svn_id> |
* @link /doc/framework/ |
*/ |
class Log { |
/** |
* Tableau associatif stockant les descripteurs de fichiers |
*/ |
private static $fichiersLog = array(); |
/** |
* Chemin de base du dossier log de l'application |
*/ |
private static $cheminLogs = ''; |
/** |
* Booleen indiquant si l'on peut correctement écrire dans les fichiers de logs |
*/ |
private static $droitLogger = true; |
/** |
* Zone horaire (pour éviter des avertissements dans les dates) |
*/ |
private static $timeZone = ''; |
/** |
* Taille maximum d'un fichier de log avant que celui ne soit archivé (en octets) |
*/ |
private static $tailleMax = 10000; |
/** |
* séparateur de chemin |
*/ |
private static $sd = DIRECTORY_SEPARATOR; |
/** |
* Extension des fichiers de log |
*/ |
private static $ext = '.log'; |
/** |
* La classe registre se contient elle-même, (pour le pattern singleton) |
*/ |
private static $log; |
/** |
* Constructeur par défaut, privé, car on accède à la classe par le getInstance |
*/ |
private function __construct() { |
self::$sd = $sd; |
// gestion de la timezone pour éviter des erreurs |
if(function_exists("date_default_timezone_set") and function_exists("date_default_timezone_get")) { |
date_default_timezone_set(self::$timeZone); |
} |
if(!is_dir(self::$cheminLogs) || !is_writable(self::$cheminLogs)) { |
self::desactiverEcriture(); |
} |
} |
public static function setCheminLog($nouveauCheminLogs) { |
self::$cheminLogs = $nouveauCheminLogs; |
} |
public static function getCheminLog() { |
return self::$cheminLogs; |
} |
public static function setTimeZone($NouvelleTimeZone) { |
self::$timeZone = $NouvelleTimeZone; |
} |
public static function setTailleMax($nouvelleTailleMax) { |
self::$tailleMax = $nouvelleTailleMax; |
} |
/** |
* Fonction qui renvoie l'instance de classe en assurant son unicité, c'est l'unique méthode qui doit être |
* utilisée pour récupérer l'objet Registre |
* @return Log le gestionnaire de log en cours |
*/ |
public static function getInstance() { |
if (self::$log instanceof Log) { |
return self::$log; |
} |
self::$log = new Log(); |
return self::$log; |
} |
/** |
* Ajoute une entrée au log spécifié par le paramètre $nomFichier |
* @param string $nomFichier le nom du fichier dans lequel écrire |
*/ |
public static function ajouterEntree($nomFichier,$entree,$mode='a+') { |
if(self::$droitLogger) { |
$date = "\n"."\n".date('d m Y H:i')."\n" ; |
// si le fichier est déjà dans le tableau et qu'on peut y écrire |
if(self::verifierOuvrirFichier($nomFichier,$mode)) { |
// on y écrit le message de log |
fwrite(self::$fichiersLog[$nomFichier],$date.$entree); |
// on vérifie si le fichier ne dépasse pas la taille maximale |
self::verifierTailleFichierOuArchiver($nomFichier); |
} else { |
// sinon on interdit l'écriture |
self::desactiverEcriture($nomFichier); |
} |
} |
} |
/** |
* Vide un fichier log indiqué |
* @param string $nomFichier le nom du fichier à vider |
*/ |
public static function viderLog($nomFichier) { |
ajouterEntree($nomFichier,'','w'); |
} |
/** |
* Vérifie la présence d'un fichier dans le tableau, ses droits d'écriture, |
* l'ouvre si nécessaire |
* @param string $nomFichier le nom du fichier dont on doit vérifier la présence |
* @return boolean true si le fichier est ouvert ou maintenant accessible, false sinon |
*/ |
public static function verifierOuvrirFichier($nomFichier,$mode) { |
// le fichier est il déjà ouvert ? |
if(in_array($nomFichier,self::$fichiersLog)) { |
// si oui peut on y écrire ? |
if(is_writable(self::$cheminLogs.$nomFichier.self::$ext)) { |
// si oui on renvoie le descripteur |
return true; |
} |
return false; |
} else { |
// sinon on l'ouvre |
$fp = @fopen(self::$cheminLogs.$nomFichier.self::$ext,$mode); |
// si l'ouverture a réussi et si le fichier a les droits d'écriture |
if($fp && is_writable(self::$cheminLogs.$nomFichier.self::$ext)) { |
// si oui on renvoie le descripteur qu'on ajoute au tableau |
self::$fichiersLog[$nomFichier] = $fp; |
return true; |
} |
return false; |
} |
} |
/** |
* Vérifie la taille d'un fichier donné et si celle ci est trop importante |
* archive le fichier de log |
* @param string $nomFichier nom du fichier à vérifier |
*/ |
private static function verifierTailleFichierOuArchiver($nomFichier) { |
if(filesize(self::$cheminLogs.$nomFichier.self::$ext) > self::$tailleMax) { |
rename(self::$cheminLogs.$nomFichier.self::$ext,self::$cheminLogs.$nomFichier.date('d_m_Y_H:i').self::$ext); |
self::ajouterEntree($nomFichier,''); |
} |
} |
/** |
* Désactive l'écriture du log et envoie un message au gestionnaire d'erreurs |
* @param string $nomFichier le nom du fichier qui a causé l'erreur |
*/ |
private static function desactiverEcriture($nomFichier = '') { |
self::$droitLogger = false; |
if($nomFichier != '') { |
$fichierDossier = 'fichier '.$nomFichier ; |
} else { |
$fichierDossier = 'dossier des logs'; |
} |
} |
/** |
* destructeur de classe, ferme les descripteurs ouverts |
*/ |
public function __destruct() { |
foreach(self::$fichiersLog as $nomFichier => $fp) { |
fclose($fp); |
} |
} |
} |
?> |
/trunk/jrest/JRest.php |
---|
New file |
0,0 → 1,302 |
<?php |
// In : utf8 url_encoded (get et post) |
// Out : utf8 |
// TODO : gerer les retours : dans ce controleur : code retour et envoi ... |
class JRest { |
/** Parsed configuration file */ |
private $config; |
/** The HTTP request method used. */ |
private $method = 'GET'; |
/** The HTTP request data sent (if any). */ |
private $requestData = NULL; |
/** Array of strings to convert into the HTTP response. */ |
private $output = array(); |
/** Nom resource. */ |
private $resource = NULL; |
/** Identifiant unique resource. */ |
private $uid = NULL; |
/** |
* Constructor. Parses the configuration file "JRest.ini", grabs any request data sent, records the HTTP |
* request method used and parses the request URL to find out the requested resource |
* @param str iniFile Configuration file to use |
*/ |
public function JRest($iniFile = 'jrest.ini.php') { |
$this->config = parse_ini_file($iniFile, TRUE); |
if (isset($_SERVER['REQUEST_URI']) && isset($_SERVER['REQUEST_METHOD']) && isset($_SERVER['QUERY_STRING'])) { |
if (isset($_SERVER['CONTENT_LENGTH']) && $_SERVER['CONTENT_LENGTH'] > 0) { |
$this->requestData = ''; |
$httpContent = fopen('php://input', 'r'); |
while ($data = fread($httpContent, 1024)) { |
$this->requestData .= $data; |
} |
fclose($httpContent); |
} |
if (strlen($_SERVER['QUERY_STRING']) == 0) { |
$len = strlen($_SERVER['REQUEST_URI']); |
} else { |
$len = -(strlen($_SERVER['QUERY_STRING']) + 1); |
} |
$urlString = substr($_SERVER['REQUEST_URI'], strlen($this->config['settings']['baseURL']), $len); |
$urlParts = explode('/', $urlString); |
if (isset($urlParts[0])) $this->resource = $urlParts[0]; |
if (count($urlParts) > 1 && $urlParts[1] != '') { |
array_shift($urlParts); |
foreach ($urlParts as $uid) { |
if ($uid != '') { |
$this->uid[] = urldecode($uid); |
} |
} |
} |
$this->method = $_SERVER['REQUEST_METHOD']; |
} else { |
trigger_error('I require the server variables REQUEST_URI, REQUEST_METHOD and QUERY_STRING to work.', E_USER_ERROR); |
} |
} |
/** |
* Execute the request. |
*/ |
function exec() { |
switch ($this->method) { |
case 'GET': |
$this->get(); |
break; |
case 'POST': |
$this->post(); |
break; |
case 'DELETE': |
$this->delete(); |
break; |
case 'PUT': |
$this->add(); |
break; |
} |
} |
/** |
* Execute a GET request. A GET request fetches a list of resource when no resource name is given, a list of element |
* when a resource name is given, or a resource element when a resource and resource unique identifier are given. It does not change the |
* database contents. |
*/ |
private function get() { |
if ($this->resource) { |
$resource_file = 'services/'.ucfirst($this->resource).'.php'; |
$resource_class = ucfirst($this->resource); |
if (file_exists($resource_file)) { |
include_once $resource_file; |
if (class_exists($resource_class)) { |
$service = new $resource_class($this->config); |
if ($this->uid) { // get a resource element |
if (method_exists($service, 'getElement')) { |
$service->getElement($this->uid); |
} |
} elseif (method_exists($service, 'getRessource')) { // get all elements of a ressource |
$service->getRessource(); |
} |
} |
} |
} else { // get resources |
// include set.jrest.php, instanticiation et appel |
} |
} |
private function post() { |
$pairs = array(); |
// Récupération des paramètres passés dans le contenu de la requête HTTP (= POST) |
if ($this->requestData) { |
$pairs = $this->parseRequestData(); |
} |
// Ajout des informations concernant l'upload de fichier passées dans la variable $_FILE |
if(isset($_FILES)) { |
foreach ($_FILES as $v) { |
$pairs[$v['name']] = $v; |
} |
// Ne pas effacer cette ligne ! Elle est indispensable pour les services du Carnet en ligne |
// qui n'utilisent que le tableau pairs dans les posts |
$pairs = array_merge($pairs, $_POST); |
} |
// gestion du contenu du post |
if(isset($_POST)) |
{ |
// Safari ne sait pas envoyer des DELETE avec gwt... |
// Nous utilisons le parametre "action" passé dans le POST qui doit contenir DELETE pour lancer la supression |
if ($pairs['action'] == 'DELETE') { |
$this->delete(); |
return; |
} |
if (count($pairs) != 0) { |
if ($this->uid) { // get a resource element |
$resource_file = 'services/'.ucfirst($this->resource).'.php'; |
$resource_class = ucfirst($this->resource); |
if (file_exists($resource_file)) { |
include_once $resource_file; |
if (class_exists($resource_class)) { |
$service = new $resource_class($this->config); |
if (method_exists($service,'updateElement')) { // Update element |
// TODO : a voir le retour ... |
if ($service->updateElement($this->uid, $pairs)) { |
$this->created(); |
} |
} |
} |
} |
} else { // get all elements of a ressource |
$this->add($pairs); |
} |
} else { |
$this->lengthRequired(); |
} |
} |
} |
private function delete() { |
$resource_file = 'services/'.ucfirst($this->resource).'.php'; |
$resource_class = ucfirst($this->resource); |
if (file_exists($resource_file)) { |
include_once $resource_file; |
if (class_exists($resource_class)) { |
$service = new $resource_class($this->config); |
if ($this->uid) { // get a resource element |
if (method_exists($service, 'deleteElement')) { // Delete element |
if ($service->deleteElement($this->uid)) { |
$this->noContent(); |
} |
} |
} |
} |
} |
} |
private function add($pairs = null) { |
if (is_null($pairs)) { |
$pairs = array(); |
// Récupération des paramètres passés dans le contenu de la requête HTTP (= POST) |
// FIXME : vérifier que l'on récupère bien les données passées par PUT |
if ($this->requestData) { |
$pairs = $this->parseRequestData(); |
} |
} |
if (count($pairs) != 0) { |
$resource_file = 'services/'.ucfirst($this->resource).'.php'; |
$resource_class = ucfirst($this->resource); |
if (file_exists($resource_file)) { |
include_once $resource_file; |
if (class_exists($resource_class)) { |
$service = new $resource_class($this->config); |
if (method_exists($service,'createElement')) { // Create a new element |
if ($service->createElement($pairs)) { |
$this->created(); |
} |
} |
} |
} |
} else { |
$this->lengthRequired(); |
} |
} |
/** |
* Parse the HTTP request data. |
* @return str[] Array of name value pairs |
*/ |
private function parseRequestData() { |
$values = array(); |
$pairs = explode('&', $this->requestData); |
foreach ($pairs as $pair) { |
$parts = explode('=', $pair); |
if (isset($parts[0]) && isset($parts[1])) { |
$parts[1] = rtrim(urldecode($parts[1])); |
$values[$parts[0]] = $parts[1]; |
} |
} |
return $values; |
} |
/** |
* Send a HTTP 201 response header. |
*/ |
private function created($url = FALSE) { |
header('HTTP/1.0 201 Created'); |
if ($url) { |
header('Location: '.$url); |
} |
} |
/** |
* Send a HTTP 204 response header. |
*/ |
private function noContent() { |
header('HTTP/1.0 204 No Content'); |
} |
/** |
* Send a HTTP 400 response header. |
*/ |
private function badRequest() { |
header('HTTP/1.0 400 Bad Request'); |
} |
/** |
* Send a HTTP 401 response header. |
*/ |
private function unauthorized($realm = 'JRest') { |
if (!isset($_SERVER['PHP_AUTH_USER']) || !isset($_SERVER['PHP_AUTH_PW'])) { |
header('WWW-Authenticate: Basic realm="'.$realm.'"'); |
} |
header('HTTP/1.0 401 Unauthorized'); |
} |
/** |
* Send a HTTP 404 response header. |
*/ |
private function notFound() { |
header('HTTP/1.0 404 Not Found'); |
} |
/** |
* Send a HTTP 405 response header. |
*/ |
private function methodNotAllowed($allowed = 'GET, HEAD') { |
header('HTTP/1.0 405 Method Not Allowed'); |
header('Allow: '.$allowed); |
} |
/** |
* Send a HTTP 406 response header. |
*/ |
private function notAcceptable() { |
header('HTTP/1.0 406 Not Acceptable'); |
echo join(', ', array_keys($this->config['renderers'])); |
} |
/** |
* Send a HTTP 411 response header. |
*/ |
private function lengthRequired() { |
header('HTTP/1.0 411 Length Required'); |
} |
/** |
* Send a HTTP 500 response header. |
*/ |
private function internalServerError() { |
header('HTTP/1.0 500 Internal Server Error'); |
} |
} |
?> |
/trunk/jrest/index.php |
---|
New file |
0,0 → 1,27 |
<?php |
/** |
* La fonction __autoload() charge dynamiquement les classes trouvées dans le code. |
* |
* Cette fonction est appelée par php5 quand il trouve une instanciation de classe dans le code. |
* |
*@param string le nom de la classe appelée. |
*@return void le fichier contenant la classe doit être inclu par la fonction. |
*/ |
function __autoload($classe) |
{ |
if (class_exists($classe)) { |
return null; |
} |
$chemins = array('', 'services/', 'bibliotheque/'); |
foreach ($chemins as $chemin) { |
$chemin = $chemin.$classe.'.php'; |
if (file_exists($chemin)) { |
require_once $chemin; |
} |
} |
} |
$jRest =& new JRest(); |
$jRest->exec(); |
?> |