Lab : Test du AMF server de Zend Framework 1.7 (Partie PHP)
Plus simple y'a pas 
Mise en place
En gros ça fonctionne comme pour instancier un service SOAP c'est à dire on instancie un objet 'serveur' auquel on va rattacher des classes ou des fonctions. Comme vous allez le voir rien de bien compliqué mais le service étant établi dans sa plus simple expression, il va falloir tester le comportement des appels. Donc mes premiers tests seront :
- établir la communication AMF over http entre flash et php ;
- définir les comportements de bases (que se passe-t-il lors du connect de flash, que se passe-t-il lors du call de flash,...);
- vérifier le typage et la structure des variables après désérialisation AMF de chaque coté (Actionscript > PHP et PHP > Actionscript).
La partie Flash est elle expliquée ds le lab Flash dans "Test du AMF server de Zend Framework 1.7 (Partie Actionscript)" 
<?php
import('com.zend.library.Zend.Amf.Server');
class Test {
private static $cpt=0;
function __construct(){
self::debug('__construction__');
}
function say($msg){
self::debug('Says : '.$msg);
return 'Message received';
}
public static function debug($txt){
self::$cpt++;
file_put_contents('log.txt',self::$cpt.' > '.$txt.PHP_EOL,FILE_APPEND);
}
}
$svr=new Zend_Amf_Server();
file_put_contents('log.txt','Starting Server'.PHP_EOL);
$svr->setClass('Test');
echo $svr->handle();
? >
Premières remarques
Si le framework est correctement installé aucun souci ça tourne tout seul et les fonctionnalités sont très proches de SOAP si pas identiques.
Quelques commandes utiles:
- setClass : permet d'ajouter une class au service
- addFunction : si vous ne voulez pas attacher d'objet ou si vous souhaitez rajouter une fonction sans redéfinir l'objet vous pouvez vous passer du setClass et simplement ajouter la fonction au service.
- setClassMap : permet de mapper le nom des classes existantes afin de l'adapter au nom des classes appelées par flash. Vous pouvez aussi comme pour SOAP
Sur le site Chinois de zfphp il est notifié que 'All attached methods and functions need docblocks' mais perso je n'ai pas eu de problème avec ma classe Test. Il va de soit que tout doit être documenté style phpdoc si on veut travailler correctement mais je n'ai pas vu la nécessité techique dans ce cas.
NB : vous pouvez attacher plusieurs fonctions et classes au service. Afin d'éviter les problèmes de noms, utiliser le namespacing en passant un string en second paramètre aux méthodes addFunction & setClass
Comportement du ZEND AMF SERVER avec une application Actionscript 3
Lors des appels
Voici donc le résultat de mes tests :
- Lors de l'appel de la méthode connect() de flash : rien -> l'url n'est même pas appelée;
- Lors de l'appel de la méthode call() de flash : le résultat est intéressant et les comportements étonnamment logiques quand on connait un peu le passé de flash et de php coté développements...
- La connexion est instanciée une seule fois lors du premier call (appel de l'url);
- Tous les appels se font bien sur la même 'session' car mon compteur n'a pas cessé de s'incrémenter;
- Si la méthode distante est publique mais pas statique : à chaque call un nouvel objet est instancié -> on repasse dans le constructeur à chaque fois;
- Si la méthode distante est publique et statique : seule la méthode est appelée et pas le constructeur;
Il faut donc prévoir un objet Singleton ou bien stocker un objet dans une statique de la classe attachée au service si on veut garder une seule instance d'objet.
- C'est une opération http et donc une application client pour une instance serveur. Donc pour faire un application multi-users, il faudra donc soit utiliser une mémoire partagée en gardant l'HTTP, soit développer une application socket standalone en PHP (un peu comme un serveur xml ou telnet mais avec les réponses en AMF).
Sérialisation / désérialisation d'objets simples
Bon ici c'est comme pour des conversions JSON ou SOAP > quand on utilise plusieurs langages, les objets d'échanges doivent rester simples aussi je n'ai testé que des structures simples. En voici un extrait (PHP > AS3)
<?PHP
...
//méthodes appelées par flash
function getObject(){
$test=new StdClass();
$test->id=123;
$test->name='toto';
$test->nickname='foo';
$test->place2be='bar';
return $test;
}
function getArray(){
$tab=array('test','fes',0,1.877,2,3,"tetst 1"=>'tool1','test 2'=>'tool2');
return $tab;
}
function getComplexArray(){
$tab=array('test','fes',0,1,2,3,'child'=>array('ctest','cfes','c0','c1',2,3,"ctetst 1"=>'ctool1','ctest 2'=>'ctool2'),"tetst 1"=>'tool1','test 2'=>'tool2');
return $tab;
}
function getBoolean(){
return true;
}
//Va recevoir les objets envoyé par flash
function printUnserializeObject($object){
ob_start()
var_dump($object);
self::debug('Objet reçu de flash : '.ob_get_clean());
return true;
}
Donc entiers, décimaux, chaines, tableau simple, tableaux complexes (plusieurs dimensions, tableaux associatifs (indices textuels à la place des offset entier), objets,... tout est passé et le typage des informations et leur structure sont restés intacts ! 8000x mieux que JSON
. Ce test était bi-directionnel (as renvoyait à PHP les données que PHP lui avait envoyées et tout est resté intact : les données de départs sont restées identiques aux données d'arrivées. Nice job de la part de Zend & d'Adobe : c'est rare quand tout passe du premier coup...
Niveau tests de sérialisation je me suis arrêté là car il y a déjà tout ça dans le wiki de zend.
Voici juste les tables de conversions PHP2AS et AS2PHP :
- AS2PHP
ActionScript PHP undefined null null null int integer number float boolean boolean String string array array xml DomDocument flash.utils.ByteArray string uint float object object RemoteClass Object class mapped object date DateTime mx.collections.ArrayCollection object
- PHP2AS
PHP ActionScript null null boolean boolean string string DomDocument xml DateTime date float number integer number Associative Array w/ mix of keys Object Associative Array w/ Numeric index of keys Array object object RemoteClass Zend_Amf_Value_TypedObject typed object Zend_Amf_Value_ByteArray flash.utils.ByteArray Zend_Amf_Value_ArrayCollection mx.collections.ArrayCollection