35,7 → 35,7 |
* @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; |
144,6 → 150,11 |
*/ |
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( |
self::HTTP_CODE_OK => 'OK', |
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); |
} |
} |
} |
?> |