Ecrire un client REST en PHP (2/3)

Suite à l’article dédié à la présentation des architectures RESTful nous allons maintenant voir comment consommer des services REST en PHP.

Appeler un service REST en PHP est une tâche extrêmement simple car nous disposons de tous les outils pour effectuer un appel HTTP, en particulier grâce aux flux.

Une requête de type GET

Dans sa forme la plus simple appeler un service REST en PHP se résumé à un appel à file_get_contents qui accepte en plus des noms de fichiers des URL.

    $livre = file_get_contents('http://bibliotheque/livre/1');

Ni plus, ni moins, $livre contient bel et bien le résultat de l’appel du service REST. Bien sûr, il incombera à votre processus de convertir le résultat en résultat exploitable par votre application.

Par exemple, si le format d’échange retenu était JSON :

    $livre = json_decode(file_get_contents('http://bibliotheque/livre/1'));

Les autres requêtes (PUT / POST / DELETE)

Pour effectuer des appels de type PUT / POST / DELETE, nous allons utiliser les flux (stream) PHP, et en particulier préparer des contextes de flux grâce à la fonction stream_context_create.

Dans ce contexte nous allons indiquer les options HTTP qui nous intéressent (à savoir la méthode d’interrogation, le contenu et quelques en têtes).

Exemple d’appel de service avec la méthode POST

   //création d'un contexte d'appel de type POST
   $opts = array(
              'http'=>array(
                      'method'=>'POST',
                      'header'=>'Content-type: application/x-www-form-urlencoded',
                   )
	);
   $context = stream_context_create($opts);

   //Utilisation du contexte dans l'appel
   $livre = file_get_contents(
                  'http://bibliotheque/livre/', 
                  false, 
                  $context);

Pour envoyer des données au service REST, il suffira d’indiquer dans notre contexte le contenu des données à envoyer dans la partie « content ».

Exemple d’appel de service avec la méthode PUT en envoyant des données

   //création d'un contexte d'appel de type PUT
   $opts = array(
              'http'=>array(
                      'method'=>'PUT',
                      'header'=>'Content-type: application/x-www-form-urlencoded',
                   )
	);
   //Ajout de données à envoyer en PUT, ici au format JSON
   $opts['http']['content'] = json_encode(
                                          array('titre'=>'PHP5 Avancé',
                                                  'edition'=>'6',
                                                  'statut'=>'en cours')
                                                );

   $context = stream_context_create($opts);

   //Utilisation du contexte dans l'appel
   $livre = file_get_contents(
                  'http://bibliotheque/livre/1', 
                  false, 
                  $context);

Et la gestion des erreurs ?

REST est basé sur le protocole HTTP qui contient entre autres comme information pertinente le statut de la requête, dont le plus célèbre représentant est probablement le fameux 404 : Not Found.

Dans nos exemples précédents nous n’avons pas géré ces codes retours, chose pourtant indispensables.

Pour cela, nous allons utiliser deux nouvelles fonctions : stream_get_contents pour récupérer le retour du service et stream_get_meta_data pour récupérer les meta données associées (en particulier les en têtes HTTP).

Adaptation de l’appel pour récupérer les en têtes

$context  = stream_context_create($opts);

if (($stream = fopen('http://enpoint/resource/', 'r', false, $context)) !== false){
   //lecture du résultat du service
   $content = stream_get_contents($stream);
   //Lecture des en têtes
   $header = stream_get_meta_data($stream);
   //Fermeture du flux !
   fclose($stream);
}

//$header sera un tableau de la forme : 
//Array ( [wrapper_data] => Array ( [0] => HTTP/1.0 200 OK [1] , ...

Un exemple plus complet de client REST en PHP

Voici ci dessous un exemple basique d’encapsulation d’un client REST en PHP.

<?php
class RestClient
{
	private $_url;
	public function setUrl ($pUrl)
	{
		$this->_url = $pUrl;
		return $this;
	}

	public function get ($pParams = array())
	{
		return $this->_launch($this->_makeUrl($pParams),
		                      $this->_createContext('GET'));
	}

	public function post ($pPostParams=array(), $pGetParams = array())
	{
		return $this->_launch($this->_makeUrl($pGetParams),
		                      $this->_createContext('POST', $pPostParams));
	}
	
	public function put ($pContent = null, $pGetParams = array())
	{
		return $this->_launch($this->_makeUrl($pGetParams),
		                      $this->_createContext('PUT', $pContent));
	}
	
	public function delete ($pContent = null, $pGetParams = array())
	{
		return $this->_launch($this->_makeUrl($pGetParams),
		                      $this->_createContext('DELETE', $pContent));
	}
	
