Mapping Data Types from PHP to Flex with Zend AMF and Flash Builder 4

I assume this has to be out there somewhere but I couldn’t find it so hopefully this helps someone who Google’s things the same way I do. And Wade has a great tutorial for doing this when you’re just dealing with the Zend AMF code but I was looking for something that worked with Flash Buidler 4′s wizards.

While using the Flash Builder 4 data-centric design wizards to generate the service calls for a PHP class I was having trouble figuring out how to make sure that the data types I had set up in my MySQL database were being carried all the way through to my Flex application. When I retrieved data from the database in PHP and then had Flash Builder 4 inspect my class, the properties of my returned class were all strings.

If you use the data-centric design wizards to create a class based on a database, the class properties are typed correctly. If you use a class you’ve already written, all of the properties come back as strings. Luckily I chatted with our Flex/PHP guru, Mihai Corlan, and he pointed me in the direction of type casting.

Essentially you just need to add a bit of extra data when the properties are returned in the class so that Zend AMF knows what type of data to expect. So for my getAllItems() method, I iterate through the records of my database and create a PHP object for each one.

As you can see in the code below, to return as an int, just add a 0; to set it as a number, add 0.0; to set it as a date, just create a new DateTime object with the data from MySQL. As you can see from the Mapping Table you’ve got coverage for pretty much every data type.

public function getAllForests()
{
     $this->connect();
     $rs = mysql_query("select * from national_forests")
          or die ("Unable to complete query.");
 
     $national_forests = array();
 
     while( $row = mysql_fetch_assoc($rs) )
     {
          $forest = new NationalForest();
          $forest->id = $row['id']+0;
          $forest->state = $row['state'];
          $forest->area = $row['area']+0.0;
          $forest->established = new DateTime($row['established']);
          $forest->closest_city = $row['closest_city'];
          $forest->name = $row['name'];
 
          array_push($national_forests,$forest);
     }
 
     return $national_forests;
}

If there’s a better way to do this, I’d love to have it.

Flex and PHP: Authentication With an HTML Login Page

One of the first things to think about when building a Flex application is how you’re going to do authentication. Most people seem to include a username and password box in Flex and then make an HTTPService or AMF call to pass the authentication credentials. But I hate that solution. Most major browsers now have support to save usernames and passwords with autocomplete and that’s become something people expect from their logins. I think it’s also important to be able to clear the “remember me” cookie just like any other cookie as opposed to having it saved in a Flash cookie. Whenever you can provide the behavior people expect from their browser, you should.

So in this example I’m going to show how you might implement an HTML form with PHP for a Flex application. As I’m new to PHP there may be some bad practices here but I’ll update the post as I get feedback.

Creating the login page in PHP and HTML

To start, we can just use PHP’s built in session functions to create the session variables and build the login page called login.php. You start off by creating a session with session_start() and then you set some session variables to their defaults. Then you’ll show the username and password fields only if the form hasn’t been submitted and use the $_SESSION variables to store the information from the user. Finally, when everything is authenticated you use the header() function to send the user to the Flex application.

<?php
session_start();
$_SESSION['logged_in'] = false;
$_SESSION['username'] = "";
 
if(isset($_POST['is_submitted']))
{
$username = $_POST['username'];
$password = $_POST['password'];
 
// If the "Remember my username" box is checked
// then we use the setcookie function to save it.
// Otherwise, we clear it out. 
if($_POST['keep_username'] == true)
     {
          setcookie("username",$username,time()+36000);
     } else {
          setcookie("username");
     }
 
// This is where you would look up the username
// and password. Normally this would be an LDAP call
// or a query to a database. In this case, I'm just
// accepting everything.
if($username && $password)
     {
// Set our session variables.
$_SESSION['logged_in'] = true;
$_SESSION['username'] = $username;
 
// Redirect to the Flex application
          header("location:flex/index.php");
     } else
{
echo "Go back and put in a username and password.";
     }
} else {
?>
 
<form name="login" action="<?php$_SERVER['PHP_SELF'] ?>" method="post">
     Username: <input type="text" name="username" value="<?php echo $_COOKIE['username']; ?>" /><br/>
     Password: <input type="password" name="password" /><br/>
     Remember my username: <input type="checkbox" name="keep_username" value="true"><br/>
<input type="hidden" name="is_submitted" value="true" />
<input type="submit" value="Submit" />
</form>
 
<?php 
}
?>

