Quick poll on debugging and tooling for Flex and PHP projects

As you know, one of my focuses as a Platform evangelist is Flex and PHP integration. Thus you can imagine I spend quite a lot of time doing Flex and PHP projects or research around these technologies. Lately I’ve been working on workflows for PHP and Flex (tooling, debugging, libraries) in light of the new tools (or new versions) we have been developing at Adobe. While some of these findings will see the light as articles, others are more intended as suggestions or feature request for upcoming versions of Flash Builder.

I’m really curious to find out:

Please take some time and drop a comment with your thoughts around these questions.

And because I understand it takes some time to answer these questions, I want to give away three Flash Builder 4 licenses. I will choose randomly three lucky people from those who take the survey. The winners will be announced at the end of March, however you’ll get the licenses once we release Flash Builder 4 (now it is in Beta 2). Make sure you fill in your real name and a valid email address.

Webinar: using PHP and Flash for developing Rich Internet Applications

On December 2nd, together with Roy Ganor from Zend, I will host an e-seminar about PHP and the Flash Platform. We will show you how you can create a Rich Internet Application using the Flex framework, Illustrator, Flash Catalyst, and Flash Builder 4. Then we will show you how to connect the Flex application to a PHP backend and how easy is to debug the PHP and Flex code using Zend Studio 7.1 and Flash Builder 4.

You can register for free here. The webinar will start at 6:00 PM Central European Time (9:00AM Pacific Standard Time) on December 2nd.

LATER UPDATE:

You can download the slides from here, and next week the recording will be available you can watch the recording over here (you need to have a Zend account).

PHP and Flex Webinars

Last week I visited Zend headquarters, and I had an interesting talk over there. One effect of this meeting is this: we will start to do webinars together with Zend.

If you want to find more about Zend Studio and other products related to PHP from Zend, or learn more about the integration between the Flash Platform and PHP (Flash Catalyst, Flash Builder, Flex framework) you shouldn’t miss this opportunity. I know that webinars don’t offer the same experience as in-person events. On the other hand, you can attend them from the comfort of your own home, there is no traveling involved and no need to convince your boss to let you attend the event.

a2a1

We haven’t set the first event date yet, but it should be in the first week of December, and probably it will be in the evening (Central European Time).

Keep an eye on my blog for the exact date and time.

Flex for PHP developers article

Finally, I managed to finish an article that introduces Flex to PHP developers comparing features from both worlds (when it makes sense). I’ve been working on this subject from the beginning of this year. Thus I am so happy that I feel I should go out and smoke a fine Cuban cigar :)

While working on this article I realized two things:

  1. Writing books it is not easy. My work doesn’t compare with a book at all, but still the amount of work was huge. Now, I have an idea about what it means to work on a book.
  2. Learning a client-side technology coming from the server-side world will never be an easy and straightforward task. You have to adapt to a new world and adopt a different mindset. But then again, with big efforts usually come big rewards! And from my experience, when I learn something new I can always apply some of this knowledge to the concepts I’ve already known.

Enjoy the article and let me know what do you think!

Working in Flash Builder 4 with Flex and PHP

On June 1st we launched the public beta for Flash Builder 4, the newest iteration of our Eclipse-based IDE for RIAs, formerly known as Flex Builder 3. While you can find both evolutionary and revolutionary features in it, in this article I will touch on the data features and Flex – PHP integration features of Flash Builder 4.

In this article, I will show you how can you use a PHP class that manages a MySQL table (offering CRUD operations) in a Flex project. I will build the Flex project and I rely on Flash Builder 4 to set up the Zend Framework for me(because I want to use remoting as a way to talk with the PHP server). After this I will use the new wizard from Flash Builder 4 that introspects a PHP class and creates the ActionScript code to consume that PHP class.

In the end I should be able to have a simple Flex app that lets me see the data records, edit them, and add a new item. For some of the features I’ll have to write code, for others Flash Builder will generate the code. All in all, I think the application can be created in less than 30 minutes.

PHP code

I have two PHP classes that I want to reuse with my Flex application. One class is a data object that acts asa wrapper for one row from my database (MySQL). Here is the structure of the table:

authors
——————————
id_aut – primary key
fname_aut string
lname_aut string

The PHP data object, called VOAuthor, looks like this:

   1: class VOAuthor {

   2:    

   3:     public $id_aut;

   4:     public $fname_aut;

   5:     public $lname_aut;

   6: }

 

