josh.code

Facebook SDK inside of Zend Framework Tutorial

 : 1513 words

It doesn’t matter if use Zend or not, you can follow along and use the code in your project as well.

Setup Zend Framework

First thing to do is download the latest version of Zend Framework. It is a free download, but you will have to create an account. If you don’t use Zend Framework (ZF from here on out) you really should. It is very extensible and versatile. I won’t continue to try and sell you on ZF (and most likely you already are using it).

Next thing to do is setup a project (of course if you have a project just use that). I won’t go into how to do this, but there is a great quickstart tutorial on Zend’s site that is a great introduction if you haven’t used ZF before.

You should now have the default ZF page if all is working correctly.

Download the Facebook SDK

Facebook has a github repo where you can download their latest SDK for PHP. Go ahead and grab the latest version and uncompress it.

We now need to add this to our ZF project.

Create a new folder under your ZF project called ‘Facebook’.

Grab the files out the src folder from the Facebook SDK and add it to ZF’s Facebook folder you just created. Your folder should have three files in it now (at least as of SDK version 3.1.1):

Now we have to change the name of the Facebook class for it to work with ZF. I am going to use Aptana Studio to edit the php files. Just like Zend Framework, if you haven’t checked it out you should. We are going to edit the facebook.php file and change the class name from Facebook to Facebook_Facebook.

require_once "base_facebook.php";

/**
 * Extends the BaseFacebook class with the intent of using
 * PHP sessions to store user ids and access tokens.
 */
class Facebook_Facebook extends BaseFacebook
{

One more thing we have to update is the application.ini file for ZF. This should be located at application/configs/application.ini. We have to tell it that we added a folder in the library.

[production]
phpSettings.display_startup_errors = 0
phpSettings.display_errors = 0
includePaths.library = APPLICATION_PATH "/../library"
bootstrap.path = APPLICATION_PATH "/Bootstrap.php"
bootstrap.class = "Bootstrap"
appnamespace = "Application"
resources.frontController.controllerDirectory = APPLICATION_PATH "/controllers"
resources.frontController.params.displayExceptions = 0
autoloadernamespaces.Facebook = "Facebook_"

[staging : production]

[testing : production]
phpSettings.display_startup_errors = 1
phpSettings.display_errors = 1

[development : production]
phpSettings.display_startup_errors = 1
phpSettings.display_errors = 1
resources.frontController.params.displayExceptions = 1

Why Facebook_Facebook?

Good question. It is for autoloading in ZF. When you use ZF you don’t need to use a bunch of requires, based on the class names ZF will figure out what class to load. This tells ZF to look in the library for a folder called Facebook with a file called facebook.php and load the class in that file. At this point we have technically added the Facebook SDK to ZF. We can create a new facebook object anywhere in our project by calling:

$fb = New Facebook_Facebook(array(
	'appId' => 'appid',
	'secret' => 'appsecret',
));

Create our new class

We will now create a class to wrap our Facebook objects to only intialize them once, when needed(lazily), centralize all configuration options. We are creating a singleton object for Facebook.

We have to create a folder in the library to hold this new class. I am naming mine Josh, cause that’s my name. After we have that create a new file named facebook.php. Here is the code for that file.

<?php
class Josh_Facebook
{

	private static $fb;

	private static function getFB()
	{
		if(self::$fb)
		{
			return self::$fb;
		}

		$bootstrap = Zend_Controller_Front::getInstance()->getParam('bootstrap');

		$options = $bootstrap->getOptions();

		$fb = New Facebook_Facebook(array(
				'appId' => $options['facebook']['appid'],
				'secret' => $options['facebook']['appsecret'],
				));

		self::$fb = $fb;

		return self::$fb;
	}

