AMFPHP Class Mapping Primer [1.9 beta]

Thought I’d post a simple AMFPHP class mapping primer.

I ran into a wall after re-arranging my VO / DAO package structure on the server, learned a few things in the process and thought I’d post .

I’m assuming your using AMFPHP 1.9 beta.

Class mapping allows your php backend and actionscript frontend to pass strongly typed variables back and forth. The benefits are obvious so I won’t cover them here.

If your not after any detailed information here’s a quick set of samples classes to get you going.

PHP


// LoginVO.php is saved as {$voPath}/com/dl/app/LoginVO.php
class LoginVO
{
   var $_explicitType="com.dl.app.LoginVO";
   public $username;
   public $password;
}

AS

package com.dl.common.Login.model.vo
{
   [RemoteClass(alias="com.dl.app.LoginVO")]
   public class LoginVO
   {
      public var username:String;
      public var password:String;
   }
}

That’s it. If your after more options and detailed info, keep reading.

Sending Strongly Typed Objects from PHP to ActionScript via AMFPHP

In PHP

You have a few options (choose one).

  • Your class can declare a variable named $_explicitType;
  • The most common approach is to use the $_explicitType class member:

    class LoginVO
    {
       var $_explicitType="com.dl.app.LoginVO";
       public $username;
       public $password;
    }
    
  • You can set $GLOBALS[‘amfphp’][‘outgoingClassMappings’][‘localclass‘] = ‘remoteclassAlias‘;
  • e.g.

    $GLOBALS['amfphp']['outgoingClassMappings']['myclass'] = "com.dl.myapp.MyClass";

    Note that the local class name associative key (‘myclass’ above) must be in lowercase with the current release of AMFPHP.

  • Rely on the reflection class (PHP 5+)
  • If your install of PHP has the ReflectionAPI you can let AMFPHP do the work *for* you. I found a few cavets using this approach on servers who’s OS uses backslashes natively. If this is the case, AMFPHP will only send the name of the class and not include any package info. There are two simple code changes you can make in AMFPHP to get around this, just drop me a line if your interested (hopefully they’ll be commited in the next release of AMFPHP).

    In actionscript

    Here to, you have a some options,

  • Use [RemoteClass] metadata tag to specify the remote class alias
  • package com.dl.common.Login.model.vo
    {
       [RemoteClass(alias="com.dl.app.LoginVO")]
       public class LoginVO
       {
          public var username:String;
          public var password:String;
       }
    }
  • Use registerClassAlias()
  • registerClassAlias(“com.dl.app.LoginVO”,LoginVO);

    Make sure your actionscript project contains a reference to the class. See my previous post for more info.


    Sending Strongly Typed Objects from ActionScript to PHP via AMFPHP

    In PHP

    Rely on the class having been registed in ActionScript with the server’s correct package [directory] structure (relative to AMFPHP’s VO base path $GLOBALS[‘amfphp’][‘customMappingsPath’]). If this is the case you, your all set.

    Note: If you haven’t already you’ll need to make sure $voPath is pointing to the correct directory in AMFPHPs global.php

    Define $GLOBALS[‘amfphp’][‘incomingClassMappings’][‘remoteclassAlias‘] = ‘localclassPackage‘. (e.g. $GLOBALS[‘amfphp’][‘incomingClassMappings’][‘com.dl.as3.userVO’] = ‘com.dl.userVO’);

    In ActionScript

    Again, you’ll need to use one of the two following…

  • Use [RemoteClass] metadata tag to specify the remote class alias
  • Use registerClassAlias()

    For more info check out

    AMFBaseSerializer.php’s getClassName() function

    AMFBaseDeserializer.php’s mapClass() function




    AMFPHP Complex Classes – Yeah I knew that

    Just blew half an hour tracking down something I *knew* I’d done before. Thought I’d post here for future ‘lapses.’

    To return a typed object from PHP to the flashplayer the process is simple, declare a variable called $_expicitType in your PHP class
    e.g.
    class SummaryVO
    {
    var $_explicitType = "com.dl.SummaryVO";
    $standards = array();
    $levels = array()
    }

    In your corresponding ActionScript class use the RemoteClass metadata tag

    package com.dl
    {
    [RemoteClass(alias="com.dl.SummaryVO")]
    public class SummaryVO
    {
    public var standards:Array;
    public var levels:Array;
    }
    }

    Note the values for $_explicitType and alias are arbitrary, they can be what ever you’d like, as long as their the same in AS and PHP (provided your only conerned with PHP=>AS mapping).

    Okay, so what happens when you have ‘complex classes’ you’d like to return? Complex meaning a class who’s members are made up of other ‘custom’ class instances.

    Let’s say, for example, that in our later code example ‘standards’ was an array of ‘StandardVO.’ The problem I ran into was that I was getting a typed ‘SummaryVO’ back from AMFPHP, however my class members were not typed, just generic objects. I checked and I had taken the necessary steps to use strongly typed SummaryVO and StandardVO classes, yet the StandardVO was coming back as a plain old Object.

    We’ll it turned out in my development flurry I hadn’t used a reference to the ‘StandardVO’ class yet!! Simply declaring an instance of StandardVO did the trick (i.e. var placeHolder:StandardVO). You could also you the includes class […] compiler option.

    Moral of the story, be sure the complier includes a reference to any class you want strongly typed from your RPCs.

    Securing AMFPHP 1.9 via Authentication

    With the loss of the methodTable in AMFPHP 1.9, comes a loss of the easily defined ‘roles.’

    Background

    Roles, for those of you who aren’t familiar, allow you to ‘protect’ who can invoke your AMFPHP services. For example, you probably wouldn’t want the following function accessible to everyone


    public function SetEmployeeSalary($amount)

    Users are ‘authenticated’ in AMFPHP via a call to


    Authenticate::login($username,$roles);

    Where $roles is a comma delimited set of roles for the user. If you open up \core\shared\util\Authenticate.php you’ll find the login method


    function login($name, $roles) {
    if(!session_id())
    {
    session_start();
    }
    $_SESSION['amfphp_username'] = $name;
    $_SESSION['amfphp_roles'] = $roles;
    }

    Authentication in AMFPHP 1.9

    To utilize authentication in 1.9, create a function with the following signature in your service (class).


    public function beforeFilter($function_called)

    A quick peek in /core/shared/app/BasicActions.php and you’ll see that, should your service (class) define this function, AMFPHP will call it before invoking your function. If beforeFilter returns true, the function is invoked, otherwise a security error is thrown.

    Here’s the simple approach I’ve taken..


    public function beforeFilter($function_called)
    {
    $memberName = $function_called."Roles";
    return (@$this->$memberName) ? Authenticate::isUserInRole($this->$memberName) : true;
    }

    So to secure any function, I simply define a member variable with the roles required.


    var $SetEmployeeSalaryRoles = "admin,hr";
    public function SetEmployeeSalary($amount)

    My beforeFilter function looks to see if functionNameRoles exists, if it does, than the user must have a role found in functionNameRoles. If functionNameRoles does not exist, no authentication is required.

    403 Forbidden Error with Adobe AIR app.

    Here’s another issue I recently ran across. I was porting an existing (working) Flex 3 project to AIR. Under AIR my RPC calls (via AMFPHP) stopped working, and would return a 403 Permission denied error.

    Using ethereal verified the 403 error. The were minor differences in the headers sent when executing the NetConnection.call() from Flex and AIR. One of those differences happened to be the “user-agent.” My gateway is hosted on a shared webhost, so this was my first guess as to what might be the problem.

    I launched charles, and sure enough, the server would reject (via 403) any POSTs made with a user-agent of “Shockwave Flash.” Changing the user-agent (via charles) to pretty much anything else fixed the problem.

    I contacted both Adobe and my webhost. The Adobe engineers stated the default user-agent has been changed for the final release of air. My webhost was gracious enough the give me a line to add to my .htacess to revoke this “filter”

    Everything is now working.

    Client.Data.UnderFlow

    I ran across the following error today, Client.Data.UnderFlow undefined? I’m using AMFPHP to enable my RPC calls. As it turns out, I the variable I was returning was of a different type than I had declared in my methodTable.