Upgrade Guide: Facebook PHP SDK v3.x to v5.x

Upgrading the Facebook PHP SDK from v3.x to v5

Mar 31, 2015

This guide will help you upgrade from version 3.x to version 4.1 v5.x of the official Facebook PHP SDK.

Update (May 7, 2015)

Inspired by a conversation with Fosco on the PHP Roundtable, the Facebook PHP SDK will start following SemVer starting with v5.0. Yay!

Wait - what happened to version 4.1? Before the decision to adopt SemVer, the official Facebook PHP SDK was going to be released as v4.1. But now v4.1 is just an alias to v5.0.0. So any references to 4.1 can be safely aliased to v5.0.0.

So when will v5 be released? I talked to Fosco in the afterglow of F8 2015 and it looks like version 4.1 of the official Facebook PHP SDK might get launched as early as the second week of April 2015. W00t! Version 5.0 should be released in place of v4.1 "soon". Perhaps v5 of the Facebook PHP SDK could be versioned vDNF. It's out now! Yay!

Back on April 30, 2014 Facebook released a replacement PHP SDK for v3.x at the 2014 F8 Facebook Developer Conference. They called it v4.0. Almost one year later, v5 is a complete refactor of v4.0 which further pushed the PHP SDK into the ranks of modern PHP projects.

The jump from v3.x to v4.0 was huge. And the jump from v4.0 to v5 was also huge. But oddly enough the upgrade from v3.x to v5 might be a little bit easier than the upgrade is from v4.0 to v5.

Know your Graph Version

Before you start upgrading your PHP app, it's important to know that the underlying Graph API has also undergone a number of changes and is now subject to a versioning schedule. Get to know what's changing if you haven't already.

You need modern PHP

Version 5 of the SDK runs on PHP 5.4 or greater with the mbstring extension installed. If you're still on PHP 5.3, you really need to upgrade since 5.3 reached EOL and is no longer getting security updates. While you're at it, why not just install PHP 5.6!

And when PHP 7 comes out later this year, you should upgrade to is as soon as it is available. Let's get a culture of current PHP versions going. You play a key role in that. Demand it with us.

Installation with Composer

Speaking of modern PHP, the recommended way to install the Facebook PHP SDK v5 is with Composer. Just add the following to your composer.json and run composer update from the command-line.

{
  "require" : {
    "facebook/php-sdk-v4": "^5.0"
  }
}

As of writing, v5 is still in development mode so you need to use the @dev minimum-stability flag. Version 5 is tagged! Yay!

And you'll also notice that you require version ~4.1.0 instead of ~5.0. That because the version aliasing stuff hasn't been completed yet. But rest assured that you'll be getting v5 of the SDK when you include ~4.1.0. Fixed! :)

There has been a feature freeze & the docs are updated on GitHub so it's just a matter of time before the docs get pushed to the developer site and v5 gets tagged as stable. The docs for version 5 have been published!

A note on SemVer: The Facebook PHP SDK v4 did not follow Semantic Versioning (SemVer). The version schema for the SDK in v4 was 4.MAJOR.MINOR|PATCH. That's why you would have to use 3 version numbers with the ~ operator like ~4.0.0. But starting with v5, the SDK follows SemVer so we just use 2 version numbers: ~5.0. Alternatively we can use the carrot ^ since the SDK follows SemVer now.

Don't forget to include the Composer autoloader in your script if you haven't already done that.

Manual Installation

You don't use Composer? You should definitely start using it. But if you're needing a quick-and-dirty install, download the zip from GitHub and extract the files on your computer somewhere.

Move the folder located at src/Facebook to the location in your web app where you store 3rd-party vendor files. You can also rename the folder to facebook-php-sdk-v5 or something.

Assuming you moved & renamed the src/Facebook folder to /my/app/third_party/facebook-php-sdk-v5, in your web framework's bootstrap scripts or at the top of your PHP script, you need to include the autoloader.

require(__DIR__.'/third_party/facebook-php-sdk-v5/autoload.php');

Introduction & Comparison

