Introduction
Cet article décrit la nouvelle extension SOAP pour PHP. Il a pour objectif de montrer la mise en œuvre des services Web, ou bien l’utilisation de SOAP pour accéder à des services Web existant. Le présent article suppose une certaine familiarité avec les services Web, SOAP et WSDL (Web Services Description Language).
Le protocole SOAP
SOAP (Simple Object Access Protocol) est un protocole léger basé sur le langage XML pour l’échange d’informations structurées entre des applications distribuées. SOAP spécifie les formats des messages XML, la manière dont ces messages doivent être traités, etc.
Les services Web sont une technologie moderne et populaire. La liste des protocoles et technologies en relation avec les services Web croît chaque jour, mais le protocole SOAP est de loin le plus important et celui qui tend à devenir le protocole standard pour l’accès aux services Web. Le protocole SOAP utilise des messages XML pour échanger des informations entre différents points.
L’extension SOAP de PHP5 est la première implémentation du protocole SOAP en C pour PHP. Elle possède certains avantages par rapport aux implémentations existantes écrites en PHP directement, le principal avantage concernant la vitesse d’exécution. L’extension est encore marquée comme expérimentale mais devrait entrer dans un état stable et fiable très rapidement.
L’extension SOAP comprend les spécifications SOAP 1.1, SOAP 1.2 et WSDL 1.1. L’objectif clé est l’utilisation de la fonctionnalité RPC du protocole SOAP. WSDL est utilisé lorsque cela est possible dans le but de rendre l’implémentation d’un service Web plus efficacement.
Un premier client simple SOAP en PHP
Pour démontrer comment mettre en œuvre un client SOAP, le service Web de demo « Delayed Stock Quote » du site XMethods sera utilisé comme cible. Avant de coder la moindre ligne de code PHP, quelques informations doivent être collectées à propos de ce service en particulier :
- le nom de la méthode RPC (
getQuote
) - l’URL où le service tourne
- l’entête
SOAPAction
pour la méthode - le namespace URI de la méthode
- les paramètres d’entrée et de sortie et les typages de la méthode
Le site http://www.xmethods.com donne le profil du service RPC qui sera utilisé dans la démo.
Nom de la méthode | getQuote |
---|---|
URL |
http://66.28.98.121:9090/soap |
SOAPAction |
urn:xmethods-delayed-quotes#getQuote |
Namespace URI | urn:xmethods-delayed-quotes |
Paramètres en entrée (Input Parameters) | Symbol String |
Paramètres de sortie (Output Parameters) | Result float |
Client PHP SOAP (sans description WSDL)
L’architecture d’appel RPC de la méthode getQuote du Web Service peut être résumée avec le schéma suivant :
Le script qui suit présente la mise en œuvre d’un client PHP SOAP pour le service Web getQuote
sans utilisation d’une description WSDL du service Web getQuote
.
<?php
$client = new SoapClient (NULL, array(
"location" => "http://66.28.98.121:9090/soap",
"uri" => "urn:xmethods-delayed-quotes",
"style" => SOAP_RPC,
"use" => SOAP_ENCODED
));
print($client->__call(
/* Nom de la méthode SOAP */
"getQuote",
/* Paramètres */
array(
new SoapParam(
/* Valeur du Paramètre */
"ibm",
/* Nom du Paramètre */
"symbol"
)
),
/* Options */
array(
/* Namespace de la méthode SOAP */
"uri" => "urn:xmethods-delayed-quotes",
/* SOAPAction HTTP Header pour la méthode SOAP */
"soapaction" => "urn:xmethods-delayed-quotes#getQuote")). "\n");
?>
Sans description WSDL, cette simple tâche requiert beaucoup de travail et de paramétrage.
Client PHP SOAP (avec description WSDL)
Heureusement, les services Web peuvent se décrire eux mêmes au client en utilisant le WSDL (Web Service Description Language).
Voici le même client PHP SOAP écrit en utilisant le document WSDL. Beaucoup de paramètres n’ont plus à être spécifiés, ces dernières informations étant renseignées directement dans le fichier WSDL.
<?php
$client = new SoapClient (
"http://services.xmethods.net/soap/urn:xmethods-delayed-quotes.wsdl"
);
print($client->getQuote("ibm"));
?>
Ce procédé est nettement plus simple.
WSDL et PHP
Quels sont les problèmes avec WSDL ? Le seul argument contraire à l’utilisation des documents WSDL réside dans la nécessité au client de charger le document WSDL à partir du serveur avant que l’appel de la méthode distante ne soit effectuée, ceci pouvant prendre un temps significatif dans un environnement Web.
L’extension SOAP propose une fonctionnalité de cache WSDL pour contrer ce problème. Le cache WSDL est mis en œuvre avec le paramètre d’initialisation soap.wsdl_cache_enabled
au niveau du fichier php.ini
ou bien en utilisant ini_set()
.
Les différentes directives gérant le cache WSDL sont les suivantes :
[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
Par défaut, le cache WSDL est activé et les fichiers sont mis en cache pour un jour.
Client SOAP et Google
Dans cet exemple, une classe permettant d’insérer les résultats du moteur de recherche Google sur un site est mise en œuvre. L’API Google est basée sur les protocoles SOAP et WSDL (XML).
Licence Google :
Pour pouvoir utiliser le Web Service de Google, il est nécessaire de récupérer un numéro de licence gratuite dans un contexte d’utilisation non commerciale.
API Google
Format de la requête
Voici la description des paramètres à envoyer pour effectuer une recherche. La casse est très importante dans ces paramètres.
Paramètre | Type | Description |
---|---|---|
key |
String |
Numéro de licence Google |
q |
String |
La recherche. Cette recherche doit impérativement être encodée au format UTF-8 |
Start |
Integer |
Index de début de la recherche. Ce paramètre permet notamment de réaliser un affichage par page des résultats |
maxResults |
Integer |
Nombre de résultats renvoyés par la requête |
filter |
Boolean |
Ce paramètre permet de filtrer les résultats renvoyés en supprimant les contenus très similaires et/ou venant du même site |
restricts |
String |
Ce paramètre permet de restreindre la recherche selon certains
critères précis.
Il est possible de restreindre la recherche aux pages venant d’un
certain pays, par exemple countryFR pour la France, ou encore à
certains topics.
Google propose 4 topics : Gouvernement Américain (unclesam), Linux
(linux), Macintosh (mac) et FreeBSD (bsd).
Il est possible de combiner les deux modes de restriction grâce à des
opérateurs booléens : NOT (-) , OR (|) , AND (.) et de faire des
regroupements : parenthèses () |
safeSearch |
Boolean |
Paramètre permettant de filtrer les contenus pour adultes |
lr |
String |
Paramètre permettant de restreindre la recherche à certains pays |
ie |
String |
Obsolète |
oe |
String |
Obsolète |
Format de la réponse
Paramètre | Description |
---|---|
documentFiltering |
Booléen indiquant si les résultats de la recherche ont été filtrés |
searchComments |
Commentaire renvoyé par Google, pouvant contenir notamment les mots clés non pris en compte |
estimatedTotalResultsCount |
Estimation du nombre de résultats renvoyés par la requête |
estimateIsExact |
Booléen indiquant si l’estimation précedente est juste |
resultElements |
Tableau contenant les résultats de la recherche |
searchQuery |
La requète que Google a reçu (Paramètre q de la recherche) |
startIndex |
Index (-1) du premier résultat. Ce paramètre sert à réaliser un affichage par page des résultats |
endIndex |
Index (-1) du dernier résultats |
searchTips |
Aide renvoyée par Google contenant notamment des conseils pour la recherche |
directoryCategories |
Liens vers les catégories de Google |
searchTime |
Nombre à virgule flottante ( Float ) indiquant le temps que Google a mis pour effectuer la recherche |
resultsElements [ summary ] |
Résumé de la page |
resultsElements [ URL ] |
L’URL du lien renvoyé |
resultsElements [ snippet ] |
Un extrait significatif de la page contenant les mots clés en gras |
resultsElements [ title ] |
Titre du résultat renvoyé |
resultsElements
[ cachedSize ] |
Poids de la page dans le cache de Google + "k" |
resultsElements
[ relatedInformationPresent ] |
Booléen indiquant si le terme ''related'' (permettant d’obtenir des pages similaires) est supporté par cette URL |
Exemple de classe client Google
Classe Cls_Google
Dans le présent tutoriel, la classe Cls_Google
se charge d’adresser les
requêtes et de traiter les réponses avec le Web Service Google :
http://api.google.com/GoogleSearch.wsdl
Les méthodes mises en œuvre dans cette classe sont les suivantes :
__construct
__destruct
f_search
f_error
La trame de la classe Cls_Google
est la suivante :
if (! defined("_GOOGLE_LICENCE_KEY")) {
define("_GOOGLE_LICENCE_KEY","v70jh5lQFHLjCvjz2a8zFPjnEclQQcff");
}
class Cls_Google {
// URL du fichier WSDL de l’API Google
private $wsdlUri = 'http://api.google.com/GoogleSearch.wsdl';
public function __construct() {
} // end function __construct
public function f_search() {
}
public static function f_error() {
}
public function __destruct() {
}
} // end class Cls_Google
Méthode__construct
Le constructeur de la classe se charge de vérifier que l’extension SOAP est bien chargée et le cas échéant tente de le charger à la volée. Le constructeur est déclaré en mode public :
public function __construct() {
// Vérification que l’extension soap est chargée
if(!extension_loaded('soap')) {
// Cette dernière n’est pas chargée, tentative de chargement
if(!@dl('soap')) {
// Le chargement n’est pas réalisé, exit
die('Impossible d\'utiliser l\'extension SOAP');
}
}
// L’extension est chargée.
return true;
} // end function __construct
Méthode f_search
On passera toutes les possibilités de recherche en tant que paramètres de la
méthode de recherche f_search
, seul le premier paramètre search
est
obligatoire, les autres sont optionnels.
Il se peut que pour une raison x ou y une erreur se produise lors de la communication avec un serveur SOAP.
L’extension SOAP de PHP fournit une classe ( SoapFault ) qui permet de gérer ces erreurs.
Les exceptions sont donc gérées dans la méthode f_search
grâce à la classe
SoapFault
.
public function f_search($search , $start=0, $maxresults=5, $filter=false,
$restrict='', $safesearch=false , $lang='') {
// Création du tableau contenant les paramètres à envoyer à Google.
$params = array(
'key' => _GOOGLE_LICENCE_KEY,
'q' => utf8_encode($search),
'start' => (int)$start,
'maxResults' => (int)$maxresults,
'filter' => (boolean)$filter,
'restrict' => $restrict,
'safeSearch' => (boolean)$safesearch,
'lr' => $lang,
'ie' => '',
'oe' => ''
);
// on tente d’instancier le client SOAP
try { $Client = new SoapClient($this->wsdlUri); }
// Erreur : on gére une exception grâce à la classe SoapFault,retour de l’objet
catch (SoapFault $fault) { return $fault; }
// On tente d’interroger le service web.’
try { $O = $Client->__call('DoGoogleSearch', $params); }
// Erreur : on gére une exception grâce à la classe SoapFault, retour de l’objet
catch (SoapFault $fault) { return $fault; }
// tout s'est bien déroulé, on retourne le résultat.
return $O;
}
Méthode f_error
La méthode f_error
prend un objet SoapFault
comme paramètre. Cette méthode
est appelée uniquement de manière statique.
public static function f_error(SoapFault $fault) {
echo 'SOAP Fault :<br/> <strong>FaultCode</strong> : ',
$fault->faultcode,
'<br/><strong>FaultString</strong> : ',
$fault->faultstring;
}
Mise en œuvre de Cls_Google
L’exemple de mise en œuvre de la classe Cls_Google
construit un
formulaire simple permettant d’interroger l’API de Google et d’en afficher les
résultats.
Le résultat renvoyé par la classe Cls_Google
est analysée grâce à la fonction
is_soap_fault()
.
<?php require_once(_FWK_PHPDIR."/Cls_Google.php");?>
<form method="post" action="<?php echo $_SERVER['PHP_SELF']; ?>">
<fieldset>
<legend> Votre recherche avec Google </legend>
<div>
<input type="text" name="search" tabindex="1" accesskey="s"/>
</div>
<div>
<input type="submit" value="et hop" tabindex="2" accesskey="g"/>
</div>
</fieldset>
</form>
<hr/>
<?php
// Le formulaire a été soumis.
if(isset($_REQUEST['search'])) {
$G = new Cls_Google(); // Nouvelle instance.
$O = $G->f_search($_REQUEST['search']); // Envoi de la requète au serveur SOAP.
// Vérification du résultat renvoyé.
if(is_soap_fault($O)) {
// Affichage des erreurs a appeler de manière statique
Cls_Google::f_error($O);
die();
}
// Tout s'est bien passé , affichage sommaire du résultat.
echo '<div>';
echo '<div>NB results ::: ', $O->estimatedTotalResultsCount, '</div>';
foreach($O->resultElements as $key => $val) {
echo '<div>\/ ';
echo '<a href="', $val->URL, '">', utf8_decode($val->title), '</a>';
echo '</div>';
echo '<p>';
echo utf8_decode($val->snippet);
echo '</p>';
echo '<div>';
echo '<a href="', $val->URL, '">', utf8_decode($val->URL), '</a> ---- ';
echo 'en cache : ', $val->cachedSize;
echo '</div>';
}
echo '</div>';
}
?>
Debugging SOAP et format des messages
Le constructeur SoapClient( )
accepte un tableau associatif en second
paramètre. Diverses options peuvent être passées dans ce tableau associatif, en
voici deux :
trace
: autorise le client à stocker les requêtes et réponses SOAP (désactivé par défaut)exceptions
: autorise le client à contrôler le mécanisme des exceptions (activé par défaut).
Les méthodes __getLastRequest
et __getLastResponse
permettent d’extraire les
informations échangées.
<?php
$client = new SoapClient("./wsdl/stockquote.wsdl",array(
"trace" => 1,
"exceptions" => 0)
);
$client->getQuote("ibm");
print "<pre>\n";
print "Request :\n".htmlspecialchars($client->__getLastRequest()) ."\n";
print "Response:\n".htmlspecialchars($client->__getLastResponse())."\n";
print "</pre>";
?>
Request :
<?xml version="1.0" encoding="UTF-8"?>
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"
xmlns:ns1="urn:xmethods-delayed-quotes" 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:getQuote>
<symbol xsi:type="xsd:string">ibm</symbol>
</ns1:getQuote>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>
Response:
<?xml version="1.0" encoding="UTF-8"?>
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"
xmlns:ns1="urn:xmethods-delayed-quotes" 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:getQuoteResponse>
<Result xsi:type="xsd:float">98.42</Result>
</ns1:getQuoteResponse>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>
Serveur SOAP PHP
Il est possible avec l’extension PHP d’écrire un service Web ou serveur SOAP, la première étape consistant à créer un document WSDL décrivant le service et pouvant être interprété par le client.
Dans la démonstration, un service Web ou serveur SOAP extrait des informations d’un serveur MySQL, informations récupérées par un client SOAP avec la méthode getParameters :
Mise en œuvre du service Web SOAP
Description du fichier WSDL mySqlParms.wsdl
La première tâche consiste à créer le document WSDL mySqlParms.wsdl
décrivant le service dans un format que la requête cliente sera capable
d’interpréter.
La section message définit deux messages. Le premier message est
getParametersRequest
, message requête pour la méthode getParameters
avec une
chaîne de caractère en paramètre appelé _param
. Le second message
getParametersResponse
est le message réponse de la méthode getParameters
,
contenant une valeur chaîne de caractères appelé _result
.
La section portType
définit une opération, getParameters
, laquelle décrit
quels sont les messages listés dans la section message et qui seront utilisés
pour la requête et la réponse.
La section binding
définit comment les messages sont transmis et encodés.
C’est dans cette section que sont indiqués que les messages sont envoyés en
mode RPC avec l’encodage SOAP à travers HTTP. Cette section décrit également le
nom d’espace (namespace) et la valeur de l’entête SOAPAction
pour la méthode
getParameters
.
En dernier lieu, la section service définit l’URL où le service est exécuté.
mySqlParms.wsdl
<?xml version ='1.0' encoding ='UTF-8' ?>
<definitions name='getParameters'
targetNamespace='http://example.org/getParameters'
xmlns:tns=' http://example.org/getParameters '
xmlns:soap='http://schemas.xmlsoap.org/wsdl/soap/'
xmlns:xsd='http://www.w3.org/2001/XMLSchema'
xmlns:soapenc='http://schemas.xmlsoap.org/soap/encoding/'
xmlns:wsdl='http://schemas.xmlsoap.org/wsdl/'
xmlns='http://schemas.xmlsoap.org/wsdl/'>
<message name='getParametersRequest'>
<part name='_param' type='xsd:string'/>
</message>
<message name='getParametersResponse'>
<part name='_result' type='xsd:string'/>
</message>
<portType name='getParametersPortType'>
<operation name='getParameters'>
<input message='tns:getParametersRequest'/>
<output message='tns:getParametersResponse'/>
</operation>
</portType>
<binding name='getParametersBinding' type='tns:getParametersPortType'>
<soap:binding style='rpc'
transport='http://schemas.xmlsoap.org/soap/http'/>
<operation name='getParameters'>
<soap:operation soapAction='urn:mySqlParms#getParameters'/>
<input>
<soap:body use='encoded' namespace='urn:mySqlParms'
encodingStyle='http://schemas.xmlsoap.org/soap/encoding/'/>
</input>
<output>
<soap:body use='encoded' namespace='urn:mySqlParms'
encodingStyle='http://schemas.xmlsoap.org/soap/encoding/'/>
</output>
</operation>
</binding>
<service name='getParametersService'>
<port name='getParametersPort' binding='getParametersBinding'>
<soap:address location='http://[insert real path here]/srv_mySqlParms.php'/>
</port>
</service>
</definitions>
Note: La fonctionnalité de cache WSDL est activée par défaut. Durant le développement, cette fonctionnalité doit être désactivée car dans le cas contraire le rafraîchissement des fichiers WSDL n’est pas réalisée.
Mise en œuvre du serveur SOAP srv_mySqlParms.php
La seconde étape consiste à créer le serveur en implémentant la fonction
getParameters( )
, laquelle sera accédée comme une fonction service par les
messages requête.
La seconde étape consiste à créer un objet SoapServer
et de le connecter à
la fonction service getParameters( )
en utilisant la méthode SoapServer
::addFunction( )
. Le constructeur SoapServer( )
possède un seul paramètre : le
chemin d’accès au document WSDL du service.
Le serveur SOAP peut fonctionner sans document WSDL comme le client SOAP mais ceci ne présente aucun intérêt compte tenu de la lourdeur de la mise en œuvre.
srv_mySqlParms.php
<?php
$prmdb["db_user"]='root';
$prmdb["db_database"]='mysql';
$prmdb["db_pwd"]='';
function getParameters($parm) {
global $prmdb;
$returnedValue = "";
$link = mysql_connect("localhost", $prmdb["db_user"], $prmdb["db_pwd"]);
mysql_select_db($prmdb["db_database"],$link);
$sql="show variables like '".$parm."%'";
$rsc = mysql_query($sql,$link);
if ($rsc) {
while ($row = mysql_fetch_assoc($rsc)) {
$returnedValue=$row["Value"];
break;
}
}
else { throw new SoapFault("Error","Retrieve has failed, unknown variable"); }
mysql_free_result($rsc);
mysql_close($link);
return $returnedValue;
}
ini_set("soap.wsdl_cache_enabled", "0"); // désactivation du cache WSDL
$server = new SoapServer("../wsdl/mySqlParms.wsdl");
$server->addFunction("getParameters");
$server->handle();
?>
Dans la mesure du possible, il est nécessaire de gérer les erreurs. Le
protocole SOAP spécifie un format spécial pour les erreurs. Pour générer de
tels messages, il est simple de générer une exception en utilisant l’objet
SoapFault
. Le premier paramètre du constructeur de SoapFault
est le code de l’erreur
(String
) et le second la description de l’erreur (String
).
Mise en œuvre du client SOAP soap_client.php
Voici un exemple de client du service Web implémenté dans le paragraphe précédent
soap_client.php
<?php
$client = new SoapClient("./wsdl/mySqlParms.wsdl");
try {
echo "<pre>\n";
print($client->getParameters("version"));
echo "\n";
print($client->getParameters("version_comment"));
echo "\n</pre>\n";
}
catch (SoapFault $exception) {
echo $exception;
}
?>
Recommandations sur l’écriture des services Web SOAP en PHP
Dans l’exemple précédent, la fonctionnalité du service Web est codée dans
une fonction simple mais il faut savoir qu’il est possible d’encapsuler les
fonctionnalités du service Web dans une classe PHP grâce à la méthode
SoapServer ::setclass
.
En implémentant une classe entière pour un service Web, toutes les méthodes de cette classe seront accessibles par SOAP sans avoir à les définir individuellement : c’est la méthode générale qu’il faut adopter.
Ci-après, voici une réécriture du service Web en respectant l’encapsulation
de la fonction getParameters
dans une classe cls_mySqlService
; pour ce qui
concerne le client, aucune modification ne sera nécessaire :
ini_set("soap.wsdl_cache_enabled", "0"); // désactivation du cache WSDL
$server = new SoapServer("../wsdl/mySqlParms.wsdl");
$server->setClass("cls_mySqlService");
$server->handle();
class cls_mySqlService {
private $prmdb = array("db_user"=>"root",
"db_database"=>"mysql",
"db_pwd"=>"");
function getParameters($parm) {
global $prmdb;
$returnedValue = "";
$link = mysql_connect("localhost", $this->prmdb["db_user"], $this->prmdb["db_pwd"]);
mysql_select_db($this->prmdb["db_database"],$link);
$sql="show variables like '".$parm."%'";
$rsc = mysql_query($sql,$link);
if ($rsc) {
while ($row = mysql_fetch_assoc($rsc)) {
$returnedValue=$row["Value"];
break;
}
}
else { throw new SoapFault("Error","Retrieve has failed, unknown variable"); }
mysql_free_result($rsc);
mysql_close($link);
return $returnedValue;
}
}