Dans cet article, nous aborderons la mise en place d'un service AMF en PHP avec la solution de Midnight Coders : Weborb PHP. Nous commencerons par créer un service rudimentaire pour aborder les méthode de service, les type de retour, les paramètres de méthodes, la portée des méthode, la gestion des erreurs et enfin, nous exploiterons l'héritage pour gérer plus "finement" l'organisation des services.
Installation
Rendez-vous sur le site de Midnight Coders et téléchargez la dernier version de Weborb PHP (J'utilise la 3.6). Vous devez disposer d'un compte pour le téléchargement(c gratuit :P)
l'installation est relativement simple, décompactez le zip dans votre dossier web, vous allez obtenir cette arborescence :
Les fichiers importants à retenir sont :
- /index.php : Vous donne accès à la console de Weborb pour tester vos services, si l'installation a bien fonctionné, elle est dès maintenant accessible
- /weborb.php : Il s'agit de la passerelle, nous reviendrons sur ce fichier un peu plus tard.
- /Services/ : L'endroit où vous allez passer beaucoup de temps, c'est là que sont rangés les services AMF.
Premier service AMF : SimpleService
Les services PHP se présentent toujours sous la forme d'un Classe PHP. Par convention, on nomme généralement le fichier avec le nom de la classe. Dans notre cas donc, nous allons créer un fichier SimpleService.php dans le dossier /Services qui contiendra une classe SimpleService.
La classe se décompose ensuite en série de méthode qui remplirons chaque une un tâche spécifique, notre première méthode respectera la procédure de tout tutorial d'initiation en retournant une chaine "Bonjour Monde", nous appellerons la méthode "direBonjour".
Tester ces Services AMF : La console de Weborb
L'intérêt majeur d'utiliser Weborb PHP, c'est la console d'administration fournit, elle propose une serie d'exemple d'utilisation, et surtout une interface de gestion qui propose notamment :
- Management : Console de test des services
- CodeGen : Un générateur de code source en fonction du support (Ajax, Flex, Flash) et en fonction du framework (Cairgorm, Puremvc, api de base)
Pour accéder à la console, rendez vous à l'index.php de l'installation de Weborb via votre navigateur, vous devriez obtenir une vue qui ressemble à ça :

Allez ensuite dans l'onglet Management > Services, dans le vollet de gauche, sont listès tous les services installés (Classes), vous pouvez afficher le détail des méthodes en cliquant sur le triangle gris à gauche du nom du service.

Pour executer notre méthode, sélectionnez là puis cliquez simplement sur le bouton Invoke, vous devriez voir s'afficher "Bonjour monde" dans le volet de droite.
Quand vous modifiez un service, inutile de rafraichir la console Weborb via le bouton rafraichir du navigateur, un bouton refresh situé en bas du volet de gauche est prévu à cet effet
Les différents type de retour
Vous avez probablement remarqué que lors des test de méthode dans la console, le volet de gauche nous propose 3 colonnes, dans la première sont affichés les noms des résultats (Result), dans la seconde le type de données retournée (Dans l'exemple précédent "String") et enfin dans la dernière colonne, la donnée retournée.
Nous allons donc voir maintenant quel type de retour propose Weborb.
Type Chaine
Premier type de retour, les type String, rien de particulier
Type numérique
Tous les types "numérique" retournés par Weborb sont retourné en type Number, ajoutez ces méthodes à notre SimpleService pour voir ça en action :
// retourne Number(3.14)
function retournFloat(){
return (float)3.14;
}
// retourne Number(5)
function retourneInt(){
return (int)5;
}
// retourne Number(NaN)
function retourneNaN(){
return NAN;
}
// ATTENTION : retourne Number(0) !!!
function retourneNaNAttention(){
return floatval("n'est pas un nombre");
}
Rien de particulier ormis le retour à 0 de la dernière méthode (dû à PHP) qui peut engendrer des effets de bord, si vous avez besoin d'un retour type Nan prévoyez un test côté serveur.
// retourne Number(NaN)
function retourneNaNOK(){
$nan = "not a number";
return (is_numeric($nan))?floatval($nan):NAN;
}
Type Boolean
Le type le plus simple, retourne simplement un Boolean
// retourne TRUE
function retourneBoolTrue(){
return TRUE;
}
// retourne FALSE
function retourneBoolFalse(){
return FALSE;
}
Type Date
Pour l'échange de date, vous devez utiliser la classe DateTime de PHP.
// retourne Date
function retourneDate(){
return new DateTime();
}
Type Array
Le "type" array de php est également supporté et retourne des Array, mais uniquement si vos array PHP utilisent une indexation NUMERIQUE. Dans le cas contraire vous recevrez un type Object (que nous verrons dans la partie suivante)
// retourne Array
function retourneArray(){
return array("Chaine",3.14,"Autre Chaine",493);
}
// retourne Array : Dans ce cas de figure,
// le tableau est réindexé de 0 à 3
function retourneArrayIndexSpe(){
return array(
2=>"Chaine",
3=>3.14,
7=>"Autre Chaine",
1=>493
);
}
Vous obtenez ce type d'affichage dans le console.

Si vous avez la version "Debug" du player Flash, il peut arriver qu'une erreur d'execution survienne quand vous selectionnez le Result dans le volet de résultat, ca n'a rien de bloquant.
Type Object
Enfin, c'est (pour le moment) de dernier type que nous verrons, le type Object. Coté php c'est assez simple, ca correspond aux array ayant une indexation non-numérique
// retourne Object
function retournObject(){
return array(
"id"=>1,
"nom"=>"Bouvry",
"prenom"=>"Stéphane",
"age"=>30,
"activites"=>array("Infographie","Développement")
);
}
Comme vous pouvez le constater, le type global est Object, ensuite on trouve les noms de propriétés avec les types correspondant.

Nous y reviendrons dans la partie consacrés aux ValueObject, mais les Instances de classe "perso" se présentent sous la forme d'Object, même si le type est conservé
Conclusion sur les types
Au final, les types de retour disponibles sont certes limités, mais cette limite est principalement la conséquence des "liberté de typage" intrinsèque à PHP, si vous êtes exigeants à ce niveau, une batterie de test sera essentielle pour bâtir une application serveur stable. Après rien ne vous empêche de vous tourner vers une application basé sur java (blazeds par exemple), beaucoup plus fin à ce niveau.
Enfin, sachez que vous pouvez également utiliser vos propres type entre Flex et PHP, ce type de donnée sont généralement qualifiés de ValueObject et sera le sujet d'un prochain article
Paramètres de méthodes
Maintenant que nous savons ce que peut nous envoyer PHP, nous allons maintenant voir l'opération inverse, à savoir envoyer des données à PHP.
En raison du faible (pénible) typage de PHP, nous allons voir ensemble que la transmission de données est simple dans le principe, mais peut devenir un véritable casse-tête pour l'applicatif serveur, car PHP gobe tout et parfois n'importe comment. Pour tester l'envoi de données, nous allons créer une application Flex donc la fonction sera d'appeler les méthodes de notre service afin de voir comment transitent les données entre Flex et Weborb.
Pour cette partie, nous allons travailler sur un nouveau service , commencez par créer un fichier EnvoisDeDonnees.php dans le dossier service, puis nous allons créer une série de méthodes rudimentaire dont la tâche sera de retourner les données reçues en paramètre.
Voyons la partie Flex, nous allons gérer une application simple qui va envoyer des données typées à notre méthode et voir ce que l'on reçois en retour. Copiez le code ci-dessous, je vous laisse le soin d'analyser le code pour comprendre le fonctionnement des RemoteObject.
<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute">
<mx:Script>
<![CDATA[
import mx.rpc.events.InvokeEvent;
import mx.collections.ArrayCollection;
import flash.utils.describeType;
import mx.rpc.events.FaultEvent;
import mx.rpc.events.ResultEvent;
// Stoque le détail du retour serveur
[Bindable] public var retour:String;
// Capte le retour du serveur
private function getResult( result:ResultEvent ) :void {
retour += "RESULT >>> " +result.result +"
";
retour += describeType(result.result);
}
// Capte les erreur
private function getFault( fault:FaultEvent ) :void {
retour += "ERREUR : " +fault.fault.faultString;
}
// Capte l'appel
private function getInvoke( invoke:InvokeEvent ):void{
retour = "INVOKE : " + invoke.message +"
";
}
// Pour gérer l'Array
[Bindable] public var arrayContent:Array = [
"Stéphane",
"Bouvry",
1.86,
true
];
[Bindable] public var objectContent:Object = {
prenom:"Stéphane",
nom:"Bouvry",
taille:1.86,
online:true
};
]]>
</mx:Script>
<mx:RemoteObject id="service"
fault="getFault(event)"
result="getResult(event)"
invoke="getInvoke(event)"
endpoint="http://exemples.jacksay.com/serviceAMF/weborb.php"
destination="EnvoisDeDonnees"
>
</mx:RemoteObject>
<mx:HBox width="100%" height="100%">
<mx:VBox>
<mx:HBox>
<mx:Label text="String : " />
<mx:TextInput id="champString" text="Valeur envoyée" />
<mx:Button click="service.envoyerChaine(champString.text)" label="Envoyer"/>
</mx:HBox>
<mx:HBox>
<mx:Label text="Number : " />
<mx:TextInput id="champNumber" text="49.3" />
<mx:Button click="service.envoyerNombre(Number(champNumber.text))" label="Envoyer" />
</mx:HBox>
<mx:HBox>
<mx:Label text="Boolean : " />
<mx:CheckBox id="champBoolean" selected="true" />
<mx:Button click="service.envoyerBooleen(champBoolean.selected)" label="Envoyer" />
</mx:HBox>
<mx:HBox>
<mx:Label text="Date : " />
<mx:DateField id="champDate" selectedDate="{new Date()}" yearNavigationEnabled="true" />
<mx:Button click="service.envoyerDate(champDate.selectedDate)" label="Envoyer" />
</mx:HBox>
<mx:HBox>
<mx:Label text="Array : " />
<mx:Button click="service.envoyerArray(arrayContent)" label="Envoyer" />
</mx:HBox>
<mx:HBox>
<mx:Label text="Object : " />
<mx:Button click="service.envoyerObjet(objectContent)" label="Envoyer" />
</mx:HBox>
<mx:HBox>
<mx:Label text="XML : " />
<mx:Button click="service.envoyerXML(describeType('donnèes'))" label="Envoyer" />
</mx:HBox>
<mx:Label fontWeight="bold" text="Appel d'un fonction qui n'existe pas" />
<mx:Label text="Dans ce cas, le fault s'execute indiquant que la fonction n'existe pas" />
<mx:HBox>
<mx:Label text="Erreur (fonction n'existe pas) : " />
<mx:Button click="service.nexistepas()" label="Envoyer" />
</mx:HBox>
<mx:Label fontWeight="bold" text="Trop de Paramètres" />
<mx:Label text="Danger : PHP s'en fout, il ne garde que le premier, enjoy :P"/>
<mx:HBox>
<mx:Label text="Erreur (Trop de paramètre) : " />
<mx:Button click="service.envoyerDonnees('arg1','arg2')" label="Envoyer" />
</mx:HBox>
<mx:Label fontWeight="bold" text="Paramètres envoyés Moisi" />
<mx:Label text="Dans ce cas attention, le service reste silencieux..."/>
<mx:HBox>
<mx:Label text="Erreur (Type non supporté) : " />
<mx:Button click="service.envoyerDonnees(Application.application)" label="Envoyer" />
</mx:HBox>
</mx:VBox>
<mx:VBox width="100%">
<mx:Label text="RETOUR : " fontWeight="bold"/>
<mx:Text text="{retour}" />
</mx:VBox>
</mx:HBox>
</mx:Application>
Voici un récapitulatif pour l'échange de type :
- String : Reçu comme une chaine de caractères
- Number (et autre type numérique) : Reçu comme un nombre
- Boolean : Reçu comme un Booléen
- Date : Reçu comme une date, le retour dans Flex est souvent taggé Object, mais c'est bien une Date
- Array : Reçu comme un array (indexation numérique)
- Object : Reçu comme un array (indexation non-numérique)
- XML : Reçu comme une chaine
Noter que PHP étant très "souple" (ce qui est un réel inconvénient), rien ne vous empêche d'envoyer une chaîne là ou côté PHP vous attendez un nombre, car malheureusement, vous ne pouvez pas gérer de typage fort en php, pour éviter d'éventuels problèmes, vous pouvez prévoir une batterie de test pour déterminer si les données reçues sont conformes à ce que vous attendez, cela représente un tâche énorme et laborieuse qui s'avérera indispensable si vos services sous dit "Ouvert".
Si vous tentez de reproduire la partie serveur chez vous, vous allez rencontrer une alerte de sécurité, la fameuse erreur Sandbox, elle vous parle notamment du célébre fichier crossdomain.xml. Ce fichier est bien connu des personnes qui ont déjà mis en place des services distants, je ne vais pas en parler dans cet article, mais dans le cadre de vos tests, placer simplement un fichier "crossdomain.xml" à la racine de votre serveur web content ce script :
<?xml version="1.0"?> <cross-domain-policy> <allow-access-from domain="*"/> </cross-domain-policy>
Dans un contexte de prodution, il vous suffira de remplacer l'étoile par le nom du domaine où est situé votre application
Gérer la portée des méthodes
Weborb PHP prend en charge l'accès des méthodes, à savoir les tags private, public et protected.
- private : Mode le plus restrictif, l'accès à la méthode n'est autorisé qu'au sein de la classe.
- protected : L'accès à la méthode n'est autorisé qu'au sein de la classe et des classes qui héritent de cette classe.
- public : Accès libre à la méthode, notez que quand vous ne donnez pas d'indication de portée, l'accès est publique.
Vous pouvez copier le code qui suit pour vérifier le comportement de Weborb
Remontée des erreurs
Weborb (et AMF de manière général) permet de gérer la remontée d'erreur vers Flex relativement proprement. Dans l'exemple précédent, nous avons vu que les erreurs de portée, étaient matérialisées dans weborb sous la forme d'un modal affichant l'erreur. Ce modal affiche en fait un FaultEvent, vous avons déjà capé ce genre d'erreur dans la première partie avec l'exemple d'un appel de méthode non référencée.
Vous pouvez également produire ce genre d'erreur dans vos services lors d'erreurs bloquantes qui surviennent durant l'exécution de vos services.
Organisation des services
Si vous avez suivis ce tutoriel depuis le début, vous devriez avoir dans votre dossier services un nombre de fichier grandissant. C'est pourquoi je consacrerez un point sur l'arborescence des services dans Weborb. Ensuite nous parlerons de la grande oublié : la fonction constructeur, et nous terminerons sur l'utilisation de l'héritage
Rangez !
NdlA : Le titre de cette partie devrez faire doucement rire les gens qui me connaissent, mais pour les autres je le répète, il faut ranger (comme disait Walker).
Par exemple, ranger chaque service dans un dossier, cela peut simplifier les switch de version de service, c'est dans ce but que Weborb propose un fichier de configuration des services, ce fichier va indiquer quel service est situé à quel endroit, voyons cela.
Créez un dossier Ranger dans le dossier services de l'installation de Weborb, puis créez une simple classe de test dans la dossier fraichement créé.
Dans le configuration de Weborb : /Weborb/WEB-INF/flex/remoting-config.xml, nous allons créer un nouveau point d'entrée à la fin du fichier (entre les balises service)
<?xml version="1.0" encoding="UTF-8"?> <service id="remoting-service" class="flex.messaging.services.RemotingService" messageTypes="flex.messaging.messages.RemotingMessage"> <adapters> <adapter-definition id="php-object" default="true"/> </adapters> <default-channels> <channel ref="my-amf"/> <channel ref="my-rtmp"/> <channel ref="my-amf-absolute"/> </default-channels> <!-- (...) Services déclaré par defaut par weborb --> <!-- Nouveau point d'entrée --> <destination id="Rangez"> <properties> <source>Ranger.RangerCBien</source> </properties> </destination> </service>
C'est aussi simple que ca, pour basculer d'un service à l'autre, il vous suffit ensuite de modifier le fichier de configuration, vous n'avez rien a changer coté client :)
Rien ne vous empêche de supprimer les services déclaré par weborb, cela aura pour conséquence de faire planter la console (mais vos service déclarés seront toujours opérationnel)
Le constructeur
Dans les exemples précédents, je me suis appliqué à créer des services (des Classes PHP) qui ne disposaient pas de méthode constructeur (__construct). C'est volontaire car son fonctionnement est relativement spécifique. Créons un nouveau service :
En testant ce service dans la console de Weborb, vous pouvez constater que le fonctionnement du constructeur se comporte exactement comme une méthode traditionnelle. A première vu oui, mais nous allons voir ce qui se passe plus précisement dans l'exemple suivant, modifier le service de cette façon :
// fichier /Services/ExempleConstructeur.php
class ExempleConstructeur {
private $donnees = "Valeur par defaut";
function __construct(){
$this->donnees = "Passage par le constructeur constaté";
}
function methodeService(){
return $this->donnees;
}
}
En testant votre méthode methodeService(), vous pouvez constater que le constructeur a bien été appelé AVANT l'exécution de la méthode !
Il est courant d'établir la connection au serveur de base de donnée dans le constructeur pour cette raison.
Héritage
Cela nous emmène tout naturellement à l'héritage qui va vous permettre de mieux organisez vos services en centralisant le code commun à tous vos services dans la classe parente.
Dans l'exemple si dessous, la classe parent définit des méthodes d'accès à une base de donnée et établis la connexion dans le constructeur, la classe enfant est par conséquent allégée et ne regroupera que les méthodes orientées métier, les accès à la base se feront via les méthodes héritées du parent. Si vous êtes parano, n'hesitez pas à sécurisé la classe parente en la taggant "abstract" et en définissant ces méthodes "protected"
// fichier /Service/ServiceParent.php
abstract class ServiceParent {
protected $statusDB = "Disconnected";
function __construct(){
$this->connectionDB();
}
protected function connectionDB(){
$this->statusDB = "Connected";
}
protected function envoyerRequete($requete){
if( $result = mysql_query($requete) ){
return $result;
} else {
throw new Exception("Erreur de requète");
}
}
}
Ce système est d'expérience une bonne pratique, notamment pour le travail en équipe, en effet il est préférable de disposer de plusieurs petites classes que d'une seule blindée de méthodes, lors de l'identification d'un bug votre travaille sera facilité car pour rappel, une erreur de code dans une méthode fait planter la classe ENTIERE.
Ensuite l'organisation des classes enfants est libre et rien ne vous empêche de hiérarchiser des classes sur plusieurs niveaux.
Concusion
Voilà, vous disposez maintenant des bases nécessaires pour réaliser des services en PHP, à vos claviers :P
Retrouvez la suite de ce tutoriaux : Weborb PHP : Les values Objects
Infos
Dans cet article, nous aborderons la mise en place d'un service AMF en PHP avec la solution de Midnight Coders : Weborb PHP.
- Niveau : initié
- Catégorie : PHP
- Publié le : 13 août 2009
- Dernière Mise à jour : 28 juillet 2011
- Notions abordées :
