18 if (!function_exists(
'curl_init')) {
19 throw new Exception(
'Facebook needs the CURL PHP extension.');
21 if (!function_exists(
'json_decode')) {
22 throw new Exception(
'Facebook needs the JSON PHP extension.');
47 if (isset(
$result[
'error_description'])) {
49 $msg =
$result[
'error_description'];
50 }
else if (isset(
$result[
'error']) && is_array(
$result[
'error'])) {
52 $msg =
$result[
'error'][
'message'];
53 }
else if (isset(
$result[
'error_msg'])) {
57 $msg =
'Unknown Error. Check getResult()';
60 parent::__construct($msg,
$code);
79 if (isset($this->result[
'error'])) {
80 $error = $this->result[
'error'];
81 if (is_string($error)) {
84 }
else if (is_array($error)) {
86 if (isset($error[
'type'])) {
87 return $error[
'type'];
101 $str = $this->
getType() .
': ';
102 if ($this->code != 0) {
103 $str .= $this->code .
': ';
123 const VERSION =
'3.2.2';
128 const SIGNED_REQUEST_ALGORITHM =
'HMAC-SHA256';
133 public static $CURL_OPTS = array(
134 CURLOPT_CONNECTTIMEOUT => 10,
135 CURLOPT_RETURNTRANSFER =>
true,
136 CURLOPT_TIMEOUT => 60,
137 CURLOPT_USERAGENT =>
'facebook-php-3.2',
143 public static $DOMAIN_MAP = array(
144 'api' =>
'https://api.facebook.com/',
145 'api_video' =>
'https://api-video.facebook.com/',
146 'api_read' =>
'https://api-read.facebook.com/',
147 'graph' =>
'https://graph.facebook.com/',
148 'graph_video' =>
'https://graph-video.facebook.com/',
149 'www' =>
'https://www.facebook.com/',
189 protected $accessToken = null;
196 protected $fileUploadSupport =
false;
203 protected $trustForwarded =
false;
216 $this->setAppId(
$config[
'appId']);
217 $this->setAppSecret(
$config[
'secret']);
218 if (isset(
$config[
'fileUpload'])) {
219 $this->setFileUploadSupport(
$config[
'fileUpload']);
221 if (isset(
$config[
'trustForwarded']) &&
$config[
'trustForwarded']) {
222 $this->trustForwarded =
true;
224 $state = $this->getPersistentData(
'state');
237 $this->appId = $appId;
258 $this->setAppSecret($apiSecret);
269 $this->appSecret = $appSecret;
280 return $this->getAppSecret();
289 return $this->appSecret;
299 $this->fileUploadSupport = $fileUploadSupport;
309 return $this->fileUploadSupport;
320 return $this->getFileUploadSupport();
332 $this->accessToken = $access_token;
345 $access_token_response = $this->_oauthRequest(
346 $this->getUrl(
'graph',
'/oauth/access_token'),
348 'client_id' => $this->getAppId(),
349 'client_secret' => $this->getAppSecret(),
350 'grant_type' =>
'fb_exchange_token',
351 'fb_exchange_token' => $this->getAccessToken(),
361 if (empty($access_token_response)) {
365 $response_params = array();
366 parse_str($access_token_response, $response_params);
368 if (!isset($response_params[
'access_token'])) {
372 $this->destroySession();
374 $this->setPersistentData(
375 'access_token', $response_params[
'access_token']
389 if ($this->accessToken !== null) {
391 return $this->accessToken;
397 $this->setAccessToken($this->getApplicationAccessToken());
398 $user_access_token = $this->getUserAccessToken();
399 if ($user_access_token) {
400 $this->setAccessToken($user_access_token);
403 return $this->accessToken;
420 $signed_request = $this->getSignedRequest();
421 if ($signed_request) {
423 if (array_key_exists(
'oauth_token', $signed_request)) {
424 $access_token = $signed_request[
'oauth_token'];
425 $this->setPersistentData(
'access_token', $access_token);
426 return $access_token;
430 if (array_key_exists(
'code', $signed_request)) {
431 $code = $signed_request[
'code'];
432 if (
$code &&
$code == $this->getPersistentData(
'code')) {
434 return $this->getPersistentData(
'access_token');
437 $access_token = $this->getAccessTokenFromCode(
$code,
'');
439 $this->setPersistentData(
'code',
$code);
440 $this->setPersistentData(
'access_token', $access_token);
441 return $access_token;
447 $this->clearAllPersistentData();
452 $code = $this->getCode();
453 if (
$code &&
$code != $this->getPersistentData(
'code')) {
454 $access_token = $this->getAccessTokenFromCode(
$code);
456 $this->setPersistentData(
'code',
$code);
457 $this->setPersistentData(
'access_token', $access_token);
458 return $access_token;
462 $this->clearAllPersistentData();
470 return $this->getPersistentData(
'access_token');
480 if (!$this->signedRequest) {
481 if (!empty($_REQUEST[
'signed_request'])) {
482 $this->signedRequest = $this->parseSignedRequest(
483 $_REQUEST[
'signed_request']);
484 }
else if (!empty(
$_COOKIE[$this->getSignedRequestCookieName()])) {
485 $this->signedRequest = $this->parseSignedRequest(
486 $_COOKIE[$this->getSignedRequestCookieName()]);
489 return $this->signedRequest;
499 if ($this->
user !== null) {
504 return $this->
user = $this->getUserFromAvailableData();
518 $signed_request = $this->getSignedRequest();
519 if ($signed_request) {
520 if (array_key_exists(
'user_id', $signed_request)) {
521 $user = $signed_request[
'user_id'];
523 if(
$user != $this->getPersistentData(
'user_id')){
524 $this->clearAllPersistentData();
527 $this->setPersistentData(
'user_id', $signed_request[
'user_id']);
533 $this->clearAllPersistentData();
538 $persisted_access_token = $this->getPersistentData(
'access_token');
542 $access_token = $this->getAccessToken();
544 $access_token != $this->getApplicationAccessToken() &&
545 !(
$user && $persisted_access_token == $access_token)) {
546 $user = $this->getUserFromAccessToken();
548 $this->setPersistentData(
'user_id',
$user);
550 $this->clearAllPersistentData();
570 $this->establishCSRFTokenState();
571 $currentUrl = $this->getCurrentUrl();
575 if ($scopeParams && is_array($scopeParams)) {
576 $params[
'scope'] = implode(
',', $scopeParams);
579 return $this->getUrl(
583 'client_id' => $this->getAppId(),
584 'redirect_uri' => $currentUrl,
585 'state' => $this->state),
599 return $this->getUrl(
603 'next' => $this->getCurrentUrl(),
604 'access_token' => $this->getUserAccessToken(),
621 return $this->getUrl(
623 'extern/login_status.php',
625 'api_key' => $this->getAppId(),
626 'no_session' => $this->getCurrentUrl(),
627 'no_user' => $this->getCurrentUrl(),
628 'ok_session' => $this->getCurrentUrl(),
629 'session_version' => 3,
640 $args = func_get_args();
641 if (is_array($args[0])) {
642 return $this->_restserver($args[0]);
644 return call_user_func_array(array($this,
'_graph'), $args);
658 return 'fbsr_'.$this->getAppId();
669 return 'fbm_'.$this->getAppId();
681 if (isset($_REQUEST[
'code'])) {
682 if ($this->state !== null &&
683 isset($_REQUEST[
'state']) &&
684 $this->state === $_REQUEST[
'state']) {
688 $this->clearPersistentData(
'state');
689 return $_REQUEST[
'code'];
691 self::errorLog(
'CSRF state token does not match one provided. ' . $this->state .
'!=' . $_REQUEST[
'state']);
711 $user_info = $this->api(
'/me');
712 return $user_info[
'id'];
726 return $this->appId.
'|'.$this->appSecret;
735 if ($this->state === null) {
736 $this->state = md5(uniqid(mt_rand(),
true));
737 $this->setPersistentData(
'state', $this->state);
758 if ($redirect_uri === null) {
759 $redirect_uri = $this->getCurrentUrl();
765 $access_token_response =
766 $this->_oauthRequest(
767 $this->getUrl(
'graph',
'/oauth/access_token'),
768 $params = array(
'client_id' => $this->getAppId(),
769 'client_secret' => $this->getAppSecret(),
770 'redirect_uri' => $redirect_uri,
775 self::errorLog($e->getMessage());
779 if (empty($access_token_response)) {
780 self::errorlog(
'No access token response');
784 $response_params = json_decode($access_token_response,
true);
785 if (!isset($response_params[
'access_token'])) {
786 self::errorlog(
'No access token in response. ' . $access_token_response);
790 return $response_params[
'access_token'];
803 $params[
'api_key'] = $this->getAppId();
804 $params[
'format'] =
'json-strings';
806 $result = json_decode($this->_oauthRequest(
807 $this->getApiUrl(
$params[
'method']),
813 $this->throwAPIException(
$result);
818 $method = strtolower(
$params[
'method']);
819 if ($method ===
'auth.expiresession' ||
820 $method ===
'auth.revokeauthorization') {
821 $this->destroySession();
836 if ($method ==
'POST' && preg_match(
"/^(\/)(.+)(\/)(videos)$/",
$path)) {
853 if (is_array($method) && empty(
$params)) {
859 if ($this->isVideoPost(
$path, $method)) {
860 $domainKey =
'graph_video';
862 $domainKey =
'graph';
865 $result = json_decode($this->_oauthRequest(
866 $this->getUrl($domainKey,
$path),
872 $this->throwAPIException(
$result);
890 if (!isset(
$params[
'access_token'])) {
891 $params[
'access_token'] = $this->getAccessToken();
896 if (!is_string($value)) {
920 $opts = self::$CURL_OPTS;
921 if ($this->getFileUploadSupport()) {
922 $opts[CURLOPT_POSTFIELDS] =
$params;
924 $opts[CURLOPT_POSTFIELDS] = http_build_query(
$params, null,
'&');
926 $opts[CURLOPT_URL] =
$url;
930 if (isset($opts[CURLOPT_HTTPHEADER])) {
931 $existing_headers = $opts[CURLOPT_HTTPHEADER];
932 $existing_headers[] =
'Expect:';
933 $opts[CURLOPT_HTTPHEADER] = $existing_headers;
935 $opts[CURLOPT_HTTPHEADER] = array(
'Expect:');
938 curl_setopt_array($ch, $opts);
941 if (curl_errno($ch) == 60) {
942 self::errorLog(
'Invalid or no certificate authority found, '.
943 'using bundled information');
944 curl_setopt($ch, CURLOPT_CAINFO,
945 dirname(__FILE__) .
'/fb_ca_chain_bundle.crt');
954 if (
$result ===
false && empty($opts[CURLOPT_IPRESOLVE])) {
956 $regex =
'/Failed to connect to ([^:].*): Network is unreachable/';
957 if (preg_match($regex, curl_error($ch), $matches)) {
958 if (strlen(@inet_pton($matches[1])) === 16) {
959 self::errorLog(
'Invalid IPv6 configuration on server, '.
960 'Please disable or get native IPv6 on your server.');
961 self::$CURL_OPTS[CURLOPT_IPRESOLVE] = CURL_IPRESOLVE_V4;
962 curl_setopt($ch, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V4);
970 'error_code' => curl_errno($ch),
972 'message' => curl_error($ch),
973 'type' =>
'CurlException',
990 list($encoded_sig, $payload) = explode(
'.', $signed_request, 2);
993 $sig = self::base64UrlDecode($encoded_sig);
994 $data = json_decode(self::base64UrlDecode($payload),
true);
996 if (strtoupper(
$data[
'algorithm']) !== self::SIGNED_REQUEST_ALGORITHM) {
998 'Unknown algorithm. Expected ' . self::SIGNED_REQUEST_ALGORITHM);
1003 $expected_sig = hash_hmac(
'sha256', $payload,
1004 $this->getAppSecret(), $raw =
true);
1005 if ($sig !== $expected_sig) {
1006 self::errorLog(
'Bad Signed JSON signature!');
1020 if (!is_array(
$data)) {
1022 'makeSignedRequest expects an array. Got: ' . print_r(
$data,
true));
1024 $data[
'algorithm'] = self::SIGNED_REQUEST_ALGORITHM;
1025 $data[
'issued_at'] = time();
1026 $json = json_encode(
$data);
1027 $b64 = self::base64UrlEncode($json);
1028 $raw_sig = hash_hmac(
'sha256', $b64, $this->getAppSecret(), $raw =
true);
1029 $sig = self::base64UrlEncode($raw_sig);
1030 return $sig.
'.'.$b64;
1040 static $READ_ONLY_CALLS =
1041 array(
'admin.getallocation' => 1,
1042 'admin.getappproperties' => 1,
1043 'admin.getbannedusers' => 1,
1044 'admin.getlivestreamvialink' => 1,
1045 'admin.getmetrics' => 1,
1046 'admin.getrestrictioninfo' => 1,
1047 'application.getpublicinfo' => 1,
1048 'auth.getapppublickey' => 1,
1049 'auth.getsession' => 1,
1050 'auth.getsignedpublicsessiondata' => 1,
1051 'comments.get' => 1,
1052 'connect.getunconnectedfriendscount' => 1,
1053 'dashboard.getactivity' => 1,
1054 'dashboard.getcount' => 1,
1055 'dashboard.getglobalnews' => 1,
1056 'dashboard.getnews' => 1,
1057 'dashboard.multigetcount' => 1,
1058 'dashboard.multigetnews' => 1,
1059 'data.getcookies' => 1,
1061 'events.getmembers' => 1,
1062 'fbml.getcustomtags' => 1,
1063 'feed.getappfriendstories' => 1,
1064 'feed.getregisteredtemplatebundlebyid' => 1,
1065 'feed.getregisteredtemplatebundles' => 1,
1066 'fql.multiquery' => 1,
1068 'friends.arefriends' => 1,
1070 'friends.getappusers' => 1,
1071 'friends.getlists' => 1,
1072 'friends.getmutualfriends' => 1,
1075 'groups.getmembers' => 1,
1076 'intl.gettranslations' => 1,
1079 'notifications.get' => 1,
1080 'pages.getinfo' => 1,
1081 'pages.isadmin' => 1,
1082 'pages.isappadded' => 1,
1084 'permissions.checkavailableapiaccess' => 1,
1085 'permissions.checkgrantedapiaccess' => 1,
1087 'photos.getalbums' => 1,
1088 'photos.gettags' => 1,
1089 'profile.getinfo' => 1,
1090 'profile.getinfooptions' => 1,
1092 'stream.getcomments' => 1,
1093 'stream.getfilters' => 1,
1094 'users.getinfo' => 1,
1095 'users.getloggedinuser' => 1,
1096 'users.getstandardinfo' => 1,
1097 'users.hasapppermission' => 1,
1098 'users.isappuser' => 1,
1099 'users.isverified' => 1,
1100 'video.getuploadlimits' => 1);
1102 if (isset($READ_ONLY_CALLS[strtolower($method)])) {
1104 }
else if (strtolower($method) ==
'video.upload') {
1105 $name =
'api_video';
1107 return self::getUrl(
$name,
'restserver.php');
1122 if (
$path[0] ===
'/') {
1128 $url .=
'?' . http_build_query(
$params, null,
'&');
1135 if ($this->trustForwarded && isset(
$_SERVER[
'HTTP_X_FORWARDED_HOST'])) {
1136 return $_SERVER[
'HTTP_X_FORWARDED_HOST'];
1142 if ($this->trustForwarded && isset(
$_SERVER[
'HTTP_X_FORWARDED_PROTO'])) {
1143 if (
$_SERVER[
'HTTP_X_FORWARDED_PROTO'] ===
'https') {
1154 if (isset(
$_SERVER[
'SERVER_PORT']) &&
1155 (
$_SERVER[
'SERVER_PORT'] ===
'443')) {
1168 if (array_key_exists(
'base_domain',
$metadata) &&
1170 return trim(
$metadata[
'base_domain'],
'.');
1172 return $this->getHttpHost();
1182 $protocol = $this->getHttpProtocol() .
'://';
1183 $host = $this->getHttpHost();
1184 $currentUrl =
$protocol.$host.$_SERVER[
'REQUEST_URI'];
1185 $parts = parse_url($currentUrl);
1189 isset($parts[
'port']) &&
1190 ((
$protocol ===
'http://' && $parts[
'port'] !== 80) ||
1191 (
$protocol ===
'https://' && $parts[
'port'] !== 443))
1192 ?
':' . $parts[
'port'] :
'';
1195 return $protocol . $parts[
'host'] . $port . $parts[
'path'];
1208 switch ($e->getType()) {
1210 case 'OAuthException':
1212 case 'invalid_token':
1216 if ((strpos(
$message,
'Error validating access token') !==
false) ||
1217 (strpos(
$message,
'Invalid OAuth access token') !==
false) ||
1218 (strpos(
$message,
'An active access token must be used') !==
false)
1220 $this->destroySession();
1237 if (php_sapi_name() !=
'cli') {
1254 return base64_decode(strtr(
$input,
'-_',
'+/'));
1267 $str = strtr(base64_encode(
$input),
'+/',
'-_');
1268 $str = str_replace(
'=',
'', $str);
1276 $this->accessToken = null;
1277 $this->signedRequest = null;
1279 $this->clearAllPersistentData();
1283 $cookie_name = $this->getSignedRequestCookieName();
1284 if (array_key_exists($cookie_name,
$_COOKIE)) {
1286 if (!headers_sent()) {
1287 $base_domain = $this->getBaseDomain();
1288 setcookie($cookie_name,
'', 1,
'/',
'.'.$base_domain);
1292 'There exists a cookie that we wanted to clear that we couldn\'t '.
1293 'clear because headers was already sent. Make sure to do the first '.
1294 'API call before outputing anything.' 1307 $cookie_name = $this->getMetadataCookieName();
1308 if (!array_key_exists($cookie_name,
$_COOKIE)) {
1313 $cookie_value = trim(
$_COOKIE[$cookie_name],
'"');
1315 if (empty($cookie_value)) {
1319 $parts = explode(
'&', $cookie_value);
1321 foreach ($parts as $part) {
1322 $pair = explode(
'=', $part, 2);
1323 if (!empty($pair[0])) {
1325 (count($pair) > 1) ? urldecode($pair[1]) :
'';
1333 if ($big === $small) {
1336 return self::endsWith($big,
'.'.$small);
1340 $len = strlen($small);
1344 return substr($big, -$len) === $small;
1366 abstract protected function setPersistentData(
$key, $value);
1376 abstract protected function getPersistentData(
$key,
$default =
false);
1384 abstract protected function clearPersistentData(
$key);
1391 abstract protected function clearAllPersistentData();
static endsWith($big, $small)
getBaseDomain()
Get the base domain used for the cookie.
getLoginStatusUrl($params=array())
Get a login status URL to fetch the status from Facebook.
$signedRequest
The data from the signed_request token.
__construct($result)
Make a new API Exception with the given result.
setAppId($appId)
Set the Application ID.
if((!isset($_SERVER['DOCUMENT_ROOT'])) OR(empty($_SERVER['DOCUMENT_ROOT']))) $_SERVER['DOCUMENT_ROOT']
getMetadataCookie()
Parses the metadata cookie that our Javascript API set.
getApiUrl($method)
Build the URL for api given parameters.
getApplicationAccessToken()
Returns the access token that should be used for logged out users when no authorization code is avail...
getUserFromAccessToken()
Retrieves the UID with the understanding that $this->accessToken has already been set and is seemingl...
getUserAccessToken()
Determines and returns the user access token, first using the signed request if present, and then falling back on the authorization code if present.
throwAPIException($result)
Analyzes the supplied result to see if it was thrown because the access token is no longer valid...
static errorLog($msg)
Prints to the error log if you aren't in command line mode.
getCurrentUrl()
Returns the Current URL, stripping it of known FB parameters that should not persist.
__toString()
To make debugging easier.
setAccessToken($access_token)
Sets the access token for api calls.
__construct($config)
Initialize a Facebook Application.
getLogoutUrl($params=array())
Get a Logout URL suitable for use with redirects.
static isAllowedDomain($big, $small)
setAppSecret($appSecret)
Set the App Secret.
$metadata['__DYNAMIC:1__']
getType()
Returns the associated type for the error.
getAccessTokenFromCode($code, $redirect_uri=null)
Retrieves an access token for the given authorization code (previously generated from www...
getApiSecret()
Get the App Secret.
getMetadataCookieName()
Constructs and returns the name of the coookie that potentially contain metadata. ...
getAccessToken()
Determines the access token that should be used for API calls.
getUserFromAvailableData()
Determines the connected user by first examining any signed requests, then considering an authorizati...
$result
The result from the API server that represents the exception information.
if(!array_key_exists('stateid', $_REQUEST)) $state
Handle linkback() response from LinkedIn.
destroySession()
Destroy the current session.
getUrl($name, $path='', $params=array())
Build the URL for given domain alias, path and parameters.
catch(Exception $e) $message
_oauthRequest($url, $params)
Make a OAuth Request.
makeRequest($url, $params, $ch=null)
Makes an HTTP request.
getUser()
Get the UID of the connected user, or 0 if the Facebook user is not connected.
$state
A CSRF state variable to assist in the defense against CSRF attacks.
setApiSecret($apiSecret)
Set the App Secret.
getFileUploadSupport()
Get the file upload support status.
getCode()
Get the authorization code from the query parameters, if it exists, and otherwise return false to sig...
parseSignedRequest($signed_request)
Parses a signed_request and validates the signature.
getSignedRequest()
Retrieve the signed request, either from a request parameter or, if not present, from a cookie...
setFileUploadSupport($fileUploadSupport)
Set the file upload support status.
_restserver($params)
Invoke the old restserver.php endpoint.
getResult()
Return the associated result object returned by the API server.
_graph($path, $method='GET', $params=array())
Invoke the Graph API.
getAppId()
Get the Application ID.
static base64UrlDecode($input)
Base64 encoding that doesn't need to be urlencode()ed.
getLoginUrl($params=array())
Get a Login URL for use with redirects.
isVideoPost($path, $method='GET')
Return true if this is video post.
setExtendedAccessToken()
Extend an access token, while removing the short-lived token that might have been generated via clien...
static base64UrlEncode($input)
Base64 encoding that doesn't need to be urlencode()ed.
Provides access to the Facebook Platform.
establishCSRFTokenState()
Lays down a CSRF state token for this process.
getAppSecret()
Get the App Secret.
useFileUploadSupport()
DEPRECATED! Please use getFileUploadSupport instead.
makeSignedRequest($data)
Makes a signed_request blob using the given data.
getSignedRequestCookieName()
Constructs and returns the name of the cookie that potentially houses the signed request for the app ...
Copyright 2011 Facebook, Inc.