Fermer
Web

Ecrire un Web Service en PHP – Partie 3 – Mettre en œuvre et déployer le service, appel par un client

Nous allons continuer notre apprentissage des Web Services dans cette troisième partie. Elle est consacrée à la mise en œuvre et au déploiement du Web Service, et l’appel par un client. Précédemment, nous avons vu la première notion importante, qui est la définition des Web Services, REST, WSDL, SOA, SOAP étant décrit dans des articles précédents, ainsi qu’une analyse de performance et de publication. La deuxième notion abordée pour ce thème est la définition des services de l’interface. Pour cela l’analyse du langage XML, WSDL permettant de décrire le mode de fonctionnement d’un Web Service, a été nécessaire.

Maintenant il est temps de créer notre serveur.

Tout d’abord, nous allons mettre en œuvre la méthode getResult(), qui sera accessible en tant que fonction de service par la demande de messages entrants à partir du Web. Ensuite, nous allons créer un objet SoapServer et communiquer avec la fonction de service à l’aide de la méthode SoapServer::addFunction(). Comme vous le verrez, le constructeur SoapServer() a un seul paramètre: le chemin du document WSDL qui décrit le service.

Mettre en œuvre et déployer le service

Après avoir vu la définition du service, l’étape suivante consiste à le mettre en œuvre en utilisant un langage de programmation. Dans mon cas je vais utiliser le PHP. Pourquoi ce langage ? Tout simplement parce que je développe tous les jours avec, et que les implémentations d’un serveur et d’un client sont très simples. Vous pouvez aussi bien utiliser JAVA, .NET, etc. Tout dépend de votre connaissance avec tel ou tel langage. L’écriture du code pour le service est très mécanique.

On va s’attaquer à notre serveur, préparez vos doigts ça va coder !

Nous allons d’abord créer un fichier nommé soap-server.php. Jusque là tout le monde suit ? 🙂

Le SoapServer peut fonctionner sans un document WSDL de la même manière que le SoapClient le peut. Mais il n’y a pas d’avantages évidents à le mettre en place de cette façon. D’ailleurs nous verrons dans le prochain chapitre l’appel d’un client SOAP sans WSDL !

Voici le code source, les commentaires parlent d’eux-même :

[sourcecode language=”php”]
<?php
/*
* Fonction getResult sert à addition ou soustraire 2 entiers et retourne le résultat
* @param $operation Type de l’opération (add/substract)
* @param $integer1 Entier 1
* @param $integer2 Entier 2
* @return $result Résultat de l’opération
*/
function getResult($operation, $integer1, $integer2) {
$result = 0;
if ($operation == "add") {
$result = $integer1 + $integer2;
}
if ($operation == "substract") {
$result = $integer1 -$integer2;
}
return $result;
}

// Désactivation du cache WSDL
ini_set("soap.wsdl_cache_enabled", "0");

// Catch l’erreur si l’instanciation la classe SoapServer
// échoue, on retourne l’erreur
try {
$server = new SoapServer(‘operation.wsdl’);
// On ajoute la méthode "getResult" que le serveur va gérer
$server->addFunction("getResult");
} catch (Exception $e) {
echo ‘erreur’.$e;
}

// Si l’appel provient d’une requête POST (Web Service)
if ($_SERVER[‘REQUEST_METHOD’] == ‘POST’) {
// On lance le serveur SOAP
$server->handle();
}
else {
echo ‘<strong>This SOAP server can handle following functions : </strong>’;
echo ‘<ul>’;
foreach($server -> getFunctions() as $func) {
echo ‘<li>’ , $func , ‘</li>’;
}
echo ‘</ul>’;
}
?>
[/sourcecode]

Dans un premier temps on définit la méthode getResult($operation, $integer1, $integer2) et le traitement de celle-ci. Le retour sera le résultat de l’opération. Ensuite on instancie l’objet SoapServer en mode WSDL. Pour cela, on lui passe en premier argument le WSDL operation.wsdl (On suppose qu’il est dans le même répertoire que notre fichier soap-server.php). Si vous voulez travailler en mode non WSDL il suffit de définir le paramètre à NULL et déclarer l’URI.

Note : Ici, pour une raison de rapidité, j’ai seulement renseigné ce paramètre mais d’autres sont possibles. Je vous propose de lire la documentation ici.

