New file |
0,0 → 1,337 |
<?php |
/** |
* Classe principale gérant les services. |
* Paramètres liés dans config.ini : |
* - serveur.baseURL |
* |
* Encodage en entrée : utf8 |
* Encodage en sortie : utf8 |
* |
* @category Php 5.2 |
* @package JRest |
* @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> |
* @copyright 2010 Tela-Botanica |
* @version $Id$ |
*/ |
// TODO : gerer les retours : dans ce controleur : code retour et envoi ... |
class Serveur { |
|
/** Nom de la méthode appelée dans un service pour éxécuter une requête de type GET. */ |
const METHODE_GET = 'getElement'; |
|
/** Nom de la méthode appelée dans un service pour éxécuter une requête de type POST. */ |
const METHODE_POST = 'getElement'; |
|
/** Nom de la méthode appelée dans un service pour éxécuter une requête de type DELETE. */ |
const METHODE_DELETE = 'getElement'; |
|
/** Nom de la méthode appelée dans un service pour éxécuter une requête de type PUT. */ |
const METHODE_PUT = 'getElement'; |
|
/** Les paramètres de configuration dynamiques internes au serveur. */ |
private static $config; |
|
/** La méthode de la requête HTTP utilisée. */ |
private $methode = 'GET'; |
|
/** Le contenu de la requête HTTP (s'il y en a). */ |
private $requeteDonnees = null;//requestData |
|
/** Version de l'API demandée. |
* Généralement deux nombres séparés par un point. Ex. : 1.0 |
* Ex. http://www.mondomaine.org/services/[apiVersion]/monService/ |
*/ |
private $apiVersion = null; |
|
/** Nom du service demandé. |
* Ex. http://www.mondomaine.org/services/apiVersion/[monService]/ |
*/ |
private $service = null;// $ressource |
|
/** Morceaux de l'url servant à préciser la ressource concerné pour service demandé. |
* Ex. http://www.mondomaine.org/services/apiVersion/monService/[maRessource/maSousResource...] |
*/ |
private $ressources = array();// $uid |
|
/** Partie de l'url servant à paramétrer le service demandé. |
* Ex. http://www.mondomaine.org/services/apiVersion/monService?monParametre1=maValeur1&monParametre2=maValeur2 |
*/ |
private $parametres = array(); |
|
/** Codes HTTP. */ |
private static $http10 = array( |
'201' => 'Created', |
'204' => 'No Content', |
'400' => 'Bad Request', |
'401' => 'Unauthorized', |
'404' => 'Not Found', |
'405' => 'Method Not Allowed', |
'406' => 'Not Acceptable', |
'411' => 'Length Required', |
'500' => 'Internal Server Error' |
); |
|
/** |
* Analyse les données envoyées au serveur, enregistre la méthode HTTP utilisée pour appeler le serveur et parse |
* l'url appelée pour trouver le service demandé. |
*/ |
public function __construct() { |
if (isset($_SERVER['REQUEST_URI']) && isset($_SERVER['REQUEST_METHOD']) && isset($_SERVER['QUERY_STRING'])) { |
if (isset($_SERVER['CONTENT_LENGTH']) && $_SERVER['CONTENT_LENGTH'] > 0) { |
$this->requeteDonnees = ''; |
$httpContent = fopen('php://input', 'r'); |
while ($data = fread($httpContent, 1024)) { |
$this->requeteDonnees .= $data; |
} |
fclose($httpContent); |
} |
if (strlen($_SERVER['QUERY_STRING']) == 0) { |
$tailleURL = strlen($_SERVER['REQUEST_URI']); |
} else { |
$tailleURL = -(strlen($_SERVER['QUERY_STRING']) + 1); |
} |
$urlString = substr($_SERVER['REQUEST_URI'], strlen(Config::get('serveur.baseURL')), $tailleURL); |
|
$urlParts = explode('/', $urlString); |
if (isset($urlParts[0]) && !empty($urlParts[0])) { |
$this->apiVersion = $urlParts[0]; |
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); |
} |
|
if (isset($urlParts[1]) && !empty($urlParts[1])) { |
$this->service = $this->traiterNomService($urlParts[1]); |
} 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); |
} |
|
if (count($urlParts) > 2 && $urlParts[2] != '') { |
array_shift($urlParts); |
array_shift($urlParts); |
foreach ($urlParts as $ressource) { |
if ($ressource != '') { |
$this->ressources[] = urldecode($ressource); |
} |
} |
} |
|
$this->nettoyerGet(); |
$this->parametres = $_GET; |
|
$this->methode = $_SERVER['REQUEST_METHOD']; |
|
// 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 { |
$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); |
} |
} |
|
private function traiterNomService($serviceNom) { |
return str_replace(' ', '', ucwords(str_replace('-', ' ', strtolower($serviceNom)))); |
} |
|
private function nettoyerGet() { |
if (isset($_GET) && count($_GET) > 0) { |
foreach ($_GET as $cle => $valeur) { |
$verifier = array('NULL', "\n", "\r", "\\", "'", '"', "\x00", "\x1a", ';'); |
$_GET[$cle] = strip_tags(str_replace($verifier, '', $valeur)); |
} |
} |
} |
|
/** |
* La méthode __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. |
*/ |
public static function chargerClasse($classe) { |
if (class_exists($classe)) { |
return null; |
} |
|
$chemins = array('', self::$config['chemins']['api']); |
foreach ($chemins as $chemin) { |
$chemin = $chemin.$classe.'.php'; |
if (file_exists($chemin)) { |
require_once $chemin; |
} |
} |
} |
|
/** |
* Execute la requête. |
*/ |
function executer() { |
switch ($this->methode) { |
case 'GET': |
$this->get(); |
break; |
case 'POST': |
$this->post(); |
break; |
case 'DELETE': |
$this->delete(); |
break; |
case 'PUT': |
$this->add(); |
break; |
} |
// Affichage des exceptions et erreurs générées par les services |
echo GestionnaireException::getExceptions(); |
} |
|
/** |
* 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() { |
Debug::printr($this); |
$Service = new $this->service(self::$config); |
if (method_exists($Service, self::METHODE_GET)) { |
$methodeGet = self::METHODE_GET; |
$Service->$methodeGet($this->ressources, $this->parametres); |
} else { |
$e = "La classe '{$this->service}' ne contient pas de méthode '".self::METHODE_GET."'."; |
trigger_error($e, E_USER_ERROR); |
} |
} |
|
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->parserDonneesRequete(); |
} |
|
// 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 = self::$config['settings']['servicesDir'].$this->ressource.'.php'; |
$resource_class = $this->ressource; |
if (file_exists($resource_file)) { |
include_once $resource_file; |
if (class_exists($resource_class)) { |
$service = new $resource_class(self::$config); |
if (method_exists($service,'updateElement')) { // Update element |
// TODO : a voir le retour ... |
if ($service->updateElement($this->uid, $pairs)) { |
$this->envoyerEnteteHttp(201);// Created |
} |
} |
} |
} |
} else { // get all elements of a ressource |
$this->add($pairs); |
} |
} else { |
$this->envoyerEnteteHttp(411);// Length required |
} |
} |
} |
|
private function delete() { |
$resource_file = self::$config['settings']['servicesDir'].$this->ressource.'.php'; |
$resource_class = $this->ressource; |
if (file_exists($resource_file)) { |
include_once $resource_file; |
if (class_exists($resource_class)) { |
$service = new $resource_class(self::$config); |
if ($this->uid) { // get a resource element |
if (method_exists($service, 'deleteElement')) { // Delete element |
if ($service->deleteElement($this->uid)) { |
$this->envoyerEnteteHttp(204);// No Content |
} |
} |
} |
} |
} |
} |
|
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->parserDonneesRequete(); |
} |
} |
|
if (count($pairs) != 0) { |
$resource_file = self::$config['settings']['servicesDir'].$this->ressource.'.php'; |
$resource_class = $this->ressource; |
if (file_exists($resource_file)) { |
include_once $resource_file; |
if (class_exists($resource_class)) { |
$service = new $resource_class(self::$config); |
if (method_exists($service,'createElement')) { // Create a new element |
if ($service->createElement($pairs)) { |
$this->envoyerEnteteHttp(201);// Created |
} |
} |
} |
} |
} else { |
$this->envoyerEnteteHttp(411);// Length required |
} |
} |
|
/** |
* Parse les données de la requête HTTP. |
* @return str[] Array of name value pairs |
*/ |
private function parserDonneesRequete() { |
$values = array(); |
$pairs = explode('&', $this->requeteDonnees); |
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; |
} |
|
/** |
* Envoyer un entête HTTP. |
* @param int $code entier indiquant le code de l'entête http à envoyer |
*/ |
public static function envoyerEnteteHttp($code) { |
if (isset(self::$http10[$code])) { |
$txt = self::$http[$code]; |
header("HTTP/1.0 $code $txt"); |
} |
} |
} |
?> |