Notice the keep_username checkbox above. If the user checks that, you use the setcookie() method to create a cookie that will store the username on the next visit. If it is unchecked, the username will be blank.

Setting up the Flex Project

Even though most Flex applications take up the entire page, nearly all of the time you embed the actual SWF file on an HTML page. In Flash Builder when you create a new Flex application in creates the SWF files and the HTML files for you that embed your application. To do that it uses a template file that you can find in the html-template folder of your project. If you haven’t done so yet, go ahead and create a new Flex project. Use the defaults (do not use PHP as the server, leave it at None/Other). When you get to the second screen, set the output folder to a subdirectory named “flex” in the directory where your login.php file is located.

With the project created the first thing you’ll want to do is edit the template file. Go into the html-template directory and rename index.template.html to index.template.php. That will let you run PHP code inside of the template file. Flash Builder will automatically create an index.php file in the flex folder you specified in the create project wizard. Next you need to make sure your application will use the right file when you run or debug it. Right click on your project, select properties, and then select Run/Debug Settings. Now select your project in that box and click the “Edit” button. On the next screen, uncheck “Use default” in the lowest box and type the path to your application. You can see mine below (I’ve got http://localhost:8888 mapped to http://rainier.cascade.mtn but your local PHP server will probably use http://localhost).

Modifying the Index Template

Next you need to edit the template you just renamed. Open it up in Flash Builder (or your favorite text editor) and you should see a bunch of HTML. At the very top of that page you’re going to add some PHP code. First, you are going to check for a url parameter named logout to be passed. If it is, you destroy the session and log the user out. If that isn’t passed, you’re going to use the session information from your login page and save that information to variables on this page. This code goes at the very top before all of the HTML in your template page.

<?php     
// Always start the session first.
     session_start();
 
     // You will also use this page to log out. If 
     // ?logout=1 is passed in the URL, destroy the 
     // session and log the user out.
     if(isset($_GET['logout']))
     {
          if($_GET['logout'] == 1)
          {
              $params = session_get_cookie_params();
              setcookie(session_name(),"", time()-3600,
                  $params["path"], $params["domain"],
                  $params["secure"], $params["httponly"]);
               session_unset();
               session_destroy();
          }
     }
 
     // Create the default values for our session.    
     $session_id = "";
     $logged_in = false;
     $username = "";
 
     // If the session is valid, replace the default
     // values with our session values.
     if($_SESSION['logged_in'] == true)
     {
          $logged_in = true;
          $session_id = $_COOKIE['PHPSESSID'];
          $username = $_SESSION['username'];
?>

Now that your variables are set, you’ll send those to Flash Player. There are a number of ways to get data into your Flex application but the most straightforward is to use a concept referred to as “flashvars”. Flashvars are parameters you can put within the embed code that will then be accessible by the Flex application. Flashvars are a great way to take a dynamic site, like one written in PHP, and change the behavior of a Flex or Flash application based on that dynamic PHP data. Look for the JavaScript in the template that includes var flashvars = {} and replace it with this code.

var flashvars = {session_id:"<?php echo $session_id; ?>",               
logged_in:"<?php echo $logged_in; ?>",
username:"<?php echo $username; ?>"};

That code will make those three variables available to your Flex application. That way before you let the user do anything in your Flex application you can check to make sure the user is logged in correctly.

Finally, at the end of the template you’ll add some code to show the user an error message if they try to access the page without logging in.

<?php              
} else {
     echo "Not authorized, go back and log in.";
}
?>

Creating the Flex Application

In this example you’ll just create a very, very basic Flex application with two states, a beginning state and a logged in state. When the application loads, check the flashvars you added to the embed code and then change to the logged in state. The logged in state also contains a button that uses the logout functionality you added to the template code. Here’s the whole Flex application.

<?xml version="1.0" encoding="utf-8"?><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/mx" 
                  minWidth="955" minHeight="600" currentState="startingScreen"
                  creationComplete="application1_creationCompleteHandler(event)">
 
<fx:Script>
     <![CDATA[
          import mx.controls.Alert;
          import mx.events.FlexEvent;
          import mx.utils.ObjectUtil;
 
          protected function btn_clickHandler(event:MouseEvent):void
          {
               navigateToURL( new URLRequest( encodeURI("index.php?logout=1")), "_self");
          }
 
          protected function application1_creationCompleteHandler(event:FlexEvent):void
          {
               if(this.parameters.logged_in == 1)
               {
                    currentState = 'loggedIn';
               }
          }
 
     ]]>
</fx:Script>
 
<fx:Declarations>
     <s:State name="startingScreen" />
     <s:State name="loggedIn" />
</fx:Declarations>
 
     <s:Panel name="Test" left="25" right="25" top="20" 
               bottom="25" title="My Application">
          <s:Button id="btn" left="10" top="10" 
                    click="btn_clickHandler(event)" 
                    label="Log Out" includeIn="loggedIn" />
          <s:Label id="lbl" left="10" top="10" 
                    text="You need to log in" 
                    includeIn="startingScreen" />
     </s:Panel>
 
</s:Application>

Conclusion and Warnings

There are a couple of notes and warnings about this method. By using two states and checking the login parameters you aren’t just depending on the embed code. That means if someone browses right to the SWF file they won’t be able to access the application because it won’t move to the next state unless it gets those parameters. That’s also part of the problem in this very simple application.

In the Flex code that changes the state I just check to see if the flashvar logged_in is set to 1. If you were to download a local version of the PHP file as well as the SWF and change that embed code you’d have access to the application. In a more robust example you would want to add a couple of hooks into the database. One way to do that would be to store the session id and the username in the database in the login.php file. Then when your Flex application loaded you could make a call to another PHP page and pass the session id and username parameters to a function which would compare it to the session and username that were stored during login to make sure it matches up.

You might also want to implement a token system with a shared secret which is a good way to authenticate against external APIs, the kind that you would create in PHP and consume in a Flex application using AMF or REST.

So keep in mind that this isn’t ready for production. But hopefully it gets you started in the right direction when creating a PHP/Flex application that is going to use an HTML login form. The fewer Flex/Flash login forms there are the better. Update: I’ve added a GitHub repository with the code. I’m hoping to add to this as I fill in some of the gaps that I warned about above.

ColdFusion versus PHP – Uploading a File

I spent part of my vacation digging into PHP. I’d like to be much savvier with PHP because we’ve seen a big chunk of Flex adoption from the PHP community and I’d like to be able to talk to those developers more intelligently. Unfortunately my server-side programming chops are very, very rusty. I started out as a ColdFusion developer and I’ve been able to play with some of the new stuff in ColdFusion 9 but I haven’t built a real CF app in a long time. So I thought I’d do a bit of a comparison and I’ve started doing simple things in PHP and then replicating them in ColdFusion. The first one is file uploading.

Disclaimer: I’m assuming there are things I’m doing wrong. File uploading is fairly basic, but part of the reason I want to blog the basic stuff is so people can let me know where I’m messing up and what I could do better. So flame on in the comments.

The Code Comparison

PHP – file_upload.php

<body>
<?php
     ini_set('display_errors',1);
     error_reporting(E_ALL | E_STRICT);
 
     if(isset($_POST['submitted']))
     {
          if( move_uploaded_file($_FILES['myfile']['tmp_name'], "{$_FILES['myfile']['name']}") )
          {
               print '<p>It verked!</p>';    
          } else {
               print '<p>Problemo!</p>';
          }         
          print_r($_FILES);
     }
?>
     <form action="upload_file.php" enctype="multipart/form-data" method="POST">
          <input type="hidden" name="MAX_FILE_SIZE" value="3000000" />
        <p><input type="file" name="myfile" /></p>
        <p><input type="submit" name="submit" value="Upload" /></p>
        <input type="hidden" name="submitted" value="true" />
    </form>
</body>

ColdFusion – file_upload.cfm

<cfifisDefined("form.submitted">
     <cfset currentPath = getCurrentTemplatePath()>
     <cfset currentDirectory = getDirectoryFromPath(currentPath)>
     <cffile action="upload" nameconflict="overwrite" filefield="form.myfile" destination="#currentDirectory#/coldfusion.gpx"/>
     <cfif cffile.filewassaved eq true>
          <p>It verked!</p>
          <cfdump var="#cffile#"/>
     <cfelse>
          <p>Problemo!</p>
     </cfif>
</cfif>
<body>
     <formaction="upload_file.cfm" enctype="multipart/form-data" method="POST">
        <p><inputtype="file" name="myfile" /></p>
        <p><inputtype="submit" name="submit" value="Upload" /></p>
        <inputtype="hidden" name="submitted" value="true" />
    </form>
</body>

Thoughts

I thought it was interesting how PHP and ColdFusion differ in the actual upload mechanism. Both of them just upload the file to a temporary location but then how the programmer deals with it is different. With PHP, you “move” the file to a location of your choice with the move_uploaded_file method. With ColdFusion you use cffile but call the upload attribute instead of the move attribute. That's why you don't put # signs around the form.myfile attribute because the upload function in cffile uses the POST info and that filefield attribute to do the moving/copying behind the scenes. Not sure which one I like better.

A few other bits. One, ColdFusion seems to give way, way more information about the file you've uploaded. I don't know if there is a way to get that kind of detail from PHP, but with ColdFusion I get a bunch of attributes like filewassaved, fileexisted, filewasappended, serverfileext, serverfilename etc. Some of that I can parse from the PHP information, like the file name and file extension, but other information, like if it was overwritten, or if the file already existed, doesn't seem possible to get with PHP. Two, it is really, really annoying to use a relative file path with ColdFusion. The move_uploaded_file function uses a relative path so it's easy to upload it to the same directory. With ColdFusion it's a pain, which is why I had to set a couple of extra variables. Also, PHP seems to love to check that you're below the max file size. You have to set one in your php.ini file and then send it again as a hidden form field. That seems completely redundant and I can't figure out why they do that.

Like I said above, this is supposed to be a pretty basic example because I want to know if I'm doing something stupid. If so, or if you have any additional info on my thoughts above, let me know.

A Week of Flash Collaboration: Authentication and Drupal Integration

afcs_logoThis is my first attempt at a series I’m hoping to start called “A Week of”, which will cover 5 topics in a particular subject over the course of the week. For the first one, I’m going to do Adobe Flash Collaboration Service and I’ll be doing a blog post on something AFCS-related every day. If you’re not familiar with Adobe Flash Collaboration Service (AFCS) I encourage you to check out my tutorial on the Developer Center to get started. Note: There is a new SDK version (0.93) on the Developer Portal, which includes everything in one place including video tutorials and an AS3-only version of the SDK. Worth checking out.

Authentication During Development
During development it’s relatively easy to authenticate against the Flash Collaboration service even if you aren’t online. The basic component for Authentication is the AdobeHSAuthenticator. During development the AdobeHSAuthenticator tag lets you pass in your Adobe ID credentials to connect to a room.

Adobe Flash Collaboration Service also lets you test and develop without having to connect to the AFCS servers with the LocalAuthenticator tag. There is an AIR application as part of the SDK that, when running, will let you use the LocalAuthenticator to pass in an arbitrary username and connect to a room on your local machine.

Guest Authentication
You can set up AFCS to just allow guests to log into your rooms without any authentication. There are a couple of things you need to do to set this up. First, you have to make sure the room is active. You can do this using the AFCSDevConsole AIR application that comes with the SDK. It’s also a good idea to make sure your room settings autoPromote guests so that when the anonymous users log in they are given publish access and can actually collaborate with other users. This can be done either in code with the RoomSettings tag or inside of the AFCSDevConsole.

afcs_dev_console

External Authentication
The most common use case is integrating your own authentication system with Adobe Flash Collaboration Service. AFCS uses a token system that can be generated using the Account Shared Secret, which is available when you create an account at the AFCS Developer Portal. The SDK comes with code for a number of server side languages including Java, PHP, ColdFusion, Python, and Ruby that help facilitate the generation of the token. In my example I took some of the sample code and scripts and integrated it with a Drupal environment. As a CMS, Drupal has its own authentication system and user administration. I created a basic module that exposes an AFCS chat room if the user is logged into the Drupal system and uses the existing Drupal username in the chat-room so that the Drupal users can chat with each other in real time.

I started off by creating an Admin page for my AFCS module that exposes a set of parameters needed to generate the token. That includes the account, or name, you create when you first sign up for AFCS, the room for this particular application, your Adobe ID username and password, and finally the shared secret.

afcs_module_settings

Now that I have those variables I can generate a token and set up the session. Using the scripts provided in the SDK it’s easy in PHP to create a new AFCSAccount object, log in, and get the session token.

$host  = "http://connectnow.acrobat.com";
$accountURL = "{$host}/" . variable_get(afcs_account, 'test');
$roomURL = "{$accountURL}/" . variable_get(afcs_room, 'test');
 
 
$account = new AFCSAccount($accountURL);
$account->login(variable_get(afcs_user, ''), variable_get(afcs_pass, ''));
$session = $account->getSession(variable_get(afcs_room, ''));
$token = $session->getAuthenticationToken(variable_get(afcs_secret, ''),$user->name,$user->name,50);

The variable_get functions are Drupal-specific but the code should be easy to follow. We first build the full roomURL based on the account and the room name. Then we login with our username and password and finally create a session based on the room. the getAuthenticationToken method takes the Shared Secret and the name that will display to all of the other connected users. The last parameter is the role, which is a number from 0 to 100 and tells AFCS what the user can do. 100 is a full host and 50 allows users to publish content and collaborate.

Now that we have the token and the roomURL we need to somehow get that into the SWF file. The external authentication example that comes with the SDK and my Drupal example both use FlashVars, which are read by the Flex application and then used to authenticate and set up the session. When the application finishes loading I call the Application.application.parameters object to start the session.

[Bindable]
public var authToken:String;
 
[Bindable]
public var roomURL:String;
 
protected function application1_applicationCompleteHandler(event:FlexEvent):void
{
     roomURL = Application.application.parameters.roomURL;
     authToken = Application.application.parameters.authToken;  
     cSession.login();
}

From there on out it’s a typical Flash Collaboration Service application that uses our token.

<rtc:AdobeHSAuthenticator id="auth" authenticationKey="{authToken}" />
<rtc:ConnectSessionContainer id="cSession" roomURL="{roomURL}"
authenticator="{auth}" autoLogin="false" width="100%" height="500"]]
     <mx:HBox width="100%" height="100%"]]
          <rtc:SimpleChat id="chat" width="80%" height="100%" />
          <rtc:Roster id="roster" width="20%" height="100%" dataProvider="{cSession.userManager.userCollection}" />
     </mx:HBox>
</rtc:ConnectSessionContainer>

Hopefully that provides an example of external authentication. If you want, you can download the Drupal module that I created here. It’s still in the very basic stages and I’ll be adding to it down the road but it does show off how to use Drupal authentication in your Adobe Flash Collaboration Service applications.