|
ClassMapping
How to map PHP data to Flash classes
IntroductionSabreAMF allows you to automatically map PHP classes to Flash classes. This is often used for the so called Data Transfer Objects or the more popular term Value Object or VO. There's a bunch of ways this can be implemented with SabreAMF. TypedObjectsIf you come from an AMFPHP background, you can normally specify a classname for every array with data using the explicitType key. AMFPHP example: <?php
$data = array(
'property1' => 'value1',
'property2' => 'value2',
);
$data['_explicitType'] = 'mypackage.MyFlashClass';
?>SabreAMF has a more OOP-way to handle this, by wrapping it in an object.. <?php
$data = array(
'property1' => 'value1',
'property2' => 'value2',
);
$object = new SabreAMF_TypedObject('mypackage.MyFlashClass',$data);
?>Typed Objects, or Custom Classes are also returned as SabreAMF_TypedObject when they originate from a flash methodcall. Check out the source of the class to find out more details. Using ITypedObjectSabreAMF also has an SabreAMF_ITypedObject interface. If you implement this interface you can create custom method to return serialized data or the name of the class. The interface forces you to implement the getAMFClassName and getAMFData methods. Check out the source of the interface for more details. This is especially useful if you need to integrate custom serialization in existing components of your framework or application. The ClassMapperThe TypedObjects only really allow you to convert classes 1-way, from PHP data to a Flash Class. The ClassMapper can also map Flash classes to PHP classes, specifically for use with VO's. The SabreAMF_ClassMapper contains these mappings. Using it is easy: <?php
SabreAMF_ClassMapper::registerClass('yourpackage.YourFlashClass','YourPHPClass');
?>Custom classmappingIn some cases you might want to be able to automatically map all classes from a specific folder. In this case you'd need the custom classmapping. It works similar to the CallbackServer, with event handlers. There are two events, onGetRemoteClass and onGetLocalClass. The first to map a PHP class to a Flash class, and the second to do the reverse. Examples: <?php SabreAMF_ClassMapper::$onGetLocalClass = 'flashToPHPClass'; SabreAMF_ClassMapper::$onGetRemoteClass = 'PHPToFlashClass'; ?> These callback classes both receive a classname as a string, and should also return a string. |
Sign in to add a comment
I just thought I throw in some code for getting VO classes to work
you need to register them first with SabreAMF_ClassMapper?::registerClass(asClass. phpClass);
this way all object are automaticly converted from AsObjects? to phpObject and the services themself need not to know of the typedobjects
function serviceExecuter($serviceName, $methodName, $arguments) { $serviceClass = SabreAMF_ClassMapper::getLocalClass($serviceName); if ($serviceClass === false) throw new SabreAMF_ClassNotFoundException($serviceName); $serviceObject = new $serviceClass(); $this->typedObjectsToInstances($arguments); $result = call_user_func_array(array($serviceObject, $methodName), $arguments); $this->instancesToTypedObjects($result); return $result; } final private function typedObjectsToInstances(&$data) { if (is_object($data) && ($data instanceof SabreAMF_TypedObject)) { $className = $data->getAMFClassName(); $objectData = $data->getAMFData(); $phpClassName = SabreAMF_ClassMapper::getLocalClass($className); if ($phpClassName === false) throw new SabreAMF_ClassNotFoundException($className); $serviceObject = new $phpClassName(); foreach($objectData AS $key=>$value) $serviceObject->$key = $value; $data = $serviceObject; } else if (is_array($data)) { foreach($data AS $key=>$value) { $this->typedObjectsToInstances($data[$key]); } } } final private function instancesToTypedObjects(&$data) { if (is_object($data)) { $_data = array(); foreach($data AS $key=>$value) $_data[$key] = $value; $Reflection = new ReflectionClass($data); $phpClassName = $Reflection->getName(); $amfClassName = SabreAMF_ClassMapper::getRemoteClass($phpClassName); if ($amfClassName === false) throw new SabreAMF_ClassNotFoundException($phpClassName); $data = new SabreAMF_TypedObject($amfClassName, $_data); $serviceObject = new $phpClassName(); } else if (is_array($data)) { foreach($data AS $key=>$value) { $this->instancesToTypedObjects($data[$key]); } } }This should not be needed, as the ClassMapper? should be able to do this automatically..
What about using classmappings when the environment does not support sending class types, like for example Flash Media Server or flash mx (could be some other variances too that i am not aware like FlashLite?), but flash media server is the main thing.
Hi Evert,
You mentioned: "This should not be needed, as the ClassMapper? should be able to do this automatically.."
Please could you clarify how it could be done automatically?
I have no problem mapping and sending correctly typed objects to flash, but mapped flash objects always arrive at my ServiceCallback? typed as SabreAMF_TypedObject?, and then arrive at my service method as plain strings (the name of the Class instead of an object of that Class)!
Are you saying that there is an automatic way to send correctly typed objects to a service method? Ideally I'd like for my service method parameters to be strictly typed (Type Hinting I think it's called in PHP)
Many thanks,
Check out the section on 'custom classmapping'
You can define your own function that maps PHP classes to AS classes, and the other way round. These methods will be called with a classname (as string) and are also supposed to send the same back..
Stoica:
I've never got the notification for your message, but let me answer this now for the sake of completeness. Simply avoid classmapping for those setup. This might be an unpopular answer, but in general I would recommend avoiding classmapping altogether and just work with basic types.
Hi Evert,
Yes, I've been using custom classmapping, but keeping it super simple:
SabreAMF_ClassMapper::$onGetLocalClass = 'flashToPHPClass'; SabreAMF_ClassMapper::$onGetRemoteClass = 'PHPToFlashClass'; function flashToPHPClass( $class_name ) { return $class_name; } function PHPToFlashClass( $class_name ) { return $class_name; }The RemoteClass? alias definitions for my AS classes are the same as my PHP Class names, and follow Zend Framework naming conventions (for autoloading).
Does the flashToPHPClass need to return a fully qualified Class name (with packages included)? All my classes get autoloaded by the Zend Framework loader when needed, so I thought this would be unnecessary.
Thanks,
Hi Darscan,
You're right.. classmapping only works with the fully qualified classnames. This could mean you simply need to do a str_replace to map . to
hope this helps, Evert
Hi Evert,
Thanks for the response.
I'm using the Flex RemoteClass? Metadata Tag just before my ActionScript? Class Definitions. Like so:
Which means that the object gets sent across the wire (from Flash to PHP) as a SomePHPPackage_Vo_Item? object (confirmed by Charles), so there is no . in the class name.
My PHP Class has that same name (and lives in: SomePHPPackage/Vo/Item.php).
Everything works great from PHP to Flash (in terms of Class Mapping), but I'm still struggling to get it working automatically from Flash to PHP.
I managed to get correctly typed objects to my service methods by implementing the concepts in the "typedObjectsToInstances" function above, but, as you mentioned, that should not be necessary.
What's weird is that the "typedObjectsToInstances" function uses SabreAMF_ClassMapper?::getLocalClass, so it would appear that the mapping is set up correctly.
I'll keep playing around.
Cheers,
Hi, Im a Flex developer and started to use SabreAMF, downloaded a few days ago and I can't save any data sent from Flex to PHP. I can read data from PHP and send them to Flex, no problem here, but the other way I couldn't. Please anybody can help me out with this??? I don't know what to do to solve my problem. Thanks in advance. Regards.
I've yourself problem, but with the object, that is:
I send from flex to PHP a object so:
package business {
}<? class UtenteRef? {
} ?>
but to PHP I receive:
SabreAMF_TypedObject? Object (
)
so when my function PHP get a data from "$utenteRef->idUtente", it's don't exist.
Why???
Hey guys,
What I'm reading all makes sense, but how did you guys setup the classmapping?
By default things are cast into SabreAMF_TypedObject? objects, and you can extract the 'real' data with 'getData'. If you automatically would like to map this to a class you need to setup the mapping.
So did you add these classes manually to ClassMapper?::registerClass or otherwise?
This form has create "?" character into my comment! however I would to work with self object from flex to php.
So if my object on flex is:
class User {
}when I receive this object into PHP across a function (ex getIdUser($user)) I would to work with object $user like for example "$user->id = 10".
thanks
Hi Scrivao,
You'll need to create the exact same class in PHP:
class User {
}And then you can map this class using ClassMapper?::registerClass('User','User');
Also, for everybody.. I would strongly suggest using the mailing lists for these types of questons:
http://osflash.org/mailman/listinfo/sabreamf_osflash.org
It's much more difficult here for myself to provide good feedback, also there's much more likely there's more people on that list who could help out providing an answer.
in particular:
<? class Utente{
} ?>
package business{
}and yet in PHP I receive the object:
SabreAMF_TypedObject? Object (
)
so I can't work with "$utente->id" ... why????
thanks
unfortunality I didn't find a solve ...
Hi Scriviao,
Make sure you post your questions to the mailing list: http://osflash.org/mailman/listinfo/sabreamf_osflash.org
There's more people watching this, and you're more likely to get answer quickly..
Let me rephrase how you can access this data:
1:
2:
SabreAMF_ClassMapper('business.Utente','Utente'); echo $utente->id;with this solution I have'll to change many files in my project... I'll contact the maillist
thanks a lot
P.S: in the latest version of SabreAMFn I've found "$utente->getAMFData();"
I spend last 2 evenings with classmapping and it was a strugle.
i made a simple test scenario with classes without properties
php
class Program {
}
and flex/as
package com {
}
in gateway.php
SabreAMF_ClassMapper?::registerClass('com.Program','Program');
Now i created a new empty class in both flex and php. in gateway.php i added a line, so have now:
SabreAMF_ClassMapper?::registerClass('com.Program','Program'); SabreAMF_ClassMapper?::registerClass('com.Newclass','Newclass'); too my surprise the ´NewClass?´ send from php to flash is just a 'Object' instead of a correct named object. After hours of searching: I checked every letter, went into sabreAmf source and printed every function result into a file but i did not found anything different except the filename I thought i got to be flash. flash probably cannot find the mapped classname. maybe there is no reference to it or whatsoever.
As we all know we use [RemoteClass(alias="com.Program")] to let flash automaticly instantiate the remote object into the local object. Till this point it goes beyond my knowledge. i dont understand how flash exactly finds this file and if it needs somekind of reference.
But i fixed my problems by using
registerClassAlias('com.Program',Program); registerClassAlias('com.Newclass',Newclass);
in the init_complete event of the flash application.
(btw of course i had included the files in flash, even when the problems were still there)
these lines solved all my problems. And all my classmappings are working now. Still its very weird, and still i am angry that i wasted 2 evenings :)