The second class, called Authors, is the class that manages the table, offering CRUD operations. The code is very straight forward, I don’t use any database abstraction layer. I have three methods one each for reading, deleting, and updating/inserting a record. I use the data object, VOAuthor, within these methods. This is the code:

   1: require_once 'VOAuthor.php';

   2:  

   3:  

   4: //connection info

   5: define("DATABASE_SERVER", "localhost");

   6: define("DATABASE_USERNAME", "mihai");

   7: define("DATABASE_PASSWORD", "mihai");

   8: define("DATABASE_NAME", "flex360");

   9:  

  10: //$o = new MyService();

  11: //print_r($o->getData());

  12:  

  13: class Authors {

  14:     

  15:     /**

  16:      * Retrieve all the records from the table

  17:      * @return an array of VOAuthor

  18:      */

  19:     public function getData() {

  20:         //connect to the database.

  21:         //we could have used an abstracting layer for connecting to the database.

  22:         //for the sake of simplicity, I choose not to.

  23:         $mysql = mysql_connect(DATABASE_SERVER, DATABASE_USERNAME, DATABASE_PASSWORD);

  24:         mysql_select_db(DATABASE_NAME);

  25:         //retrieve all rows

  26:         $query = "SELECT id_aut, fname_aut, lname_aut FROM authors_aut ORDER BY fname_aut";

  27:         $result = mysql_query($query);

  28:         //throw (new Zend_Amf_Exception('error', 11));

  29:         $ret = array();

  30:         while ($row = mysql_fetch_object($result, "VOAuthor")) {

  31:             $ret[] = $row;

  32:         }

  33:         mysql_free_result($result); 

  34:         return $ret;

  35:     }

  36:         /**

  37:      * Update one item in the table

  38:      * @param VOAuthor to be updated 

  39:      * @return NULL

  40:      */

  41:     public function saveData($author) {

  42:         if ($author == NULL)

  43:             return NULL;

  44:         //logMe($author);

  45:         //connect to the database.

  46:         $mysql = mysql_connect(DATABASE_SERVER, DATABASE_USERNAME, DATABASE_PASSWORD);

  47:         mysql_select_db(DATABASE_NAME);

  48:         if ($author->id_aut > 0) {

  49:             //save changes

  50:             $query = "UPDATE authors_aut SET fname_aut='".$author->fname_aut."', lname_aut='".$author->lname_aut."' WHERE id_aut=".  $author->id_aut;

  51:         } else {

  52:             //add new record

  53:             $query = "INSERT INTO authors_aut (fname_aut, lname_aut) VALUES ('".$author->fname_aut."', '".$author->lname_aut."')";

  54:         }

  55:         $result = mysql_query($query);

  56:         return NULL;

  57:     }

  58:     

  59:     public function deleteData($author) {

  60:         if ($author == NULL)

  61:             return NULL;

  62:         

  63:         //connect to the database.

  64:         $mysql = mysql_connect(DATABASE_SERVER, DATABASE_USERNAME, DATABASE_PASSWORD);

  65:         mysql_select_db(DATABASE_NAME);

  66:         //add new record

  67:         $query = "DELETE FROM authors_aut WHERE id_aut = ".$author->id_aut;

  68:         $result = mysql_query($query);

  69:         return NULL;

  70:     }

  71:     

  72: }

These two scripts sit inside of a folder called “remoting” in the root of the Apache web server document root folder.

Creating the Flex project and the UI of the app

I have the PHP files in place; now it’s time to create the Flex application. First, I need to create a new Flex project. Having opened the Flash Builder 4, I use the “New Flex Project” wizard to create a new Flex project called FlexPHPAMF. Make sure that in the wizard you choose PHP as the server technology, and you give the correct information for the document root of the web server (absolute path and URL).

Now let’s create the UI of the application. To do this, I chose to go in design mode where I added two buttons and a data grid to the stage. One button is labeled “Get Data” and the other one “Save Data”.

fb_0

Using the data service panel

Finally, I am ready to use the new data wizards.At the bottom of Flash Builder, there is a view called “Data/Services”. Click on “Connect to Data/Service” link. A wizard starts, and you can select the type of the data service.

fb_php_1

Because I want to use the PHP classes I have, I choose PHP and I click “Next”.

fb_php_2 

On this page, you can select to either reuse an existing service from the server, or to let the wizard create a stub for you. Because I have the service, I want to use the Import PHP class option. Flash Builder introspects the service in order to detect the supported operations (in this case, because I am using remoting, it detects the public methods and the return type). But to do so, it needs to install the Zend Framework to your web server.

What is very cool is that you don’t have to worry about the steps needed to install Zend Framework. Flash Builder will do it for you automatically.

fb_php_3

After the Zend Framework is installed, on the last page of the wizard you’ll see the available methods. Click Finish.

fb_php_4

Now, in the Data/Services view you should have a tree, with the root node named Authors. This is the name of the PHP class that you want to use in Flex. Under this node you have Data Types, and the three methods: deleteData(), getData(), saveData().

fb_php_5 

At the same time if you take a look at the project structure in the Package explorer, you’ll notice that new packages and files were created.

fb_php_6

There is a new package called services.authors, and here you’ll find the service class that you’ll use to connect to the PHP class (Authors.as). This class actually extends a base class that implements all the logic using RemoteObject. The reason for this approach is that when you need to do your customization, you’ll do it in the Authors class. If you decide to use Flash Builder to regenerate the code for the same service (maybe as a result of a change on the server side), this operation will not over write your custom code.

The same goes for the data object (or value object). Flash Builder generated the ActionScript value object to match the PHP class VOAuthor. But again, you might want to do some customizations, and you can do it by touching VOAuthor that extends _Super_VOAuthor. All the generated code stays in the _Super_VOAuthor.

And finally, you’ll find a folder called services that gives you quick access to the Authors.php class.

It is time to fill the data grid with the data from the server. To do so, first you need to make sure that the return type for the getData() operation is an array of VOAuthor objects. If you right click on the name of the operation in the Data/Services view and choose Configure Return Type, you have a way to specify the array of VOAuthor:

