Reputation: 4975
When using the Facebook Graph API to return more than 500 elements (like a friend list) paging is required. What's a good way to do this?
Upvotes: 2
Views: 3205
Reputation: 4975
Here is the way that I use paging on my own apps.
http://developsocialapps.com/facebook-friends-list-and-paging/
The library has most of the code needed. The main method is getGraphObjectWithPaging. It gets the object with the graph API and then keeps looping as long as there is a next page in the response or the $maxpages has been reached. One peculiarity is that sometimes Facebook returns the next page as the same page you just got, so it checks for this and stops at that point too.
class FacebookApp {
public $appId;
private $appSecret;
private $nameSpace;
public $userId;
public $token;
public $tokenExpires;
// get your own from http://www.w3.org/P3P/
public $p3p = 'P3P:CP="IDC DSP COR ADM DEVi TAIi PSA PSD IVAi IVDi CONi HIS OUR IND CNT"';
/* construct object
appid, secret, and namespace from app settings */
public function __construct($id, $secret, $namespace) {
$this->appId = $id;
$this->appSecret = $secret;
$this->nameSpace = $namespace;
}
/* return json data from a graph api object using paging
$object = object to get
limit = limit parameter for API object
maxpages = maximum number of pages to get */
function getGraphObjectWithPaging($object,$limit=500,$maxpages=10) {
$data = array();
$url = $this->getGraphUrl($object,$limit);
// loop through API calls until maxpages or no paging->next
while ($maxpages > 0) {
$response = $this->makeCurlRequest($url);
if ($repsonse === false) {
// something went wrong
break;
} else {
$jsonarray = json_decode($response,true);
if (isset($jsonarray['error'])) {
// something went wrong
break;
} else {
// add current data to data array
$data = array_merge ($data,$jsonarray['data']);
if (isset($jsonarray['paging']['next'])) {
if ($url == $jsonarray['paging']['next']) {
// for some reason facebook sometimes returns a next url which is the same as we just got, so exit here
break;
} else {
// keep looping
$url = $jsonarray['paging']['next'];
$maxpages--;
}
} else {
// no more pages
break;
}
}
}
}
return array("data"=>$data); // using data so it is the same format as other API repsonses
}
/* constructs graphs url */
public function getGraphUrl($object,$limit=false) {
$url = "https://graph.facebook.com/".$object;
if (strpos($url,"?") === false) $url .= "?";
else $url .= "&";
$url .= "access_token=".$this->token;
if ($limit !== false) $url .= "&limit=".$limit;
return $url;
}
/* uses curl to get a url, use $postarray to make a post, otherwise it will get */
public function makeCurlRequest($url,$postarray=false) {
$return = false;
try {
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_HEADER, false);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
if($postarray !== false){
curl_setopt ($ch, CURLOPT_POST, true);
curl_setopt ($ch, CURLOPT_POSTFIELDS, $postarray);
}
$response = curl_exec($ch);
$responseInfo = curl_getinfo($ch);
curl_close($ch);
if ($responseInfo['http_code']==200) {
$return = $response;
}
} catch (Exception $e) {
$return = false;
}
return $return;
}
/* sets userid and token from signed request, return true or false if authorized */
public function initOauthUserFromSignedRequest() {
$authorized = false;
if (isset($_REQUEST['signed_request'])) {
$data = $this->parseSignedRequest($_REQUEST['signed_request']);
if ($data !== false) {
if (isset($data['user_id']) && isset($data['oauth_token'])) {
$this->userId = $data['user_id'];
$this->token = $data['oauth_token'];
$this->tokenExpires = $data['expires'];
$authorized = true;
}
}
}
return $authorized;
}
/* require user to authorize and have permissions for page
redirect_uri = url to return after user has authorized like redirect.php
success_uri = url to redirect to on successful authorization like mypage.php
scope = comma separted list of permissions */
function requireAuthorization($redirect_uri,$success_uri=false,$scope=false) {
if ($success_uri === false) {
// if no success_uri use current page, all files for app must be in same directory
$success_uri = substr($_SERVER['REQUEST_URI'],strrpos($_SERVER['REQUEST_URI'],"/")+1);
}
$this->setCookie ("success_uri",$success_uri,0); // we will use this on the redirect_uri page
$requireauth = true;
if ($this->initOauthUserFromSignedRequest()) { // user has authorized
if (($scope === false) || ($this->hasAllPermissions($scope))) { // now check for perms
$requireauth = false;
}
}
if ($requireauth) { // user is either not authorized or doesn't have permissions
$url = $this->getAuthUrl($this->getCanvasUrl($redirect_uri),$scope);
echo "<html>\n<body>\n<script>\ntop.location.href='".$url."';\n</script></body></html>";
exit();
}
}
/* checks to see if has permissions, scope is comma separated list */
public function hasAllPermissions($scope) {
$return = false;
$cookiename = "permissions_".$this->appId."_".$this->userId;
$requiredpermissions = explode(",",$scope);
// first check cookie
if (isset($_COOKIE[$cookiename])) {
$return = true;
$permissions = json_decode($_COOKIE[$cookiename],true);
foreach ($requiredpermissions as $perm) {
if ($permissions['data'][0][$perm] != 1) {
$return = false;
break;
}
}
}
// if didn't have all in cookie, then see if it is in graph
if ($return == false) {
$permissions = $this->getGraphObject("me/permissions");
if ($permissions !== false) {
$this->setCookie($cookiename,json_encode($permissions),0);
$return = true;
foreach ($requiredpermissions as $perm) {
if ($permissions['data'][0][$perm] != 1) {
$return = false;
break;
}
}
}
}
return $return;
}
/* sets a cookie with p3p headers */
public function setCookie($name,$value,$expires) {
if ($this->p3p != '') {
header($this->p3p);
$this->p3p = '';
}
setcookie ($name,$value,$expires,"/");
}
/* returns url for oauth authorization
redirect_uri = url to return after user has authorized
scope = comma separted list of permissions */
public function getAuthUrl($redirect_uri,$scope=false) {
$url = "https://www.facebook.com/dialog/oauth/?client_id=".$this->appId."&redirect_uri=".rawurlencode($redirect_uri);
if ($scope !== false) $url .= "&scope=".rawurlencode($scope);
return $url;
}
/* returns url to app canvas page, $page like mypage.php?foo=bar */
public function getCanvasUrl($page) {
if ($_SERVER['HTTPS'] == "on") $protocol = "https";
else $protocol = "http";
return $protocol."://apps.facebook.com/".$this->nameSpace."/".$page;
}
/* parses signed_request parameter and returns data object, returns false if sigs don't match */
public function parseSignedRequest($signed_request) {
list($encoded_sig, $payload) = explode('.', $signed_request, 2);
$data = json_decode(base64_decode(strtr($payload, '-_', '+/')), true);
$sig = base64_decode(strtr($encoded_sig, '-_', '+/'));
$expected_sig = hash_hmac('sha256', $payload, $this->appSecret, true);
if ($sig == $expected_sig) {
return $data;
} else {
return false;
}
}
}
Here is how to use it on a page:
$facebookapp = new FacebookApp($GLOBALS['facebookAppId'],$GLOBALS['facebookAppSecret'],$GLOBALS['facebookNamespace']);
$facebookapp->requireAuthorization($GLOBALS['facebookRedirectPage']);
$friends = $facebookapp->getGraphObjectWithPaging("me/friends");
Upvotes: 2