Expériences


Double initialisation Java

Posté dans Astuces, Programmation par Enisséo le 19 juin 2008

J’ai découvert il y a peu de temps une subtilité dans le fonctionnement du langage Java. Il s’agit du déroulement de l’étape de construction d’une nouvelle instance d’une classe.

Saurez-vous deviner le résultat de l’exécution du programme suivant :

public abstract class ClasseA {
 
	private String parent = "A";
 
	public ClasseA() {
		System.out.println(parent);
		System.out.println(enfant());
	}
 
	public abstract String enfant();
 
	private static class ClasseB extends ClasseA {
		private String enfant = "B";
 
		public ClasseB() {
			System.out.println(enfant());
		}
 
		@Override
		public String enfant() {
			return this.enfant;
		}
	}
 
	public static void main(String[] args) {
		new ClasseB();
	}
}

(plus…)

Opérateurs ++ et —

Posté dans Programmation par Enisséo le 25 mars 2008

Ce court article a pour objectif de répondre à une question fondamentale et dont la plupart des programmeurs ont une réponse erronée : Quelle est la différence entre i++ et ++i ?

Avant de donner la réponse, une petite question pour se mettre en situation : Quelles vont être les valeurs de j et de k à la fin de l’exécution de ce code ?

int i = 5;
int j = ++i + i--;
int k = i++ + --i;

(plus…)

Tutoriel : Présentation “PowerPoint” en ActionScript

Posté dans Programmation par Enisséo le 19 février 2008

Introduction

Contrairement à ce que le titre peut signifier, ce tutoriel va se limiter à une partie moins évidente pour l’affichage d’un diaporama (slideshow) en langage ActionScript : la présentation des diapositives (slides) sur plusieurs lignes et plusieurs colonnes de manière intelligente et dynamique. Le résultat de ce tutoriel est la disposition d’un ensemble d’images de manière à ce qu’elles occupent le maximum d’espace, en fonction de la taille de la fenêtre principale et de la taille de chaque image.

Ce tutoriel est destiné à des programmeurs ayant tout de même des notions d’ActionScript - notamment au niveau de la manipulation d’objets MovieClip, des observateurs (listeners) et du chargement de clips (typiquement MovieClipLoader) - et de programmation orientée objet. Je ne m’attarderai que sur les parties un peu plus orientées “algorithmes”.

Voici le résultat auquel nous parviendrons à la fin de ce tutoriel :

Résultat 1

(plus…)

Concevoir une application en programmation orientée objet

Posté dans Programmation par Enisséo le 23 décembre 2007

Cet article fait suite à une série d’expériences que j’ai pu avoir dans la gestion de projets de petite taille en informatique. Il présente les quelques points importants qui dirigent actuellement ma vision de la conception d’une application, notamment en programmation orientée objet, et qui me permettent d’appréhender les problèmes qui peuvent être rencontrés. Ces règles de “bonne conception” sont les résultats d’analyses personnelles sur les raisons des succès ou des échecs de certaines solutions conceptuelles que j’ai été amené à utiliser et sont également le reflet de ma vision actuelle de la programmation orientée objet.

1. Prototyper dans le but de réajuster la conception.
Même avec plusieurs années d’expériences, il est impossible de concevoir efficacement une application du premier coup, sans tester le modèle par un prototype. À l’inverse, il ne faut pas non plus se lancer dans le prototypage sans avoir réfléchi suffisamment à la solution. Un équilibre est donc à trouver.
On effectue tout d’abord une phase de conception relativement conséquente qui a pour buts de décomposer l’application en sous-ensembles et de dégager les problèmes principaux en termes d’intégration et de communication entre ces sous-ensembles. Cette phase entraîne la réalisation d’un prototype à l’échelle de la conception, c’est-à-dire qui met en œuvre les différentes parties de l’application et la communication entre ces parties, sans rentrer davantage dans le détail. Ce prototype permet de valider ou de remettre en question la première solution trouvée. S’enchaînent ensuite une série de raffinements de conception et de prototypes qui amène à une conception valide, qui répond au problème. Les différents prototypes réalisés permettent de surcroît de tester une première fois l’intégration de chaque sous-ensemble et peuvent servir de base pour l’élaboration de procédures de test plus spécifiques.

