Cheap VPS & Xen Server

Residential Proxy Network - Hourly & Monthly Packages

Using Amfphp 1.9 with the Adobe Flex 2 SDK


This article shows how you can make PHP interact with Adobe Flex. Adobe Flex is a technology to support the development and deployment of rich Internet applications based on their proprietary Macromedia Flash platform.

Requirements

The following is a list of the required libraries to run this tutorial. I recommand the you download all the files before starting the tutorial.

  • Amfphp 1.9 beta 2 by Patrick Mineault
  • Flex 2.01 SDK by Adobe
  • Ant 1.7 by Apache
  • Flex Ant Tasks on Adobe Labs
  • commons-net-1.4.1.jar (required for FTP task)
  • jakarta-oro-2.0.8.jar (required for FTP task)

Installation

Apache Ant

Extract the contents of apache-ant-1.7.0-bin.zip to your hard drive. We will refer to this location as [ANT_HOME]. Make sure you add [ANT_HOME]/bin to your system path.

Flex Ant Tasks

Extract the flexTasks.jar file from flex_ant_tasks_012307.zip into the [ANT_HOME]/lib directory.

Flex 2.01 SDK

NOTE: You must have an Adobe Account to download the Flex 2 SDK.

Extract the contents of flex_sdk_2.zip to your hard drive. We will refer to this location as [FLEX_SDK].

Project Workspace

Directory Structure

Choose a location on your hard drive to store your project. Create a new directory with the following directory structure. We will refer to this location as [PROJECT_HOME].

  • build
  • config
    • flex
  • docs
    • api
    • app
  • dist
  • lib
    • flex
    • php
  • src
    • flex
    • php
  • web

Amfphp

Installation

Extract the contents of the amfphp-1.9.beta.20070126.zip file into your [PROJECT_HOME]/src/php directory. We will refer to this location as [AMFPHP_HOME].

Before we can start using Amfphp we must first edit the gateway.php file located in the [AMFPHP_HOME] directory. Edit the charset setting. Select the charset appropriate for your language. In this case we will be using the English charset. Replace the original charset with the English one below.

$gateway->setCharsetHandler( “none”, “ISO-8859-1”, “ISO-8859-1” );

MySQL

Creating the Employee’s table

This tutorial assumes that you are fimilar with basic MySQL administration. If you need assistance with MySQL please refer to the MySQL Documentation.

For this tutorial we will be using the test database that comes pre-installed with MySQL. Login to your MySQL server and selected the test database. Execute the SQL script below to create the Employee’s table.

   CREATE TABLE Employee (
empId bigint AUTO_INCREMENT NOT NULL,
firstName varchar(255),
lastName varchar(255),
phone varchar(255),
email varchar(255),
title varchar(255),
PRIMARY KEY(empId),
INDEX Employee_empId_INDEX (empId));

PHP Code

Creating your PHP services

DISCLAIMER: The php classes used in this tutorial are based off classes generated by Titantic Linux DAO Generator. Comments have been removed for readablility. The Datasource.php class is the only class generated by DAO GEN that did not need any modifications. The remaining classes generated by DAO GEN will not work with AmfPhp without modification.

Amfphp looks for your php services in the [PROJECT_HOME]/src/php/amfphp/services/ directory by default. You can modify this setting by changing the $servicesPath variable in the globals.php file under your [PROJECT_HOME]/src/php/amfphp/ directory. Methods prefixed with an underscore ‘_’ are considered private and won’t be accessible to your Flex application. We will store our php files using package style naming. Create the following directory structure under your [PROJECT_HOME]/src/php/amfphp/services/ directory.

  • org
    • amfphp
      • tutorials

WARNING: Be careful when you cut-n-paste the php code snippets. Amfphp will throw an error if the php files begin with whitespace or carriage return. Make sure nothing preceeds your opening php tag.

The Datasource.php class is a helper class used for communicating with your database.

Open your text editor and create a new file. Cut-n-paste the php code below into your new file. Save the file as Datasource.php under your [PROJECT_HOME]/src/php/amfphp/services/org/amfphp/tutorials/ directory.

<?php

