My favorites | Sign in
Project Logo
                
Search
for
Updated Oct 21, 2007 by evertpot
Labels: Featured
ClassMapping  
How to map PHP data to Flash classes

Introduction

SabreAMF 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.

TypedObjects

If 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 ITypedObject

SabreAMF 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 ClassMapper

The 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 classmapping

In 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.


Comment by baskamer, Jan 07, 2008

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]);
            }
        }
    }
Comment by evertpot, Jan 07, 2008

This should not be needed, as the ClassMapper? should be able to do this automatically..

Comment by stoica.ionut, Apr 29, 2008

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.

Comment by Dars...@gmail.com, Dec 14, 2008

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,

Comment by evertpot, Dec 14, 2008

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.

Comment by Dars...@gmail.com, Dec 15, 2008

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,

Comment by evertpot, Dec 15, 2008

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

Comment by Dars...@gmail.com, Dec 15, 2008

Hi Evert,

Thanks for the response.

I'm using the Flex RemoteClass? Metadata Tag just before my ActionScript? Class Definitions. Like so:

[RemoteClass(alias="SomePHPPackage_Vo_Item")]

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,

Comment by ealonso04, Jan 22, 2009

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.

Comment by scriviao...@yahoo.it, Jan 23, 2009

I've yourself problem, but with the object, that is:

I send from flex to PHP a object so:

package business {

[RemoteClass(alias="business.UtenteRef?")]
Bindable?
public class UtenteRef? {
public var idUtente :uint; public var nome :String = ""; public var pubblicita :String = ""; public var tipoUtente :TipoUtente? = new TipoUtente?();
}
}

<? class UtenteRef? {

var $idUtente; var $nome; var $pubblicita; var $tipoUtente; //TipoUtente?

} ?>

but to PHP I receive:

SabreAMF_TypedObject? Object (

amfClassName:private? => business.UtenteRef? amfData:private? => Array
(
tipoUtente? => SabreAMF_TypedObject? Object
(
amfClassName:private? => business.TipoUtente? amfData:private? => Array
(
descrizione? => sportello idTipoUtente? => 5 prezzoImponibile? => 0
)

)
nome? => TRIESTE AMC idUtente? => 208 pubblicita? =>
)

)

so when my function PHP get a data from "$utenteRef->idUtente", it's don't exist.

Why???

Comment by evertpot, Jan 23, 2009

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?

Comment by scriviao...@yahoo.it, Jan 23, 2009

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 {

var id:uint;
}

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

Comment by evertpot, Jan 23, 2009

Hi Scrivao,

You'll need to create the exact same class in PHP:

class User {

public $id;
}

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.

Comment by scriviao...@yahoo.it, Jan 26, 2009

in particular:

<? class Utente{

public $id; public $nome;

} ?>

package business{

[RemoteClass(alias="business.Utente")] Bindable? public class Utente {
public var id:uint; public var nome: String = "";
}
}

and yet in PHP I receive the object:

SabreAMF_TypedObject? Object (

amfClassName:private? => business.Utente amfData:private? => Array
(
id? => 0 nome? => pippo
)

)

so I can't work with "$utente->id" ... why????

thanks

Comment by scriviao...@yahoo.it, Jan 29, 2009

unfortunality I didn't find a solve ...

Comment by evertpot, Jan 29, 2009

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:

$data = $utente->getData();
echo $data['id'];

2:

SabreAMF_ClassMapper('business.Utente','Utente');
echo $utente->id;
Comment by scriviao...@yahoo.it, Jan 30, 2009

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();"

Comment by gill...@gmx.net, May 06, 2009

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 {

[RemoteClass(alias="com.Program")] public class Program {

}

}

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 :)


Sign in to add a comment
Hosted by Google Code