2. Ne pas perdre de vue que le langage orientée objet est avant tout un modèle.
Le langage objet s’apparente au langage machine mais y ajoute des structures abstraites compréhensibles par l’Homme. Il s’apparente donc plutôt à un modèle qu’à une mise en œuvre du programme, même si ce modèle a une granularité suffisamment fine pour que sa traduction en langage machine soit complètement définie. Lors de la conception d’une application, il faut utiliser le plus possible ces spécificités du modèle (typage, héritage, …) pour décrire de la manière la plus précise et la plus cohérente possible l’application.

3. Utiliser au maximum les notions de programmation orientée objet.
Si l’application doit être réalisée en programmation orientée objet, autant que la conception utilise ce modèle au maximum ! Sans tomber dans l’excès, il ne faut pas hésiter à définir des classes propres à l’application plutôt que d’utiliser les structures génériques du langage. Classiquement, si le nom d’un article d’un magasin peut être représenté par une simple chaîne de caractères (String), sa référence est plus abstraite : il peut s’agir d’un numéro, d’une série de caractères alphabétiques ou encore d’un mélange de caractères de toutes sortes. Une classe spéciale pour désigner cet attribut est alors tout à fait justifiée.
La programmation orientée objet apporte en plus des solutions pour résoudre des problèmes de conception. Les modèles de conception existants (Design Patterns) permettent de se rendre compte de l’étendue des possibilités. L’héritage permet notamment d’ajouter des attributs et méthodes spécifiques à une classe, et donc de définir un ensemble de types qui correspondent précisément aux besoins. La surcharge peut quant à elle ajouter des comportements ou des fonctionnalités de manière transparente.

4. Donner du sens à chaque structure et chaque choix conceptuel.
Une classe possède des propriétés et des fonctionnalités qui permettent d’agir sur son comportement interne. De la même façon qu’une voiture ne sait pas où et comment se garer sans conducteur, un objet n’a pas à savoir comment s’enregistrer dans une base de données ou un fichier. De plus, les propriétés d’une classe sont supposées définir les objets complètement : un attribut qui n’est jamais instancié ou utilisé dans un certain cas d’utilisation doit être remis en question. Aussi, la décomposition en classes et méthodes doit éviter de donner des rôles superflus à des classes.
Enfin, si une classe a pour objectif de n’être instanciée qu’une seule fois, autant définir ses attributs et méthodes de manière statique ! Cela évite les erreurs de multiple instanciation et les lourdeurs d’utilisation.

5. Considérer et utiliser les interfaces comme des contrats.
En programmation orientée objet une interface a pour objectif d’indiquer qu’une classe respecte un certain contrat et peut être utilisée de la manière prévue. La définition d’une interface permet donc d’envisager d’autres implémentations pour une certaine fonctionnalité, sans modifier les signatures des méthodes. On peut ainsi utiliser une interface plutôt qu’une classe pour définir une entrée ou sortie d’une méthode si on envisage à terme la généricité de cette dernière. De plus, l’utilisation d’une interface a peu d’impact en terme de performance puisque la vérification du respect du contrat qu’elle définit est effectuée à la compilation.

6. Choisir les structures de collections génériques en leur donnant un sens.
Seule la méthode ou la classe qui gère un groupe de données de même type est supposée connaître la structure précise de ce groupe. Du point de vue du modèle, et donc des entrées et sorties de cette méthode ou cette classe, seules les propriétés de ce groupe doivent être indiquées : est-il ordonné ? Y a-t-il possibilité de doublons ? Comment s’effectue l’accès aux données ? De cette manière, il n’y a que l’information pertinente qui est indiquée. De plus, s’il y a changement de l’implémentation pour une quelconque raison, celle-ci est transparente pour l’extérieur.
Le langage Java possède par exemple une multitude d’interfaces permettant de décrire des propriétés spécifiques pour un groupe de données, sans dévoiler la classe spécifique qui l’implémente. Pour indiquer qu’une méthode renvoie un groupe d’objets de manière générale on utilisera l’interface Collection. Si on veut préciser que chaque objet n’apparaît qu’une fois dans le groupe, on écrira Set, et si la collection est ordonnée alors on pourra employer List.