class Datasource
{
var $dbLink;

function Datasource($dbHost, $dbName, $dbuser, $dbpasswd)
{
$this->dbLink = mysql_connect ($dbHost, $dbuser, $dbpasswd);
mysql_select_db ($dbName, $this->dbLink);
}

function _execute($sql)
{
$result = mysql_query($sql, $this->dbLink);
$this->_checkErrors($sql);
return $result;
}

function _executeBlind($sql)
{
$result = mysql_query($sql, $this->dbLink);
return $result;
}

function _nextRow ($result)
{
$row = mysql_fetch_array($result);
return $row;
}

function _checkErrors($sql)
{
$err=mysql_error();
$errno=mysql_errno();

if($errno)
{
$message = “The following SQL command “.$sql.” caused Database error: “.$err.”.”;

print “Unrecowerable error has occurred. All data will be logged.”;
print “Please contact System Administrator for help! \n”;
print ” \n”;
exit;
}
else
{
return;
}
}
}

?>

The Employee class is your value object. The $_explicitType variable informs Amfphp that this class maps to an actionscript class in your Flex application.

Open your text editor and create a new file. Cut-n-paste the php code below into your new file. Save the file as Employee.php under your [PROJECT_HOME]/src/php/amfphp/services/org/amfphp/tutorials/ directory.

<?php

class Employee
{
var $empId;
var $firstName;
var $lastName;
var $phone;
var $email;
var $title;
// explicit actionscript package
var $_explicitType = “org.amfphp.tutorials.Employee”;
}

?>

The EmployeeDao.php class provides methods for preforming the basic CRUD operation. The save and remove methods both expected associative array’s as parameters and return Employee objects.

Open your text editor and create a new file. Cut-n-paste the php code below into your new file. Save the file as EmployeeDao.php under your [PROJECT_HOME]/src/php/amfphp/services/org/amfphp/tutorials/ directory. Depending on how you setup your database you might need to modify the EmployeeDao contructor to reflect you database settings.

<?php

require_once(‘./Employee.php’);
require_once(‘./Datasource.php’);