	protected function _createContext($pMethod, $pContent = null)
	{
		$opts = array(
              'http'=>array(
                            'method'=>$pMethod, 
                            'header'=>'Content-type: application/x-www-form-urlencoded',
		                    )
		);
		if ($pContent !== null){
			if (is_array($pContent)){
				$pContent = http_build_query($pContent);
			}
			$opts['http']['content'] = $pContent; 
		}
		return stream_context_create($opts);
	}
	
	protected function _makeUrl($pParams)
	{
		return $this->_url
		       .(strpos($this->_url, '?') ? '' : '?')
		       .http_build_query($pParams);
	}
	
	protected function _launch ($pUrl, $context)
	{
		if (($stream = fopen($pUrl, 'r', false, $context)) !== false){
			$content = stream_get_contents($stream);
			$header = stream_get_meta_data($stream);
			fclose($stream);
			return array('content'=>$content, 'header'=>$header);
        }else{
        	return false;
        }
	}
}

L’exemple est simplifié à outrance pour les besoins de l’article, mais il peut servir de base pour n’importe quelle application ayant besoin d’interroger un serveur REST.

Exemples d’appels avec le client REST développé

$rest = new RestClient();

//lecture d'un livre
$livre = $rest->setUrl('http://bibliotheque/livre/1')->get();

//ecriture d'un livre
$rest->setUrl('http://bibliotheque/livre')->post($unLivre);

//modification d'un livre
$rest->setUrl('http://bibliotheque/livre/1')->put($unLivre);

//supression d'un livre
$rest->setUrl('http://bibliotheque/livre/1')->delete();

Liens

Liens internes

Liens externes

27 réflexions au sujet de « Ecrire un client REST en PHP (2/3) »

  1. Ping : Qu’est-ce que REST ? (1/3) | Gerald's Blog

  2. Bonjour,

    Puis-je vous demander les raisons (ou la raison) du choix d’utiliser file_get_contents() plutôt que cUrl pour les requêtes HTTP ?

    Hormis la simplicité de mise en oeuvre y’a-t-il des différences majeures entre ces deux choix techniques ?

  3. Bonjour,

    ici le choix de file_get_contents / fopen était guidé par un pur esprit de simplicité, et parce que cUrl n’est pas présent en standard sur PHP.

    « Il me semble » (sans jamais avoir testé) que cUrl est plus rapide, mais c’est à vérifier.

  4. Intéressant, j’espère que tu va traiter la problématique de sécurité et d’authentification dans la 3ème partie 😉
    Autant en SOAP c’est prévu, autant en REST on peut faire tout et n’importe quoi.

    • Je n’avais pas prévu de parler de la problématique sécurité & authentification dans la 3ème partie, mais de toute façon j’avais déjà prévu une 4ème partie pour la gestion des erreurs…. alors pourquoi pas étendre vers une 5ème partie !

  5. Merci pour cette réponse. Il me semble aussi que cUrl est plus rapide mais plus verbeux donc effectivement moins adapté didactiquement parlant.

    Je rejoins le commentaire de Benji pour la partie 3 😉

  6. Ping : À lire cette semaine 8 | www.freva.net

  7. En complément à cet article, voici des liens vers un logiciel (nommé REST_Client) auquel j’ai participé qui permet justement de faire office de client REST mais en passant par cURL. Un des avantages d’utiliser cURL c’est ses fonctionnalités avancées qui sont la parallélisation des requêtes HTTP qui est utile si vous souhaitez de hautes performances quand vous solicitez fortement un serveur REST.

    Un article en français sur le REST_Client

    Les sources de REST_Client sur github

  8. bonjour,

    ce super tuto m’a permis de comprendre certaines choses. Mais il manque le 3ème partie ❓ Est-il possible d’avoir la suite ? 😕
    Merci et bonne continuation

  9. merci pour ce tuto juste concernant partie serveur sachant que je développe une application mobile et j’ai besoin d’exploiter l’architecture Rest j’ai un une partie back office que je dois le développer avec php en utilisant rest ainsi partie client (partie mobile) pouvez vous m’aidez 🙂

  10. Bonjour, vu le nombre de questions à propos du choix de file_get_contents(), je voulais signaler à ceux qui se pose la question, que si la directive PHP allow_url_fopen n’est pas activée les interrogations distantes avec file_get_contents et fopen génèrerons une erreur fatale, et que dans ce cas précis vous trouverez votre salut avec cUrl

  11. Si vous constatez des lenteurs pour contacter votre serveur REST avec ce client, pensez à ajouter :
    ‘header’=>’Connection: close\r\n’ à la ligne 40 de la classe restClient.php.
    Cela est du à l’instruction par défaut du serveur « connection:keep-alive » (chez moi il y avait 15 secondes!)

    Sinon Excellent tuto!!!

Laisser un commentaire

Votre adresse de messagerie ne sera pas publiée. Les champs obligatoires sont indiqués avec *