Version 3.x of the PHP SDK, offered one big base class that did "all the things". You could instantiate the big Facbeook class and inject an array of configuration via the constructor.

# The v3.x way
$fb = new Facebook(array(
  'appId'  => '{app-id}',
  'secret' => '{app-secret}',
  ));

Version 5 has a surprisingly similar starting point.

# The v5 new-hotness way
$fb = new Facebook\Facebook([
  'app_id'     => '{app-id}',
  'app_secret' => '{app-secret}',
  'default_graph_version' => 'v2.3',
  ]);

You'll notice that v5 is namespaced under the Facebook\ namespace.

At first glance you might think v5 also has one big base class that does all the things. In reality, v5 consists of many, many different classes and the Facebook\Facebook just ties them all together.

Configuration

There were a number of configuration options in version 3.x of the PHP SDK. Not all of the configuration values match up between v3 and v5 so let's compare them.

App ID & Secret

The configuration keys for app ID and secret are named differently between the two SDK's.

SDK App ID Key App Secret Key
v3.x appId secret
v5 app_id app_secret

Persistent Data

By default, v3 would initialize a native PHP session and store a number of values for you including the the access token. In v5, the access token is not stored in persistent data, you have to do that yourself.

In fact there is only one value v5 will store in persistent data for you. Since the SDK needs to "remember" the CSRF token generated during the Facebook Login process (OAuth 2.0), the SDK will try to save the token to a native PHP session.

In v5, there is no session interaction unless you are using the PHP SDK for Facebook Login.

Overwriting Persistent Data

In v3, all of the main domain logic was written in the BaseFacebook abstract class. The Facebook class extended from BaseFacebook and contained methods like getPersistentData() that would give you control of how the persistent data was being stored & retrieved.

To overwrite the persistent data handler in v5, you'll need to create a class that implements the PersistentDataInterface, instantiate it, then inject it into the Facebook constructor.

Let's see how we could do this in CodeIgniter v2.

First we need to tell the PHP SDK how your web framework stores persistent data by coding to the PersistentDataInterface. For this example I'm creating a file in application/third_party/fb_persistent_data_interface.php, but you can put yours wherever you want.

# application/third_party/fb_persistent_data_interface.php

use Facebook\PersistentData\PersistentDataInterface;

class CIFacebookPersistentDataHandler implements PersistentDataInterface
{
  // Prefix to use for session variables.
  private $sessionPrefix = 'FBRLH_';
  
  // The main CodeIgniter instance
  private $CI;

  public function __construct()
  {
    $this->CI =& get_instance();
    // If you haven't already...
    //$this->CI->load->library('session');
  }
  
  /**
   * Retrieves a value from a session.
   */
  public function get($key)
  {
    return $this->CI->session->userdata($this->sessionPrefix.$key);
  }

  /**
   * Stores a value into a session.
   */
  public function set($key, $value)
  {
    $this->CI->session->set_userdata($this->sessionPrefix.$key, $value);
  }
}

Then we need to inject an instance of our handler into the Facebook service with the persistent_data_handler configuration option. For this example, I'm just going to jam all the logic in a controller.

Autoloading: This example assumes you've already included the autoloader from composer or the PHP SDK somewhere (like the base index.php file). If not, you'll need to uncomment the require() to the autoloader.

# controllers/foo.php

// If you haven't included the autoloader yet
//require(APPPATH.'third_party/facebook-php-sdk-v5/autoload.php');
require(APPPATH.'third_party/fb_persistent_data_interface.php');

class Foo extends CI_Controller
{
  function bar()
  {
    $fb = new Facebook\Facebook([
      // Your app ID & secret config here
      // . . .
      'persistent_data_handler' => new CIFacebookPersistentDataHandler(),
      ]);
  }
}

Now when you use Facebook Login with the PHP SDK, it'll make use of CodeIgniter's native session handling. See "Authentication" below for more on Facebook Login with v5.

The "allowSignedRequest" option

The allowSignedRequest was a configuration option of v3 that was a workaround for having the main BaseFacebook class doing way too many things.

Tangent: What is a signed request?

