If you’re an Actionscript developer then there’s a chance that you would have used AMFPHP in your time. It’s a shame that the project isn’t being maintained – since the last release was in 2008 – nevertheless, it works and that’s what matters. I’ve been having a crack at making AS3 and AMFPHP play nice together and it’s turned out quite well, apart from a few issues, with AS3 mainly.
I’m a firm believer of reusing code, so instead of creating some functions for projects, I create sets of classes so that when I go to do other projects, development time is reduced as I’ve written scalable and usable code rather than re-writing all my stuff. So I’ve done about creating a set of AMFPHP Service classes that I’d like to share with you.
Before I paste the code, I am going to publish my AS3 and AS3 libraries on Google code but I just need to find the time and also iron out any bugs or nasty looking code.
So, AMFPHP is a serialisation service and we connect to it using good old NetConnection. Now, I’ve created three classes: a “RemoteConnectionService” that acts as the base class; a “RemoteConnectionServiceEvent” that acts as the event class for our base; and “AMFPHPService” that we use for AMF3 communication.
So, we’ll begin with the base:
{
import com.adobe.serialization.json.JSON;
import com.firestartermedia.lib.as3.events.RemoteConnectionServiceEvent;
import flash.events.NetStatusEvent;
import flash.net.NetConnection;
import flash.net.Responder;
public class RemoteConnectionService extends NetConnection
{
public var handleReady:Boolean = true;
public var data:Object;
public var gatewayURL:String;
public var responder:Responder;
private var loadedEvent:String;
private var readyEvent:String;
private var faultEvent:String;
private var dataType:String
public function RemoteConnectionService(gatewayURL:String, loadedEvent:String='', readyEvent:String='', faultEvent:String='', encoding:uint=3)
{
this.gatewayURL = gatewayURL;
this.loadedEvent = loadedEvent;
this.readyEvent = readyEvent;
this.faultEvent = faultEvent;
this.dataType = dataType;
objectEncoding = encoding;
if ( gatewayURL )
{
responder = new Responder( handleResponseResult, handleResponseFault );
addEventListener( NetStatusEvent.NET_STATUS, handleNetEvent );
connect( gatewayURL );
}
}
private function handleNetEvent(e:NetStatusEvent):void
{
dispatchEvent( new RemoteConnectionServiceEvent( faultEvent, e.info.code ) );
}
private function handleResponseResult(data:Object):void
{
dispatchEvent( new RemoteConnectionServiceEvent( loadedEvent, data ) );
if ( handleReady )
{
handleLoaderDataReady( data );
}
}
public function handleLoaderDataReady(data:Object):void
{
this.data = data;
dispatchEvent( new RemoteConnectionServiceEvent( readyEvent, data ) );
}
private function handleResponseFault(data:Object):void
{
dispatchEvent( new RemoteConnectionServiceEvent( faultEvent, data ) );
}
}
}
I’ve simply set everything up so that we connect to the gateway service when the class has been constructed, and we also apply the responder so that we can handle the data – through the “loadedEvent” and the “readyEvent”. The reason I have two events is that you may want to create a class that extends the base and manipulates the data somehow, this means that you only need to have one event listener that listens for the “readyEvent” and your extending class listens to the “loadedEvent”, does its stuff and dispatches the “readyEvent” with the data.
Now the event class:
{
import flash.events.Event;
public class RemoteConnectionServiceEvent extends Event
{
public static const NAME:String = 'RemoteConnectionServiceEvent';
public static const LOADED:String = NAME + 'Loaded';
public static const READY:String = NAME + 'Ready';
public static const FAULT:String = NAME + 'Fault';
public var data:Object;
public function RemoteConnectionServiceEvent(type:String, data:Object=null, bubbles:Boolean=true, cancelable:Boolean=false)
{
super( type, bubbles, cancelable );
this.data = data;
}
}
}
And finally our AMF3 service class:
{
import com.firestartermedia.lib.as3.data.RemoteConnectionService;
import com.firestartermedia.lib.as3.events.RemoteConnectionServiceEvent;
import flash.net.ObjectEncoding;
public class AMFPHPService extends RemoteConnectionService
{
public function AMFPHPService( url:String )
{
super( url, RemoteConnectionServiceEvent.LOADED, RemoteConnectionServiceEvent.READY, RemoteConnectionServiceEvent.FAULT, ObjectEncoding.AMF3 );
}
public function init(method:String, ... args):void
{
switch ( args.length )
{
case 1:
call( method, responder, args[0] );
break;
case 2:
call( method, responder, args[0], args[1] );
break;
case 3:
call( method, responder, args[0], args[1], args[2] );
break;
case 4:
call( method, responder, args[0], args[1], args[2], args[3] );
break;
case 5:
call( method, responder, args[0], args[1], args[2], args[3], args[4] );
break;
case 6:
call( method, responder, args[0], args[1], args[2], args[3], args[4], args[5] );
break;
case 7:
call( method, responder, args[0], args[1], args[2], args[3], args[4], args[5], args[6] );
break;
case 8:
call( method, responder, args[0], args[1], args[2], args[3], args[4], args[5], args[6], args[7] );
break;
case 9:
call( method, responder, args[0], args[1], args[2], args[3], args[4], args[5], args[6], args[7], args[8] );
break;
case 10:
call( method, responder, args[0], args[1], args[2], args[3], args[4], args[5], args[6], args[7], args[8], args[9] );
break;
default:
call( method, responder );
break;
}
}
}
}
You’ll see that the main bulk is handled in the base class, although it may not look that way because of the “switch”. You see, the “NetConnection.call()” allows one to pass “…(rest)” style arguments after the first two. Because of a problem with Adobe’s Flash Player – maybe they were out when they should have been writing this – we can’t pass rest style arguments to another function, so I’ve had to use this really dirty method and just run a switch that checks the arguments passed in the rest arguments and send them accordingly. I’ve decided I won’t be sending more than 10 arguments, but it’s up to you where you stop!
So how do you use this? Well here’s an example:
{
import com.firestartermedia.lib.as3.data.amfphp.AMFPHPService;
import com.firestartermedia.lib.as3.events.RemoteConnectionServiceEvent;
import flash.display.Sprite;
import flash.net.URLVariables;
public class App extends Sprite
{
private var gatewayURL:String = 'http://localhost/amfphp/gateway.php';
public function App()
{
var service:AMFPHPService = new AMFPHPService( gatewayURL );
service.addEventListener( RemoteConnectionServiceEvent.FAULT, handleFault );
service.addEventListener( RemoteConnectionServiceEvent.READY, handleReady );
service.init( 'CI.execute', 'main', { 1: 'bob', 2: 'face', 3: 'hello' } );
}
private function handleFault(e:RemoteConnectionServiceEvent):void
{
trace('fault ' + e.data);
}
private function handleReady(e:RemoteConnectionServiceEvent):void
{
trace('ready ' + e.data['1']);
}
}
}
You should have a trace return “readyYou said: hello my name is”. Now this is depending on your “HelloWorld.say” method being like this:
Enjoy!