fb_php_7

To bind the data grid to the result of the getData() operation you have to go into design mode, and then drag and drop the getData() (from the Data/Services view) to the data grid.

If you switch to code view, you’ll see that some ActionScript code was added to the flexPHPAMF.mxml. What you want to do is to grab the line from the function and create a new function that will be called when you click the “Get Data” button and paste the code inside of it. My code looks like this:

   1: protected function getData():void

   2: {

   3:    getDataResult.token = authors.getData();

   4: }

   5: [...]

   6:  

   7: <s:Button x="43" y="220" label="Get Data" click="getData()"/>

The wizard altered the data grid itself, you have the name of the table fields as labels, and the data provider of the data grid is set to the lastResult of the getDataResult object (this object was added to your code by the wizard, in order to help you manage the results of the authors service).

If you run the application and press the “Get Data” button, you should see the data.

Adding editing capabilities

The final part of this tutorial is adding the editing capabilities. I want to be able to edit an existing record using the data grid itself, and I want to have a form and a new button. First go back into design mode, drag and drop an new button on the stage, and change the label to “New”. Then, you’ll use another wizard to generate the form for editing. To do so, you select from the Data/Services view the Data Types node, expand it, and select the VOAuthor node. Right click it and select Generate Form. In the wizard choose “Make form editable” and click Next.

fb_php_8

In the second page of the wizard deselect the “id_aut” property. This value will be auto-inserted by the database itself. Click Finish.

fb_php_9

After the form is generated, you want to grab the form and position it maybe below the buttons. Also, you may want to change the labels. Finally add a new button in the form and label it “Add”.

Switch back to code view. Now, you need to make some changes. First of all (I think it is a bug), the text fields are not editable (they are Text instead of TextInput). So change the type to TextInput to make them editable. Next, give an ID to the Form itself. I want to have the form invisible when the application is loaded, and only make it visible when the users clicks “New”. For that you set the visible property on the form to false, and you add some code to the New button. Here is my code:

   1: <s:Button x="224" y="220" label="New" click="myForm.visible = true"/>

   2: <mx:Form x="46" y="277" id="myForm" visible="false">

   3:         <mx:FormItem label="First Name:">

   4:             <mx:TextInput id="fname" text=""/>

   5:         </mx:FormItem>

   6:         <mx:FormItem label="Last Name:">

   7:             <mx:TextInput id="lname" text=""/>

   8:         </mx:FormItem>

   9:         <mx:FormItem>

  10:             <s:Button label="Add"/>

  11:         </mx:FormItem>

  12: </mx:Form>

And if you look in the code, you’ll notice a new object was created:

   1: <authors:VOAuthor id="vOAuthor"/>

You’ll use this instance of the value object VOAuthor to send the new item to the server (remember, the PHP saveData() method expects a single argument of type VOAuthor).

To do so, you need to create a binding between the two text fields from the form and the properties fname_aut and lname_aut of the VOAuthor.

   1: <fx:Binding destination="vOAuthor.fname_aut" source="fname.text"/>

   2: <fx:Binding destination="vOAuthor.lname_aut" source="lname.text"/>

The last missing piece is the actual call to the saveData() method. This is very simple;on the click event of the “Save” button you add the call to the authors object getData() method, passing the local instance of VOAuthor (vOAuthor) as the argument:

   1: <s:Button label="Add" click="authors.saveData(vOAuthor)"/>

You can add a new record now. What about editing an existing one? For this we will need a little bit more code. First, you want to register a listener on the data grid in order to know when something was edited (if you set the property editable to true on the data grid, then you can edit information inline). The event you are looking for is itemEditEnd:

   1: <mx:DataGrid x="44" y="37" id="dataGrid" dataProvider="{getDataResult.lastResult}" 

   2:         itemEditEnd="dataEdited(event)" editable="true">

Now,let’s define the listener, dataEdited(). Add the following code in the script section of the page:

   1: private function dataEdited(event: DataGridEvent):void 

   2: {

   3:      vOAuthor = event.itemRenderer.data as VOAuthor;

   4: }

This code uses the instance of the value object VOAuthor to store the data used by the edited row.

The last two things you have to do is to create a function called saveData() that is called when the user clicks the “Save Data” button, and to add initialization code for the vOAuthor object when the users clicks the “New” button (this way you avoid updating an item instead of adding a new one if you first edit a row and then add one):

   1: private function saveData():void {

   2:    if (vOAuthor != null)

   3:       authors.saveData(vOAuthor);

   4: }

   1: <s:Button x="224" y="220" label="New" 

   2:     click="myForm.visible = true; vOAuthor = new VOAuthor()"/>

