Tutoriel Flex 3 et AMFPHP (partie 2)

17 September 2008 par Dimitri Ho

Cet article est la suite du tutoriel précédent (Tutorial Flex 3 et AMFPHP), qui expliquait comment configurer AMFPHP (partie serveur) et le projet sous Flex Builder (partie cliente). Je reprendrai néanmoins cette première partie avec une variante "quick-and-dirty". Nous verrons ensuite comment appeler des méthodes avec arguments et mapper automatiquement les classes entre le code ActionScript et PHP.

Prérequis

Création d'un service AMFPHP

Pour installer AMFPHP, décompressez l'archive et copiez le dossier amfphp dans le DocumentRoot du serveur Apache (i.e. on doit pouvoir accéder à cette adresse http://localhost/amfphp/gateway.php).

Dans amfphp/services, créez un dossier trombimti. Ajoutez le script TrombiService.php dans ce nouveau dossier.

amfphp/services/trombimti/TrombiService.php

< ?php
class TrombiService
{
  public function hello()
  {
    return "Salut !";
  }
}

Il s'agit du service qu'utilisera notre application Flex pour récupérer les données du serveur. Par défaut, on peut invoquer n'importe quelle méthode appartenant à la classe TrombiService. Vous pouvez d'ores et déjà tester notre service grâce à l'application Flex livrée avec AMFPHP. Ca se passe ici :

http://localhost/amfphp/browser/

Création du projet

Dans Flex Builder, créez un projet Flex.

Nous allons commencer par créer un RemoteObject dans le fichier src/main.mxml, qui permettra à l'application de se connecter au service AMFPHP.

src/main.mxml

< ?xml version="1.0" encoding="utf-8"?>
<mx :Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute"></mx>
<mx :RemoteObject id="backendService" destination="dummy" source="trombimti.TrombiService"
       endpoint="http://localhost/amfphp/gateway.php">
<mx :method name="hello" result="trace(event.result as String)" />
</mx>
<mx :Button label="Go" click="backendService.getOperation('hello').send();"/>

Quelques explications :

  • source : le nom du service auquel on veut se connecter ;
  • endpoint : l'URL du gateway AMFPHP ;
  • destination : ne sert à rien dans cet exemple. En général, une destination doit être définie dans un fichier de configuration à part, définition qui contient entre autres le endpoint que nous avons mis directement en attribut ici ;
  • La balise mx:method permet quant à elle d'indiquer le nom d'une méthode que l'on peut invoquer à partir du RemoteObject. On peut bien sûr en rajouter d'autres comme nous le verrons un peu plus loin.

Lancez l'application en mode debug, puis cliquez sur le bouton. Si tout se passe bien vous devriez voir "Salut !" dans la console.

Nous avons vu comment créer un service AMFPHP et invoquer une méthode sans argument, retournant une chaine de caractères. C'est ici que s'arrêtait le tutoriel de l'article précédent.

Méthodes avec arguments

Créons maintenant une nouvelle méthode côté serveur qui prend une chaîne de caractère en argument et renvoie vrai si la chaîne vaut "p3L1c@n".

amfphp/services/trombimti/TrombiService.php

< ?php
class TrombiService
{
  public function hello()
  {
     return "Salut !";
  }
  public function guesspassword($pass)
  {
     return strcmp($pass, "p3L1c@n") == 0;
  }
}

Dans src/main.mxml, rajoutez une balise mx:method.

< ?xml version="1.0" encoding="utf-8"?>
<mx :Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute">
</mx>
<mx :RemoteObject id="backendService" destination="dummy"
  source="trombimti.TrombiService"
  endpoint="http://localhost/amfphp/gateway.php">
<mx :method name="hello" result="trace(event.result as String)" />
</mx>
<mx :method name="guesspassword" result="trace(event.result as Boolean)">
</mx>
<mx :arguments>
  <arg1>p3L1c@n</arg1>
</mx>
<mx :Button label="Go" click="backendService.getOperation('hello').send();"/>
<mx :Button label="Go2" click="backendService.getOperation('guesspassword').send();" x="49"/>

Notez que pour la méthode guesspassword, je convertis bien le résultat en booléen.

Le nom des balises à l'intérieur de la balise mx:arguments importe peu. Cependant, si la méthode doit prendre plusieurs arguments en paramètres, il est impératif que ces balises aient des noms différents, et que l'ordre de leurs déclarations soit respecté.

