Par le passé, je dois avouer avoir fait une tentative, une seule, pour créer à la main un fichier WSDL (Web Services Description Language) et je me suis promis que plus jamais je ne voulais revivre cette expérience. Certains outils existent pour automatiser cette pénible tâche et ceux que j'avais testé jusqu'à maintenant généraient un fichier .wsdl statique.
Pour expliquer simplement, un fichier WSDL comprend une structure XML qui définit les fonctions accessibles par le service, ses paramètres, les types et ce qui est retourné. Lorsque le comportement d'une des fonctions du service web change, on est obligé soit de modifier à la main la description, soit de regénérer le fichier WSDL et le redéposer sur le serveur.
Jusqu'à ce qu'on découvre l'objet Zend_Soap_AutoDiscover du Zend Framework. Sa force : être en mesure d'inspecter une classe au runtime et de générer à la volée le fichier WSDL correspondant.
Voici un petit tutoriel pour créer un premier service web, réduit à sa plus simple expression. J'offrirai le service de retourner le pourcentage de taxe associé au nom envoyé par le client.
1. Créer une classe qui offrira les services
Ici, ça peut être n'importe quelle classe, pas nécessairement une construite spécifiquement pour le service web. Une seule condition doit être respectée : suivre le format de documentation des docblocks car AutoDiscover se base sur commentaires pour construire la description WSDL.
Chaque fonction de la classe doit être précédée d'un docblock qui doit suivre la syntaxe suivante :
- @param type param_name
- @return type
// Ex: fichier "tax.class.php"Si vous rencontrez l'erreur "Fatal error: Uncaught SoapFault exception: [WSDL] SOAP-ERROR: Parsing WSDL: Missing <message>...", c'est probablement que votre docblock est mal structuré. Le commentaire doit absolument débuter par /**, avec deux astérisques (dans Eclipse, vous devriez voir une différence dans la coloration syntaxique).
class Tax {
/**
* Return tax value
* @param string $type
* @return float
*/
public function getTaxValue($type = 'TPS'){
// récupérer les taxes d'une quelconque façon,
// par exemple d'une base de données
// Taxe sur les Produits et Services
if($type == 'TPS'){
return 5.0;
}
// Taxe de Vente du Québec
elseif ($type == 'TVQ'){
return 7.5;
}
else {
return 0;
}
}
}
2. Manipuler les requêtes du côté serveur
Comme c'est vous qui offrez un service, vous devrez préparer une page qui effectuera les manipulations des requêtes clients. Ce fichier aura deux utilités :
- retourner la description du WSDL lorsqu'un paramètre sera passé
- traiter la demande
// Ex: fichier "service.php"Si l'erreur suivante se produit "Fatal error: Uncaught exception 'Zend_Soap_Server_Exception' with message 'SOAP extension is not loaded.'", c'est que l'objet Zend_Soap_Server utilise l'extension php_soap.so (ou .dll). Activez la dans php.ini.
require_once('Zend/Soap/AutoDiscover.php');
require_once('Zend/Soap/Server.php');
require_once('tax.class.php');
if(isset($_GET['wsdl'])){
// inspecter la classe Tax et retourner la description
$wsdl = new Zend_Soap_AutoDiscover();
$wsdl->setClass('Tax');
$wsdl->handle();
}
else{
// traitement
$server = new Zend_Soap_Server('http://localhost/WSDL/service.php?wsdl');
$server->setClass('Tax');
$server->handle();
}
Vous êtes maintenant prêts à fournir les taux de taxes.
3. Créer une requête en tant que client
Probablement qu'en temps normal, ce ne sera pas vous qui utiliserez votre service web mais mieux vaut le tester avant de l'offrir à vos clients. Ceux-ci pourront utiliser le langage de leur choix pour faire appel à votre service, mais nous, comme nous utilisons déjà le Zend Framework, nous allons nous servir d'un autre objet au nom évocateur de Zend_Soap_Client. Cet objet peut aussi être utilisé lorsque vous voudrez vous-même utiliser un service offert par un autre fournisseur. Rappelez-vous que du point de vue du client, il ne voit pas que c'est programmé en PHP, ils ne voient que la définition WSDL pour pouvoir communiquer avec lui.
Une fois la connection établie, le client peut appeler les fonctions comme un objet. Ici, il aura accès à la fonction getTaxValue.
// Ex: fichier "appel.php"Ce qui devrait afficher ceci :
require_once('Zend/Soap/Client.php');
try {
$client = new Zend_Soap_Client('http://localhost/WSDL/service.php?wsdl');
echo 'La TPS est de : ' . $client->getTaxValue('TPS') . ' %';
echo 'La TVQ est de : ' . $client->getTaxValue('TVQ') . ' %';
}
catch(Zend_Exception $e){
echo $e->getMessage();
}
La TPS est de : 5 %
La TVQ est de : 7.5 %
excellent article.
Par contre dans la partie client, il est inutile de faire un include tax.class.php. Cette class est seulement côté server.
@++
Oups, tu as raison!
ça fonctionne nickel.
C'est simple. C'est ludique.
Je dis bravo et merci (je vais pouvoir aborder sereinement soap sur mon projet)
Vlad
Bonjour, lorsque je fais tourner votre code g un message d'erreur dont je n'arrives pas à identifier la cause. Auriez-vous une idée pour m'orienter?
Merci.
Claude
---------------> erreur:
Exception information:
Message: Wrong Version
Stack trace:
#0 /var/www/virgox/library/Zend/Soap/Client.php(1113): SoapClient->__soapCall('getTaxValue', Array, NULL, NULL, Array)
#1 /var/www/virgox/application/modules/default/controllers/IndexController.php(35): Zend_Soap_Client->__call('getTaxValue', Array)
#2 /var/www/virgox/application/modules/default/controllers/IndexController.php(35): Zend_Soap_Client->getTaxValue('TPS')
#3 /var/www/virgox/library/Zend/Controller/Action.php(513): IndexController->cliAction()
#4 /var/www/virgox/library/Zend/Controller/Dispatcher/Standard.php(289): Zend_Controller_Action->dispatch('cliAction')
#5 /var/www/virgox/library/Zend/Controller/Front.php(954): Zend_Controller_Dispatcher_Standard->dispatch(Object(Zend_Controller_Request_Http), Object(Zend_Controller_Response_Http))
#6 /var/www/virgox/library/Zend/Application/Bootstrap/Bootstrap.php(97): Zend_Controller_Front->dispatch()
#7 /var/www/virgox/library/Zend/Application.php(366): Zend_Application_Bootstrap_Bootstrap->run()
#8 /var/www/virgox/public/index.php(50): Zend_Application->run()
#9 {main}
Request Parameters:
array (
'controller' => 'index',
'action' => 'cli',
'module' => 'default',
)
...suite...
je pense que c'est lié au cache. G l'impression que les dernières modifs du wsdl lorsque je modifie la classe de service (Tax) ne sont pas prises immédiatement en compte. Le lendemain cela fonctionnait.
Merci.
Claude
Tant mieux si c'était lié au cache, ça doit dépendre du setup de chaque serveur.
Bonjour,
le fichier service.php fait il partie de Zend ? Je ne le trouve nulle part et il n'est a priori pas expliqué. Hors tout le problème que je rencontre avec SOAP c'est la génération du fichier WSDL
A+
Le fichier service.php, c'est l'équivalent du contenu qui est expliqué au point 2.
OK, je voulais absolument qu'il y ait un serveur.php...
Merci pour la réponse, ça fonctionne.
S'il vous plait,
Est que je peux avoir les noms de chaque fichiers, et comment les utiliser
Merci beaucoup pour votre aide
Bien sûr. À titre d'exemple, j'ai ajouté des noms génériques : tax.class.php, service.php et appel.php.
Bonjour,
Ça fait maintenant un moment que j'utilise ce système (qui fonctionne très bien en PHP). Mais je suis confronté maintenant à un nouveau problème.
Je tente de créer un client ASP (classic) mais le format WSDL de Zend_Soap_AutoDiscover n'est pas reconnu.
En effet, le format du fichier WSDL pour l'ASP est différent du celui généré par Zend_Soap_AutoDiscover.
exemple : http://ws.textanywhere.net/ta_SMS.asmx?wsdl
Ma question est la suivant : avec Zend_Soap_AutoDiscover, peut-on créer ce genre de format ? pour que mon WebService PHP puisse etre lu par le PHP et l'ASP ??
Merci.
Olivier
Salut Olivier, je n'ai jamais travaillé avec un client SOAP à partir d'ASP classique. Mais je soupçonne que c'est la version de SOAP qui cause problème. Le standard actuel est 1.2 (par défaut dans Zend mais tu peux le faire basculer à 1.1). Tu pourrais regarder comment est fait l'implémentation de la portion client en PHP et tenter de le reproduire en ASP.
$url = 'http://abc.com/somservice.asmx?wsdl';
$options = array("soap_version" => SOAP_1_1);
$client = new Zend_Soap_Client($url,$options);
Exemple tiré d'ici.
Salut, Et merci pour ta réponse.
Niveau Client, je sais qu'on peut spécifier la version. Mais comment déterminer la version de mon fichier WSDL ?
Merci
Olivier
Bonjour
Article très intéressant :)
Petite question : vous utilisez un chemin relatif commencant par Zend/ dans vos includes. Soit! j'adapte mon chemin relatif en conséquence mais je tombe sur une erreur identique à l'intérieur même des classes du framework.
Dois-je modifier l'ensemble des classes du framework comme je l'ai fait avec mon service ? ou y-a-t-il une astuce dont je n'aurais pas connaissance ?
après moultes recherches ces dernières 48h, j'ai enfin trouvé la réponse à mon problème ...
pour ceux qui se poseraient la même question que moi ("Il n'y a pas de question idiote, seulement une réponse idiote" Albert Einstein),
voici la solution :
Au début du document racine, ajouter cette instruction
set_include_path('.' . PATH_SEPARATOR . 'E:/ZF/library' . PATH_SEPARATOR . get_include_path());
A quoi cela correspond ?
En fait, on inclue dans les chemins de recherche par défaut le répertoire courant ('.'), le répertoire contenant les classes Zend ('E:/ZF/library') et on rappelle les chemins par défaut déjà déclarés (get_include_path())
En espérant avoir été clair pour les débutants Zend comme moi ;)
Trop bon votre tutoriel. Depuis hier je me casse le tête sur zend pour produire un webservice. Mon problème est maintenant résolu. Merci !
Salut Infinite. Très bon tuto. Mon soucis est la fameuse "SOAP-ERROR: Parsing WSDL". Le WSDL s'affiche bien. Je suis en phase de développement en local sous Debian. Que dois-je indiquer dans mon hosts?
Voici ce que j'ai actuellement: 127.0.1.1 nom_domaine.com alias
Pour atteindre le soap, je tape l'url:
nom_domaine.com/webservice/soap.
Est-ce correct ou dois-je ajouter quelque chose?