SabreAMF 0.3 released + more info on class mappings
Renaun Erickson recently wrote an article on how to use SabreAMF with Flex
2’s services.xml
configuration file..
He had some positive critique and also made me realize I made a mistake in confusing the ‘source’ and ‘destination’ properties in Flex’ RemoteObject. I updated the CallBackServer as soon as I could to fix that.. Be sure to download the updated version.
Also, take a look at the article, as its quite complete in explaining how to setup Flex/FDS to communicate with SabreAMF.
The extended version
Renaun modified the CallBackServer a bit to work more like AMFPHP. I understand the need for this and I might add in an extra class to support this behaviour.. It would definitely an optional one.
The most important change was that his CallBackServer now constructs classes
based on the destination and automatically invokes methods. He also added in
support for AMFPHP’s _explicitType
attribute to automatically convert arrays
and objects to a Flash/Flex class.
First thing I should note is that his implementation only works for the AMF3 part.. the AMF0 part does not have his extended behaviour. If he would have created a new class which constructed a CallBackServer instead, and listened to the onInvokeService this would work for both AMF0 and AMF3. I listed below how I could recommend extending the system. Most of the code is copy-pasted from Renauns version, except that I moved it into a separate class.
<?php
// Warning, untested
require_once 'SabreAMF/CallbackServer.php';
class ServiceMapper {
private $server;
private $baseClassPath;
function __construct() {
//Construct the server
$this->server = new SabreAMF_CallbackServer();
// Listen to the event
$this->server->onInvokeService = array($this,'invokeService');
}
function invokeService($service,$method,$arguments) {
$dirname = realpath("./" . $this->baseClassPath);
//Does the classpath exist
if (is_dir($dirname)) {
chdir($dirname);
} else {
throw new Exception('Could not locate base class path: ' . $this->baseClassPath);
}
$classpath = $dirname . str_replace('.','/',$service) . '.php';
//Can we actually open the file?
if (!is_readable($classpath)) {
throw new Exception('Could not open file: ' . $classpath);
}
require_once $classpath;
$classname = str_replace('.','_',$source);
if (!class_exists($classname)) {
throw new Exception('Class not found: ' . $classname);
}
$serviceInstance = new $class;
// Invoking the method
return call_user_func_array(array($serviceInstance,$method),$arguments);
}
function setBaseClassPath($classPath = 'services/') {
$this->baseClassPath = $classPath;
}
function exec() {
return $this->server->exec();
}
}
$server = new ServiceMapper();
$server->setBaseClassPath('services/');
$server->exec();
?>
I will probably add this class to the source (under a different name) when this is well tested.
Second thing is the _explicitType
part. While this works easily if you have
an AMFPHP background, SabreAMF has a more OOP equivalent for this. There are 3
different ways to map classes (force types).
<?php
$data = array(
'prop1' => 'val1',
'prop2' => 'val2',
);
// If want to force a flash class in AMFPHP you would add in:
$data['_explicitType'] = 'FlashClass';
// The SabreAMF equivalent:
// Be sure this class is included in your application
require_once 'SabreAMF/TypedObject.php';
$data = new SabreAMF_TypedObject('FlashClass',$data);
?>
If you are making classes on the PHP backend which are replicas of Flash/Flex classes there is an even easier way to do it, using the classmapper.
<?php
//This is done on the beginning of the application, as this is usually a global thing and only has to be done at 1 point
require_once 'SabreAMF/ClassMapper.php';
SabreAMF_ClassMapper::registerClass('FlexClassName','PHPClassName');
?>
This will work in both directions, so if you make a request with a certain flex class, it will also be translated to the PHP class.
There is a third way to map classes, this is mostly useful if you already have
a set of classes in your framework and you want to do extra modifications to
the data before it gets sent back to the flashplayer. This is done through the
ITypedObject
interface..
<?php
require_once 'SabreAMF/ITypedObject.php';
class MyClass implements SabreAMF_ITypedObject {
public $property1;
public $property2;
public $secretProperty;
function getAMFClassName() {
return 'org.rooftop.MyFlexClass';
}
function getAMFData() {
return array(
'property1' => $this->property1;
'property2' => $this->property2;
);
}
}
?>
Comments
Evert •
UPDATE: fixed coding bugstom •
thanks for the update![NikO] •
HelloReally great work here !
i try to use the Client.php to test a amfphp gateway, but it seems there is difference in AMF0 sends by Flash and your serialised data :
In Flash AMF0 data : After the /1 there is nothing :
2F 31 00 00
In your serialisez data, after the /1 there is that :
2F 31 FF FF
So, the methods i call return the good answer value, but the mysql query is not apply
Do you have an issue ?
Regards
Evert •
Hi Niko,The difference between flash' serialized data and mine is that I don't supply the proper length for the body, instead I submit FF FF FF FF.. This property is generally ignored (haven't seen a system that needed this to be correct).
If problems arise because of this I will definitely fix this..
I dont really understand the other issue you are having.. can you elobate?
Evert
[ NikO ] •
HiThanks for your replay, i understand now what means these hexa numbers :)
For the other problems, it s my fault, i look at data send in a second query, and see that the first query is send too, so i do some modification in a MyClientAMF.php to have it to work as i want, you can take a look here :
http://niko.informatif.org/blog/flash_remoting_securite#faux_client
Regards,
Evert •
MYmXI,Although I feel like your comment might be generated by a spam bot, at least you're not leaving any links and I'm just gonna say.. thanks very much :)
Evert