	public static function __callStatic ( $name, $args )
	{

        $callback = array ( self::getFB(), $name ) ;
        return call_user_func_array ( $callback , $args ) ;
    }
}
?>

Let’s step through this class. The $fb variable is a static variable, this means it can be used without an instance of the class. In fact everything is static so we never will initialize this class. The first time this class is called it will initialize the facebook class for use with our app_id and app_secret we have put in our application.ini (I will get to this in a second). After initializing the class it stores it in the static variable $fb, so the next time it is called it will just use the already initialized object.

The function __callStatic will map whatever function you are trying to call with the underlying Facebook function. For example Josh_Facebook::api(‘/me’) will map to Facebook->api(‘/me’). It will then return whatever was returned by that function. What is nice about this is that we don’t have to map every function to the facebook functions.

Now let’s tell Zend Framework about our new namespace and facebook details.

[production]
phpSettings.display_startup_errors = 0
phpSettings.display_errors = 0
includePaths.library = APPLICATION_PATH "/../library"
bootstrap.path = APPLICATION_PATH "/Bootstrap.php"
bootstrap.class = "Bootstrap"
appnamespace = "Application"
resources.frontController.controllerDirectory = APPLICATION_PATH "/controllers"
resources.frontController.params.displayExceptions = 0

autoloadernamespaces.Josh = "Josh_"
autoloadernamespaces.Facebook = "Facebook_"

facebook.appid = PRODUCTION_APP_ID
facebook.appsecret = PRODUCTION_APP_SECRET

[staging : production]

[testing : production]
phpSettings.display_startup_errors = 1
phpSettings.display_errors = 1

[development : production]
phpSettings.display_startup_errors = 1
phpSettings.display_errors = 1
resources.frontController.params.displayExceptions = 1

facebook.appid = DEV_APP_ID
facebook.appsecret = DEV_APP_SECRET

Exactly like our Facebook_ namespace we need to add a Josh_ namespace (or whatever you named your folder). The next things we add are our facebook app_id and app_secret (you will get these from Facebook when you create an application). The awesome part about implementing the Facebook SDK this way is that you can seamlessly jump between two applications. When you are developing a Facebook application if you test locally and also have a production server you can’t use the same application as Facebook requires a domain name. If you have setup Zend Framework correctly when you load up you local test copy (remember to set you APPLICATION_ENV to development) it will load your development app_id and app_secret and then on production it will load those.

Let’s use this in an Example

Inside of the Facebook SDK there is an example of how to use it. We actually will just take that example and modify to work in our ZF installation. It’s honestly deleting a few lines and changing three other lines.

First open up application/views/scripts/index/index.phtml. This is the default file that is loaded when you go to your projects root (or public folder). Ctrl+A it and delete it all. Put in this in it’s place:

<!--?php <br ?-->// See if there is a user from a cookie using our new class
$user = Josh_Facebook::getUser();

if ($user) {
  try {
    // Proceed knowing you have a logged in user who's authenticated. Notice we don't have
	// to initialize an object and we just call the class.
    $user_profile = Josh_Facebook::api('/me');
  } catch (FacebookApiException $e) {
echo '
'.htmlspecialchars(print_r($e, true)).'
';
    $user = null;
  }
}

?>

<!--?php if ($user) { ?-->
Your user profile is
<!--?php } else { ?-->

<!--?php } ?-->
<script type="text/javascript">// <!&#91;CDATA&#91;
	window.fbAsyncInit = function() {
		FB.init({
			appId: '<?php echo Josh_Facebook::getAppID() ?>',
			cookie: true,
			xfbml: true,
			oauth: true
		});
		FB.Event.subscribe('auth.login', function(response) {
			window.location.reload();
		});
		FB.Event.subscribe('auth.logout', function(response) {
			window.location.reload();
		});
		};
		(function() {
			var e = document.createElement('script'); e.async = true;
			e.src = document.location.protocol +
			'//connect.facebook.net/en_US/all.js';
			document.getElementById('fb-root').appendChild(e);
		}());
</script>

First thing we do differently from the Facebook example is delete the require as ZF will do all the autoloading for us. We then take out the initialization code for the facebook object as our new class will do that. We then have to change all the references to $facebook to Josh_Facebook. We also need to change the -> to :: as we are calling static functions. If you have put in correct app settings into your application.ini you should be able to run this.

Another thing to note is that we can call this Facebook class in the view. It’s not correct MVC design as any data should be coming from the model layer (you can easily use this in the model layer), but what is nice is that you can call echo Josh_Facebook::getAppID() in the view to get the app_id for the Javascript SDK.

I don’t use Zend Framework

There is not much modification for this. First thing is that we lose autoload so we have to add requires to the top of our Josh_Facebook file (of course modify this to work in your installation)

require('.\library\Facebook\facebook.php');

and our facebook example page(I renamed this to non_zend_facebook.php instead of facebook.php)

require('.\non_zend_facebook.php');

Don’t forget to call your functions from the class name Non_Zend_Facebook. The final step is to modify your non_zend_facebook.php file to have your app_id and app-secret loaded.

private static function getFB()
	{
		if(self::$fb)
		{
			return self::$<span class="hiddenGrammarError" pre="">fb;
		}

		$fb</span> = New Facebook_Facebook(array(
				'appId' => 'APP_ID',
				'secret' => 'APP_SECRET',
				));

		self::$fb = $fb;

		return self::$fb;
	}

We can now use this class anywhere we include our non_zend_facebook.php file.

Download this project (I only have the changes to Zend Framework not an entire install of it and the non Zend Framework files).

View the demo of this.

comments powered by Disqus