Ensuite on ajoute la méthode getResult au serveur qui va gérer les requêtes SOAP. Pour passer plusieurs fonctions au serveur il suffit d’utiliser un tableau de noms de fonctions.

[sourcecode language=”php”]
$server->addFunction(array("getResult", "getInt"));
[/sourcecode]

Enfin on lance le serveur SOAP lorsque la requête est supposée être dans les données brutes POST de la requête HTTP. Dans le cas où le serveur ne reçoit pas une requête POST on affiche la liste des fonctions disponibles par le Web Service.

Vous enregistrez le fichier, et si tout se passe bien on se rendez-vous sur cette adresse : http://localhost/server-soap.php

Vous devriez voir :

Affichage des fonctions disponibles par le Web Service

Tout marche ?

Appel du Web Service par le client

Voici un client pour accéder à notre propre serveur SOAP.

Note : On suppose qu’il est dans le même répertoire que notre fichier soap-server.php et operation.wsdl

[sourcecode language=”php”]
$client = new SoapClient("operation.wsdl");
echo $client->getResult("add", 3, 4);
[/sourcecode]
Résultat d'une addition par le client SOAP

C’est un peu plus facile, n’est-ce pas ?

Quels sont les problèmes avec WSDL ?

Le seul argument contre son utilisation est que le client doit charger systématiquement le document WSDL à partir du serveur, avant que la requête RPC soit faite, ce qui peut prendre beaucoup de temps dans un environnement Web. Afin d’accélérer les choses, on utilise une fonctionnalité de mise en cache WSDL. Celle-ci se fait en modifiant les variables de configuration se trouvant dans le php.ini : soap.wsdl_cache_enabled, soap.wsdl_cache_dir et soap.wsdl_cache_ttl. Par défaut, le cache WSDL est activée et le cache des fichiers WSDL est d’un jour.

Note : Il est également possible de modifier “à la volée” depuis votre script ces variables avec la méthode ini_set() de PHP.

Voici la section SOAP pour votre fichier php.ini avec les valeurs par défauts. Vous pouvez le coller dans votre propre fichier.

[sourcecode language=”bash”]
[soap]
soap.wsdl_cache_enabled = "1"
; enables or disables WSDL caching feature

soap.wsdl_cache_dir = "/tmp"
; sets the directory name where SOAP extension will put cache files

soap.wsdl_cache_ttl = "86400"
; (time to live) sets the number of second while cached file will be used
; instead of original one
[/sourcecode]

Quelles sont les zones à problème avec notre serveur et le client ?

Pour commencer, il ne traite pas les erreurs. Qu’advient-il lorsque le serveur ne reconnaît pas les paramètres demandés ? Le protocole SOAP spécifie un format particulier de messages pour signaler des erreursSoapFault. Pour générer de tels messages, le serveur doit lever une exception en utilisant l’objet SoapFault. Le premier paramètre du constructeur des erreurs SOAP est la chaîne du code d’erreur, le second est une chaîne de description de l’erreur.

[sourcecode language=”php”]
function getResult($operation, $integer1, $integer2) {
$result = 0;

if (($operation != "add") && ($operation != "substract")) {
throw new SoapFault("Server", "Veuillez utiliser une methode d’operation valable (add/substract).");
}
if (!$integer1 || !$integer2) {
throw new SoapFault("Server", "Veuillez indiquer 2 entiers.");
}
if ($operation == "add") {
$result = $integer1 + $integer2;
}
if ($operation == "substract") {
$result = $integer1 -$integer2;
}

return $result;
}
[/sourcecode]

Il est nécessaire que le client doit être implémenté de telle manière à intercepter les exceptions renvoyées par SoapFault.

[sourcecode language=”php”]
try {
echo $client->getResult("divide", 3, 4);
} catch (SoapFault $e) {
echo $e;
}
[/sourcecode]
Lancement d'un exception d'erreur SoapFault

En second lieu, il est préférable d’encapsuler les fonctionnalités de Web Service dans une classe PHP. Dans ce cas, nous n’avons pas besoin d’utiliser des variables globales et ajoutez chaque méthode SOAP individuellement au serveur : on peut ajouter une classe entière, et toutes ses méthodes seraient accessibles via SOAP.