class EmployeeDao
{

var $conn;

function EmployeeDao()
{
$this->conn = new Datasource(“localhost”, “test”, “”, “”);
}

function load($empId)
{
if (!$empId)
{
return false;
}

$sql = “SELECT * FROM Employee WHERE (empId = “. $empId .”) “;

return $this->_singleQuery($sql);
}

function loadAll()
{
$sql = “SELECT * FROM Employee ORDER BY empId ASC “;

$searchResults = $this->_listQuery($sql);

return $searchResults;
}

function _create($valueObject)
{
$sql = “INSERT INTO Employee ( firstName, lastName, phone, “;
$sql = $sql.”email, title) VALUES (‘”.$valueObject[firstName].”‘, “;
$sql = $sql.”‘”.$valueObject[lastName].”‘, “;
$sql = $sql.”‘”.$valueObject[phone].”‘, “;
$sql = $sql.”‘”.$valueObject[email].”‘, “;
$sql = $sql.”‘”.$valueObject[title].”‘) “;
$result = $this->_databaseUpdate($sql);

$sql = “SELECT last_insert_id()”;
$result = $this->conn->_execute($sql);

if ($row = $this->conn->_nextRow($result))
{
return $row[0];
}
else
{
return false;
}
}

function save($valueObject)
{
if( array_key_exists(“empId”,$valueObject) )
{
if ($valueObject[empId] == 0)
{
$insertId = $this->_create($valueObject);
if($insertId)
{
$temp = new Employee();
$temp->empId = $insertId;
$temp->firstName = $valueObject[firstName];
$temp->lastName = $valueObject[lastName];
$temp->phone = $valueObject[phone];
$temp->email = $valueObject[email];
$temp->title = $valueObject[title];

return $temp;
}
}
else
{
if($this->_update($valueObject))
{
$temp = new Employee();
$temp->empId = $valueObject[empId];
$temp->firstName = $valueObject[firstName];
$temp->lastName = $valueObject[lastName];
$temp->phone = $valueObject[phone];
$temp->email = $valueObject[email];
$temp->title = $valueObject[title];
return $temp;
}
}
}

return false;

}

function _update($valueObject)
{
$sql = “UPDATE Employee SET firstName = ‘”.$valueObject[firstName].”‘, “;
$sql = $sql.”lastName = ‘”.$valueObject[lastName].”‘, “;
$sql = $sql.”phone = ‘”.$valueObject[phone].”‘, “;
$sql = $sql.”email = ‘”.$valueObject[email].”‘, “;
$sql = $sql.”title = ‘”.$valueObject[title].”‘”;
$sql = $sql.” WHERE (empId = “.$valueObject[empId].”) “;
$result = $this->_databaseUpdate($sql);

if ($result != 1)
{
return false;
}

return true;
}

function remove($valueObject)
{
if (!array_key_exists(“empId”,$valueObject) )
{
return false;
}

$sql = “DELETE FROM Employee WHERE (empId = “.$valueObject[empId].”) “;
$result = $this->_databaseUpdate($sql);

if ($result != 1)
{
return false;
}

$temp = new Employee();
$temp->empId = $valueObject[empId];
$temp->firstName = $valueObject[firstName];
$temp->lastName = $valueObject[lastName];
$temp->phone = $valueObject[phone];
$temp->email = $valueObject[email];
$temp->title = $valueObject[title];

return $temp;
}

function removeAll()
{
$sql = “DELETE FROM Employee”;
$result = $this->_databaseUpdate($sql);

return true;
}

function _databaseUpdate($sql)
{
$result = $this->conn->_execute($sql);

return $result;
}

function _singleQuery($sql)
{
$valueObject = new Employee();
$result = $this->conn->_execute($sql);

if ($row = $this->conn->_nextRow($result))
{
$valueObject->empId = $row[0];
$valueObject->firstName = $row[1];
$valueObject->lastName = $row[2];
$valueObject->phone = $row[3];
$valueObject->email = $row[4];
$valueObject->title = $row[5];
}
else
{
return false;
}
return $valueObject;
}

function _listQuery($sql)
{
$searchResults = array();
$result = $this->conn->_execute($sql);

while ($row = $this->conn->_nextRow($result))
{
$temp = new Employee();
$temp->empId = $row[0];
$temp->firstName = $row[1];
$temp->lastName = $row[2];
$temp->phone = $row[3];
$temp->email = $row[4];
$temp->title = $row[5];
array_push($searchResults, $temp);
}

return $searchResults;
}
}

?>

Remote Objects

services-config.xml

Open your text editor and create a new file. Cut-n-paste the xml code below into your new file. Save the file as services-config.xml under your [PROJECT_HOME]/config/flex/ directory.

<?xml version=”1.0″ encoding=”UTF-8″?>
<services-config>

<services>
<service-include file-path=”remoting-config.xml” />
</services>

<channels>
<channel-definition id=”my-amfphp” class=”mx.messaging.channels.AMFChannel”>
<endpoint uri=”http://{server.name}:{server.port}/{context.root}/amfphp/gateway.php” class=”flex.messaging.endpoints.AMFEndpoint”/>
</channel-definition>
</channels>

</services-config>

remoting-config.xml

Open your text editor and create a new file. Cut-n-paste the xml code below into your new file. Save the file as remoting-config.xml under your [PROJECT_HOME]/config/flex/ directory.

<?xml version=”1.0″ encoding=”utf-8″ ?>
<service id=”amfphp-flashremoting-service” class=”flex.messaging.services.RemotingService” messageTypes=”flex.messaging.messages.RemotingMessage”>

<default-channels>
<channel ref=”my-amfphp”/>
</default-channels>

<destination id=”empService”>
<properties>
<source>*</source>
</properties>
</destination>

</service>

Flex

Our actionscript classes will be using the same package style naming as our php classes. Create the following directory structure under your [PROJECT_HOME]/src/flex/ directory.

  • org
    • amfphp
      • tutorials

Employee.as

Open your text editor and create a new file. Cut-n-paste the actionscript code below into your new file. Save the file as Employee.as under your [PROJECT_HOME]/src/flex/org/amfphp/tutorials/ directory.