7. Décomposer l’application en modules aux rôles clairement définis et aux entrées et sorties génériques.
Une application met généralement en jeu plusieurs sous-ensembles de fonctionnalités. La décomposition de cette application par ces sous-ensembles a plusieurs conséquences bénéfiques :

  • parallélisation du travail par séparation de l’équipe en petites groupes ;
  • planification, conception et développement indépendants ;
  • développements autonomes en matière de technologies.

Cependant, pour pouvoir réaliser cette décomposition, la définition précise des rôles et limites de chaque module ainsi que les structures de données qui permettent la communication entre les modules est nécessaire. Il faut notamment veiller à ce que les entrées et les sorties soient, dans la mesure du possible et selon les contraintes d’intégration et de coût (ressources, performances…), les plus génériques possibles, c’est-à-dire les plus indépendantes du langage de programmation. Cela permet alors de modifier éventuellement la partie communication entre les modules en réalisant par exemple une partie de l’application en code natif, ou une autre disponible sous forme de service Web, sans avoir à redéfinir un ensemble de classes ayant pour objectif d’adapter les structures choisies au protocole de communication. On préfèrera donc l’utilisation des types classiques tels que les entiers ou les chaînes de caractères et les classes qui ne sont que des agrégations de ces types. On évitera plus particulièrement l’utilisation de structures de collections propres au langage. On utilisera donc plutôt des échanges de données sous forme de tableaux. On peut cependant s’autoriser quelques libertés dans des cas spécifiques.

8. Ne pas résoudre un besoin technique ou un problème par un biais conceptuel.
Le cas est typique : lors de la phase de développement on s’aperçoit qu’il manque une certaine fonctionnalité pour une classe ou un module. Il faut alors chercher une solution pour répondre à ce manque. Si la solution trouvée est une “astuce”, c’est-à-dire l’utilisation d’une partie de l’application (module, classe, méthode, attribut…) d’une manière différente (même légèrement) de ce pourquoi elle a été pensée et conçue, alors il faut très certainement continuer à chercher ! En effet, très généralement une “astuce” en entraîne une autre et la solution finale n’a plus aucun sens au niveau du modèle. Reprendre la conception, éventuellement le prototypage, et modifier le code existant pour suivre la nouvelle conception n’est pas du temps de perdu, à plus forte raison pour des applications de grande taille ou qui exigent une certaine robustesse.

9. Ne pas se contenter d’une solution à moitié convaincante et ne pas mélanger deux solutions fonctionnelles.
Une solution qui n’est pas formalisée complètement, donc une solution partielle, ne doit en aucun cas servir de base pour le développement de l’application. À coup sûr des problèmes apparaîtront et l’invalideront. Elle servira éventuellement – et seulement – de support pour la réalisation d’un premier prototype.
Si deux solutions pour un même problème sont valides, il faut éviter de prendre les avantages de chacune et en faire une combinaison. Le risque est de rendre deux sous-parties incompatibles et de trouver alors des solutions conceptuelles qui finalement rendent la qualité du modèle final inférieure à celle des modèles initiaux. En somme, lorsque plusieurs solutions sont possibles, on évitera de les mélanger sans précaution.

10. Ce qui a fonctionné pour une application ne fonctionnera pas pour une autre.
C’est la règle de base. Il n’y a pas de solution conceptuelle magique qui fonctionne pour tout les cas de figure. Même s’il existe des architectures qui peuvent être appliquées à beaucoup de cas, il subsiste toujours des exceptions. Il faut donc à chaque nouveau projet passer par une phase de conception conséquente pour éviter de remettre en cause la solution à chaque problème rencontré.

Les défauts de la Programmation Orientée Objet

Posté dans Programmation par Enisséo le 30 octobre 2007

La programmation orientée objet a été la grande évolution dans le domaine de la programmation informatique et des langages. Elle a introduit des concepts innovants et faciles d’accès qui ont séduit de nombreux développeurs. Cependant, avec un peu d’expérience, il en ressort des difficultés qui font que la programmation objet en tant que telle peut être un frein dans la construction d’applications, et risque éventuellement dans les années à venir de laisser sa place à d’autres techniques.

 