[sourcecode language=”php”]
class Operation {

function getResult($operation, $integer1, $integer2) {

}
function getString($string) {

}

$server->setClass("Operation");
}
[/sourcecode]

Comme vous pouvez le voir, j’ai utilisé la méthode SoapServer::setClass() pour ajouter à l’objet SoapServer la classe Operation.

Qu’est-ce qu’il y a dedans ?

Êtes-vous curieux de connaître le format du message SOAP, ou l’espoir de débugger votre Service Web ? Si oui, cette section est pour vous.

Le débogage d’un Web Service est toujours difficile. Le client interprète directement la réponse du Web Service et si la réponse n’est pas correctement formatée (qui ne respecte pas le format WSDL du service), il affiche une erreur du type :

Retour d'erreur du client SOAP en cas d'erreur

Mais il ne renvoie pas l’erreur PHP du serveur SOAP.

Le constructeur SoapClient() accepte un tableau associatif comme second paramètre. Différentes options peuvent être passées par ce tableau associatif. En voici seulement deux :

  • trace : permet au client de stocker les demandes et les réponses SOAP (désactivées par défaut)
  • exceptions : permet au client de contrôler le mécanisme d’exception (activé par défaut)

Jetez un œil à l’exemple du client SOAP suivant. Il est dérivé de l’exemple précédent, et montre précisément les données transmises entre le client et le serveur. Afin de récupérer cette information, nous utilisons les méthodes __getLastRequest() et __getLastResponse().

[sourcecode language=”php”]
try {
echo $client->getResult($operation, $integer1, $integer2);
print "<pre>\n";
print "Request :\n".htmlspecialchars($client->__getLastRequest()) ."\n";
print "Response:\n".htmlspecialchars($client->__getLastResponse())."\n";
print "</pre>";
} catch (SoapFault $e) {
echo $e;
}
[/sourcecode]

Voici la sortie du script. Il est un peu modifié, pour le rendre plus lisible et plus facile à comprendre.

Requête :

[sourcecode language=”xml”]
<?xml version="1.0" encoding="UTF-8"?>
<SOAP-ENV:Envelope
xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"
xmlns:ns1="urn:xmethods-delayed-calcul"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:SOAP-ENC="http://schemas.xmlsoap.org/soap/encoding/"
SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">
<SOAP-ENV:Body>
<ns1:getResult>
<operation xsi:type="xsd:string">add</operation>
<integer1 xsi:type="xsd:int">3</integer1>
<integer2 xsi:type="xsd:int">4</integer2>
</ns1:getResult>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>
[/sourcecode]

Réponse :

[sourcecode language=”xml”]
<?xml version="1.0" encoding="UTF-8"?>
<SOAP-ENV:Envelope
xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"
xmlns:ns1="urn:xmethods-delayed-calcul"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:SOAP-ENC="http://schemas.xmlsoap.org/soap/encoding/"
SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">
<SOAP-ENV:Body>
<ns1:getResultResponse>
<result xsi:type="xsd:string">7</result>
</ns1:getResultResponse>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>
[/sourcecode]

D’autres implémentations de SOAP pour PHP

[samples id=”1343″]

Conclusion

Dans cet article, j’ai décrit seulement les fonctionnalités de base de l’extension SOAP pour PHP. En réalité, ce dernier est beaucoup plus complexe et permet de faire bien d’autre chose. Mais il n’est pas possible de vous montrer toutes ces caractéristiques dans un article aussi court.

L’extension SOAP est totalement documentée à ici.

Peut-être que ceux-ci seront des points de départ pour de futurs articles. D’ailleurs, une prochaine partie sera consacrée à l’utilisation de token, et sécurité des Web Services.

J’espère que cette partie vous a motivé à utiliser les Web Services, notamment en PHP. J’attends vos retours, sur les erreurs, remarques concernant le déploiement de vos propres Web Services.

Mots-clé : clientPHPsamplesserveursoapSoapClientSoapServerwsdl
Yohann Poiron

L’auteur Yohann Poiron

J’ai fondé le BlogNT en 2010. Autodidacte en matière de développement de sites en PHP, j’ai toujours poussé ma curiosité sur les sujets et les actualités du Web. Je suis actuellement engagé en tant qu’architecte interopérabilité.