The final code of the MXML file should look like this:

   1: <?xml version="1.0" encoding="utf-8"?>

   2: <s:Application xmlns:fx="http://ns.adobe.com/mxml/2009" xmlns:s="library://ns.adobe.com/flex/spark" xmlns:mx="library://ns.adobe.com/flex/halo" minWidth="1024" minHeight="768" xmlns:authors="services.authors.*">

   3:     <fx:Script>

   4:         <![CDATA[

   5:             import mx.events.DataGridEvent;

   6:             import services.authors.VOAuthor;

   7:             import mx.events.FlexEvent;

   8:             import mx.controls.Alert;

   9:             

  10:             protected function getData():void

  11:             {

  12:                 getDataResult.token = authors.getData();

  13:             }

  14:             

  15:             private function dataEdited(event: DataGridEvent):void 

  16:             {

  17:                 vOAuthor = event.itemRenderer.data as VOAuthor;

  18:             }

  19:             

  20:             private function saveData():void {

  21:                 if (vOAuthor != null)

  22:                     authors.saveData(vOAuthor);

  23:             }

  24:  

  25:         ]]>

  26:     </fx:Script>

  27:     <fx:Binding destination="vOAuthor.fname_aut" source="fname.text"/>

  28:     <fx:Binding destination="vOAuthor.lname_aut" source="lname.text"/>

  29:     <fx:Declarations>

  30:         <s:CallResponder id="getDataResult"/>

  31:         <authors:Authors id="authors" destination="Authors" endpoint="http://localhost/flexPHPAMF-debug/gateway.php" 

  32:             fault="Alert.show(event.fault.faultString)" 

  33:             showBusyCursor="true" source="Authors"/>

  34:         <authors:VOAuthor id="vOAuthor"/>

  35:     </fx:Declarations>

  36:     <mx:DataGrid x="44" y="37" id="dataGrid" dataProvider="{getDataResult.lastResult}" 

  37:         itemEditEnd="dataEdited(event)" editable="true">

  38:         <mx:columns>

  39:             <mx:DataGridColumn headerText="id_aut" dataField="id_aut"/>

  40:             <mx:DataGridColumn headerText="fname_aut" dataField="fname_aut"/>

  41:             <mx:DataGridColumn headerText="lname_aut" dataField="lname_aut"/>

  42:         </mx:columns>

  43:     </mx:DataGrid>

  44:     <s:Button x="43" y="220" label="Get Data" click="getData()"/>

  45:     <s:Button x="129" y="220" label="Save Data" click="saveData()"/>

  46:     <s:Button x="224" y="220" label="New" click="myForm.visible = true; vOAuthor = new VOAuthor()"/>

  47:     

  48:     <mx:Form x="46" y="277" id="myForm" visible="false">

  49:         <mx:FormItem label="First Name:">

  50:             <mx:TextInput id="fname" text=""/>

  51:         </mx:FormItem>

  52:         <mx:FormItem label="Last Name:">

  53:             <mx:TextInput id="lname" text=""/>

  54:         </mx:FormItem>

  55:         <mx:FormItem>

  56:             <s:Button label="Add" click="authors.saveData(vOAuthor)"/>

  57:         </mx:FormItem>

  58:     </mx:Form>

  59:     

  60: </s:Application>

The delete operation is left as homework for you! Basically you want to get the selected row, grab the object data used by that row and save it into vOAuthor, and then call the authors.deleteData(vOAuthor).

Conclusions

If you are not sure what remoting and the Zend Framework are, you can read my post that explains these matters: Flex and PHP: remoting with Zend AMF. This article uses Flex Builder 3 (the previous version of Flash Builder 4).

You might be wondering about many-to-many or one-to-many associations. Well, at least for now there is no support for PHP (at least there is no wizard that handles any association and generates all the code). Still, I think I could do it much easier using these wizards than without them. Having the glue code generated for me and the Value Object created automatically, being able to have a form generated for a particular data model are all great help. You save time, you save money. Remember with any line of code you write, you might introduce a bug or more. Having more lines generated for you means less bugs :) .

If you want to take a look at my project, you can download it from here (you’ll find the PHP files and the dump of the database in the service folder from this archive). If you want to install Flash Builder 4, you can grab the beta version from here and use it.

Finally I encourage you to try this for yourself, and let me know what you think. If you find bugs or you want more features in Flash Builder 4, be sure you go to http://bugs.adobe.com and ask your friends to vote for the bug. In the end, this is a beta product. If we can improve it, why not?

Flex and PHP session in Lisbon, Portugal

This week, on April 29th, I will do a Flex and PHP session in Lisbon, Portugal. You can find more details here (time, location, talks).

After the sessions, we could have some beer or wine, and talk a little bit about Flex/Catalyst… See you there :)

How to debug Flex/AIR and PHP applications

Another question I get quite often is “How do I debug my Flex (or AIR) and PHP application?”. Usually I take some time to show a few debug methods when I am presenting Flex/AIR and PHP workflows.

There are several methods that you can use to debug Flex/AIR and PHP applications, each with its own advantages and disadvantages:

  1. Use Flex Builder and Zend debuggers.
    1. Pros: it is arguably the most elegant and efficient way; it is not intrusive
    2. Cons: you need to have Flex Builder and Zend Studio installed together; it does not work for AIR applications
  2. Use a server side logging function for PHP, and Flex Builder debugger for Flex
    1. Pros: it works great for both Flex and AIR apps; you need only Flex Builder
    2. Cons: it isn’t so straight-forward; you need to walk through the code and call the logging function until you pin-point the problem. It is very much like doing JavaScript debugging with Alert or PHP debugging with Refresh and die(var_dump($myVar)). It is intrusive because you have to add code to the PHP that is not needed in production.
  3. Use a proxy sniffer, such as Charles
    1. Pros: it understands AMF format; it is not intrusive
    2. Cons: it is shareware, and it can’t be used for AIR apps
  4. FirePHP. This is actually similar to method two above. The only difference is that you need to install a Firefox extension, and then using this extension and some PHP code you can see the output of the FirePHP logging functions directly inside of your Firebug console.
    1. Pros: it is easy to read the four different types of messages (log, info, warn, error) in the console, and it may be familiar for those who already use Firebug (such as AJAX developers)
    2. Cons: it works only in Firefox because of the dependency on Firebug; it doesn’t work for AIR, and it is intrusive (you have to add code for it)

