AMF Flash Remoting with Zend and CI

Derek Jones edited this page Jul 5, 2012 · 15 revisions
Clone this wiki locally

Category:Contributions::Libraries::AMF

This is a tutorial on how to implement Flash Remoting in CodeIgniter using the Zend Framework.

Description:

Action Message Format(AMF) is a binary file format representing a serialized ActionScript object. The AMF file type is used throughout the Flash Player for data storage and data exchange. For example in the Flash Player AMF is used in SharedObjects, RemoteObjects, LocalConnection, ByteArray, RTMP, and all RPC operations. Some of the benefits of AMF include:

**File Size* - AMF objects are very small and are compressed using zlib. **Fast Serialization/ Deserialization* - AMF is transformed using native C code in the Flash Player making it very fast. The AMF format was designed to serialize and deserialize quickly under low memory and slower CPU conditions making it perfect for the web. AMF data is parsed directly into objects, meaning there is no lag for interpretation or parsing of AMF making the creation of objects complete in a single pass. **Native Types and Custom classes supported* - You can serialize any object in Flash Player with the only exception being a displayObject.you can also map serialized objects back to custom class instanced provided the custom class is in the Flash Player when the AMF object is deserialized.

AMF existed in ActionScript 2 and was just called AMF as of ActionScript 3 the AMF protocol has been updated and is referred to as AMF3. For historical reasons the original AMF format is now referred to as AMF0. One of the main upgrades to AMF3 is that the object is now zlib compressed for faster transfer do to the smaller file size and the additional of data types that were released with ActionScript 3.

Author:

Matt Johnson Lead Interactive Developer Coolfire Media St. Louis, MO USA E: mjohnson@coolfiremedia.com W: www.coolfiremedia.com

The following components are needed to make this work:

1: CodeIgniter Installation 2: The Zend Framework (minimal) 3: Flash CS3 (Actionscript 3)

Part 1: Download and integrate the Zend Framework

(A) Start by visiting framework.zend.com and download the current minimal package, which at the time of authoring is 1.7.2. This will provide you with all the core components needed to implement AMF into your project. Once unzipped you should create the following folder and copy over your Zend framework files. Make sure the Zend package contains the AMF directory.

system->application->libraries->Zend

(B) Now update your config file to enable hooks if this has not been done already.


(system->application->config)

$config['enable_hooks'] = TRUE;

(C) Define a generic hook pre-controller for using Zend and other frameworks


(system->application->config->hooks)

$hook['pre_controller'][] = array(
  'class' => 'App_hook',
  'function' => 'index',
  'filename' => 'app_hook.php',
  'filepath' => 'hooks'
);

(D) Create the app_hook described in the pre-controller to make sure your Zend files are located and loaded correctly in the include path.


<?php 
if(!defined('BASEPATH')) exit('No direct script access allowed');

class App_hook
{
    function index()
    {
        ini_set('include_path',ini_get('include_path').PATH_SEPARATOR.BASEPATH.'application/libraries/');
    }
}

(E) Finally, create a library file that you can use to load Zend classes or other framework classes that you may install later. There are a lot of different options here, but I typically like to load an array through the constructor or individual items through the load method shown below. Use whatever method suits you best.


<?php if (!defined('BASEPATH')) {exit('No direct script access allowed');}

class Apps
{
    function __construct($class = NULL)
    {
        if(!empty($class) && is_array($class))
        {
            foreach($class as $item)
            {
                $this->load($item);
            }
        }
    }

    function load($class)
    {
        require_once (string) $class . EXT;
    }
}

Part 2: Load Zend AMF and create a Remoting Class

(A) In your application directory, create a new controller called "remote.php" and implement Zend AMF by loading the server class and creating a new AMF Server object. In the setClass method, use "$this" so the server knows to find the requested method in the Remote controller itself.

Then call the handle() method which will process the incoming AMF request and return the data to your Flash application. At this point you should also be able to point your browser to this controller and receive the message "Zend AMF Endpoint" which lets you know that everything is running as expected.


class Remote extends Controller 
{
    function Remote()
    {
        parent::Controller();    
    }

    function index()
    {
        $this->load->library("Apps");
        $this->apps->load("Zend/AMF/Server");

        $server = new Zend_Amf_Server();
        $server->setClass($this);
        echo $server->handle();
    }

    function getData()
    {
        return array(1,2,3,4,5);
    }
}

