Tout d'abord, un petit rappel : qu'est-ce qu'AMFPHP ?

AMFPHP est une librairie PHP open source qui permet de connecter Flex ou Flash à un backend en PHP, en utilisant le format AMF. Mais pourquoi est-ce si intéressant ? Tout simplement parce que AMFPHP va s'occuper tout seul de la sérialisation et de la désérialisation de vos données ! Exemple : vous envoyez depuis Flex une chaîne de caractères, PHP reçoit une chaîne de caractères. Vous envoyez un tableau, PHP reçoit un tableau, etc. Mais là où ça devient encore plus intéressant, c'est qu'il est aussi possible d'envoyer un objet « User » par exemple et de recevoir dans PHP un objet « User ». De même dans l'autre sens. Vous voyez tout de suite l'intérêt. Entrons maintenant dans le vif du sujet.

Pré-requis : vous devez disposer de Flex 2 (ou Flex 3 beta 1 ou 2), d'un serveur Apache (Wamp, EasyPHP, etc.) et de la librairie AMFPHP 1.9.

Récupérons tout d'abord la librairie AMFPHP 1.9 à l'adresse suivante : http://www.5etdemi.com/uploads/amfphp-1.9.beta.20070513.zip

Attention : Cette version est une version beta. Dans cette version, le code generator ne marche pas (Not yet implemented) La version 2.0 ne devrait pas trop tarder. En effet, suite au 'retrait' de Patrick Mineault du projet, un nouveau leader a repris le flambeau et nous prépare cette version. Patience, donc... ;)

Pour plus d'infos : http://www.5etdemi.com/blog/

Théoriquement, on ne devrait pas l'utiliser en production. Cependant, dans mes tests persos, je n'ai rencontré aucun bug pour le moment : certes, il manque encore quelques trucs, mais ce qui est là fonctionne parfaitement.

Alors pourquoi s'en priver ! ;-)

Maintenant, nous allons créer un nouveau répertoire sur notre serveur dans lequel se trouvera notre projet. A la racine de notre serveur (www), créons donc un dossier "phiphou". Puis nous plaçons dans ce dossier le dossier 'amfphp' contenu dans l'archive téléchargée.

Ouvrons ensuite dans un navigateur l'url suivante :

http://localhost/phiphou/amfphp/gateway.php

Si tout va bien, nous devrions obtenir ce message :

amfphp and this gateway are installed correctly. You may now connect to this gateway from Flash.

Testons maintenant le ServiceBrowser. Pour cela, cliquez sur Load the service browser, il devrait se lancer. Une fois lancé, le ServiceBrowser nous affiche une fenêtre de configuration. Pour le moment, nous laissons tout par défaut, et nous cliquons sur Save. Si il n'y a pas de message d'erreur, c'est que tout va bien. Nous allons pouvoir maintenant créer notre premier service amfphp.

Pour cela, nous allons créer un fichier ServiceClass.php, avec le code suivant :

<?php
 
class ServiceClass
{
     function ServiceClass()
     {
     //constructeur
     }
 
     /**
     * sayHello
     * retourne à Flex le message 'Hello from PHP'
     */
     function sayHello()
     {
          return "Hello from PHP";
     }
}
?>

Puis nous allons placer ce fichier php dans le répertoire des services d'amfphp. Ici, www\phiphou\amfphp\services\ .

Relançons maintenant le browser de services, cliquons sur notre service dans la colonne de gauche, puis cliquons sur 'Call'.

Voici ce que vous devriez obtenir :

On voit que la méthode sayHello du service "ServiceClass" renvoie bien le message "Hello from PHP.

Voyons maintenant comment cela se passe côté Flex.

Ouvrons FlexBuilder et créons un nouveau projet "phiphou".

Dans le fichier .mxml principal (phiphou.mxml), rentrons le code suivant :

 
<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml"
	layout="absolute" >
	
<mx:RemoteObject id="services" destination="amfphp" source="ServiceClass">
	<mx:method name="sayHello" 
               result="sayHello_resultHandler(event)"
               fault="faultHandler(event)"/>
</mx:RemoteObject>
 
<mx:Script>
<![CDATA[
 
     import mx.rpc.events.FaultEvent;
     import mx.rpc.events.ResultEvent;
     import mx.controls.Alert;		
	
     /**
     * handleFault
     * Si erreur, on l'affiche
     */
     private function faultHandler(fault:FaultEvent):void
     {
          Alert.show(fault.fault.faultString, fault.fault.faultCode.toString());
     }
 
     /**
     * sayHello
     * Appel de la methode sayHello de notre classe PHP
     */
     public function sayHello() :void
     {
          services.getOperation('sayHello').send();
     }
		
     /**
     * sayHello_resultHandler
     * Résultat pour sayHello
     */
     private function sayHello_resultHandler(evt:ResultEvent) :void
     {
          infos.text = evt.result.toString();		
     }
]]>
</mx:Script>
	
<mx:TextArea x="10" y="10" id="infos"/>
<mx:Button x="10" y="62" label="Appel du service" click="sayHello()"/>
</mx:Application>

Testons maintenant l'application. Nous obtenons une erreur :

MessagingError message='Destination 'amfphp' either does not exist or the destination has no channels defined (and the application does not define any default channels.)'

Il nous faut écrire un fichier xml qui va contenir la destination et les channels du Service. Ce fichier se trouvera à la racine de notre projet Flex et s'appellera par exemple "services-config.xml" . Le voici donc :