Signed request's are Facebook's flavor of a JSON web token. They are base64-encoded, URL-escaped payloads of JSON data packaged with a hash signature to valide that the source of the data is trusted.

Signed request's are made available to your app in a number of ways:

  1. From within app canvas & page tabs using HTTP POST key signed_request.
  2. From the fbsr_{your-app-id} cookie the JavaScript SDK sets when the {cookie:true} option is enabled.
  3. From deauthorize callback URL POST requests
  4. And prolly some other places :)

Back to the "allowSignedRequest" option

If the allowSignedRequest option was enabled, the SDK would check for a signed request from the signed_request POST variable (found within the context of a canvas app or Page tab).

If the allowSignedRequest option was disabled or no signed request could be found, the SDK would look for the signed request in the cookie from the JavaScript SDK. There wasn't a way to disabled this cookie-fallback so if you had an old cookie stored on your site, you'd get an old signed request and access token. This was particularly annoying behavior.

In v5, signed requests are obtained from an app canvas/Page tab context using the FacebookCanvasHelper (see the "Getting Access Tokens From App Canvas/Page Tabs" section below) and they are obtained from the JavaScript SDK cookie using the FacebookJavaScriptHelper (see the "Getting Access Tokens From The JavaScript SDK" section below).

Proxies with "trustForwarded"

During the Facebook Login process, on the page that a user is redirected to after being presented with the app authentication dialog, the PHP SDK will try to detect the current URL as a connivence to the developer.

Since detecting the current URL is actually non-trivial in PHP, there are a number of environments that could generate different results. If enabled, the trustForwarded option in v3 would tell the SDK to check the HTTP header X_FORWARDED_HOST when detecting the hostname.

Most common environments don't need to worry about tweaking the URL-detection algorithms, especially since v5 majorly improves on the algorithm from v3. But if you do need to customize the URL detection, in v5 you can overwrite the default behavior by coding to the UrlDetectionInterface and injecting an instance into the Facebook constructor via the url_detection_handler configuration key.

use Facebook\Url\UrlDetectionInterface;

class MyUrlDetectionHandler implements UrlDetectionInterface
{
  /**
   * Get the complete current URL
   */
  public function getCurrentUrl()
  {
    // Do logic here to detect the URL
    $url = '';
    return $url;
  }
}

$fb = new Facebook\Facebook([
  // Your app ID & secret config here
  // . . .
  'url_detection_handler' => new MyUrlDetectionHandler(),
  ]);

Authentication

Authenticating a user with your app has changed quite a bit in v5, but it is all for the better.

Checking If A User Is Logged In

A common way of checking if a user was "logged in" to an app in v3 was to check getUser() for a user ID. The logic behind this method is as follows: if a signed request can be detected, decoded & validated and it contains a user_id in the payload, then the user has previously authenticated the app and can be considered "logged in".

# v3 example of "logged in" status

$user = $fb->getUser();

// If we have a $user id here, it means we know the user is logged into
// Facebook, but we don't know if the access token is valid.
if ($user) {
  try {
    // Proceed knowing you have a logged in user who's authenticated.
    $profile = $fb->api('/me');
  } catch (FacebookApiException $e) {
    $user = null;
  }
}

In v5, you can use the same logic but you'll need to be explicit about what context you plan on obtaining the signed request. You do this with "helpers" (more on this later).

# v5 example of "logged in" status

// Choose your app context helper
$helper = $fb->getCanvasHelper();
//$helper = $fb->getPageTabHelper();
//$helper = $fb->getJavaScriptHelper();

// Grab the signed request entity
$sr = $helper->getSignedRequest();

// Get the user ID if signed request exists
$user = $sr ? $sr->getUserId() : null;

if ($user) {
  try {
    // Get the access token
    $accessToken = $helper->getAccessToken();
  } catch(Facebook\Exceptions\FacebookSDKException $e) {
    // There was an error communicating with Graph
    echo $e->getMessage();
    exit;
  }
}

Bugs With Getting An Access Token In v3

Hands down, the worst part of the PHP SDK v3.x was how it tried to obtain access tokens. I don't know about you, but I have less hair and probably less years of my life due to the bugginess of this code.

