Home  >  

Setting Up AMFPHP for Flash and Facebook API

Author photo
AddThis Social Bookmark Button
     Following good software engineering principles and with the goal of making Flash-based Facebook applications easier to develop, there are well known technologies to utilize. So far we have been introduced to the basics of Flash - Facebook API communication. In the second article we discussed the various methods to embed swfs into the Facebook canvas. Equipped with that knowledge, we can make a good step forward. In software engineering, experts say that we should develop applications “close to the problem”.
series badge
Find the rest of this series on the series page.


What does it mean to be “close to the problem”?

Well, basically that means that we should develop our own routines to make the coding on the application much easier. For example, if we develop a facebook application that calculates whether today is the birthday of some of our friends, we should create a method "close" to the problem, like:
 

//assuming we have a class called friends
friends -> getTodaysBirthDays();
and not:
 

//this is not what is called “coding close to the problem”
friend = friends -> getFriend();
today = getDate();
hasBirthDay = hasBirthDay(today, friend);
Notice that the code written here is language independent and it does apply to all languages, ActionScript, PHP, C#, etc. Working with those habits in mind, bigger applications can be easier developed and maintained. The same applies for Facebook application development. Assuming we want to develop an application that schedules weddings, of course we need methods like this:
 

getTodaysWeddings()
scheduleWedding()
clearWedding()
I know that this might be still a bit confusing to you, but believe me, you will get a better understanding of this later. In order to successfully communicate to the Facebook PHP library with Flash, we need AMFPHP. Yes I know that there is an ActionScript 3.0 library for Facebook, but the library is still not considered stable and Adobe will make good efforts to update the library. We will be kept updated. For now, let's rely on the PHP library that is already developed and stable. As mentioned earlier, we will need AMPPHP. Why AMFPHP? Because it allows us to access PHP methods with Flash via web services. AMFPHP is covered in depth on lots of other sites, Google for it if you want to know more. I will cover AMFPHP only in such extend that is necessary for Facebook application development. Here is the image of the of what we want to achieve at the end of this article:
article3_img1.png
You can see here the communication between all the layers. At the bottom, there the Facebook with its application platform. At the layer above we see the Facebook API library written in PHP. Above we see AMFPHP acting like a layer between PHP and Flash. Finally at the top, there is the frontend, the swf file that will finally display all the data in the fashion we want. Like earlier discussed, there is an ActionScript 3.0 library that will be officially published by Adobe, but even then this method will still be useful.
Now after we have a vision of how this works, we need to work on the actual implementation. The goal of this article is to create one method in AMFPHP that will connect to the FB API and display the result. Now you are asking why AMFPHP, why not simply call PHP scripts from Flash. Imaging we have an application with loooots of task, like addWedding, clearWedding, etc. Using the standard Flash to PHP procedure, we would have to create a separate script for each of the tasks, like “addWedding.php” to add a wedding. Inside Flash we would call the scripts. Having so many tasks this could become really unmanageable. There must be a centralized way to store all the methods for one application. Having that in mind, the solution with AMFPHP becomes logical.
First create a local folder on your drive for the whole project. Here on my disk something like c:\dev\fb_amfphp\.
Make sure you install Flash Tracer to see trace messages in FireFox.
Download the Facebook API library for PHP here (http://svn.facebook.com/svnroot/platform/clients/packages/facebook-platform.tar.gz) and extract it in that folder. You should see the Facebook folder now inside your project folder. Go to the AMFPHP website (http://www.amfphp.org/) and download the latest version of the library. Extract it in the project folder. Your folder should look similar to this:
article3_img2.png
We should upload this to the webserver where our Facebook application is located. One thing we must remember is that we need to have the AMFPHP installation for every Facebook application. I tried to make it have only one AMFPHP folder on the webserver and utilize it, but in most cases I had trouble. So this is why I decided to copy the 500 kb folder every time I make a new Facebook application. There might be better ways to do this, but this worked out for me.
After uploading the contents of the folder, you should point your browser to
http://www.mysite.com/myFBApp/amfphp/gateway.php
As you already might know, if you see the page saying “AMFPHP and this gateway are installed correctly” then we are on the right track. Now we can move on and talk about Flash.
As we already know, there are plenty of tutorials on the web about installing AMFPHP. Because we do not want to repeat knowledge here, this tutorial covers AMFPHP installation with Facebook API.
After we uploaded our Facebook PHP lib folder, we need to make it accessible for AMFPHP. When uploaded, the path to the Facebook library should be similar to this:
http://www.mysite.com/myFBApp/facebook/
So, in order to make this work, there must be a way to import this into the AMFPHP script. As you already might know, the main file in AMFPHP we will be working with is the class file inside the services folder. So open yout text editor and add this code:
 

<?php

class HelloFBServices {
 
 	function HelloFBServices(){
  	}
 
}

?>
Save this file exactly as "HelloFBServices.php" in the amfphp/services/ folder. We now have a template ready for creating web services. Now we need to upload the PHP file and test to see it is working. In order to do that we need to add a small method just to make sure everything is fine:
 

function sayMyName($name){
 	return $name;
}
We will add this just after the constructor, so the complete script needs to look like this:
 

<?php

class HelloFBServices {
 
 	function HelloFBServices(){
  	}
 
 	function sayMyName($name){
  		return $name;
  	}
 
}

?>
Save this and upload to the remote server. Now we call the method browser via the link:
http://www.mysite.com/myFBApp/amfphp/browser/
So, if you every worked with AMFPHP before, you will see a familiar screen:
article3_img3.png
Test the method we created to make sure AMFPHP is working.
In order to reference the Facebook lib folder we need to make one init method inside that will initialize the connections to Facebook libraries. The method looks like this:
 

function init(){
 
 	//refenrece to the facebook api key and secret
 	require_once("../../keys.php");
 	//reference to the actual facebook api
 	require_once("../../facebook/client/facebook.php");
 
}
Just to make sure you follow it right, the keys file needs to be placed inside project folder, with the following contents:
 

<?php
    
define("API_KEY", "yourApiKey");
define("SECRET", "yourSecret");
    
?>
Save this as "keys.php" and upload it to the remote Facebook application folder. Now the line of code:
 

require_once("keys.php");
makes more sense.
Now that we know that everything is OK, we can move on. It's nice to make the application in stages. This is sometimes called "tracer ammunition programming” when we develop everything step by step by making sure every part of the app is working properly.
Now let’s connect with Flash to AMFPHP. For now, for the sake of simplicity, we just need to connect and call the sayMyName method, after that we need to enhance the method for Facebook application development.
Open Flash, you can use the same main.fla we used before, just erase all code and contents from the stage or you can create a brand new one.
article3_img4.png
 

//get the base url of the swf, to improve portability of code
var baseURL:String = Tools.getBaseURL(LoaderInfo(this.root.loaderInfo).loaderURL);

//concat the connection string
var gatewayUrl:String = baseURL + "amfphp/gateway.php";
//create a new net connection
var gateway:NetConnection = new NetConnection();
//connect to gateway
gateway.connect(gatewayUrl);

//create responders for the calls
var responder:Responder = new Responder(onResult, onFault);

//call the method
gateway.call("HelloFBServices.sayMyName", responder, &#8220;Mirza&#8221;);

//this callback will receive the results
function onResult(result):void{
 	//load all cuts
 	trace(result);
}

//catch the fault
function onFault(fault:String):void{
 	trace('onFault invoked');
 	trace(fault);
}
You should see the result. If you don’t see it, check the fault description. Please make sure to move on only if this works because this stage is very important. As the next step, we will combine the AMFPHP code together with the Facebook PHP library. As you can see, AMFPHP will work with the API while Flash “won’t notice” much of that. Flash will only call the methods and the actual implementation will be left to PHP. Finally, here is what you have to add to the existing PHP in order to connect to Facebook:
 

//say my name with using facebook api
function sayMyName($sessID, $userID){
 	//init all
 	$this -> init();
 	//create new facebook instance
 	$facebook = new Facebook(API_KEY, SECRET);
 	//set the current user with id and session
 	$facebook->set_user($uid, $sessID);
 	//make the api call
 	$nameArr = $facebook->api_client->users_getInfo($uid, $field);
 	//get the desired string
 	$name = $nameArr[0][$field];
 	//give it to flash
 	return $name;
}
Reopen Flash and add the following line of code at the top:
 

//get the facebook parameters
var paramObj:Object = LoaderInfo(this.root.loaderInfo).parameters;
//get the uid
var uid:String = String(paramObj["fb_sig_user"]);
//get the session key
var sessID:String = String(paramObj["fb_sig_session_key"]);
modify the existing line of code below:

//call the method
gateway.call("HelloFBServices.sayMyName", responder, &#8220;Mirza&#8221;);
with this one:

//call the method
gateway.call("HelloFBServices.sayMyName", responder, uid, sessID);
So the complete PHP should look like this:
 

<?php

class HelloFBServices {
 
 	function HelloFBServices(){
  		//init();
  	}
 
 	function init(){
  		require_once("../../keys.php");
  		require_once("../../facebook/client/facebook.php");
  	}
 
 	function sayMyName($uid, $sessID){
  		//init all
  		$this -> init();
  		//create new facebook instance
  		$facebook = new Facebook(API_KEY, SECRET);
  		//set the current user with id and session
  		$facebook->set_user($uid, $sessID);
  		//make the api call
  		$nameArr = $facebook->api_client->users_getInfo($uid, "name");
  		//get the desired string
  		$name = $nameArr[0]["name"];
  		//give it to flash
  		return $name;
  	}
 
}
  
?>
When you call your application in http://apps.facebook.com/yourApp/index.php you should see your name in the Flash Tracer output window.
As you can see, there is a lot of stuff going on here. Let’s go line by line. First we see the function signature:
 

function sayMyName($uid, $sessID){
The function in PHP receives the user id and the session ID.
 

//init all
$this -> init();
We call here the init function to import the facebook classes. I did not found a workaround for this.
 

//create new facebook instance
$facebook = new Facebook(API_KEY, SECRET);
This is self-explanatory, the constants are imported from the keys.php file.
 

//set the current user with id and session
$facebook->set_user($uid, $sessID);
This is an important line of code, we use the session id to identify the session with the user. Without this line of code the rest would be useless.

//make the api call
$nameArr = $facebook->api_client->users_getInfo($uid, "name");
This is the actual Facebook API call. We request a name.
 

//get the desired string
$name = $nameArr[0]["name"];
Here we retrieve the line name from the array structure.
 

//give it to flash
return $name;
That is for PHP and in AS3 we changed this line of code:
 

//call the method
gateway.call("HelloFBServices.sayMyName", responder, uid, sessID);
This way we passed the two variables uid and sessID to AMFPHP. So, finally we made a call from Flash to PHP, and PHP worked with FB. This is a great step forward. Now you can add as many methods to the PHP file as you want, you don’t have to care where the methods are stored and Flash with help of AMF, Flash will treat them almost like native functions. There is one thing we need to test before we move on. We need to make a call to the database because there will be a lot of scenarios where we will need to grab data from databases, like scores, notes etc. In this case we will use the familiar mySQL database. We will just extend the sample here and to make the database call. But first of all we need to make the database. Create the database “fbTest”. Use this sql to create a small table with one row with a string inside:
 

CREATE TABLE IF NOT EXISTS `fb_test` (
  `id` int(255) NOT NULL auto_increment,
  `note` varchar(255) NOT NULL,
  PRIMARY KEY  (`id`)
) ENGINE=MyISAM  DEFAULT CHARSET=latin1 AUTO_INCREMENT=2 ;

INSERT INTO `fb_test` (`id`, `note`) VALUES
(1, 'My origins are database like...');
You should now have a table with one row saying "'My origins are database like...” in the column.
Open the HelloFBServices.php we previously worked on.
Add this like of code to the init function:

require_once("../../DBConstants.php");
Add this method after the init function of code:

//connect to db
function connect(){
 	$this -> connID = mysql_connect(DB_HOST, DB_USER, DB_PASS);
 	if($this -> connID){
  		if(@mysql_select_db(DB_NAME)){
   			$ip = $_SERVER["REMOTE_ADDR"];
   			return true;
   		}	
  	}
 	return false;
}
Create a new PHP file called “DBConstants.php”:

<?php

define("DB_HOST", "yourMySQLHost");
define("DB_USER", "yourDatabaseUserName");
define("DB_PASS", "******");
define("DB_NAME", "fbTest");

?>
This is just a nice include file that will hold the database connection information. This will make our code more flexible and much easier to maintain.
Add this new method at below the “sayMyName” function:

//read from DB
function readFromDatabase(){
 	//init the classes
 	$this -> init();
 	//connect to database
 	if(!$this -> connect()){
  		return false;
  	}
 	//make the sql string
 	$sql = sprintf("SELECT * FROM fb_test");
 	//make the result
 	$result = mysql_query($sql);
 	//fetch the result
 	$arr = mysql_fetch_array($result);
 	// return the string from db to flash
 	return $arr['note'];
}
So, in order not to get confused, here is how the complete PHP class looks:
 

<?php

class HelloFBServices {
 
 	function HelloFBServices(){
  		
  	}
 
 	function init(){
  		require_once("../../keys.php");
  		require_once("../../DBConstants.php");
  		require_once("../../facebook/client/facebook.php");
  	}
 
 	//connect to db
 	function connect(){
  		$this -> connID = mysql_connect(DB_HOST, DB_USER, DB_PASS);
  		if($this -> connID){
   			if(@mysql_select_db(DB_NAME)){
    				$ip = $_SERVER["REMOTE_ADDR"];
    				return true;
    			}	
   		}
  		return false;
  	}
 
 	function sayMyName($uid, $sessID){
  		//init all
  		$this -> init();
  		//create new facebook instance
  		$facebook = new Facebook(API_KEY, SECRET);
  		//set the current user with id and session
  		$facebook->set_user($uid, $sessID);
  		//make the api call
  		$nameArr = $facebook->api_client->users_getInfo($uid, "name");
  		//get the desired string
  		$name = $nameArr[0]["name"];
  		//give it to flash
  		return $name;
  	}
 
 	//read from DB
             function readFromDatabase(){
  	//init the classes
  	$this -> init();
  	//connect to database
  	if(!$this -> connect()){
   		return false;
   	}
  	//make the sql string
  	$sql = sprintf("SELECT * FROM fb_test");
  	//make the result
  	$result = mysql_query($sql);
  	//fetch the result
  	$arr = mysql_fetch_array($result);
  	// return the string from db to flash
  	return $arr['note'];
              }
 
 
}
  
?>
Now, in Flash you should call the method this way:
 

 //call the method
gateway.call("HelloFBServices.readFromDatabase", responder);
When the swf is compiled, uploaded and called through the facebook application canvas, we should see the result in the output window. Wow, this is great, we made the first database calls along with facebook api calls.
We are now equipped with great knowledge to create interesting applications, look forward to see you in the next article as we will discuss api calls in more detail. See ya!


Continue to Part 4, Creating a Custom API for Flash Using Facebook APIP and PHP.

To view the entire series please visit http://www.insideria.com/series-facebook-dev.html

Read more from Mirza Hatipovic. Mirza Hatipovic's Atom feed mirzahat on Twitter

Comments

12 Comments

magico said:

Thank you Mirza great tutorial like the others!

but i got some problems :(

got it working until the facebook integration
i mean it works with the amfphp and so on
but
when i use it into facebook i got always onfault problem

i get corretly UID and SESSID I traced them
but then i always get the onFault

what can be the error?
can you please help me?

thank you very much! and please keep the great work with your tutorials
i'm following you on twitter also :)

Mirza Hatipovic said:

There is one error,

there is no Tools.getBaseURL method at all. I have it because I included a reference. here is how it can be solved:

var baseURL:String = getBaseURL(LoaderInfo(this.root.loaderInfo).loaderURL);

and here is the definition of getBaseURL (place in the same frame):

function getBaseURL(url:String):String {
if (url.indexOf("?") == -1) {
var lastSlashIndex = url.lastIndexOf("/");
url = url.substring(0, lastSlashIndex+1);
} else {
var indexOfUpitnik = url.lastIndexOf("?");
url = url.substring(0, indexOfUpitnik+1);
lastSlashIndex = url.lastIndexOf("/");
url = url.substring(0, lastSlashIndex+1);
}
return url;
}

Ben Johnson said:

Your tutorials are great!

I have written a Flex/AS3 application that uses the Facebook API (both PHP and the new AS3 flavors). I use AMFPHP to also call some PHP functions on my server that does some read/writes to a database. It seems to use quite a few of the same technologies you are using.

I'm running in to a problem though.

When I run the application "outside" of Facebook (just going directly to the uploaded swf on my server), it runs fine. All the AMFPHP calls (using a RemoteObject and a service defined in services-config.xml) execute, the PHP SQL runs, data is returned, and the callback in my Flex runs nicely.

Then I tried to stick it "inside" Facebook. I set it up as a FBML application and embedded the swf using fb:swf. I then pointed facebook to the php file with the fb:swf FBML. When I run the app from inside Facebook though, the swf loads and displays fine. But the fields that would have been updated through the AMFPHP calls aren't. I know the PHP functions on my server aren't even being called (I'm logging from them to check). It seems like Facebook is "diverting" the AMFPHP calls or blocking them or something.

So my questions are, do you have any idea what one should check or why this might be happening?

Is your example swf above displayed through Facebook as a FBML or iFrame? SHould that matter if using AMFPHP?

I see you are using a "NetConnnection" class rather than a "RemoteObject" to run AMFPHP. Would this possibly be a factor when working with Facebook?

Anyway, I appreciate your help!

Ben Johnson said:

OK, regarding the problem above I had: I'm a dope.

The problem was that I did not have a crossdomain.xml file on my server. I didn't really think that was necessary since the swf is on my server, but apparently the Facebook layer makes this important. I added one and it worked fine. :-)

Neil said:

Adobe have now released an official Facebook API. Shouldn't need to do all that work with PHP anymore.

Neil

Mirza said:

It's good to have the knowledge of both :-)

The next tutorials deal with the AS api

http://nekretnine387.com/

Frazko said:

I'm using sos max and I can't get the traces from facebook app why?... also the app only got onfault why could it be?.. .. is that FBML tag or iframe?? thanks...

Clement said:

I got problem from using localhost. Could I test this sample using localhost?

Clement said:

I got a problem from using localhost. Could I test this sample using localhost?

Charles J said:

Hi there,

This looks great, but I have some newby questions to ask you
As I understand the flash passes the $uid and $sessID to the php via amfphp.

and the flash gets it through LoaderInfo(this.root.loaderInfo).parameters;

Now does it means the flash is reloaded with the parameters?
var uid:String = String(paramObj["fb_sig_user"]);
var sessID:String = String(paramObj["fb_sig_session_key"]);

I dont quite follow that part

Other question
Is there a way maybe to get the uid and the sessID winthin the php
first method and return it to the flash ?
and then recall another method and passing it the values ??

Colin Luzio said:

Again nice tutorial, though for some reason when I uploaded the amfphp folder and pointed my browser to gateway.php I get this message: Internal Server Error
The server encountered an internal error or misconfiguration and was unable to complete your request

Does anyone know what I'm doing wrong?

Cheers,

Seb S said:

Hi,
thanks for the tutorial. However, I seem to be having some problems. I uploaded the Hello FBservices file into the services folder and when I click on it in the browser I get this message:

Error retrieving service info:

Invalid AMF message

Fatal error: Uncaught exception 'VerboseException' with message 'Cannot modify header information - headers already sent by (output started at /home/content/c/o/l/colinluzio/html/amfphp/services/HelloFBServices.php:26)' in /home/content/c/o/l/colinluzio/html/amfphp/core/amf/app/Gateway.php:191
Stack trace:
#0 [internal function]: amfErrorHandler(2, 'Cannot modify h...', '/home/content/c...', 191, Array)
#1 /home/content/c/o/l/colinluzio/html/amfphp/core/amf/app/Gateway.php(191): header('Content-type: a...')
#2 /home/content/c/o/l/colinluzio/html/amfphp/gateway.php(154): Gateway->service()
#3 {main}
thrown in /home/content/c/o/l/colinluzio/html/amfphp/core/amf/app/Gateway.php on line 191

Do you know why? Thanks

Leave a comment


Tag Cloud

Question of the Week: Dream App

If you had an unlimited budget and unlimited resources what application would you build and why would you build it?

Answer

Latest Features

Recommended for You

@InsideRIA on Twitter

Archives

  • Or, visit our complete archive.  

About This Site

Welcome to the premiere community site for all things RIA sponsored by O'Reilly Media and Adobe Systems Incorporated.