{Benjamin PREVOT}

Produire un WebService SOAP avec Zend Framework

Plusieurs méthodes existent pour créer des WebServices SOAP.

  • Contract First ou Top-Down : le fichier WSDL est écrit en premier, puis l'implémentation.
  • Code First ou Bottom-Up : l'implémentation est d'abord faite, puis le fichier WSDL est généré à partir du code écrit.

Il est souvent recommandé d'utiliser la première méthode, mais rédiger un fichier WSDL peut vite devenir compliqué (même si de plus en plus d'outils aide à sa réalisation).

Nous allons voir dans cette article comment mettre en place la 2ème méthode.

Pour réaliser les exemples ci-dessous, j'ai utilisé la version 1.10.3 du framework de Zend que vous pouvez télécharger ici : http://framework.zend.com/download/archives.

Création de la classe de service

Avant de commencer l'implémentation avec le framework Zend, nous allons mettre en place une classe qui regroupera les différentes méthodes qui seront exposées via notre WebService.

Cette classe sera enregistrée dans un sous-répertoire des controllers (services) dans le fichier MonWebService.php.

controllers/services/MonWebService.php
<?php
class MonWebService {
	// ...
}
?>
 

Nous allons simplement définire une fonction qui fera l'addition de 2 entiers passés en paramètres.

Bien entendu, il est possible de définir autant de méthodes qu'on le souhaite.

La classe est alors codée comme suit :

controllers/services/MonWebService.php
<?php
class MonWebService {
 
	/**
	 * Addition de 2 entiers
	 * @param integer $a
	 * @param integer $b
	 * @return integer
	 */
	public function add($a, $b) {
		return $a + $b;
	}
 
}
?>

Vous pouvez remarquer le commentaire de la fonction : il sera repris lors de la génération du fichier WSDL. N'oubliez pas de l'adapter à vos besoins.

En fait, en respectant la syntaxe PHP docblock, le fichier WSDL généré pour typer les paramètres et les valeurs de retours des fonctions exposées.

Mise en place du controller

Nous allons maintenant définir le controller qui permettra de retourner la WSDL et de traiter les requêtes SOAP.

Pour cela, nous créons un controller appelé WsController avec une seul action index.

controllers/WsController.php
<?php
require_once APPLICATION_PATH . '/controllers/services/MonWebService.php';
 
class WsController extends Zend_Controller_Action {
 
	public function indexAction() {
		if (is_null($this->getRequest()->getParam('wsdl'))) {
			// Traitement de la requête
			$server = new Zend_Soap_Server('http://monserveur/ws/?wsdl');
			$server->setClass('MonWebService');
			$server->handle();
		} else {
			// Retour de la WSDL
			$wsdl = new Zend_Soap_AutoDiscover();
			$wsdl->setClass('MonWebService');
			$wsdl->handle();
		}
		exit;
	}
 
}
?>

Nous vérifions si l'utilisateur a demandé la WSDL (présence du paramètre wsdl) ou a fait un requête.

Ainsi, pour afficher la définition du WebService, il suffit d'appeler l'adresse http://monserveur/ws/?wsdl.

Pour appeler le WebService, l'adresse à appeler est http://monserveur/ws/.

Test du WebService

Pour tester notre WebService, plusieurs méthodes sont possibles.

Personnellement, j'utilise le logiciel soapUI : http://www.eviware.com/soapUI/soapui-products-overview.html.

Pour ceux qui ne souhaitent pas télécharger cet outil, nous allons mettre en place un controller permettant d'appeler notre WebService.

Création du controller

Dans un premier temps, nous allons créer un controller appelé WsClientController :

controllers/WsClientController.php
<?php
class WsClientController extends Zend_Controller_Action {
 
	public function indexAction() {
		// Récupération des 2 paramètres
		$a = $this->getRequest()->getParam('a');
		$b = $this->getRequest()->getParam('b');
 
		// Appel du WebService
		$client = new Zend_Soap_Client('http://monserveur/ws/?wsdl');
		$result = $client->add($a, $b);
 
		// Passage des informations à la vue
		$this->view->a = $a;
		$this->view->b = $b;
		$this->view->result = $result;
	}
 
}
?>