Let's step back a sec.

There are three primary ways your app can obtain a user access token:

  1. From the signed request data POSTed to your app from within app canvas or Page tab context.
  2. From the signed request data in the cookie that the JavaScript SDK set.
  3. From a code obtained in the callback URL after a successful OAuth 2.0 login flow. (Which starts with an authorization URL or "login link" that a user follows.)

The main problem with obtaining an access token in v3 is that it would try to "best guess" which of the three methods you wanted to use. If it failed obtaining the access token with one method, it would automatically fallback to checking the other methods.

This guessing sparked scenarios where the PHP SDK would return one access token when a user visits your canvas app for the first time and then the SDK would suddenly deliver a different access token when the user would visit a different page within your app. Grrrr! Is it this exact bug that inspired me to start contributing to the official Facebook PHP SDK back in May of 2014.

Getting an Access Token

In v3, you could obtain a user access token from the getAccessToken() method. For the reasons described above, this buggy, shoe-stringy code would return inconsistent results.

When version v4.0 came out in April of 2014, it did a great job of separating all the methods of obtaining access tokens using "helpers". The helpers are classes that focus on returning access tokens using one, and only one, of the three methods I described above.

In version v5, creating instances of these helpers is super easy using the the factory methods that exist on the Facebook super service class.

Getting Access Tokens From Facebook Login (manual redirect)

You can implement Facebook Login (OAuth 2.0) with the PHP SDK by using the FacebookRedirectLoginHelper. An instance of this helper can be obtained from the Facebook super service by using the getRedirectLoginHelper() method.

$fb = new Facebook\Facebook([/* . . . */]);

$helper = $fb->getRedirectLoginHelper();

Once you get an instance of the FacebookRedirectLoginHelper, you can use it to generate an authorization URL to start an OAuth 2.0 flow. You pass to this method the redirect URL (where the user is redirected to after approving or denying the app authorization request), and an array of permissions you want to ask them for.

$permissions = ['email', 'user_posts']; // optional
$callback = 'http://example.com/fb-login-callback.php';
$loginUrl = $helper->getLoginUrl($callback, $permissions);

echo '<a href="'.$loginUrl.'">Log in with Facebook!</a>';

Then in your callback script, fb-login-callback.php, you grab an instance of the FacebookRedirectLoginHelper again and call the getAccessToken() method.

$accessToken = $helper->getAccessToken();

This one-liner is fine if all goes well, but there are a number of possible scenarios our callback script should account for.

  1. If all goes well, $accessToken will be an instance of the AccessToken entity. (See "The AccessToken Entity" section below.)
  2. If there was a problem communicating with the Graph API, the getAccessToken() method will throw a FacebookSDKException, so you'll want to wrap that bad boy with a try/catch to catch the exception and display a proper error message to the user.
  3. If the user denied the request, $accessToken will be null. What this really means is that the SDK could not find an OAuth code in the GET params. Keep reading.
  4. If there is no code param, there should be a number of error_* params that describe why there's no code. The error params can be accessed using the following methods: getError(), getErrorCode(), getErrorReason(), and getErrorDescription(). We can assume that if getError() returns null, then all the other error getter methods will also return null.
  5. In the event that both getAccessToken() and getError() methods return null, then whoever is hitting your callback endpoint ain't coming from Facebook. In this situation I generally return a 400 Bad Request HTTP response. It's also probably a good idea to log this suspicious request.

If we update our callback script to account for all these scenarios, we get a lot more bullet-proof code.

# fb-login-callback.php
$fb = new Facebook\Facebook([/* . . . */]);

$helper = $fb->getRedirectLoginHelper();
try {
  $accessToken = $helper->getAccessToken();
} catch(Facebook\Exceptions\FacebookSDKException $e) {
  // There was an error communicating with Graph
  echo $e->getMessage();
  exit;
}

