Foundations of a Facebook App Framework

Now that we have several classes developed, it’s time to put them together in a simple framework that will help us quickly and easily develop Facebook apps.

Directory Structure

All our shared classes are in a directory called lib. For the sake of making previous examples cleaner this was kept in the www along with demo, but now that we plan to make real apps, I think it’s wise to move it up a level so as not to be in www. The structure will look like this:

/lib/
/www/demo/

Since we move cache.sqlite we need to update /lib/cache.php.

public $dbpath = "/path/to/lib/cache.sqlite";

Facebook App Object

We’ll add a function initUserFromReuqest to /lib/facebookapp.php to wrap initOauthUserFromSignedRequest. This way, if Facebook ever changes their signing format, we can make the change in our class instead of everywhere in our apps.

    /*    wrapper in case facebook changes their signed_request    */
    public function initUserFromReuqest() {
        return $this->initOauthUserFromSignedRequest();
    }

Framework Functions

We’ll add a new file /lib/appframework.php to hold functions for our framework. I’ve been going back and forth about whether or not to make it an object and I think not is best. It will be using global variables and such, and I don’t like objects depending on them. The first thing we’ll add to the framework is a consolidation of all the initialization we’ve been putting in the heads of our files.

require_once("cache.php");
require_once("curlclient.php");
require_once("facebookapp.php");

Next we’ll add functions to initialize all the common objects and require Facebook authorization with permissions. We’ll also move the p3p header here and take set it to blank in facebookapp.php.

We’ll finally get around to fixing the Safari cookie issue. We’ll do it by checking to see if there is a default cookie and if not redirecting to the same page on the top level. This page will then set the default cookie and redirect back to the canvas page. To keep from going in an endless loop if cookies are disabled, we’ll check for the query string “defaultcookieset”. Somehow setting this cookie will make cookies in the iframe work.

/*    initilaize a facebook app object
    $requireauth = true or false to require authorization
    $additionalpermissions = comma separated list of permissions    */
function initFacebookApp($requireauth=false,$additionalpermissions=false) {

    global $facebookapp, $cache, $db;

    // set p3p header, get your own from http://www.w3.org/P3P/
    header('P3P:CP="IDC DSP COR ADM DEVi TAIi PSA PSD IVAi IVDi CONi HIS OUR IND CNT"'); // 

    // initialize objects
    initObjects();

    // fix for cookies in iframe for safari
    if (stripos($_SERVER['HTTP_USER_AGENT'],"safari") !== false) {
        if (isset($_GET['setdefaultcookie'])) {
            // top level page, set default cookie then redirect back to canvas page
            setcookie ('default',"1",0,"/");
            $url = substr($_SERVER['REQUEST_URI'],strrpos($_SERVER['REQUEST_URI'],"/")+1);
            $url = str_replace("setdefaultcookie","defaultcookieset",$url);
            $url = $facebookapp->getCanvasUrl($url);
            echo "<html>\n<body>\n<script>\ntop.location.href='".$url."';\n</script></body></html>";
            exit();
        } else if ((!isset($_COOKIE['default'])) && (!isset($_GET['defaultcookieset']))) {
            // no default cookie, so we need to redirect to top level and set
            $url = $_SERVER['REQUEST_URI'];
            if (strpos($url,"?") === false) $url .= "?";
            else $url .= "&";
            $url .= "setdefaultcookie=1";
            echo "<html>\n<body>\n<script>\ntop.location.href='".$url."';\n</script></body></html>";
            exit();
        }
    }

    // if auth required, do redirect to facebook dialog
    if ($requireauth) {
        if ($additionalpermissions !== false) $GLOBALS['facebookPermissions'] .= ",".$additionalpermissions;
        $facebookapp->requireAuthorization($GLOBALS['facebookRedirectPage'], false, $GLOBALS['facebookPermissions']);
        $authorized = true; // can't get here unless app is authorized with permissions
    } else {
        $authorized = $facebookapp->initUserFromReuqest();
    }
    return $authorized;
}

/*	initialized objects needed for app	*/
function initObjects() {
	global $facebookapp, $cache, $db;
	$facebookapp = new FacebookApp($GLOBALS['facebookAppId'],$GLOBALS['facebookAppSecret'],$GLOBALS['facebookNamespace']);
	$cache = new Cache($GLOBALS['facebookNamespace']);
	$db = new mysqli($GLOBALS['dbHost'],$GLOBALS['dbUser'],$GLOBALS['dbPassword'],$GLOBALS['dbName']);
	$db->set_charset('utf8'); // make sure it's utf 8
}

/* closes objects from initObjects */
function closeObjects() {
    global $db;
    $db->close();
}

Facebook App Files

Now we need to modify all the files in /demo/ to use the framework like this:

require_once("../../lib/appframework.php");
require_once("config.php");

$authorized = initFacebookApp(true,"friends_location");

Then on the bottom of each file adding:

<?php closeObjects(); ?>

We can now go back to our app’s canvas page to see if everything works as before.

Cache Cron

We can also set up a cron file for our cache in /lib/_cron_cache.php. Depending on how granular we want the caching, we can set it up to run at whatever interval. I think hourly is good for apps.

require_once("cache.php");
$cache = new Cache("");
$cache->clearCache();

If we are running the cron, we also need to update /lib/cache.php.

public $usingcron = true;

Download the Files for this Post

  • soniyo

    without all thi code how can we setcookies and redirect page to canvas

  • tomkincaid

    Are you talking about the safari issue. There’s a block of code that checks for a cookie and if not present redirects out of the iframe then sets the cookie. Once this cookie is set, all future cookies in the iframe should work.