Search code examples
phpfacebookfacebook-graph-apipagination

How to use paging in the Facebook Graph API?


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?


Solution

  • 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");