if (isset($accessToken)) {
  // User authenticated your app!
  // Save the access token to a session and redirect
  $_SESSION['facebook_access_token'] = (string) $accessToken;
  // Log them into your web framework here . . .
  // Redirect here . . .
  exit;
} elseif ($helper->getError()) {
  // The user denied the request
  // You could log this data . . .
  var_dump($helper->getError());
  var_dump($helper->getErrorCode());
  var_dump($helper->getErrorReason());
  var_dump($helper->getErrorDescription());
  // You could display a message to the user
  // being all like, "What? You don't like me?"
  exit;
}

// If they've gotten this far, they shouldn't be here
http_response_code(400);
exit;

Getting Access Tokens From App Canvas/Page Tabs

When you have an app that runs in app canvas or a Page tab, an access token can be obtained from the signed request that is POSTed to your app but this will only ever be true if the user has already authorized your app. If the user has not authorized your app yet, you'll need to log them in using the OAuth 2.0 login flow as described in the section above or by using the JavaScript SDK.

If an access token exists in the signed request, it can be obtained using the FacebookCanvasHelper. An instance of this helper can be generated from the Facebook super service by using the getCanvasHelper() method.

$fb = new Facebook\Facebook([/* . . . */]);

$helper = $fb->getCanvasHelper();
try {
  $accessToken = $helper->getAccessToken();
} catch(Facebook\Exceptions\FacebookSDKException $e) {
  // There was an error communicating with Graph
  // Or there was a problem validating the signed request
  echo $e->getMessage();
  exit;
}

if ($accessToken) {
  // Logged in.
}

Page tabs and canvas apps are virtually identical but there are a few important differences. One added feature of a Page tab app is that the signed request will contain additional data about the parent page.

There is a FacebookPageTabHelper that helps you interface with the components available to your Page tab. The page tab helper extends from the FacebookCanvasHelper so all the methods are adopted as well. And there are a few additional methods you get when using the FacebookPageTabHelper.

$fb = new Facebook\Facebook([/* . . . */]);

$helper = $fb->getPageTabHelper();

// Returns info about the parent page
$pageData = $helper->getPageData();

// A boolean of whether or not the
// authenticated user is an admin
// of the parent page
$isAdmin = $helper->isAdmin();

// The ID of the parent page
$pageId = $helper->getPageId();

Getting Access Tokens From The JavaScript SDK

The JavaScript SDK has a really great UI for Facebook Login. It is a better user experience to use the JavaScript SDK to authenticate a user into your app than it is to use the PHP SDK's OAuth 2.0 flow with the FacebookRedirectLoginHelper.

I know what you're thinking: Once I log a user in with the JavaScript SDK, how does my PHP script access the access token?

By default the JavaScript SDK won't store any info about the user on your domain unless you explicitly tell it to. So in your FB.init() method, you'll need to enable the cookie option with {cookie:true}. This tells the JavaScript SDK to set a cookie on your domain that contains a signed request with information about the authenticated user.

FB.init({
  appId   : '{app-id}',
  cookie  : true,
  version : 'v2.3'
});

If an access token exists in the signed request that was set by the JavaScript SDK, it can be obtained using the FacebookJavaScriptHelper. An instance of this helper can be generated from the Facebook super service by using the getJavaScriptHelper() method.

$fb = new Facebook\Facebook([/* . . . */]);

$helper = $fb->getJavaScriptHelper();
try {
  $accessToken = $helper->getAccessToken();
} catch(Facebook\Exceptions\FacebookSDKException $e) {
  // There was an error communicating with Graph
  // Or there was a problem validating the signed request
  echo $e->getMessage();
  exit;
}

if ($accessToken) {
  // Logged in.
}

The AccessToken Entity

Another big change in v5 is that access tokens are not treated as plain-old strings, but they are represented with an AccessToken entity.

There are two ways to get the access token as a plain-old string.

// These statements are equivalent
echo (string) $accessToken;
echo $accessToken->getValue();

Having AccessToken entities gives us some more power when handling access tokens. For example, you can do a strict check to ensure that you definitely have an access token.

if ($accessToken instanceof Facebook\Authentication\AccessToken) {
  // Logged in.
}

You can also typehint function & method arguments that take an access token.

function (Facebook\Authentication\AccessToken $token) {
  // . . .
}