package org.amfphp.tutorials
{
[RemoteClass(alias=”org.amfphp.tutorials.Employee”)]
[Bindable]
public class Employee
{
public var empId:int;
public var firstName:String;
public var lastName:String;
public var phone:String;
public var email:String;
public var title:String;
}
}

Main.mxml

Open your text editor and create a new file. Cut-n-paste the mxml code below into your new file. Save the file as Main.mxml under your [PROJECT_HOME]/src/flex directory.

<mx:Application xmlns:mx=”http://www.adobe.com/2006/mxml” backgroundColor=”#FFFFFF”>
<mx:Script>
<![CDATA[
import mx.utils.ArrayUtil;
import org.amfphp.tutorials.Employee;
import mx.collections.ArrayCollection;
import mx.rpc.events.ResultEvent;
import mx.controls.Alert;
import mx.rpc.events.FaultEvent;

[Bindable]
private var dp:ArrayCollection;

[Bindable]
private var emp:Employee = new Employee();
private var index:Number;
private var token:Object;

private function faultHandler(fault:FaultEvent):void
{
Alert.show(fault.fault.faultString + “\n” + fault.fault.faultDetail, fault.fault.faultCode.toString());
}

private function resultHandler(event:ResultEvent):void
{
dp = new ArrayCollection( ArrayUtil.toArray(event.result) );
}

private function changeHandler(event:Event):void
{
emp = Employee(DataGrid(event.target).selectedItem);
}

private function saveHandler(event:ResultEvent):void
{
index = Number(event.token.index);
if( index > -1 )
{
dp.setItemAt(event.result,index);
}
else
{
dp.addItem(event.result);
}
}

private function removeHandler(event:ResultEvent):void
{
index = Number(event.token.index);
if( index > -1 )
{
dp.removeItemAt(index);
}
}

private function remove():void
{
token = empRO.remove.send(emp);
token.index = dp.getItemIndex(emp);
emp = new Employee();
}

private function cancel():void
{
emp = new Employee();
}

private function save():void
{
var newEmp:Employee = new Employee();
newEmp.empId = emp.empId;
newEmp.firstName = employee_first_name.text;
newEmp.lastName = employee_last_name.text;
newEmp.phone = employee_phone.text;
newEmp.email = employee_email.text;
newEmp.title = employee_title.text;
token = empRO.save.send(newEmp);
token.index = dp.getItemIndex(emp);
emp = new Employee();
}
]]>
</mx:Script>

<mx:RemoteObject id=”empRO” destination=”empService” source=”org.amfphp.tutorials.EmployeeDao” fault=”faultHandler(event)” showBusyCursor=”true”>
<mx:method name=”loadAll” result=”resultHandler(event)”/>
<mx:method name=”save” result=”saveHandler(event)”/>
<mx:method name=”remove” result=”removeHandler(event)” />
</mx:RemoteObject>

<mx:DataGrid width=”345″ id=”employee_list” dataProvider=”{dp}” change=”changeHandler(event)”>
<mx:columns>
<mx:DataGridColumn headerText=”Last name” dataField=”lastName”/>
<mx:DataGridColumn headerText=”First name” dataField=”firstName”/>
<mx:DataGridColumn headerText=”Telephone” dataField=”phone”/>
<mx:DataGridColumn headerText=”Email” dataField=”email”/>
<mx:DataGridColumn headerText=”Title” dataField=”title”/>
</mx:columns>
</mx:DataGrid>

<mx:Button label=”Get Employee List” click=”empRO.loadAll.send();”/>

<mx:Form width=”345″ height=”200″>
<mx:FormHeading label=”Selected Employee” />
<mx:FormItem label=”First Name”>
<mx:TextInput id=”employee_first_name” text=”{emp.firstName}” />
</mx:FormItem>
<mx:FormItem label=”Last Name”>
<mx:TextInput id=”employee_last_name” text=”{emp.lastName}”/>
</mx:FormItem>
<mx:FormItem label=”Telephone”>
<mx:TextInput id=”employee_phone” text=”{emp.phone}” />
</mx:FormItem>
<mx:FormItem label=”Email”>
<mx:TextInput id=”employee_email” text=”{emp.email}”/>
</mx:FormItem>
<mx:FormItem label=”Title”>
<mx:TextInput id=”employee_title” text=”{emp.title}”/>
</mx:FormItem>
</mx:Form>