Lancez l'application en mode debug et cliquez sur le bouton "Go2". Vous devriez voir true dans la console.

Mapping de classes

Jusqu'ici, nous n'avons utilisé que des types simples qui sont automatiquement reconnus des deux côtés (boolean et string). En effet, même si le type statique du champ result (event.result) était Object, son type dynamique est bien String ou Boolean (il suffit de voir que le cast as String ne renvoit pas null).

Si on essaie de passer l'instance d'une classe perso de l'AS vers PHP (avec le passage en argument), AMFPHP transforme l'objet en tableau associatif pour PHP. De même, une classe perso en PHP devient un Object en AS. Je vous invite à lire la documentation d'AMFPHP (http://www.amfphp.org/docs/datatypes.html) concernant les types de donnée reconnus automatiquement.

Le mapping des classes permet d'éviter de reconstruire les classes "à la main" à partir des types Object ou tableau associatif.

Commençons par créer un dossier vo/trombimti dans amfphp/services. Faites bien attention à ne pas oublier le dossier vo, c'est en effet dans ce dossier que AMFPHP va chercher les classes PHP pour le mapping AS vers PHP. De nombreux tutoriels font l'erreur de placer ces classes dans le même dossier que le service, ce qui fait que le type de l'objet reste en réalité un tableau associatif.

Ajoutez Person.php dans ce nouveau dossier.

amfphp/services/vo/trombimti/Person.php

< ?php
class Person
{
  var $firstName;
  var $lastName;
  var $_explicitType = "trombimti.Person";
 
  function getFirstName()
  {
     return $this->firstName;
  }
}

Ajoutez également 2 nouvelles méthodes au service :

< ?php
require_once "../vo/trombimti/Person.php"
 
class TrombiService
{
  public function hello()
  {
     return "Salut !";
  }
 
  public function guesspassword($pass)
  {
     return strcmp($pass, "p3L1c@n") == 0;
  }
 
  public function getFirstName($p)
  {
    return $p->getFirstName();
  }
 
  public function getPerson()
  {
     $p = new Person();
     $p->firsname = "Dimitri";
     $p->lastname = "Ho";
     return $p;
  }
}

Lors de l'appel à la méthode getFirstName, la ligne 19 ne doit pas provoquer d'erreur si le mapping a bien fonctionné.

Dans le projet Flex Builder, créez un dossier src/trombimti.

Ajoutez-y une classe ActionScript nommée Person.as.

src/trombimti/Person.as

package trombimti
{
  [RemoteClass(alias="trombimti.Person")]
  [Bindable]
  public class Person
  {
    public var firstName:String;
    public var lastName:String;
  }
}

Le metatag [RemoteClass(alias="trombimti.Person")] indique à AMFPHP où trouver la classe PHP correspondante, de la même manière que l'attribut _explicitType dans la classe PHP. Dans les faits, je me suis rendu compte que pour le mapping PHP vers AS, il faut et il suffit que les deux chaînes soient identiques (remplacer "trombimti.Person" par "foo" par exemple fonctionne).

Modifiez également le fichier main.mxml afin d'invoquer les 2 méthodes :

src/main.mxml

< ?xml version="1.0" encoding="utf-8"?>
<mx :Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute" applicationComplete="init()"></mx>
<mx :Script>
< ![CDATA[
  import trombimti.Person;
  [Bindable]
 
  public var person:Person = new Person();
 
  public function init():void
  {
     person.firstName = "Foo";
     person.lastName = "Bar";
  }
]]>
</mx>
<mx :RemoteObject id="backendService" destination="dummy"
  source="trombimti.TrombiService"
  endpoint="http://localhost/amfphp/gateway.php">
<mx :method name="hello" result="trace(event.result as String)" />
</mx>
<mx :method name="guesspassword" result="trace(event.result as Boolean)">
</mx>
<mx :arguments>
   <arg1>p3L1c@n</arg1>
</mx>
<mx :method name="getPerson" result="trace(event.result as Person)" />
<mx :method name="getFirstName" result="trace(event.result as String)">
</mx>
<mx :arguments>
   <arg1>{person}</arg1>