Comme vous pouvez le remarquer, l'appel du WebService se fait en 2 étapes :

  • La création du client Zend_Soap_Client
  • L'appel de la méthode directement sur l'instance du client

Ce code ne tient pas compte de l'existence des paramètres, de leur type...

Il s'agit simplement d'un test pour valider le bon fonctionnement de notre WebService.

Création de la vue

Il nous faut maintenant afficher le résultat.

views/scripts/wsclient/index.phtml
<h1>
<?php printf('%s + %s = %s', $this->a, $this->b, $this->result) ?>
</h1>

Nous affichons simplement la formule mathématique et le résultat.

Test

En appelant l'adresse http://monserveur/wsclient?a=1&b=2, on obtient 1 + 2 = 3.

Le résultat de l'addition a bien été calculé par le WebService.

Erreurs rencontrées

SOAP extension is not loaded

L'extension PHP permettant de gérer les appels SOAP n'est pas activée dans le fichier de configuration.

Il faut éditer le fichier php.ini et activer php_soap.dll (ou php_soap.so)

  • 17 juin 2010 à 14h21

    [...] Ce billet était mentionné sur Twitter par Gregoire Pineau et Alexandre JULIEN, Benjamin Prevot. Benjamin Prevot a dit: Produire un WebService SOAP avec Zend Framework – http://bit.ly/bNvc5b [...]

  • Myna
    4 août 2010 à 15h31

    Merci pour ce tutoriel, très clair:)
    Une toute petite coquille, il manque le ; à la fin de l’action index dans le WsClientController : $this->view->b = $b;

  • 4 août 2010 à 19h01

    Merci Myna pour ce commentaire;)
    J’ai corrigé le code.

  • 25 juillet 2011 à 12h47

    Bonjour les amis, mais comment peut t-on securiser ce web service???

  • 17 août 2011 à 07h59

    Bonjour Lali,

    Je ne suis pas expert en sécurité, donc ma réponse sera assez générale;-)
    Je pense en particulier à l’utilisation du protocole HTTPS (pour éviter que les données ne soient transférées en clair) ainsi qu’à la mise en place d’une authentification (via un fichier .htpasswd par exemple).

  • Damien
    16 novembre 2011 à 14h53

    Merci pour ce tuto, par contre, une toute petite remarque. Il est OBLIGATOIRE de commenter la fonction appelée « add » dans votre exemple et de bien typer les variables dans le commentaire + le retour. Sinon, ça ne fonctionne pas, et on perd 2 jours… comme moi ^^

    En espérant que ça permette à quelqu’un d’autre d’éviter de perdre du temps, je recommande donc de marquer en gros que c’est obligatoire.

  • 16 novembre 2011 à 16h02

    Bonjour Damien,

    Merci du conseil.

    La fonction add est pourtant clairement documentée il me semble : action réalisée, types d’entrée et de retour.

    Par contre, il est vrai que j’ai été un peu avare d’explication sur son utilisation.

    Je ferai mieux la prochaine fois;)

  • Damien
    16 novembre 2011 à 16h55

    Oui, la fonction que tu présente est documentée, c’est pour ça que ça marchait lors de mes tests. Mais lors d’autres tests avec mes propres fonctions, je me suis retrouvé coincé à cause du fait que je n’avais pas encore documenté.

    Je pense qu’il est donc utile de bien mettre qu’il est obligatoire de nommer les @param et @return sinon le retour est vide.

  • 16 novembre 2011 à 18h06

    OK, désolé, je n’avais pas compris ta remarque.

    J’ai ajouté un commentaire en rouge dans l’article.

  • Nicolas
    28 juin 2012 à 16h02

    Bonjour,

    Je viens de suivre le tuto à la lettre et ça fait deux jours que j’ai un retour 1 + 2 =

    Il ne m’affiche pas le résultat. … Je pense qu’il ne passe même pas dans la fonction add ! J’ai pourtant bien mis (au dessus de ma fonction add) :

    /**
    * Addition de 2 entiers
    * @param integer $a
    * @param integer $b
    * @return integer
    */

    Merci d’avance pour votre aide.

  • 29 juin 2012 à 07h57

    Bonjour,

    Y a-t-il des messages d’erreurs dans les logs ?

    Il pourrait être aussi intéressant de tester avec une valeur fixe pour la variable result ($result = 3;) dans le controller afin de valider que le problème ne vient pas de l’affichage.

  • Nicolas
    29 juin 2012 à 10h31

    Je n’ai rien dans les logs … En forçant le résultat à 3, il s’affiche bien donc cela ne vient pas de l’affichage. Je suis vraiment bloquer. Si vous avez un projet qui marche à m’envoyer par mail je veux bien voir si il marche aussi chez moi. Dans mon php.ini j’ai bien ajouté l’extension soap.so aussi.

    Merci d’avance

  • Nicolas
    29 juin 2012 à 11h56

    quand je print_r($result) et print_R($client->getFunctions()) j’obtiens :

    1 + 2 =
    result : NULL

    result : Array
    (
    [0] => void add(anyType $a, anyType $b)
    [1] => void string()
    )

    Comme si il ne prenait en compte le PHP doc block …

  • 29 juin 2012 à 13h43

    Avez-vous testé le Web Service via un client comme SoapUI ?

    Le résultat est-il correct ?

  • Nicolas
    29 juin 2012 à 15h17

    Non je n’ai jamais utilisé ce type de client. Le résultat devrait être le même que mon Zend_Soap_Client ?

  • Nicolas
    29 juin 2012 à 15h19

    Le problème est (je pense), que mes paramètres ne sont pas typés :

  • Nicolas
    29 juin 2012 à 15h29

    Pardon, le code HTML ne s’est pas copié:):

    part name= »a » type= »xsd:anyType »

  • 30 juin 2012 à 09h25

    En effet,

    Le non typage des paramètre est étrange.

    En regardant la WSDL, j’ai bien
    <message name= »addIn »>
    <part name= »a » type= »xsd:int »/>
    <part name= »b » type= »xsd:int »/>
    </message>
    Quelle version de Zend est utilisée ?

  • Nicolas
    2 juillet 2012 à 11h12

    La dernière version. La 1.11.12 …

  • Nicolas
    2 juillet 2012 à 11h37

    Quand je réécris le PHP doc block il me met bien xsd:int, et quand je rafraichis une 2 eme fois il me met xsd:anyType :/

  • Salem
    5 juillet 2012 à 18h24

    merci pour ce tuto ça m’a énormément aidé.
    j’ai pris ce code et je l’ai adapté à mon ptit projet, mais j’ai eu un ptit souci
    j’explique:
    supposons que j’ai ajouté les lignes qui suit à la fonction « add » de la classe « MonWebService » :
    …..
    $monModel = new Application_Model_MonModel();
    $data = $monModel->recuererDonnees();
    …..
    en mettant juste ces lignes il me sort un Fatal Error :
    Fatal Error : Uncaught SoapFault exception: [Receiver] Unknown error in C:\xampp\ZendFramework……
    comme s’il ne reconnais pas la classe MonModel

    merci d’avance pour votre aide

  • Salem
    6 juillet 2012 à 15h38

    bonjour à tous

    j’ai résolu mon problème, c’étai jute un problème d’encodage à la base de données (utf8 le sauveur!)
    voyons maintenant comment gérer les types complexes

  • miska
    10 avril 2013 à 15h31

    Bonjour,

    Merci, j’ai suivi votre tuto pas à pas avec mon application zf le tout en local, mais quand j’ai appelé l’action index du contrôleur WsClent j’ai obtenu cette erreur:
    #
    Parsing WSDL: Couldn’t load from ‘http://192.168.x.y/ws/?wsdl‘ : failed to load external entity « http://192.168.x.y/ws/?wsdl »
    #

    Autres choses que j’ai pas compris,
    1. Vous dites que le wsdl sera généré, par quelle action et où? est ce que c’est en appelant l’action index du contrôleur WsContrôler via un bouton cliqué par exemple ?
    2. le répertoire ws est-il à créer ou bien c’est auto

  • 10 avril 2013 à 19h03

    Bonjour miska,

    1. La WSDL est générée grâce à l’appel de la méthode handle() de la classe Zend_Soap_AutoDiscover

    2. En fait, il n’y a pas de répertoire à créer, le contexte « ws » correspond au controller WsController.

    Il faut faire l’appel en fonction du controller que vous avez mis en place (TestController => http://monserveur/test/?wsdl).

  • miska
    14 avril 2013 à 18h11

    Bonjour,

    Merci, Je suis tjr bloqué,

    ma classe « MonWebService.php »:

    ################
    class MonWebService {
    /*Parametres du serveur*/
    /**
    * Addition de 2 entiers
    * @param integer $a
    * @param integer $b
    * @return integer
    */
    public function add($a, $b) {
    return $a + $b;
    }
    }
    ################

    mon WsController

    #####################
    class WsController extends Zend_Controller_Action
    {
    /**
    * The ws action – show the home page
    */
    public function tstAction() {
    echo « TST »; exit;
    }
    public function indexAction() {
    require_once APPLICATION_PATH . ‘/modules/default/controllers/services/MonWebService.php’;

    // $this->_helper->viewRenderer->setNoRender();

    if (is_null($this->getRequest()->getParam(‘wsdl’))) {
    // Traitement de la requête
    $server = new Zend_Soap_Server(‘http://127.0.0.1/ws/?wsdl‘);
    $server->setClass(‘MonWebService’);
    $server->handle();
    } else {
    // Retour de la WSDL
    $wsdl = new Zend_Soap_AutoDiscover();
    $wsdl->setClass(‘MonWebService’);
    $wsdl->handle();
    }
    exit;
    }

    }
    #######################

    mon view default/ws/index.phtml

    #############

    a, $this->b, $this->result)
    ?>

    #############

    mon view default/index/index.phtml

    #####################
    W S D L

    W S

    W S C
    #####################

    Quand je click sur « W S D L », j’obtient dans le view « Addition de 2 entiers » et deux champs text s’affichent,

    mais je trouve pas de fichier wsdl partout dans mon appli, et si je click ensuite sur « ws » une erreur s’affiche « erreur lors de la récupération de données du serveur »

    Merci d’avance

  • 14 avril 2013 à 19h53

    Bonjour miska,

    Avez-vous suivi exactement le tutoriel ?

    Par exemple, je vois que l’appel à require se situe dans la méthode indexAction et non en début de fichier, comme dans mon code.

    Il y a également des appels à exit en plus.

    Essayez de repartir d’un projet vierge et d’y ajouter mon code au fur et à mesure pour tester.

    Vous pourrez ensuite compléter avec vos propres fonctionnalités.

  • miska
    15 avril 2013 à 12h04

    Bonjour,

    Le génération du wsdl marche quand j’ai juste changé ma manière d’appel d’actions via l’ajax, Apparemment l’ajax génére du html comme interprétation du wsdl ??, maintenant c’est l’étape suivante qui se bloque encore,

    ########
    2013-04-14T16:26:57+02:00 DEBUG (7): SOAP-ERROR: Parsing WSDL: Couldn’t load from ‘http://localhost/ws?wsdl‘ : Premature end of data in tag html line 2
    #######

    Mon WsController:

    #########
    class WsController extends Zend_Controller_Action
    {

    public function indexAction() {
    require_once APPLICATION_PATH . ‘/modules/default/controllers/services/MonWebService.php’;

    if (is_null($this->getRequest()->getParam(‘wsdl’))) {
    // Traitement de la requête
    $server = new Zend_Soap_Server(‘http://127.0.0.1/ws/?wsdl‘);
    $server->setClass(‘MonWebService’);
    $server->handle();
    } else {
    // Retour de la WSDL
    $wsdl = new Zend_Soap_AutoDiscover();
    $wsdl->setClass(‘MonWebService’);
    $wsdl->handle();
    }
    exit;
    }

    }
    #########

    Mes lien d’appel pour les actions :
    « j’ai enlever les tag html car j’ai noté que leur contenu ne s’affiche pas après l’envoi du message »
    ############
    a href= »url(array(‘module’=>’default’, ‘controller’=>’ws’, ‘action’=>’index’, ‘wsdl’=>1));?> »>generer wsdl /a
    a href= »url(array(‘module’=>’default’, ‘controller’=>’ws’, ‘action’=>’index’));?> »> appeler ws /a
    ############

    Merci de votre aide

  • miska
    15 avril 2013 à 17h30

    Bonjour,

    J’ai bien suivi le tutoriel, la ligne « require_once » est maintenant en debut de fichier mais toujours erreur en cliquant sur le lien « appeler ws »:
    ########

    WSDL

    SOAP-ERROR: Parsing WSDL: Couldn’t load from ‘http://127.0.0.1/ws/?WSDL‘ : Premature end of data in tag html line 2

    #######

  • 15 avril 2013 à 18h23

    Bonjour miska,

    C’est déjà bon signe que la WSDL soit générée correctement;)

    Par contre, pour faire un test de Web Service, il ne faut pas simplement appeler l’URL dans un navigateur.

    L’utilisation d’un client comme SoapUI est nécessaire ou un controller peut également faire l’affaire (voir WsClientController dans le tutoriel)

  • miska
    17 avril 2013 à 12h28

    Bonjour,

    Merci de votre explication,

    J’ai testé avec le controller WsClientController et toujours même problême:

    ##
    2013-04-17T11:16:39+02:00 DEBUG (7): SOAP-ERROR: Parsing WSDL: Couldn’t load from ‘http://localhost/ws/?wsdl‘ : failed to load external entity « http://localhost/ws/?wsdl »
    ##
    mon WsClentController:
    #######
    class WsClientController extends Zend_Controller_Action {

    public function indexAction() {
    // Récupération des 2 paramètres
    $a = $this->getRequest()->getParam(‘a’);
    $b = $this->getRequest()->getParam(‘b’);

    // Appel du WebService
    $client = new Zend_Soap_Client(‘http://localhost/ws/?wsdl‘);
    $result = $client->add($a, $b);

    // Passage des informations à la vue
    $this->view->a = $a;
    $this->view->b = $b;
    $this->view->result = $result;
    }

    }
    #######

    Mais pourquoi aprés la génération je ne trouve pas de fichier wsdl enregistré quelque part? ou peut être je ne comprends pas bien la logique de fonctionnement de zend soap, je suppose que les choses passent comme suit:

    1- Appeler l’action index du controller ws avec URL sans paramètre wsdl pour générer le fichier wsdl (via Zend_Soap_AutoDiscover) et l’enregistrer quelque part dans le dossier d’application

    2- Appeler l’action index du controller ws avec URL qui contient paramètre wsdl pour exécuter le serveur (via Zend_Soap_Server) basé sur le fichier déjà généré.

    3- Tester le serveur avec un client web service (zend ou autre).

    Merci de me corriger et orienter SVP.

  • 17 avril 2013 à 13h55

    Bonjour miska,

    La WSDL est générée dynamiquement à chaque appel, il ne faut pas l’enregistrer.

    Il suffit de préciser son chemin (http://localhost/ws/?wsdl) au client qui récupérera automatiquement les informations dont il a besoin pour exécuter le code.

  • Gilles
    20 novembre 2013 à 17h38

    Bonjour à tous,
    j’utilise la version 1.12 de Zend Framework. J’aimerai récupérer le fichier wsdl généré à partir du webservice. Mais à chaque fois que j’essaie http://localhost/ws/?wsdl, mon serveur web (xampp) fait une redirection vers une autre page (la page d’accueil xampp) et le fichier wsdl n’est toujours pas généré. Comment dois je procéder dans ce cas?

  • 20 novembre 2013 à 18h42

    Bonjour Gilles,

    Cette redirection a-t-elle lieu uniquement pour la page WSDL ? Ou pour d’autres types de pages ?

Les avatars utilisés proviennent du site Gravatar. Pour créer un compte, vous pouvez vous inscrire.
* Champs obligatoires