<mx:HBox>
<mx:Button id=”btnSave” click=”save();” label=”Save” enabled=”{dp != null}” />
<mx:Button id=”btnDelete” click=”remove();” label=”Delete” enabled=”{employee_list.selectedIndex != -1}”/>
<mx:Button id=”btnCancel” click=”cancel();” label=”Cancel”/>
</mx:HBox>

</mx:Application>

Apache Ant

Creating your build file

Open your text editor and create a new file. Add the line below to the very beginning of the file. This line will identify this file as an XML file. It is very important that no text or whitespace preceed this line. Now save this file as build.xml under your [PROJECT_HOME] directory.

<?xml version=”1.0″ encoding=”utf-8″?>

Next, we define our project using Ant’s project tag. Using the tag’s name and basedir attributes we specify a project name and base directory. Setting the basedir attribute to a period indcates that it will use the current directory. The remaining Ant tags we will be defining should be placed inside this project tag.

<project name=”My Flex App” basedir=”.”>

</project>

To use the Flex Ant Task library we must define a task definition. We do this by adding the following task definition tag immediately after are opening project tag.

<taskdef resource=”flexTasks.tasks”/>

After the task definition tag we define some Ant properties that we will be using in our build file. The first and most important property is the FLEX_HOME property. The FLEX_HOME property is required by the Flex Ant Task to locate the Flex compilers. Create a FLEX_HOME property and set its location attribute to the [FLEX_SDK] directory (example: C:\Flex). Create a app.name property and set its value attribute to any name you like. We will refer to this name as [APP_NAME]. The app.name property is used as the zip file name when you distribute your project and it is also passed to the mxmlc compiler as the context-root name. The context-root is used when referencing your Flex application on the URL so it is important that your app.name property not contain any special characters or spaces. We will refer to your server as [SERVER_NAME].

http://[SERVER_NAME]/[APP_NAME]

The remaining properties defined are used to provide short-cuts to directories in the [PROJECT_HOME] directory.

<property name=”FLEX_HOME”   location=”[FLEX_SDK]”/>
<property name=”app.name”    value=”[APP_NAME]”/>
<property name=”build.home”  location=”${basedir}/build”/>
<property name=”src.home”    location=”${basedir}/src”/>
<property name=”web.home”    location=”${basedir}/web”/>
<property name=”lib.home”    location=”${basedir}/lib”/>
<property name=”config.home” location=”${basedir}/config”/>
<property name=”dist.home”   location=”${basedir}/dist”/>
<property name=”docs.home”   location=”${basedir}/docs”/>

Now we define our first target. The clean target will be used to clean-up between builds and distributions.

<target name=”clean”>
<delete includeemptydirs=”true”>
<fileset dir=”${build.home}” includes=”**/*”/>
<fileset dir=”${dist.home}” includes=”**/*”/>
</delete>
</target>

The prepare target will prepare the build directory for distribution. It begins by creating the build/${app.name} directory if it does not already exist. It then copies our php source and web directories over to the build/${app.name} directory.

<target name=”prepare”>
<mkdir dir=”${build.home}/${app.name}”/>
<copy todir=”${build.home}/${app.name}”>
<fileset dir=”${src.home}/php”/>
<fileset dir=”${web.home}”/>
</copy>
</target>