There are also a number of handy methods on the AccessToken entity.

// Returns expiration as a DateTime entity
$expiresAt = $accessToken->getExpiresAt();

// Returns boolean
$expired = $accessToken->isExpired();

// Returns boolean
$isLong = $accessToken->isLongLived();

// Returns boolean
$isAppToken = $accessToken->isAppAccessToken();

// Returns the app secret proof as a string
// This is used to sign requests to the Graph API
// All requests made using the PHP SDK are
// signed automatically for you
$proof = $accessToken->getAppSecretProof('{app-secret}');

Getting a Long-Lived (Extended) Access Token

In v3, you could exchange a short-lived access token for a long-lived access token using the setExtendedAccessToken() method.

# v3 example
// Returns the default short-lived access token
$accessToken = $fb->getAccessToken();

// Do all the work to extend it...
$fb->setExtendedAccessToken();

// Returns a long-lived access token now
$accessToken = $fb->getAccessToken();

There were a number of bugs and issues associated with the way v3 performed these actions. In v5 this process got decoupled by using an instance of OAuth2Client.

You can generate an instance of the OAuth2Client by using the getOAuth2Client() method on the Facebook super service.

$cilent = $fb->getOAuth2Client();

try {
  // Returns a long-lived access token
  $accessToken = $cilent->getLongLivedAccessToken('{short-lived-token}');
} catch(Facebook\Exceptions\FacebookSDKException $e) {
  // There was an error communicating with Graph
  echo $e->getMessage();
  exit;
}

The OAuth2Client has a number of cool features like a debugToken() method that returns an AccessTokenMetadata entity with information about the access token to be used to validate against or debug.

try {
  $metaData = $cilent->debugToken('{access-token}');
} catch(Facebook\Exceptions\FacebookSDKException $e) {
  // There was an error communicating with Graph
  echo $e->getMessage();
  exit;
}

var_dump($metaData->getAppId());
var_dump($metaData->getApplication());
var_dump($metaData->isError());
var_dump($metaData->getIssuedAt());

// These all throw a FacebookSDKException
$metaData->validateAppId('{app-id}');
$metaData->validateUserId('{user-id}');
$metaData->validateExpiration();

Requests

The primary way of making requests to the Graph API in v3 was via the api() method.

# GET request in v3
$res = $fb->api('/me');

# POST request in v3
$res = $fb->api('/me/feed', 'POST', $data);

# DELETE request in v3
$res = $fb->api('/123', 'DELETE', $data);

The Graph API only supports the GET, POST and DELETE HTTP verbs. In v5, each of these verbs get their own corresponding method.

# GET request in v5
$res = $fb->get('/me', '{access-token}');

# POST request in v5
$res = $fb->post('/me/feed', $data, '{access-token}');

# DELETE request in v5
$res = $fb->delete('/123', $data, '{access-token}');

If you don't want to have to send in '{access-token}' with every method call in v5, you can use the setDefaultAccessToken() method.

$fb->setDefaultAccessToken('{access-token}');

# These will fall back to the default access token
$res = $fb->get('/me');
$res = $fb->post('/me/feed', $data);
$res = $fb->delete('/123', $data);

Responses

Responses from the api() method in v3 would always return a plain-old PHP array.

# v3 response example
$res = $fb->api('/me');

var_dump($res);

// array(10) { ...

In v5, the responses from the get(), post() & delete() methods return a FacebookResponse entity which is an object that represents an HTTP response from the Graph API.

If all you want is a plain-old PHP array just like v3 used to do, you call the getDecodedBody() method on the FacebookResponse entity.

# v5 response example
$res = $fb->get('/me');

var_dump($res->getDecodedBody());

// array(10) { ...

But v5 can return the response data as a collection which can be really handy for performing actions on the response data.

I won't get into too much detail with this, but the collections come in the form of GraphObjects and they can be obtained from the FacebookResponse entity using the getGraphObject() method.

# v5 response example
$res = $fb->get('/me');

$node = $res->getGraphObject();

var_dump($node->getProperty('id'));
// string(3) "123"

