Subversion Repositories Applications.framework

Compare Revisions

Ignore whitespace Rev 359 → Rev 360

/trunk/framework/RestServeur.php
29,13 → 29,13
* Encodage en sortie : utf8
*
* @category Php 5.2
* @package Framework
* @package Framework
* @author Jean-Pascal MILCENT <jpm@tela-botanica.org>
* @copyright Copyright (c) 2010, Tela Botanica (accueil@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>
* @license GPL v3 <http://www.gnu.org/licenses/gpl.txt>
* @license CECILL v2 <http://www.cecill.info/licences/Licence_CeCILL_V2-en.txt>
* @since 0.3
* @version $Id: RestServeur.php 344 2011-06-10 07:51:56Z jpm $
* @version $Id: RestServeur.php 359 2011-08-30 13:53:45Z delphine $
* @link /doc/framework/
*/
// TODO : gerer les retours : dans ce controleur : code retour et envoi ...
95,6 → 95,12
* L'utiliser quand le serveur ou un service soulève une erreur ou une exception. */
const HTTP_CODE_ERREUR = '500';
/** Motif de l'epression régulière vérfiant la version de l'API. */
const MOTIF_API_VERSION = '/^[0-9]+(?:[.][0-9]+)*$/';
/** Motif de l'epression régulière vérfiant le nom du service. */
const MOTIF_SERVICE_NOM = '/^[a-z0-9]+(?:[-][a-z0-9]+)*$/';
/** Mettre à true pour activer l'affichage des messages d'erreurs et de débogage.
* @var boolean */
private static $debogageActivation = false;
143,6 → 149,11
* @var array
*/
private static $config = array();
 
/** Tableau contenant les messages d'erreur et/ou d'avertissement du Serveur.
* @var array
* */
private static $messages = array();
/** Codes HTTP. */
private static $http10 = array(
176,6 → 187,7
$this->initialiserRequeteDonnees();
$urlParts = $this->decouperUrlChemin();
$this->initialiserApiVersion(array_shift($urlParts));
$this->initialiserServiceNom(array_shift($urlParts));
$this->initialiserRessource($urlParts);
184,8 → 196,9
// Enregistrement en première position des autoload de la méthode gérant les classes des services
spl_autoload_register(array(get_class(), 'chargerClasse'));
} else {
self::envoyerEnteteStatutHttp(self::HTTP_CODE_ERREUR);
$e = "La classe Serveur du TBFRamework nécessite, pour fonctionner, l'accès aux variables serveurs REQUEST_URI, REQUEST_METHOD et QUERY_STRING.";
trigger_error($e, E_USER_ERROR);
self::ajouterMessage($e);
}
}
210,7 → 223,7
private function decouperUrlChemin() {
if (isset($_SERVER['REDIRECT_URL']) && $_SERVER['REDIRECT_URL'] != '') {
if (isset($_SERVER['REDIRECT_QUERY_STRING'])) {
if (isset($_SERVER['REDIRECT_QUERY_STRING']) && !empty($_SERVER['REDIRECT_QUERY_STRING'])) {
$url = $_SERVER['REDIRECT_URL'].'?'.$_SERVER['REDIRECT_QUERY_STRING'];
} else {
$url = $_SERVER['REDIRECT_URL'];
218,13 → 231,13
} else {
$url = $_SERVER['REQUEST_URI'];
}
//echo '<pre>'; print_r($_SERVER); echo '</pre>';
if (strlen($_SERVER['QUERY_STRING']) == 0) {
$tailleURL = strlen($url);
} else {
$tailleURL = -(strlen($_SERVER['QUERY_STRING']) + 1);
}
//echo '<br/>url : '.$url;
$urlChaine = '';
if (strpos($url, Config::get('serveur.baseURL')) !== false) {
$urlChaine = substr($url, strlen(Config::get('serveur.baseURL')), $tailleURL);
235,24 → 248,48
}
private function initialiserApiVersion($apiVersion) {
if (isset($apiVersion) && !empty($apiVersion)) {
if ($this->verifierApiVersion($apiVersion)) {
$this->apiVersion = $apiVersion;
self::$config['chemins']['api'] = Config::get('chemin_modules').$this->apiVersion.DS;
} else {
$e = "Aucune version d'API n'a été spécifié dans l'url qui doit avoir la forme suivante http://www.mondomaine.org/services/apiVersion/monService/";
trigger_error($e, E_USER_ERROR);
self::envoyerEnteteStatutHttp(self::HTTP_CODE_MAUVAISE_REQUETE);
$e = "Aucune version d'API n'a été spécifiée.\n".
"La version doit respecter l'expression régulière suivante : ".self::MOTIF_API_VERSION.".\n".
"L'url doit avoir la forme suivante : http://www.mondomaine.org/services/apiVersion/monService/";
self::ajouterMessage($e);
self::cloreAccesServeur();
}
}
private function verifierApiVersion($apiVersion) {
$apiOk = false;
if (isset($apiVersion) && !empty($apiVersion) && preg_match(self::MOTIF_API_VERSION, $apiVersion)) {
$apiOk = true;
}
return $apiOk;
}
private function initialiserServiceNom($serviceNom) {
if (isset($serviceNom) && !empty($serviceNom)) {
if ($this->verifierServiceNom($serviceNom)) {
$this->service = $this->traiterNomService($serviceNom);
} else {
$e = "Aucun service n'a été spécifié dans l'url qui doit avoir la forme suivante http://www.mondomaine.org/services/apiVersion/monService/";
trigger_error($e, E_USER_ERROR);
self::envoyerEnteteStatutHttp(self::HTTP_CODE_MAUVAISE_REQUETE);
$e = "Aucune nom de service n'a été spécifié.\n".
"La nom du service doit respecter l'expression régulière suivante : ".self::MOTIF_SERVICE_NOM.".\n".
"L'url doit avoir la forme suivante : http://www.mondomaine.org/services/apiVersion/monService/";
self::ajouterMessage($e);
self::cloreAccesServeur();
}
}
private function verifierServiceNom($serviceNom) {
$serviceNomOk = false;
if (isset($serviceNom) && !empty($serviceNom) && preg_match(self::MOTIF_SERVICE_NOM, $serviceNom)) {
$serviceNomOk = true;
}
return $serviceNomOk;
}
private function traiterNomService($serviceNom) {
return str_replace(' ', '', ucwords(str_replace('-', ' ', strtolower($serviceNom))));
}
294,20 → 331,28
if (class_exists($classe)) {
return null;
}
 
$classeTrouvee = false;
$chemins = array('', self::$config['chemins']['api']);
foreach ($chemins as $chemin) {
$chemin = $chemin.$classe.'.php';
if (file_exists($chemin)) {
require_once $chemin;
$classeTrouvee = true;
}
}
if ($classeTrouvee === false) {
self::envoyerEnteteStatutHttp(self::HTTP_CODE_RESSOURCE_INTROUVABLE);
$e = "La classe '$classe' du service n'a pas été trouvée par le serveur.\n".
"Cela peut signifier que le nom du service saisi comporte une erreur.";
self::ajouterMessage($e);
self::cloreAccesServeur();
}
}
/**
* Execute la requête.
*/
function executer() {
public function executer() {
switch ($this->methode) {
case 'GET':
$this->get();
322,11 → 367,13
$this->put();
break;
default :
$e = "La méthode HTTP '{$this->methode}' n'est pas prise en compte par ce serveur REST.";
trigger_error($e, E_USER_WARNING);
self::envoyerEnteteStatutHttp(self::HTTP_CODE_METHODE_NON_AUTORISE);
header('Allow: GET, POST, DELETE, PUT');
$e = "La méthode HTTP '{$this->methode}' n'est pas prise en compte par ce serveur REST.\n".
"Consulter l'entête Allow pour connaître les méthodes autorisées.";
self::ajouterMessage($e);
}
// Affichage des exceptions et erreurs générées par les services
$this->gererErreurs();
$this->cloreAccesServeur();
}
 
/**
341,9 → 388,10
$methodeGet = self::METHODE_GET;
$Service->$methodeGet($this->ressources, $this->parametres);
} else {
self::envoyerEnteteStatutHttp(self::HTTP_CODE_RESSOURCE_INTROUVABLE);
$e = "Le service '{$this->service}' ne contient pas la méthode '".self::METHODE_GET."' nécessaire ".
"lors de l'appel du service via la méthode HTTP GET.";
trigger_error($e, E_USER_ERROR);
self::ajouterMessage($e);
}
}
}
350,7 → 398,6
 
private function post() {
$paires = $this->parserDonneesRequete();
 
if (count($paires) != 0) {
if (isset($paires['action']) && $paires['action'] == 'DELETE') {// Altnative à l'utilisation de DELETE
$this->delete();
361,40 → 408,48
$Service = new $this->service(self::$config);
if (method_exists($Service, self::METHODE_POST)) {
$methodePost = self::METHODE_POST;
if ($Service->$methodePost($this->ressources, $paires)) {
$info = $Service->$methodePost($this->ressources, $paires);
if ($info === true) {
$this->envoyerEnteteStatutHttp(self::HTTP_CODE_CREATION_OK);
}
} else {
self::envoyerEnteteStatutHttp(self::HTTP_CODE_RESSOURCE_INTROUVABLE);
$e = "Le service '{$this->service}' ne contient pas la méthode '".self::METHODE_POST."' nécessaire ".
"lors de l'appel du service via la méthode HTTP POST.";
trigger_error($e, E_USER_ERROR);
"lors de l'appel du service via la méthode HTTP GET.";
self::ajouterMessage($e);
}
}
}
} else {
$this->envoyerEnteteStatutHttp(self::HTTP_CODE_CONTENU_REQUIS);
$e = "Le service '{$this->service}' requiert de fournir le contenu à modifier dans le corps ".
"de la requête avec la méthode HTTP POST.";
self::ajouterMessage($e);
}
}
 
private function put() {
$paires = $this->parserDonneesRequete();
 
if (count($paires) != 0) {
if ($this->service != null) {
$Service = new $this->service(self::$config);
if (method_exists($Service, self::METHODE_PUT)) {
$methodePut = self::METHODE_PUT;
if ($Service->$methodePut($this->ressources, $paires)) {
$info = $Service->$methodePut($this->ressources, $paires);
if ($info === true) {
$this->envoyerEnteteStatutHttp(self::HTTP_CODE_CREATION_OK);
}
} else {
self::envoyerEnteteStatutHttp(self::HTTP_CODE_RESSOURCE_INTROUVABLE);
$e = "Le service '{$this->service}' ne contient pas la méthode '".self::METHODE_PUT."' nécessaire ".
"lors de l'appel du service via la méthode HTTP PUT (ou équivalant).";
trigger_error($e, E_USER_ERROR);
"lors de l'appel du service via la méthode HTTP GET.";
self::ajouterMessage($e);
}
}
} else {
$this->envoyerEnteteStatutHttp(self::HTTP_CODE_CONTENU_REQUIS);
$e = "Il est nécessaire de fournir du contenu dans le corps de la requête pour créer une nouvelle ressource.";
self::ajouterMessage($e);
}
}
 
404,19 → 459,25
$Service = new $this->service(self::$config);
if (method_exists($Service, self::METHODE_DELETE)) {
$methodeDelete = self::METHODE_DELETE;
if ($Service->$methodeDelete($this->ressources, $paires)) {
$info = $Service->$methodeDelete($this->ressources);
if ($info === true) {
$this->envoyerEnteteStatutHttp(self::HTTP_CODE_SUPPRESSION_OK);
} else {
} else if ($info === false) {
$this->envoyerEnteteStatutHttp(self::HTTP_CODE_RESSOURCE_INTROUVABLE);
$e = "La ressource à supprimer est introuvable. Il se peut qu'elle ait été préalablement supprimé.";
self::ajouterMessage($e);
}
} else {
self::envoyerEnteteStatutHttp(self::HTTP_CODE_RESSOURCE_INTROUVABLE);
$e = "Le service '{$this->service}' ne contient pas la méthode '".self::METHODE_DELETE."' nécessaire ".
"lors de l'appel du service via la méthode HTTP DELETE (ou équivalant).";
trigger_error($e, E_USER_ERROR);
"lors de l'appel du service via la méthode HTTP GET.";
self::ajouterMessage($e);
}
}
} else {
$this->envoyerEnteteStatutHttp(self::HTTP_CODE_MAUVAISE_REQUETE);
$e = "Il est nécessaire d'indiquer dans l'url la ressource à supprimer.";
self::ajouterMessage($e);
}
}
457,6 → 518,18
}
/**
* Termine l'accès au serveur après envoir envoyer les messages.
*/
private static function cloreAccesServeur() {
// Gestion des exceptions et erreurs générées par les services
self::gererErreurs();
// Envoie des messages d'erreur et d'avertissement du serveur
self::envoyerMessages();
// Nous terminons le script
exit(0);
}
/**
* Si des exceptions ou des erreurs sont soulevées par le serveur ou les services, elles sont gérées par cette méthode.
* Si nous avec des erreurs d'un type différent d'E_USER_NOTICE (réservé au débogage), elle sont renvoyées sur la sortie
* standard (via echo).
495,5 → 568,28
}
}
}
/**
* Permet d'ajouter un message d'erreur ou d'avertissement qui sera envoyé au client.
* Le message doit être au format texte et en UTF-8.
* @param string $message le message à envoyer.
*/
public static function ajouterMessage($message) {
if (isset($message) && !empty($message)) {
self::$messages[] = $message;
}
}
/**
* Envoie au client les éventuels messages d'erreur et d'avertissement du Serveur.
* Le format d'envoie est text/plain encodé en UTF-8.
*/
private static function envoyerMessages() {
if (count(self::$messages) > 0) {
header("Content-Type: text/plain; charset=utf-8");
print implode("\n", self::$messages);
}
}
}
?>