On peut facilement concéder à la programmation orientée objet plusieurs atouts. Le premier est marketing : modéliser l’ensemble d’un programme par des objets tels que ceux qui nous entourent est un concept attirant, à portée d’esprit voire même palpable. Cela permet normalement un apprentissage plus aisé de la programmation, en comparaison avec d’autres types de programmation comme la fonctionnelle, pour ne citer qu’elle. Un deuxième atout est la grande part de détection des erreurs à la compilation : les langages Java et C++, par exemple, se sont dotés de compilateurs performants qui, grâce à un typage fort des objets et à tout un tas de notions (héritage, visibilité…), permettent au développeur de corriger une partie du code très en amont. Le dernier atout est une certaine proximité avec les langages bas niveau : l’appel d’une méthode en programmation objet peut se traduire assez intuitivement en C ou Assembler. La réécriture et l’utilisation d’un code bas niveau en code objet se réalisent plus simplement qu’avec d’autres types de programmation. Je passerai sous silence les avantages procurés par le succès de la programmation orientée objet et du langage C++ en particulier, ce qui a amené au fil du temps un certain nombre de bibliothèques et de solutions conceptuelles.

 

Venons-en aux problèmes que l’on peut rencontrer lorsque l’on programme en objet. Je l’ai dit plus haut : le concept est pour moi purement marketing, et n’apporte pas de vraie solution pratique pour la programmation. En effet ce qui caractérise le monde, ce qui le fait vivre et évoluer, ce n’est pas tant la description des objets qui y habitent mais plutôt - et la notion est importante - les interactions entre ces objets. Depuis des années les modèles objet essaient de proposer des solutions plus ou moins bonnes pour répondre à ce manque.

Lorsque l’on travaille avec des bases de données, par exemple, un schéma souvent rencontré consiste en 3 niveaux : des objets qui représentent les informations et qui ne sont décrits que par des attributs, des objets permettant de communiquer avec la base de données, et enfin - et c’est là où je veux en venir - une couche d’objets qui permettent de faire la liaison entre la base de données et les objets du langage. On a besoin ici de traducteurs qui permettent de faire passer les informations du langage vers les informations de la base de données (classiquement de traduire un objet en langage SQL, et inversement). Ces objets-traducteurs n’ont pas d’existence réelle, ils n’ont pas de légitimité en tant qu’objets mais plutôt en tant que “méthodes”. Ils représentent ainsi en quelque sorte une limite à la programmation orientée objet.

Un modèle de conception fréquemment utilisé est celui de l’Observateur. Il permet à un objet observé de notifier un évènement à des objets observateurs, pas forcément connus de l’objet observé (dans le sens où l’observé ne sait pas qui l’observe et quand) ; il s’agit typiquement de provoquer des actions asynchrones : appel à une méthode lorsqu’un utilisateur clique sur un bouton, lorsque des données arrivent sur un flux, lorsqu’un attribut spécial est modifié… Ce modèle est implémenté de manière très sommaire dans les langages orienté objet : l’objet observé enregistre des objets observateurs par le biais de deux méthodes (ajout et suppression), puis quand une action spéciale est effectuée il parcourt la liste des observateurs et appelle une méthode particulière, généralement définie dans une interface spécifique à chaque type d’observateur. On ne peut s’y tromper : ce système est particulièrement lourd à mettre en place et comporte de nombreux défauts. Pour pouvoir observer les modifications sur un attribut par exemple il est obligatoire que l’objet qui possède les fonctions de modification de cet attribut mette en place tout un système d’observation (gestion de la liste et appel de la méthode sur chaque observateur à chaque modification de l’attribut). Il est donc limité à ce que le développeur d’un objet a bien voulu écrire. Toutes les difficultés de mise en place et d’utilisation de ce système peuvent être constatées lors du développement d’une application avec interface graphique en utilisant le modèle Modèle-Vue-Contrôleur (MVC) : un contrôleur est observateur des actions utilisateur sur la vue, elle-même observateur des modifications sur les données du modèle, modifications initiées par les contrôleurs.

 

Un autre problème important de la programmation orientée objet se dégage lors du développement d’applications qui se doivent robustes et ayant le moins de bugs possible. Il s’agit de logiciels utilisés dans des domaines critiques : santé, aviation, gestion de centrales éléctriques, etc. Le concept objet ne donne pas en soi de solutions qui permettent de spécifier des contrats que le programme doit respecter. Les contrats déterminent les règles que doit respecter un programme pour qu’il soit jugé correct en spécifiant pour les méthodes dans quels domaines ils ont été écrits et les invariants qui doivent subsister entre les attributs. On va par exemple pouvoir indiquer qu’une méthode n’est valide que pour un certain domaine d’entrée (les entiers positifs inférieurs à 100) et qu’un attribut doit toujours être supérieur à un autre. Le programme va se charger de vérifier que les règles sont respectées à la compilation et pendant l’exécution, et ceci quel que soit le contexte d’exécution (classiquement en multi-threads). La spécification de ces règles est actuellement très complexe en programmation orientée objet et la diffusion d’objets respectant des contrats n’est pas facilitée par des automatismes en matière de compilation et de documentation.