<?xml version="1.0" encoding="UTF-8"?>
<services-config>
	<services>
		<service id="amfphp-flashremoting-service" class="flex.messaging.services.RemotingService" messageTypes="flex.messaging.messages.RemotingMessage">
			<destination id="amfphp">
				<channels>
					<channel ref="my-amfphp"/>
				</channels>
				<properties>
					<source>*</source>
				</properties>
			</destination>
		</service>
	</services>
	<channels>
		<channel-definition id="my-amfphp" class="mx.messaging.channels.AMFChannel" >
			<endpoint uri="http://localhost:80/phiphou/amfphp/gateway.php" class="flex.messaging.endpoints.AMFEndpoint" />
		</channel-definition>
	</channels>
</services-config>

Le point le plus important dans ce fichier xml est la ligne uri="http://localhost:80/phiphou/amfphp/gateway.php". C'est le chemin vers notre gateway. (sous Flex 3 nous devons spécifier le port, ce n'est pas nécessaire sous Flex 2).

Maintenant, nous devons faire prendre en compte ce fichier de configuration par le compilateur de Flex. Pour cela, allons dans les propriétés du projet Flex, et nous modifions les arguments du compilateur, comme ceci :

Comme vous le voyez, j'ai mis fr_FR à la place de en_US. Sous Flex 2, ça n'a pas d'incidence, mais si vous travaillez avec la beta de Flex 3 vous devez laisser en_US.

Testons à nouveau. Nous obtenons maintenant ceci :

Le service a bien renvoyé la chaine "Hello from PHP".

Pour l'afficher dans le TextArea, j'ai fait infos.text = evt.result.toString(); car evt.result n'est pas de type string. Testons qu'amfphp avait bien renvoyé une string, en faisant :

infos.text = (evt.result is String).toString();

Nous obtenons alors "TRUE", ce qui nous confirme qu'amfphp a bien renvoyé une String.

Mais amfphp peut sérialiser bien d'autres types. Essayons avec une Array, par exemple.

Modifions notre service comme ceci :

function sayHello()
{
     $tableau = array("chaine", 45, true);
     return $tableau;
}

j'ai gardé le nom 'sayHello' de la fonction pour ne pas avoir à modifier trop le code côté Flex.

Côté flex, nous modifions notre code comme ceci :

private function sayHello_resultHandler(evt:ResultEvent) :void
{	
     infos.text = (evt.result is Array).toString();
     infos.text += "\n"+(evt.result[0]is String).toString();
     infos.text += "\n"+(evt.result[1]is Number).toString();
     infos.text += "\n"+(evt.result[2]is Boolean).toString();	
}

Nous obtenons alors comme résultat 4 fois true, ce qui nous confirme que Flex reconnait bien les types envoyés par amfphp.

Voyons maintenant comment cela se passe dans le sens Flex->amfphp.

Rajoutons d'abord à notre service PHP la fonction qui sera appelée par Flex, comme ceci :

function sendToPHP($p)
{
     return is_string($p);
}

Cette fonction renverra un booléen qui nous dira si le paramètre reçu était bien de type string.

côté flex, nous modifions notre code comme ceci :

 
<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml"
	layout="absolute" >
	
<mx:RemoteObject id="services" destination="amfphp" source="ServiceClass">
    <mx:method name="sendToPHP" 
               result="sendToPHP_resultHandler(event)"
               fault="faultHandler(event)"/>
</mx:RemoteObject>
 
<mx:Script>
<![CDATA[
 
import mx.rpc.events.FaultEvent;
import mx.rpc.events.ResultEvent;
 import mx.controls.Alert;		
	
     /**
     * handleFault
     * Si erreur, on l'affiche
     */
     private function faultHandler(fault:FaultEvent):void
     {
           Alert.show(fault.fault.faultString, fault.fault.faultCode.toString());
     }
 
	
     /**
     * sendToPHP
     * Appel de la methode sayHello de notre classe PHP
     */
     public function sendToPHP() :void
     {
          services.getOperation('sendToPHP').send("un test");
     }
		
     /**
     * sendToPHP_resultHandler
     * Résultat pour sendToPHP 
     */
     private function sendToPHP_resultHandler(evt:ResultEvent) :void
     {
          infos.text = evt.result.toString();	
     }
 
	]]>
</mx:Script>
	
<mx:TextArea x="10" y="10" id="infos" height="72"/>
<mx:Button x="10" y="90" label="Appel du service" click="sendToPHP()"/>
</mx:Application>

Testons l'application : elle nous affiche bien TRUE, ce qui signifie que la chaine "un test" envoyée par Flex à bien été reconnue comme une string coté php.

Si nous essayons :

services.getOperation('sendToPHP').send(45);

nous obtiendrons FALSE, ce qui nous montrera encore une fois qu'amfphp et flex peuvent bien 'partager' les types de données.

Note : pour ceux qui souhaitent gérer des accents et autres caractères spéciaux, vous devez modifier votre fichier gateway.php, en spécifiant le mode de décodage, comme ceci (ligne 127) :

$gateway->setCharsetHandler( "utf8_decode", "UTF-8", "latin1" );

Avec une base sql en latin1, ça fonctionne bien avec les accents...

Tout ceci est très bien, mais peut-on utiliser avec amfphp des types persos ? C'est l'objet de la SECONDE PARTIE de ce tutorial.