The compile target will compile our Main.mxml file using the Flex Ant Task mxmlc tag. The mxmlc tags contains several attributes and subtags that control how Flex will complie our applicaton.

  • The file attribute specifies the source file that the Flex compiler will use when compiling our application. Set this attribute to ${src.home}/flex/Main.mxml.
  • The output attribute specifies the location and name of our resulting swf file. Set this attribute to ${build.home}/${app.name}/Main.swf .
  • The context-root attribute specifies the context-root our Flex application will use. Set this attribute to ${app.name} so that it uses the ${app.name} property.
  • The services attribute specifies the location of our services-config.xml file. Set this attribute to ${config.home}/flex/services-config.xml.
  • The load-config subtag specifies the location of the flex-config.xml file using the filename attribute. Set the filename attribute to ${FLEX_HOME}/frameworks/flex-config.xml. This is the default Flex complier configuration file that is distributed with the Flex 2 SDK.
  • The source-path subtag specifies the location of the source files using the path-element attribute. We declare two source-path subtags. Set the path-element on the first to ${FLEX_HOME}/frameworks to include the Flex 2 SDK in the source path. Set the path-element on the second to ${src.home}/flex to include our project’s Actionscript source code.
  • The subtag specifies the location of SWC library files using the . The library-path subtag contains several include subtags which are used to specify other files and directories to include. Set the dir attribute of the library-path subtag to ${FLEX_HOME}/frameworks. Set the name attribute of the first include subtag to include the Flex 2 SDK libraries. Set the name attribute of the second include subtag to include the Flex 2 SDK bundles. Set the name attribute of the third include subtag to include our project’s Actionscript libraries.
  • The default-size subtag specifies our Flex applications default size. Set the width attribute to 500 and the height attribute to 600.

<target name=”compile” depends=”prepare”>
<mxmlc
file=”${src.home}/flex/Main.mxml”
output=”${build.home}/${app.name}/Main.swf”
actionscript-file-encoding=”UTF-8″
context-root=”${app.name}”
services=”${config.home}/flex/services-config.xml”>

<!– Get default compiler options. –>
<load-config filename=”${FLEX_HOME}/frameworks/flex-config.xml”/>

<!– List of path elements that form the roots of ActionScript class hierarchies. –>
<source-path path-element=”${FLEX_HOME}/frameworks”/>
<source-path path-element=”${src.home}/flex”/>

<!– List of SWC files or directories that contain SWC files. –>
<compiler.library-path dir=”${FLEX_HOME}/frameworks” append=”true”>
<include name=”libs” />
<include name=”../bundles/{locale}” />
<include name=”${lib.home}/flex/” />
</compiler.library-path>

<!– Set size of output SWF file. –>
<default-size width=”500″ height=”600″ />
</mxmlc>
</target>

The distribute target will create a zip file of our project for easy distribution.

<target name=”dist” depends=”compile”>
<zip destfile=”${dist.home}/${app.name}.zip”
basedir=”${build.home}”
update=”true”/>
</target>

The deploy target is optional. I provided it to make project deployment even easier for those using remote servers. If you want to use the FTP task ant provides you must have commons-net-1.4.1.jar and jakarta-oro-2.0.8.jar in the [ANT_HOME]/lib directory. Replace the server attribute with your server. The app.name property in the remotedir attribute must exist prior to running the deploy task. Do not remove the app.name property from the remotedir attribute because your Flex application was compiled using app.name as the context-root.

<target name=”deploy” depends=”compile”>
<ftp server=”[SERVER_NAME]”
remotedir=”/home/username/www/${app.name}”
userid=”username”
password=”password”
depends=”yes”>
<fileset dir=”${build.home}/${app.name}”/>
</ftp>
</target>

Build,Deploy and Distribute your Project

Run the following commands from your [PROJECT_HOME] directory. Windows users can use the command-prompt. Linux and Mac users can use the terminal.

To compile your project run the compile task. When the compile task has finished executing you will have a directory in your [PROJECT_HOME]/build directory with the name you specified in the app.name property. Copy or ftp the app.name directory to your web server’s document root directory.

ant compile

To deploy your project run the deploy task. This task will ftp the contents of the [PROJECT_HOME]/build directory to your web server’s document root directory.

ant deploy

To distribute your project run the dist task. This task will zip the contents of the [PROJECT_HOME]/build directory. The zip file name will be the one you specified in the app.name property.

ant dist

Running your Application

Testing your PHP services

Before running our Flex application we need to test our php services to ensure that are functioning correctly. We can test our php services using the Amfphp services browser. Substitute <context-root> with the value you used for the app.name propery.

http://[SERVER_NAME]/[APP_NAME]/amfphp/browser/

Run your Application

http://[SERVER_NAME]/[APP_NAME]/Main.swf

Comments

comments