Dans le même genre, la programmation orientée objet souffre, malgré les bibliothèques existantes en la matière (nUnit, jUnit…), d’un manque d’assistance lors des phases de test. La spécification de tests de manière plus abstraite permettrait de gagner du temps dans leur rédaction et également d’augmenter de manière significative leur qualité. Actuellement, la rédaction de jeux de tests valides et fiables s’avère parfois plus longue et complexe que la conception de l’application en elle-même. De plus, le code d’un jeu de test n’est en général pas exempt de bugs. Il devient donc difficile d’être sûr à 100% qu’un programme répond à sa spécification. Par extension, on peut également reprocher aux langages orientés objet un manque d’abstraction dans le débuggage. Il s’agit en général d’afficher les variables ou d’exécuter le programme pas à pas, ce qui rend la détection de la source de l’erreur longue et fastidieuse.

 

Un aspect à la popularité grimpante semble également faire défaut à la programmation orientée objet dans le sens où elle ne fournit pas de solutions simples à comprendre et à utiliser : la synchronisation. Cette idée met en évidence les difficultés immenses qui apparaissent obligatoirement à terme dans le développement d’une application complexe : la gestion des exécutions en parallèles de parties de l’application. Si les langages objets sont pour la plupart dotés de systèmes de synchronisation (création de threads, gestion des files d’attente, sémaphores, verrous…), il n’en reste pas moins que ceux-ci sont bien souvent un casse-tête pour les concepteurs : quelles opérations peuvent être parallélisées ? quelles variables peuvent être modifiées simultanément ? comment assurer que telle ou telle opération va être exécutée avant une autre ? y’a-t-il un risque d’interblocage ? Pour l’instant, en programmation orientée objet, le concepteur doit mettre la main à la pâte en ayant en tête que le débuggage d’applications multi-threadées n’est pas une mince affaire.

 

Pour terminer, un dernier concept difficile à mettre en œuvre en programmation orientée objet est celui de l’extensibilité. De nombreuses applications ont vu le jour et dans lesquels la notion d’extensions (plugins) est très présente : les produits Mozilla, Eclipse… C’est à coup sûr une des raisons du succès de ces logiciels. Cependant, la programmation orientée objet n’offre pas de solution clef en main pour construire des applications extensibles. Il faut alors recourir à tout un système plus ou moins complexe qui fait souvent intervenir des opérations de réflexion au sens programmation, c’est-à-dire d’analyse de la structure des classes.

 

Quelques pistes de solutions commencent à émerger pour contrer ces manques majeurs de la programmation orientée objet. Le langage C# fournit par exemple des réponses syntaxiques à qui permettent de gérer plus facilement un système d’observateurs. Les concepts de programmation par aspect et de programmation par contrat font leur entrée et peuvent être intégrés à des langages objets. Java bénéficie par exemple des bibliothèques AspectJ et jContract. Cependant, il s’agit principalement d’astuces syntaxiques ou conceptuelles, voire de surcouches du langage, et il subsiste donc des soucis en terme d’efficacité mémoire et de performances.

La programmation orientée objet a donc un côté très attrayant pour le développeur : elle bénificie d’une popularité gigantesque et d’un concept simple et accrocheur. Malgré tout, elle doit évoluer vers de nouvelles formes de programmation afin de répondre à des exigences de plus en plus fortes, notamment au niveau de la création d’interfaces graphiques et d’applications multi-threadées ou distribuées.

Feux d’artifices

Posté dans Programmation par Enisséo le 20 août 2007

Voici un petit exemple de la technique du tutorial précédent : une fois pour les traînées de feu au lancement de la fusée et une autre évidemment lors de l’éclat des feux. J’utilise également la classe mx.transitions.Tween pour générer aléatoirement les déplacements des fusées en l’air.