// Functional-style!
$node->map(function($value, $key) {
  // . . .
});

Exceptions

Sometimes the Graph API will return an error response. Sad face. In v3, if an error response was returned, the api() method would throw a FacebookApiException. So you would need to handle the errors by catching the exception.

# v3 error response example
try {
  $res = $fb->api('/123');
} catch (FacebookApiException $e) {
  echo $e->getMessage();
  exit;
}

Similarly in v5 a FacebookSDKException will be thrown if something goes wrong.

# v5 error response example
try {
  $res = $fb->get('/123');
} catch (Facebook\Exceptions\FacebookSDKException $e) {
  echo $e->getMessage();
  exit;
}

There are more than just the base FacebookSDKException in v5 but I won't get into too much detail on the other types of exceptions. But know that the other exceptions extend from FacebookSDKException so you can be assured that whenever you catch FacebookSDKException, you're catching any of the extended exceptions as well.

The FacebookSDKException is not limited to error responses from the Graph API. There are other instances when a base FacebookSDKException might be thrown in v5. Some examples include a signed request fails to validate, a file you want to upload cannot be found and so on.

File Uploads

There was a fileUpload configuration option in v3.x that allowed you to enable file upload support. You would set this to true and make use of CURLFile to upload a file. Behind the scenes this would trigger the request to be sent as multipart/form-data instead of the default application/x-www-form-urlencoded.

# The v3.x way
$fb = new Facebook(array(
  // . . .
  'fileUpload' => true,
  ));

$data = array(
  'source' => new CURLFile('path/to/file.png', 'image/png'),
  'message' => 'My file!'
  );

$fb->api('/me/feed', 'POST', $data);

In v5, you don't have to explicitly enable file uploads as it will automatically detect that you are uploading a file. And you also don't need to rely on the CURLFile class which is really handy if you don't have the cURL extension installed. Out of the box v5 supports streams as a fallback HTTP client.

$fb = new Facebook\Facebook([/* . . . */]);

$data = [
  'source' => $fb->fileToUpload('/path/to/photo.jpg'),
  'message' => 'My file!',
  ];

$response = $fb->post('/me/photos', $data, '{access-token}');

You may also notice that when you use the fileToUpload() method, you don't have to explicitly define the mime-type of the file, that is detected automatically.

Uploading Videos

If you're uploading a video, that requires using the videoToUpload() method.

$fb = new Facebook\Facebook([/* . . . */]);

$data = [
  'source' => $fb->videoToUpload('/path/to/video.mp4'),
  'title' => 'My video',
  'description' => 'My amazing video!',
  ];

$response = $fb->post('/me/videos', $data, '{access-token}');

Logging A User Out

It's usually not a common practice to make use of the log out feature within the PHP SDK as this will log a user out of Facebook completely, not just your app. So unless you've got a specific edge case where you need to do this, it's not recommended that you use it.

That said, you can log a user out of Facebook in v5 using the getLogoutUrl() method on the FacebookRedirectLoginHelper.

$fb = new Facebook\Facebook([/* */]);

$helper = $fb->getRedirectLoginHelper();

$logoutUrl = $helper->getLogoutUrl('{access-token}', 'http://example.com');
echo '<a href="' . $logoutUrl . '">Logout of Facebook!</a>';

Where http://example.com is the URL the user should be redirected to after logging out.

New Features in v5

Aside from the new features I've already mentioned, there are tons of new features in v5 that weren't available in v3. Three big features are easy batch requests & easy pagination and an injectable HTTP client. But that's for another blog post. :)

Final Thoughts - Abstraction!

Facebook changes their development platform all the time. There's already plenty of talk of more breaking changes slated for version 4.2 of the PHP SDK.

It's not a bad idea to implement the Facebook PHP SDK into your web framework via a layer of abstraction with which you would instantiate your own service class to handle the configuration, authentication, requests & responses that tie directly into the official SDK. That way when the SDK changes, you only have to update one area of your code and it makes upgrading less horrible.

Good luck!

If you found this guide helpful, say, "Hi" on twitter! I'd love to hear from you. :)