</mx>
<mx :Button label="Go" click="backendService.getOperation('hello').send();"/>
<mx :Button label="Go2" click="backendService.getOperation('guesspassword').send();" x="49"/>
<mx :Button label="getPerson" click="backendService.getOperation('getPerson').send();" x="217"/>
<mx :Button label="getFirstName" click="backendService.getOperation('getFirstName').send();" x="105"/>

Vérifiez que getPerson affiche bien [object Person] dans la console, et que getFirstName renvoit Foo.

Sources et liens

http://www.amfphp.org/docs/

http://viconflex.blogspot.com/2007/04/mapping-vos-from-flex-to-php-using.html

http://www.sephiroth.it/tutorials/flashPHP/flex_remoteobject/index.php

Article sur l'(in)utilité du mapping des classes AS vers PHP :

http://www.5etdemi.com/blog/archives/2007/01/why-you-shouldnt-use-class-mapping-and-vos-in-amfphp/

Tags: , , , ,

7 commentaires pour “Tutoriel Flex 3 et AMFPHP (partie 2)”

  1. switcherdav dit :

    Salut,

    J’utilise depuis peu amfphp et je n’arrive pas à faire passer de PHP->FLEX un objet d’une classe perso avec une propriété également d’une classe perso

    Avec ton exemple, c’est comme si je voulais pour la personne, son service sachant que le service est décrit par une classe Service

    J’aurais :

    class Personne
    {
    public var service:Service

    }

    class Service
    {
    public var lieu:String ;

    }

    Il me dit tout le temps que le service est null malgré le cast

    Sous le browser il m’indique bien un objet complet.

    Des idées ?

    Merci en tous cas pour le tuto

  2. imed dit :

    sa marche bien si j utilise une application et un seul module
    mais a partir de deux modules flex qui utilisent amfphp le deuxieme ne fonctionne pas meme avec un autre channel
    ça me derange beaucoup

  3. jeremy dit :

    bonjour,
    ce tuto ma mi l’eau a la bouche, car il présente une notion que j’ai grandement besoin de connaitre, maleureusement aprés 45minute a essayé de corriger les erreur de syntaxe, je perd patience et j’abandonne… sa ne marche pas :(

  4. ohazar dit :

    J’ai enfin réussi à le faire tourner ! :D

    Alors d’accord avec d’autres posts/remarques – même si c’est super sympa de faire un tuto – un tuto qui ne marche pas est contre-productif, et produit l’effet inverse – a savoir dégouter les moins acharnés ^^ Donc vérifiez votre tuto au moins une fois svp avant de le mettre en ligne ;)

    Ceci étant, pour ceux qui veulent le faire marcher, voila ce qu’il faut corriger (j’espere ne pas oublier d’etapes) :

    – pas d’espace entre entre le chevron ouvrant et le point d’interrogation pour débuter un script php :
    “< ?php” devient “<?php”

    – idem pour les balises xml :
    “< ?xml” devient “<?xml”

    – idem pour les balises flex
    “<mx :method” devient “<mx:method”

    – il faut spécifier le type pour les balises flex fermantes :
    “” devient “”

    – il faut corriger deux ou trois encapsulations :

    p3L1c@n

    devient

    p3L1c@n

    – enfin, l’erreur qui m’a fait perdre une bonne demi-journée. La ligne :
    require_once “../vo/trombimti/Person.php”

    …. va produire une erreur qui sera difficile a déceler :( grrrr …

    Il faut écrire à la place :
    require_once(“../vo/trombimti/Person.php”);

    Voila, tout ca pour redire que ces erreurs sont bien idiotes, et qu’elles font perdre un temps précieux …. si vous ne voulez par relire les articles que vous écrivez, n’écrivez pas – c’est mieux ;) merci

  5. ohazar dit :

    citer

    – il faut spécifier le type pour les balises flex fermantes :
    “<mx :method” devient “<mx:method”

  6. ohazar dit :

    citer

    – il faut corriger deux ou trois encapsulations :

    devient

  7. ohazar dit :

    Désolé, pour les encapsulations, vous devrez trouver tout seuls : le systeme de commentaire n’accepte pas les copier coller :P
    (En gros, il faut ouvrir la balise “method”, puis la balise “arguments”, puis fermer la balise “arguments”, puis la balise “methods” … gnia)

Laisser un commentaire


× 8 = forty eight