Génération de classes PHP hiérarchisées à partir d'un fichier WSDL

0 commentaire

Ma dernière mono-manie est née au moment où je devais communiquer avec un WebService en SOAP.

De nature à vouloir tout développer en objet et à être fan de l'auto-complétion dans mon IDE préféré Zend Studio, je souhaitais pouvoir développer mes communications avec SOAP sans me poser la question ni sur les méthodes disponibles ni sur les paramètres nécessaires et leur type. La solution pour connaitre les méthodes disponibles, leur paramètre et le type de chacun des paramètres est soit de lire le fichier WSDL, qui peut alors atteindre des centaines de lignes voire des milliers, soit lire la doc associée au WSDL, pas forcément disponible selon le fournisseur du WebService. Dans tous les cas, cela n'empêche pas que le développement des communications avec le WebService reste très peu normé et reste alors au bon choix du développeur.

D'une part, certains développeurs vont préférer utiliser curl, d'autre la classe SoapClient. Puis, même si le développeur choisit de faire appel à la classe SoapClient, il peut décider d'implémenter seulement les méthodes qui l'intéressent dans son cas et donc de les organiser dans une classe unique ou plus simplement au sein de fonctions indépendantes - ce que j'ai personnellement fait lorsque j'ai du communiquer avec l'API OVH en 2008/2009. Dans tous les cas, il peut paraître plus simple d'implémenter seulement les méthodes utiles à l'application en cours, mais sur le long terme, il me semble toujours plus pérenne d'avoir une "librairie" stable et complète afin de maitriser l'ensemble du périmètre ainsi que d'harmoniser la méthode de gestion des appels et de réception des réponses. Ainsi, cela revient à utiliser une sorte de framework dédié au WebService SOAP utilisé. Inutile de vous exposer les avantages d'un framework (et les désavantages de certains).

D'autre part, utilisant Zend Studio, je me suis habitué à l'auto-complétion sur les fonctions natives PHP ainsi que sur les fonctions, classes, méthodes de classes, constantes (de classes), etc - certains diront, "rien ne vaut notepad", bonne blague.... De ce fait, il est toujours plus aisé et rapide de développer avec une aide à la saisie évitant ainsi toute erreur de développement (appel de méthodes mal écrit, appel de fonction erronée, etc). Tout ceci est donc "automatique" à partir du moment où l'on fait appel à des fonctions natives PHP, ou à des méthodes de classe existantes dans le framework ou CMS en cours de développement. Mais lors du développement de la communication avec un WebService, il est alors assez long de créer les méthodes adéquates pour communiquer avec le WebService si on souhaite à minima faire un code générique et réutilisable dans une autre partie de votre projet par d'autres développeurs. Récemment, j'ai donc été amené à communiquer avec un serveur SharePoint par ses WebServices SOAP afin d'effectuer les opérations de type CRUD. Il m'était alors nécessaire de développer les méthodes pour créer des nouveaux éléments, les mettre à jour, les lire et les supprimer si nécessaire. J'avais donc le choix entre développer les méthodes seulement nécessaires pour mon projet ou générer l'ensemble des méthodes accessibles par le WebService SOAP SharePoint. Mon choix s'est donc porté sur la génération de classes contenant l'ensemble des méthodes à l'image du WSDL fourni par le serveur SharePoint. Mon code de départ permettant de générer l'ensemble de ces classes était assez séquentiel, et pas très adapté aux multitudes de cas possibles que fournissent les WebServices SOAP.

Est donc née mon envie d'avoir un système générique, évolutif et le plus structuré possible, qui par l'utilisation d'options permettrait de modifier le comportement de la génération des classes et de s'adapter aux différents WebServices SOAP. Il me semblait nécessaire de pouvoir :

  • organiser hiérarchiquement les classes générées
  • définir la manière de déterminer le nom des classes (et donc de les organiser)
  • générer ou non un fichier d'autoload de l'ensemble des classes générées
  • changer la manière de passer les paramètres aux méthodes SOAP (tableau avec nom des paramètres en index, tableau de "parameters", paramètres simples)
  • modifier la manière de prendre en charge la réponse du WebService

Tout ceci est donc accessible via des options. De plus, après de multiples tests sur un grand nombre de différents WebServices SOAP, j'ai pris en compte plusieurs informations non présentes dans les informations retournées par les méthodes natives __getTypes() et __getFunctions(). Les informations présentes que j'ai alors pris en compte sont les suivantes :

  • documentation : balise "documentation" pouvant être présente dans presque chaque type de balise du WSDL
  • énumération : les énumérations contenues par les balises "enumeration" permettent de lister les valeurs possibles pour un paramètre donné d'une méthode donnée
  • attributs : les paramètres d'une méthode peuvent contenir des attributs définissant par exemple le caractère obligatoire de ce dernier (minOccurs, maxOccurs). Mais on peut aussi y trouver d'autres attributs jugés utiles par le créateur du WSDL (valeur par défaut : attribut "default"). Tous ces attributs sont alors pris en compte lors de génération de la classes représentant les paramètres nécessaire à une méthode
  • héritage : la balise "extension" par son attribut "base" permet d'indiquer que l'élément contenant cette dernière est un héritage de l'élément indiqué dans l'attribut "base"
  • import : un fichier WSDL peut être amené à charger des fichiers WSDL ou XSD depuis un lien externe ou relatif à l'url du WSDL. Les fichiers externes chargés permettent de compléter la définition du WSDL d'origine. Ces imports sont alors contenus par la balise "import" dont l'url du fichier externe est contenue soit par l'attribut "location" soit par l'attribut  "schemaLocation"

Certains me demanderont : quelle utilité ? N'existe-t-il pas déjà un développeur ayant développé ce type de fonctionnalité ?

Mes réponses : relire mon article - il existe déjà quelques autres solutions répondant à mon envie, mais pas aussi précisément et aussi adapté que ce que j'ai fait. Mais je vous rassure, je m'en suis rendu compte après, j'ai donc testé ces systèmes, et ils m'ont conforté dans mon développement. De plus, j'ai récemment eu des retours positifs sur mon développement, donc cela me confirme que je ne suis pas seul au monde !

Assez parlé, vous pouvez trouver mon module sur les plateformes de partage open source suivantes :

Vous y trouverez à la fois le module de génération des classes mais aussi de multiples exemples, alors peut-être que celui qui vous intéresse y est déjà. Dans tous les cas, n'hésitez pas à me faire vos retours, quel qu'il soit, soit dans les commentaires de l'article soit sur la plateforme de votre choix. J'y répondrai très volontiers.

A vos tests !

Mots clefs :