Using Flex Builder and Zend debuggers

This is my favorite, and I believe it is the fastest way to debug your code. Although you can’t use this approach for debugging AIR projects, I think it is worth it to create a twin Flex project just to be able to use this approach.

You can read more about this method here, or you can watch my video tutorial here.

Using a server side logging function

Actually, this was my first approach for debugging AIR and PHP applications back in 2007 when I created my first AIR application. At that time, I was using XML-RPC to communicate between the client and server, and as I started to hit bugs I needed something to debug (at that time the PHP debugger wasn’t so reliable, and it was hard to make it work in the Eclipse IDE).

I still use this approach today, when I want to log some variables on the PHP side. I use this function:

   1: function logMe($var) {
   2:     $filename = dirname(__FILE__) . PATH_SEPARATOR .'__log.txt';
   3:
   4:     if (!$handle = fopen($filename, 'a')) {
   5:         echo "Cannot open file ($filename)";
   6:         return;
   7:     }
   8:
   9:     $toSave = var_export($var, true);
  10:     fwrite($handle, "[" . date("y-m-d H:i:s") . "]");
  11:     fwrite($handle, "\n");
  12:     fwrite($handle, $toSave);
  13:     fwrite($handle, "\n");
  14:     fclose($handle);
  15: }

This function uses a text file (__log.txt) to dump its single argument, together with a time stamp. I can use this function to log primitives as well as complex types. Here is an example of how the file looks after I dump some variables:

   1: [09-03-11 21:56:01]
   2: NULL
   3: [09-03-11 21:56:10]
   4: VOAuthor::__set_state(array(
   5:    'id_aut' => 2,
   6:    'fname_aut' => 'William11',
   7:    'lname_aut' => 'Shakespeare',
   8: ))
   9: [09-03-28 15:18:37]
  10: array (
  11:   0 =>
  12:   VOAuthor::__set_state(array(
  13:      'id_aut' => '1',
  14:      'fname_aut' => 'Dante',
  15:      'lname_aut' => 'Alighierie',
  16:   )),
  17:   1 =>
  18:   VOAuthor::__set_state(array(
  19:      'id_aut' => '4',
  20:      'fname_aut' => 'Niccolo',
  21:      'lname_aut' => 'Machiavelli',
  22:   )),
  23:   2 =>
  24:   VOAuthor::__set_state(array(
  25:      'id_aut' => '3',
  26:      'fname_aut' => 'Umberto',
  27:      'lname_aut' => 'Eco',
  28:   )),
  29:   3 =>
  30:   VOAuthor::__set_state(array(
  31:      'id_aut' => '2',
  32:      'fname_aut' => 'William',
  33:      'lname_aut' => 'Shakespear',
  34:   )),
  35: )

This method is very simple, however it is intrusive since you will have calls to the function in your code, and you don’t want these calls to end up in your production code/server. Second, debugging in this way is not so fast, especially if you don’t have a good idea about where in your code base the problem could be.

Using Charles to debug

You can use Charles (which is a  Proxy Sniffer) to see what messages are exchanged between the Flex client and the server. This method doesn’t work for AIR applications. Charles knows how to decode AMF messages, thus if you are using Remoting, then you can still see the communication.

After you start Charles, be sure you configure your browser to use Charles as a proxy:

firefox_config

Next, start your Flex application and make a call to the server. In Charles, you can see the request, what method was called from Flex, and the response sent back by the server. In the screen capture below, you can see the information for a remote call on a PHP class using Zend AMF.

charles

