phpflashamfamfphpzend-amf

AMFPHP: Serializing Flash Objects over HTTP without the gateway


Flash + AMFPHP is a great combination. But there are cases, when Flash Remoting with NetConnection isn't the right tool, for various reasons. Rob had a great post on this some time ago: http://www.roboncode.com/articles/144

He also has a nice example on how to deliver AMF to a http request, without the POST and AMF-request package to call the function that NetConnection sends, using Zend_AMF.

// Include the Zend Loader
include_once 'Zend/Loader.php';
// Tell the Zend Loader to autoload any classes we need
// from the Zend Framework AMF package
Zend_Loader::registerAutoload();

// Create a simple data structure
$data = array('message' => 'Hello, world!');
// Create an instance of an AMF Output Stream
$out = new Zend_Amf_Parse_OutputStream();
// We will serialize our content into AMF3 for this example
// You could alternatively serialize it as AMF0 for legacy
// Flash applications.
$s = new Zend_Amf_Parse_Amf3_Serializer($out);
$s->writeObject($data);

// Return the content (we have found the newline is needed
// in order to process the data correctly on the client side)
echo "\n" . $out->getStream();

I really like this approach and would be very hapy to replicate it with AMFPHP. Why AMFPHP, you ask? The 'newest' version uses amf-ext, a C PHP extension, to serialize and deserialize the data. It is much faster than the php way ZendAMF is still using.

Of course I already played around with AMFPHP and tried to build the necessary objects and use the Serializer class. I even got a valid AMF string, but the real data was always wrapped by a 'method package' that told the receiver this was a answer to 'Service.method' call.

So is there a way to serialize Flash Objects directly, without the gateway and method wrapper, in AMFPHP?

Thanks.


Solution

  • Okay, it got it to work now.

    It's a little bit more complicated than the Zend_AMF solution, but much faster. Here is my code:

    $data = array('message' => 'Hello, world!');
    
    // Create the gateway and configure it
    $amf = new Gateway();
    Amf_Server::$encoding = 'amf3';
    Amf_Server::$disableDebug = true;
    
    // Construct a body
    $body = new MessageBody("...", "/1", array());
    $body->setResults($data);
    $body->responseURI = $body->responseIndex . "...";
    
    // Create the object and add the body
    $out = new AMFObject();
    $out->addBody($body);
    
    // Get a serializer and use it
    $serializer = new AMFSimpleSerializer();
    $result = $serializer->serialize($out);
    

    As you see there is a new class AMFSimpleSerializer that I built:

    class AMFSimpleSerializer extends AMFSerializer
    {
        function serialize(&$amfout)
        {
            $encodeCallback = array(&$this,"encodeCallback");
    
            $body = &$amfout->getBodyAt(0);
    
            $this->outBuffer = "";
            $this->outBuffer .= amf_encode($body->getResults(), $this->encodeFlags, $encodeCallback);
            $this->outBuffer = substr($this->outBuffer, 1);
    
            return $this->outBuffer;
        }
    }
    

    This class only works if amfext is installed, but could easily be modded to use the php enocding process. I didn't implement it, because I built this on a heavily modified version of AMFPHP.

    I hope I replaced all the classes from my code with there real AMFPHP counterparts. I'll try to test this tomorrow and update this answer if necessary.

    After I was finished I recognized that now almost nothing from AMFPHP was actually left in the class, it's just a call to amf_encode and the deletion of the first byte so that the client can understand what he gets.

    Easy, simple, fast.