A Flash animation should have appeared here, but it seems that your browser has an older version of the Flash Player or it is not installed at all. Please, install the last release of the Flash Player now, then reload this page.

Tutoriel : Fontaine aux lucioles avec Flash

Posté dans Programmation par Enisséo le 19 août 2007

Présentation

Un peu de script et de graphisme avec un tutoriel Flash sur la réalisation d’une sorte de fontaine à lucioles multicolores ! Rien de compliqué, il faut cependant avoir quelques bases en Flash, notamment dans la création de clips, et quelques notions d’ActionScript. J’utilise personnellement la version 8 de Flash, mais je pense que l’animation est réalisable avec des versions antérieures.

Voici le résultat auquel nous allons parvenir :

A Flash animation should have appeared here, but it seems that your browser has an older version of the Flash Player or it is not installed at all. Please, install the last release of the Flash Player now, then reload this page.

(plus…)

Tutoriel : Web Service avec PHP5 et nuSOAP

Posté dans Programmation par Enisséo le 28 juillet 2007

Présentation - Objectifs

Le but de ce tutoriel est de fournir une première approche de la création de services web (WebServices) en PHP, version 5, à l’aide de la librairie nuSOAP. Je ne rentrerai pas dans les détails concernant les Web Services. Je consacrerai à ces derniers un article spécial ou des liens intéressants dans un coin. Pour comprendre cet article, il est nécessaire d’avoir des bases en PHP, XML et Web Service (SOAP : Simple Object Access Protocol).

À la fin de ce tutoriel, vous serez capables - je l’espère - de créer votre propre service web en PHP, et également de l’utiliser.

Création d’un Web Service

Avant toute chose, vous allez avoir besoin de la librairie nuSOAP, disponible ici. Il s’agit d’une bibliothèque qui fournit des méthodes qui permettent de créer un serveur ou un client SOAP. Pour la création d’un client, il peut y avoir des conflits entre le nom de la classe nuSOAP soapclient et des classes dans les librairies natives de PHP. Si c’est le cas, renommez soapclient en soap_client (par exemple) dans tous les fichiers de la librairie nuSOAP.

Création de la méthode du service

Créez un fichier, service.php par exemple, qui correspondra au Web Service. Écrivez une méthode PHP que vous voudrez exporter, par exemple sayHello. Cette méthode prend en paramètre un prénom et renvoie une chaîne de bienvenue :

function sayHello($name) {
    return "Bienvenue ".$name;
}

Code de la méthode sayHello

Enregistrement du service

Il suffit maintenant d’enregistrer cette méthode en tant qu’opération de Web Service. Pour cela, on inclut le fichier principal de nuSOAP. Ce dernier permet de créer un objet de type soap_server, qui va permettre, à l’aide de la méthode register, d’enregistrer notre méthode sayHello.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// Inclusion de la librairie NuSOAP
require_once("nusoap/lib/nusoap.php");
 
// Définition de la méthode sayHello 
function sayHello($name) {
    return "Bienvenue ".$name;
}
 
// Création du service 
$server = new soap_server; 
 
// Enregistrement de la méthode 
$server->register('sayHello'); 
?>

Code du fichier service.php

Traitement des requêtes SOAP

Pour terminer, il va falloir traiter les requêtes des clients. On appelle la méthode service de l’objet soap_server en lui passant le contenu de la requête. Le fichier service.php permet donc à la fois de construire le Web Service et de traiter les requêtes client. On rajoute ainsi au fichier service.php :

15
16
17
// Invoque le service 
$requete_HTTP_brute = (isset($HTTP_RAW_POST_DATA)?$HTTP_RAW_POST_DATA:''); 
$server->service($requete_HTTP_brute);

Code de traitement des requêtes client

Les Web Services sont indépendants du protocole de communication. Dans le cas des sites web, le protocole est généralement HTTP. PHP traite les requêtes HTTP des clients (fournies par le serveur web) et permet d’accéder directement aux données “intéressantes” (variables GET et POST) de cette requête. Cependant, dans le cas d’un Web Service, la requête ne suit pas le protocole HTTP mais le protocole SOAP. C’est alors la méthode service qui va analyser cette requête et, en l’occurence, renvoyer le résultat, également sous format SOAP. PHP permet d’avoir accès à la requête brute par l’intermédiaire de la variable $HTTP_RAW_POST_DATA. Il faut cependant parfois modifier la configuration always_populate_raw_post_data (la mettre à la valeur On) du fichier php.ini.