Optionally, you can load a model as the server class. This works well if you want to use a single controller to both display the flash website and function as the remote.

Here you simply load the model, then pass the name of the model to the AMF server.

Just note that in this example the NetConnection path is "http://www.yoursite.com/home/remote" but the actual call would be nc.call("AmfModel.getInfo",resp);


class Home extends Controller {

    function Home()
    {
        parent::Controller();    
    }

    function index()
    {    
        $info["title"] = "Flash App";
        $this->load->view("flashapp",$info);
    }

    function remote()
    {
        $this->load->model("amfmodel");

        $this->load->library("Apps");
        $this->apps->load("Zend/AMF/Server");

        $server = new Zend_Amf_Server();
        $server->setProduction(true);
        $server->setClass("AmfModel");
        echo $server->handle();
    }
}

Optional AMF Server Model


class AmfModel extends Model
{
    function AmfModel()
    {
        parent::Model();
    }

    function getInfo()
    {
        return array("this","is","info");
    }
}

Part 3: Create a new Flash CS3 application and test the connection

(A) Open Flash CS3 and create a new ActionScript 3 Flash File. Then open the actions panel and enter the following code.


import flash.net.*;

var nc:NetConnection = new NetConnection();
nc.connect("http://www.testsite.com/remote");

var resp:Responder = new Responder(onResult,onError);

function onResult(e:Object)
{
    trace(e);
}

function onError(e:Object)
{
    trace(e);
}

nc.call("Remote.getData",resp);

This code will create a new NetConnection object and attempt to make a connection to the remote controller that we created earlier. In this case I have it running on a local test server so your address will be different. Create a new Responder object and give it the names of your methods for handling successful requests and errors, then simply define those two Responder methods and trace out the results to test your Zend AMF Server.

Finally, call the Zend AMF Server through the NetConnection and access the simple getData method. If everything is working correctly, Flash will output "1,2,3,4,5" letting you know that the array of integers was transferred from PHP to Flash.

Part 4: AMF Object Encoding

When configuring the NetConnection in Flash you can set the object encoding as either AMF0 or AMF3.


var nc:NetConnection = new NetConnection();

/*
nc.objectEncoding = ObjectEncoding.AMF0;

OR

nc.objectEncoding = ObjectEncoding.AMF3;
*/

In most cases you will never need to worry about the encoding, but this does come into play if you want to send a ByteArray from Flash to the Zend AMF Server, and especially if you want to send mixed data types. You will typically encounter a ByteArray if you try to capture BitmapData from Flash and transmit it through the Zend AMF Server encoded as a JPEG or a PNG to be saved server-side. In this case a ByteArray must be sent as AMF3 or you will receive the "BadVersion" error in Flash.

So if you are trying to send multiple data types, I have found it best to put all items in an Object, encode the Object as AMF3, then transmit and access the individual items on the server side, like so.



/////////////////////////////////////////////////
// FLASH SIDE
/////////////////////////////////////////////////

import com.adobe.images.*;

//Set the NetConnection encoding
nc.objectEncoding = ObjectEncoding.AMF3;

//Create a new Object
var imgObj:Object = new Object();

//Capture the MovieClip image
var imgCapture:BitmapData = new BitmapData(clip_mc.width,clip_mc.height);
imgCapture.draw(clip_mc);

//Create a new Jpeg Encoder and encode the image
var jpgEncoder:JPGEncoder = new JPGEncoder(100);
var imgData:ByteArray = jpgEncoder.encode(imgCapture);

//Add the data to your Object
imgObj.name = "test_image.jpg";
imgObj.data = imgData;

//Transmit the Object to the Zend AMF Server
nc.call("Remote.saveJpgImage",resp,imgObj); 


/////////////////////////////////////////////////
// PHP SIDE
/////////////////////////////////////////////////

function saveJpgImage($obj)
{    
    $success = file_put_contents(PATH_TO_FOLDER.$obj->name,$obj->data);
    if($success) return "image saved";
    else return "error encountered";
}

Closing Remarks:

This is just a simple example of using AMF and you can get much more complex. A great resource for learning more is from the original AMFPHP project or through the Community Wiki at Zend (http://framework.zend.com/wiki/display/ZFPROP/Zend_Amf+-+Wade+Arnold)

The Zend programming documentation for AMF can be found here http://framework.zend.com/manual/en/zend.amf.html