This method is great for quickly pin-pointing what Flex sends to a PHP script. If your problem is not solved here, then you have to switch back to method 1 or 2. Or you can alter your code to output the variables from your script, and in Charles you can see the dump (for the example below I just added to my script something like die(var_dump($result));:

charles2

Using FirePHP to debug

You can find information about how to use FirePHP here and here.

firePHP

If you are using Zend Framework, then it is even easier beacause you don’t have to download and install the FirePHPCore library. You need only Firefox, Firebug, and FirePHP extensions installed. Again, you cannot use this with AIR projects. Zend Framework lets you enable and disable the logging very easily:

   1: $writer->setEnabled(false);
   2: $profiler->setEnabled(false);

This means that you don’t have to remove the debug calls when you go to production.

Conclusion

There are many ways to debug Flex/AIR and PHP applications. Depending on the nature of your project, and the nature of your problem, you can use more than one. I hope this post will help you, especially when you take the first steps in Flex and remoting.

Flex and remoting with PHP, which library is the best: Zend AMF, AMFPHP, WebORB for PHP, or SabreAMF?

You’ve decided to go for remoting as the way to communicate with your PHP server, and you’re wondering which of these four libraries is the best: Zend AMF, AMFPHP, WebORB for PHP, and SabreAMF?

Well, it will probably surprise you, but I don’t think one is necessarily better than another. There are some small differences, but for me none is so big that I can declare one as the winner. It depends on your specific needs, and the context of your current workflow with PHP apps. And I will explain in a minute why that is.

Performance

For me performance is one of the top criteria that I would use to choose one over another. All these libraries are written in PHP, and although I didn’t conduct an extensive test, I don’t think consider one faster than another. If you wrote a PHP module in C, to add some new API to PHP for remoting, then you could have something much faster. For example, AmFast (a remoting library for Python written in C) is up to 18x faster than PyAMF written in Python.

As the situation stands today, I don’t think we have a winner here. If someone performs a test and finds that I am wrong, please let me know!

Documentation and ease of use

Here, the alternatives are not so level. While implementing examples with each one of them, I must say AMFPHP was the easiest to use. The documentation is OK, since it has been around for some time you can find articles explaining how to use it; and it has a Services Browser, a Flex application that lets you test your PHP services. This can be very handy when you are debugging a project. There is a folder where you can drop your service and it will be automatically picked up, and a folder for your VOs. These two folders can be configured.

WebORB has good documentation too, and it comes with examples. There is also a Flex application that provides easy access to some quick start articles, a browser service, a code generator, and a way to configure the destinations. I didn’t find this code generator particularly helpful. For example, I expected to generate an ActionScript class out of the PHP model class, instead it generated a wrapper class for the remote object, and it tried to generate the ActionScript model. I say it tried to generate, because it generates a strange data model, which basically has a property for each method on your PHP remote class of Object type. It has a special folder where you have to add your services and VOs.

The next one is Zend AMF. It has decent documentation and is the newest on the market. The guy who developed it, actually is the same guy who created AMFPHP, so it must be good. However it doesn’t have a service browser, so it is harder to debug the PHP code compared to the previous two. But it has a big advantage: it is part of Zend Framework. So if you are already using Zend Framework, or you plan to use features from it, then this is the choice for you. You need to create a bootstrap file to start the AMF server, to add the services (either one by one, or by folder), and to set the correspondence between the ActionScript and PHP data model classes.

The last one is SabreAMF. The documentation is not so good and there are not so many articles, but if you know what remoting is, then you can find your way. It is little bit strange to use, as it doesn’t impose any particular structure. You have to create a boot strap file, where you configure the corresponding data model classes.  And you need to create a callback function that will do the lookup for your service classes. Actually, if you change the callback function a little bit, you can even call your defined functions, not only object’s methods.

Footprint

The smallest ones are SabreAMF and AMFPHP. These two just do remoting. Zend AMF is part of Zend Framework, an application framework for PHP (so you have a lot of functionality, and a lot of code). WebORB for PHP does remoting, plus messaging and data management. So again, there is more code in it.

Future

It seems all four libraries are maintained, although two of them have companies behind them: Zend AMF with Zend Technologies, and WebORB for PHP with Midnight Coders. The other two have one smart developer behind them. The thing is that it doesn’t really matter who is behind it, because it is really easy to switch from one framework to another one. Thus, if in the future you find that your PHP library for remoting is no longer maintained, it is not the end of the world; you can switch to another one.

Mirror, mirror on the wall, who is the most beautiful of them all?

Instead of spending too much time deciding which one to choose, just play with it or look at an example and see for yourself which one you are in tune with. If you have some special needs, for example, if you are already using Zend Framework, or using another framework, then it is even easier to choose.

As I already said, even if you eventually find that you made the wrong choice, it is not the end of the world, you can switch to another one very easily.

Happy remoting, people!

PS. If you have a favorite, do you mind posting a comment to briefly explain why you chose it? Thanks!

Flex and PHP: remoting with SabreAMF

In the past months I have written a lot about Flex and PHP remoting using AMFPHP, ZendAMF, and WebORB. However, there is another library, SabreAMF, that can be used to do remoting. This article explains how to use it.

If you don’t know what remoting is (or AMF), then it might be a good idea to read my earlier post first (look for the “What is AMF and remoting and why should you use it” section).

Installing SabreAMF

You can download the library from here, and here you can find some documentation. I will say upfront, that indeed, compared to the other three PHP libraries for remoting, SabreAMF offers less documentation.

Unzip the archive somewhere on your hard-disk. You have two alternatives for using it with your PHP code:

  1. You can add the library to your PHP include_path in your php.ini file. To do this you need to create a folder, called for example “mysabre”, and in this folder create a folder called “SabreAMF”. In this last folder copy all the files from the archive (AMF0, AMF3, examples, and so on). Next, open the php.ini file and search for the line include_path and add the absolute path to the folder mysabre. On my machine, this looks like this:
    include_path = “/frameworks/mysabre”
  2. Alternatively, you can create a folder called “SabreAMF” inside of your PHP working directory, and copy to this folder all the files and folders from the archive.

You are now ready to move to the PHP code you need for this example.

Create the Flex project

Usually, when I work with Flex and PHP projects, I prefer to use Flex Builder and Zend Studio installed together. It is possible, however, to work with Flex Builder and a PHP plugin to help you with the PHP code. Either way, you should create a Flex project that uses PHP on the server side (if you plan to use Zend Studio and Flex Builder, first create a Zend PHP Project, then use the Add Flex Nature wizard to add Flex PHP nature on the project). This way you streamline the deployment of the SWF file (the compiled result of the Flex project) to the PHP server. I chose to create a new project called “flex_sabre”.

Next, create a folder inside the PHP server root named “sabreamf_remoting”, and add this folder to the project. Choose New > Folder, and then click on the Advanced button.

Create the PHP code

The example will use a PHP class to manage one table from a MySQL server.

In the “sabreamf_remoting” folder, create three PHP files: MyService.php, VOAuthor.php, and index.php. Open the MyService.php page and paste the following code (you need to update the connection information for your specific database setup; to do this, look for the four constants at the top of the class):

   1: <?php
   2: require_once 'VOAuthor.php';
   3:
   4: //conection info
   5: define("DATABASE_SERVER", "localhost");
   6: define("DATABASE_USERNAME", "mihai");
   7: define("DATABASE_PASSWORD", "mihai");
   8: define("DATABASE_NAME", "flex360");
   9:
  10: //$o = new MyService();
  11: //print_r($o->getData());
  12:
  13: class MyService {
  14:
  15:     /**
  16:      * Retrieve all the records from the table
  17:      * @return an array of VOAuthor
  18:      */
  19:     public function getData() {
  20:         //connect to the database.
  21:         //we could have used an abstracting layer for connecting to the database.
  22:         //for the sake of simplicity, I choose not to.
  23:         $mysql = mysql_connect(DATABASE_SERVER, DATABASE_USERNAME, DATABASE_PASSWORD);
  24:         mysql_select_db(DATABASE_NAME);
  25:         //retrieve all rows
  26:         $query = "SELECT id_aut, fname_aut, lname_aut FROM authors_aut ORDER BY fname_aut";
  27:         $result = mysql_query($query);
  28:         //throw (new Zend_Amf_Exception('eroare ca asa vreau eu', 11));
  29:         $ret = array();
  30:         while ($row = mysql_fetch_object($result, "VOAuthor")) {
  31:             $ret[] = $row;
  32:         }
  33:         mysql_free_result($result);
  34:         return $ret;
  35:     }
  36:         /**
  37:      * Update one item in the table
  38:      * @param VOAuthor to be updated 
  39:      * @return NULL
  40:      */
  41:     public function saveData($author) {
  42:         if ($author == NULL)
  43:             return NULL;
  44:         //connect to the database.
  45:         $mysql = mysql_connect(DATABASE_SERVER, DATABASE_USERNAME, DATABASE_PASSWORD);
  46:         mysql_select_db(DATABASE_NAME);
  47:         if ($author->id_aut > 0) {
  48:             //save changes
  49:             $query = "UPDATE authors_aut SET fname_aut='".$author->fname_aut."', lname_aut='".$author->lname_aut."' WHERE id_aut=".  $author->id_aut;
  50:         } else {
  51:             //add new record
  52:             $query = "INSERT INTO authors_aut (fname_aut, lname_aut) VALUES ('".$author->fname_aut."', '".$author->lname_aut."')";
  53:         }
  54:         $result = mysql_query($query);
  55:         return NULL;
  56:     }
  57:
  58:     public function deleteData($author) {
  59:         if ($author == NULL)
  60:             return NULL;
  61:
  62:         //connect to the database.
  63:         $mysql = mysql_connect(DATABASE_SERVER, DATABASE_USERNAME, DATABASE_PASSWORD);
  64:         mysql_select_db(DATABASE_NAME);
  65:         //delete a record
  66:         $query = "DELETE FROM authors_aut WHERE id_aut = ".$author->id_aut;
  67:         $result = mysql_query($query);
  68:         return NULL;
  69:     }
  70:
  71:     public function setCredentials($user, $password, $charset) {
  72:         return true;
  73:     }
  74: }
  75:
  76: ?>

This is the class you will call from Flex. It has three methods: one to get all the records from the table, another to update the values for one record, and the third to delete a record.

Let’s create the code for the Value Object, the data model. This is used by the MyService class to wrap one row from the table. Thus, the method getData() returns an array of VOAuthor, and the method saveData() receives one argument: the VOAuthor of the row that was changed. Open the file VOAuthor.php and add this code:

   1: <?php
   2: class VOAuthor {
   3:     public var $id_aut;
   4:     public var $fname_aut;
   5:     public var $lname_aut;
   6: }
   7: ?>

As you can see, this class is very simple; it just provides the same members as the fields from the table. Finally let’s create the code for index.php file. This is the plumbing code that exposes the MyService class to Flex clients with the help of SabreAMF. Add the following code:

   1: <?php
   2: require_once 'SabreAMF/CallbackServer.php';
   3: require_once 'MyService.php';
   4: require_once 'VOAuthor.php';
   5:
   6: function myCallback($serviceName, $methodName, $arguments) {
   7:     // You can use the servicename to lookup and instantiate a service class
   8:     $serviceObject = new $serviceName;
   9:     if (!is_array( $arguments)) {
  10:         $arguments = array($arguments);
  11:     }
  12:     // check the php manual if you don't know what this function does
  13:     return call_user_func_array(array($serviceObject, $methodName), $arguments);
  14: }
  15:
  16: //map the AS VO to PHP VO
  17: SabreAMF_ClassMapper::registerClass('org.corlan.VOAuthor','VOAuthor');
  18:
  19:
  20: // First we'll create the server object
  21: $server = new SabreAMF_CallbackServer();
  22: // setup the callback
  23: $server->onInvokeService = 'myCallback';
  24: // parse the request and spit out the response
  25: $server->exec();
  26: ?>

And this is the part where you have to pay attention. Chances are that if you encounter problems later, the root of the problems is in this file. First of all, you use an instance of SabreAMF_CallbackServer to set up the server that knows how to handle the remoting calls from Flex. Next, you add a PHP function as the callback for the onInvokeService property of the SabreAMF_CallbackServer. This function will be called each time the PHP server receives a remote call. Next, you define the callback function. In my code, the function is called myCallback.

You might wonder why you need to do this, and why it is not done automatically for you by SabreAMF. Indeed, it would have been very easy to have a default function registered for you, so you don’t have to do this copy & paste step. However, there is a reason: you can write your own function and you can handle different calls in different ways. In other words, it is easy to customize how a specific remote call should be handled by your code. If you are not satisfied with this answer, please let me hear your opinion :D

And finally, you use the static method registerClass(‘package.remoteclass,’localclass’) on SabreAMF_ClassMapper object to tell PHP which ActionScript VO corresponds to the PHP VO.

And now, you are ready to move to the client code.

Creating the Flex application

This is very simple code. You have the MXML file with a RemoteObject to do the calls to the PHP class. Then you have a data grid to display and edit the existing records, and two buttons: one for save and one to get the data. Next there are a bunch of functions, some are listeners for various events (result event or fault event on the remote object, change item on the data grid). Here is the code:

   1: <?xml version="1.0" encoding="utf-8"?>
   2: <mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute"
   3:     creationComplete="init()" >
   4: <mx:Script>
   5:     <![CDATA[
   6:         import mx.controls.Alert;
   7:         import mx.rpc.events.FaultEvent;
   8:         import org.corlan.VOAuthor;
   9:         import mx.events.DataGridEvent;
  10:         import mx.rpc.events.ResultEvent;
  11:
  12:         private var item:VOAuthor;
  13:
  14:         private function init():void {
  15:             myRPC.getData();
  16:         }
  17:         private function getDataListener(event:ResultEvent):void {
  18:             dg.dataProvider = event.result;
  19:         }
  20:         private function editListener(event :D ataGridEvent):void {
  21:             item = event.itemRenderer.data as VOAuthor;
  22:         }
  23:         private function saveDataListener(event:ResultEvent):void {
  24:             item = null;
  25:             init();
  26:         }
  27:         private function saveData():void {
  28:             if (item == null)
  29:                 return;
  30:             myRPC.saveData(item);
  31:         }
  32:         private function faultListener(event:FaultEvent):void {
  33:             Alert.show(event.fault.faultString);
  34:         }
  35:
  36:     ]]>
  37: </mx:Script>
  38:
  39: <mx:RemoteObject id="myRPC" destination="MyService" endpoint="http://localhost/sabreamf_remoting/index.php" source="MyService" fault="faultListener(event)">
  40:     <mx:method name="getData" result="getDataListener(event)"/>
  41:     <mx:method name="saveData" result="saveDataListener(event)"/>
  42: </mx:RemoteObject>
  43: <mx:VBox>
  44:     <mx:DataGrid id="dg" editable="true" itemEditEnd="editListener(event)" />
  45:     <mx:Button label="Save" click="saveData()" />
  46:     <mx:Button label="Get Data" click="init()" />
  47: </mx:VBox>
  48:
  49: </mx:Application>

And finally, you have to create the ActionScript Value Object, org.corlan.VOAuthor:

   1: package org.corlan
   2: {
   3:     [Bindable]
   4:     [RemoteClass(alias="org.corlan.VOAuthor")]
   5:     public class VOAuthor
   6:     {
   7:         public var id_aut:int;
   8:         public var fname_aut:String;
   9:         public var lname_aut:String;
  10:     }
  11: }

With this object, you must pay a little bit of attention. Normally, the value for the alias attribute should be VOAuthor (as the PHP class doesn’t have a package name). However, if you want receive typed objects in Flex and not anonymous objects, you have to put the same value as the fully qualified class name itself (and this is the value you set up in the index.php file too).

Conclusion

That’s it. You can download the project from here. I will write an article soon that compares the four available libraries for PHP and Flex remoting.

Do you want to join the Flex Builder Pre-Release Program?

As you know, we are working hard on the next version of Flex Builder (code name Gumbo). There will be many additions that should appeal to PHP developers, and we want to hear your thoughts on these features. So, if you want to have the chance to be part of the Flex Builder pre-release program, just visit this link and fill in the survey: http://www.surveymonkey.com/s.aspx?sm=p1w3z8obcRi28iht1_2fFzbQ_3d_3d

Next Page →