Test du service

Il ne reste plus qu’à tester le service. Rendez-vous à l’adresse correspondant au fichier service.php (http://localhost/testnusoap/service.php par exemple). Vous devriez alors voir le message :

<SOAP-ENV:Envelope soap-env:encodingstyle="http://schemas.xmlsoap.org/soap/encoding/" xmlns:soap-env="http://schemas.xmlsoap.org/soap/envelope/" 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:Body>
    <SOAP-ENV:Fault>
        <faultcode xsi:type="xsd:string">Client</faultcode>
        <faultactor xsi:type="xsd:string"></faultactor>
        <faultstring xsi:type="xsd:string">method '' not defined in service</faultstring>
        <detail xsi:type="xsd:string"></detail>
    </SOAP-ENV:Fault>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>

Résultat de l’appel au service

Il s’agit d’un message d’erreur au format SOAP qui indique qu’aucune méthode du service n’a été spécifiée par le client : votre service fonctionne a priori. Nous allons tester maintenant la méthode sayHello en écrivant un client pour le Web Service.

Accès à un Web Service

Il existe plusieurs solutions en PHP pour accéder à un Web Service. La bibliothèque nuSOAP en fournit une, mais il existe des méthodes et objets natifs en PHP5 que je ne developperai pas ici.

Dans un fichier séparé, test-sayHello.php par exemple, nous allons créer un client SOAP. Pour cela, il faut indiquer l’adresse du service :

1
2
3
4
5
6
<<php
// Inclusion de la librairie NuSOAP 
require_once("/nusoap/lib/nusoap.php"); 
 
// Création du client 
$client = new soapclient("http://localhost/testnusoap/service.php");

Code de création du client SOAP

On va maintenant appeler la méthode sayHello du service. Pour cela, on fournit le nom de la méthode et les paramètres de cette méthode :

7
8
9
10
11
12
// Appel de la méthode sayHello du service 
$response = $client->call('sayHello', array(
    'name'	=> "Michel"
    ));
 
?>

Code d’appel de la méthode sayHello

L’objet $response contient alors la réponse SOAP. Si nous l’affichons à l’aide de var_dump($response);, nous obtenons pour l’exemple de la méthode sayHello :

string(16) "Bienvenue Michel"

Résultat de l’appel à la méthode sayHello

Pour aller plus loin

Ce tutoriel n’aborde pas l’API nuSOAP en profondeur. Pour plus d’informations, référez-vous à la documentation de l’API.

Nous avons été tout de même en mesure de créer un WebService et de l’utiliser grâce à la librairie nuSOAP qui permet d’abstraire largement le protocole SOAP. Il est évidemment possible d’agrémenter notre service d’autres méthodes, en utilisant le même principe. Comme nous l’avons vu, SOAP prévoit un système d’erreur spécifiques (les Fault). La librairie nuSOAP permet de gérer ces erreurs, notamment en vérifiant le type de la réponse et en utilisant le champ fault de cette réponse. Il est également possible d’afficher les requêtes et réponses SOAP (en XML donc) en utilisant les champs request et response de l’objet soapclient.

La distribution d’un Web Service passe généralement par une description de celui-ci dans un format particulier : WSDL (Web Services Description Language). nuSOAP permet également de générer cette description. Il faut pour cela utiliser la méthode configureWSDL de l’objet soap_server. À l’inverse, il est envisageable de se servir du WSDL d’un service existant pour créer un client SOAP.

Conclusion

Comme vous avez pu le voir, la création d’un Web Service en PHP est relativement simple. Il n’y a en plus que très peu de problèmes rencontrables au niveau de la configuration de l’environnement. Néanmoins, seules les méthodes et fonctions de base ont pour l’instant été abordées. La création d’un Web Service plus complet et complexe pourra être l’objet d’un prochain article.

Cet article peut être modifié ou approfondi ultérieurement. Il ne se veut pas complet et n’est pas exempt d’erreurs. N’hésitez pas à les signaler.

Vous pouvez m’adresser vos remarques ou suggestions à enisseo@hotmail.com. Je ne manquerai pas de vous répondre.