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'];
84 }
else if (is_array(
$error)) {
86 if (isset(
$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';
134 CURLOPT_CONNECTTIMEOUT => 10,
135 CURLOPT_RETURNTRANSFER =>
true,
136 CURLOPT_TIMEOUT => 60,
137 CURLOPT_USERAGENT =>
'facebook-php-3.2',
144 protected static $DROP_QUERY_PARAMS =
array(
154 'api' =>
'https://api.facebook.com/',
155 'api_video' =>
'https://api-video.facebook.com/',
156 'api_read' =>
'https://api-read.facebook.com/',
157 'graph' =>
'https://graph.facebook.com/',
158 'graph_video' =>
'https://graph-video.facebook.com/',
159 'www' =>
'https://www.facebook.com/',
206 protected $fileUploadSupport =
false;
213 protected $trustForwarded =
false;
226 $this->setAppId(
$config[
'appId']);
227 $this->setAppSecret(
$config[
'secret']);
228 if (isset(
$config[
'fileUpload'])) {
229 $this->setFileUploadSupport(
$config[
'fileUpload']);
231 if (isset(
$config[
'trustForwarded']) &&
$config[
'trustForwarded']) {
232 $this->trustForwarded =
true;
234 $state = $this->getPersistentData(
'state');
247 $this->appId = $appId;
268 $this->setAppSecret($apiSecret);
279 $this->appSecret = $appSecret;
290 return $this->getAppSecret();
299 return $this->appSecret;
309 $this->fileUploadSupport = $fileUploadSupport;
319 return $this->fileUploadSupport;
330 return $this->getFileUploadSupport();
342 $this->accessToken = $access_token;
355 $access_token_response = $this->_oauthRequest(
356 $this->getUrl(
'graph',
'/oauth/access_token'),
358 'client_id' => $this->getAppId(),
359 'client_secret' => $this->getAppSecret(),
360 'grant_type' =>
'fb_exchange_token',
361 'fb_exchange_token' => $this->getAccessToken(),
371 if (empty($access_token_response)) {
375 $response_params =
array();
376 parse_str($access_token_response, $response_params);
378 if (!isset($response_params[
'access_token'])) {
382 $this->destroySession();
384 $this->setPersistentData(
385 'access_token', $response_params[
'access_token']
399 if ($this->accessToken !== null) {
407 $this->setAccessToken($this->getApplicationAccessToken());
408 $user_access_token = $this->getUserAccessToken();
409 if ($user_access_token) {
410 $this->setAccessToken($user_access_token);
430 $signed_request = $this->getSignedRequest();
431 if ($signed_request) {
433 if (array_key_exists(
'oauth_token', $signed_request)) {
434 $access_token = $signed_request[
'oauth_token'];
435 $this->setPersistentData(
'access_token', $access_token);
436 return $access_token;
440 if (array_key_exists(
'code', $signed_request)) {
441 $code = $signed_request[
'code'];
442 if (
$code &&
$code == $this->getPersistentData(
'code')) {
444 return $this->getPersistentData(
'access_token');
447 $access_token = $this->getAccessTokenFromCode(
$code,
'');
449 $this->setPersistentData(
'code',
$code);
450 $this->setPersistentData(
'access_token', $access_token);
451 return $access_token;
457 $this->clearAllPersistentData();
462 $code = $this->getCode();
463 if (
$code &&
$code != $this->getPersistentData(
'code')) {
464 $access_token = $this->getAccessTokenFromCode(
$code);
466 $this->setPersistentData(
'code',
$code);
467 $this->setPersistentData(
'access_token', $access_token);
468 return $access_token;
472 $this->clearAllPersistentData();
480 return $this->getPersistentData(
'access_token');
490 if (!$this->signedRequest) {
491 if (!empty($_REQUEST[
'signed_request'])) {
492 $this->signedRequest = $this->parseSignedRequest(
493 $_REQUEST[
'signed_request']);
494 }
else if (!empty(
$_COOKIE[$this->getSignedRequestCookieName()])) {
495 $this->signedRequest = $this->parseSignedRequest(
496 $_COOKIE[$this->getSignedRequestCookieName()]);
499 return $this->signedRequest;
509 if ($this->
user !== null) {
514 return $this->
user = $this->getUserFromAvailableData();
528 $signed_request = $this->getSignedRequest();
529 if ($signed_request) {
530 if (array_key_exists(
'user_id', $signed_request)) {
531 $user = $signed_request[
'user_id'];
533 if($user != $this->getPersistentData(
'user_id')){
534 $this->clearAllPersistentData();
537 $this->setPersistentData(
'user_id', $signed_request[
'user_id']);
543 $this->clearAllPersistentData();
547 $user = $this->getPersistentData(
'user_id', $default = 0);
548 $persisted_access_token = $this->getPersistentData(
'access_token');
552 $access_token = $this->getAccessToken();
554 $access_token != $this->getApplicationAccessToken() &&
555 !($user && $persisted_access_token == $access_token)) {
556 $user = $this->getUserFromAccessToken();
558 $this->setPersistentData(
'user_id', $user);
560 $this->clearAllPersistentData();
580 $this->establishCSRFTokenState();
581 $currentUrl = $this->getCurrentUrl();
585 if ($scopeParams && is_array($scopeParams)) {
586 $params[
'scope'] = implode(
',', $scopeParams);
589 return $this->getUrl(
593 'client_id' => $this->getAppId(),
594 'redirect_uri' => $currentUrl,
595 'state' => $this->state),
609 return $this->getUrl(
613 'next' => $this->getCurrentUrl(),
614 'access_token' => $this->getUserAccessToken(),
631 return $this->getUrl(
633 'extern/login_status.php',
635 'api_key' => $this->getAppId(),
636 'no_session' => $this->getCurrentUrl(),
637 'no_user' => $this->getCurrentUrl(),
638 'ok_session' => $this->getCurrentUrl(),
639 'session_version' => 3,
650 $args = func_get_args();
651 if (is_array($args[0])) {
652 return $this->_restserver($args[0]);
654 return call_user_func_array(
array($this,
'_graph'), $args);
668 return 'fbsr_'.$this->getAppId();
679 return 'fbm_'.$this->getAppId();
691 if (isset($_REQUEST[
'code'])) {
692 if ($this->state !== null &&
693 isset($_REQUEST[
'state']) &&
694 $this->state === $_REQUEST[
'state']) {
698 $this->clearPersistentData(
'state');
699 return $_REQUEST[
'code'];
701 self::errorLog(
'CSRF state token does not match one provided. ' . $this->state .
'!=' . $_REQUEST[
'state']);
721 $user_info = $this->api(
'/me');
722 return $user_info[
'id'];
736 return $this->appId.
'|'.$this->appSecret;
745 if ($this->state === null) {
746 $this->state = md5(uniqid(mt_rand(),
true));
747 $this->setPersistentData(
'state', $this->state);
768 if ($redirect_uri === null) {
769 $redirect_uri = $this->getCurrentUrl();
775 $access_token_response =
776 $this->_oauthRequest(
777 $this->getUrl(
'graph',
'/oauth/access_token'),
779 'client_secret' => $this->getAppSecret(),
780 'redirect_uri' => $redirect_uri,
788 if (empty($access_token_response)) {
792 $response_params = json_decode($access_token_response,
true);
793 if (!isset($response_params[
'access_token'])) {
797 return $response_params[
'access_token'];
810 $params[
'api_key'] = $this->getAppId();
811 $params[
'format'] =
'json-strings';
813 $result = json_decode($this->_oauthRequest(
814 $this->getApiUrl(
$params[
'method']),
820 $this->throwAPIException(
$result);
825 $method = strtolower(
$params[
'method']);
826 if ($method ===
'auth.expiresession' ||
827 $method ===
'auth.revokeauthorization') {
828 $this->destroySession();
843 if ($method ==
'POST' && preg_match(
"/^(\/)(.+)(\/)(videos)$/",
$path)) {
860 if (is_array($method) && empty(
$params)) {
866 if ($this->isVideoPost(
$path, $method)) {
867 $domainKey =
'graph_video';
869 $domainKey =
'graph';
872 $result = json_decode($this->_oauthRequest(
873 $this->getUrl($domainKey,
$path),
879 $this->throwAPIException(
$result);
897 if (!isset(
$params[
'access_token'])) {
898 $params[
'access_token'] = $this->getAccessToken();
903 if (!is_string($value)) {
927 $opts = self::$CURL_OPTS;
928 if ($this->getFileUploadSupport()) {
929 $opts[CURLOPT_POSTFIELDS] =
$params;
931 $opts[CURLOPT_POSTFIELDS] = http_build_query(
$params, null,
'&');
933 $opts[CURLOPT_URL] =
$url;
937 if (isset($opts[CURLOPT_HTTPHEADER])) {
938 $existing_headers = $opts[CURLOPT_HTTPHEADER];
939 $existing_headers[] =
'Expect:';
940 $opts[CURLOPT_HTTPHEADER] = $existing_headers;
942 $opts[CURLOPT_HTTPHEADER] =
array(
'Expect:');
945 curl_setopt_array($ch, $opts);
948 if (curl_errno($ch) == 60) {
949 self::errorLog(
'Invalid or no certificate authority found, '.
950 'using bundled information');
951 curl_setopt($ch, CURLOPT_CAINFO,
952 dirname(__FILE__) .
'/fb_ca_chain_bundle.crt');
961 if (
$result ===
false && empty($opts[CURLOPT_IPRESOLVE])) {
963 $regex =
'/Failed to connect to ([^:].*): Network is unreachable/';
964 if (preg_match($regex, curl_error($ch), $matches)) {
965 if (strlen(@inet_pton($matches[1])) === 16) {
966 self::errorLog(
'Invalid IPv6 configuration on server, '.
967 'Please disable or get native IPv6 on your server.');
968 self::$CURL_OPTS[CURLOPT_IPRESOLVE] = CURL_IPRESOLVE_V4;
969 curl_setopt($ch, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V4);
977 'error_code' => curl_errno($ch),
979 'message' => curl_error($ch),
980 'type' =>
'CurlException',
997 list($encoded_sig, $payload) = explode(
'.', $signed_request, 2);
1000 $sig = self::base64UrlDecode($encoded_sig);
1001 $data = json_decode(self::base64UrlDecode($payload),
true);
1003 if (strtoupper(
$data[
'algorithm']) !== self::SIGNED_REQUEST_ALGORITHM) {
1005 'Unknown algorithm. Expected ' . self::SIGNED_REQUEST_ALGORITHM);
1010 $expected_sig = hash_hmac(
'sha256', $payload,
1011 $this->getAppSecret(), $raw =
true);
1012 if ($sig !== $expected_sig) {
1013 self::errorLog(
'Bad Signed JSON signature!');
1027 if (!is_array(
$data)) {
1029 'makeSignedRequest expects an array. Got: ' . print_r(
$data,
true));
1031 $data[
'algorithm'] = self::SIGNED_REQUEST_ALGORITHM;
1033 $json = json_encode(
$data);
1034 $b64 = self::base64UrlEncode($json);
1035 $raw_sig = hash_hmac(
'sha256', $b64, $this->getAppSecret(), $raw =
true);
1036 $sig = self::base64UrlEncode($raw_sig);
1037 return $sig.
'.'.$b64;
1047 static $READ_ONLY_CALLS =
1048 array(
'admin.getallocation' => 1,
1049 'admin.getappproperties' => 1,
1050 'admin.getbannedusers' => 1,
1051 'admin.getlivestreamvialink' => 1,
1052 'admin.getmetrics' => 1,
1053 'admin.getrestrictioninfo' => 1,
1054 'application.getpublicinfo' => 1,
1055 'auth.getapppublickey' => 1,
1056 'auth.getsession' => 1,
1057 'auth.getsignedpublicsessiondata' => 1,
1058 'comments.get' => 1,
1059 'connect.getunconnectedfriendscount' => 1,
1060 'dashboard.getactivity' => 1,
1061 'dashboard.getcount' => 1,
1062 'dashboard.getglobalnews' => 1,
1063 'dashboard.getnews' => 1,
1064 'dashboard.multigetcount' => 1,
1065 'dashboard.multigetnews' => 1,
1066 'data.getcookies' => 1,
1068 'events.getmembers' => 1,
1069 'fbml.getcustomtags' => 1,
1070 'feed.getappfriendstories' => 1,
1071 'feed.getregisteredtemplatebundlebyid' => 1,
1072 'feed.getregisteredtemplatebundles' => 1,
1073 'fql.multiquery' => 1,
1075 'friends.arefriends' => 1,
1077 'friends.getappusers' => 1,
1078 'friends.getlists' => 1,
1079 'friends.getmutualfriends' => 1,
1082 'groups.getmembers' => 1,
1083 'intl.gettranslations' => 1,
1086 'notifications.get' => 1,
1087 'pages.getinfo' => 1,
1088 'pages.isadmin' => 1,
1089 'pages.isappadded' => 1,
1091 'permissions.checkavailableapiaccess' => 1,
1092 'permissions.checkgrantedapiaccess' => 1,
1094 'photos.getalbums' => 1,
1095 'photos.gettags' => 1,
1096 'profile.getinfo' => 1,
1097 'profile.getinfooptions' => 1,
1099 'stream.getcomments' => 1,
1100 'stream.getfilters' => 1,
1101 'users.getinfo' => 1,
1102 'users.getloggedinuser' => 1,
1103 'users.getstandardinfo' => 1,
1104 'users.hasapppermission' => 1,
1105 'users.isappuser' => 1,
1106 'users.isverified' => 1,
1107 'video.getuploadlimits' => 1);
1109 if (isset($READ_ONLY_CALLS[strtolower($method)])) {
1111 }
else if (strtolower($method) ==
'video.upload') {
1112 $name =
'api_video';
1114 return self::getUrl(
$name,
'restserver.php');
1129 if (
$path[0] ===
'/') {
1135 $url .=
'?' . http_build_query(
$params, null,
'&');
1142 if ($this->trustForwarded && isset(
$_SERVER[
'HTTP_X_FORWARDED_HOST'])) {
1143 return $_SERVER[
'HTTP_X_FORWARDED_HOST'];
1149 if ($this->trustForwarded && isset(
$_SERVER[
'HTTP_X_FORWARDED_PROTO'])) {
1150 if (
$_SERVER[
'HTTP_X_FORWARDED_PROTO'] ===
'https') {
1161 if (isset(
$_SERVER[
'SERVER_PORT']) &&
1162 (
$_SERVER[
'SERVER_PORT'] ===
'443')) {
1175 if (array_key_exists(
'base_domain',
$metadata) &&
1177 return trim(
$metadata[
'base_domain'],
'.');
1179 return $this->getHttpHost();
1189 $protocol = $this->getHttpProtocol() .
'://';
1190 $host = $this->getHttpHost();
1191 $currentUrl =
$protocol.$host.$_SERVER[
'REQUEST_URI'];
1192 $parts = parse_url($currentUrl);
1195 if (!empty($parts[
'query'])) {
1197 $params = explode(
'&', $parts[
'query']);
1198 $retained_params =
array();
1200 if ($this->shouldRetainParam($param)) {
1201 $retained_params[] = $param;
1205 if (!empty($retained_params)) {
1206 $query =
'?'.implode($retained_params,
'&');
1212 isset($parts[
'port']) &&
1213 ((
$protocol ===
'http://' && $parts[
'port'] !== 80) ||
1214 (
$protocol ===
'https://' && $parts[
'port'] !== 443))
1215 ?
':' . $parts[
'port'] :
'';
1233 foreach (self::$DROP_QUERY_PARAMS as $drop_query_param) {
1234 if (strpos($param, $drop_query_param.
'=') === 0) {
1252 switch ($e->getType()) {
1254 case 'OAuthException':
1256 case 'invalid_token':
1260 if ((strpos(
$message,
'Error validating access token') !==
false) ||
1261 (strpos(
$message,
'Invalid OAuth access token') !==
false) ||
1262 (strpos(
$message,
'An active access token must be used') !==
false)
1264 $this->destroySession();
1281 if (php_sapi_name() !=
'cli') {
1298 return base64_decode(strtr($input,
'-_',
'+/'));
1311 $str = strtr(base64_encode($input),
'+/',
'-_');
1312 $str = str_replace(
'=',
'', $str);
1320 $this->accessToken = null;
1321 $this->signedRequest = null;
1323 $this->clearAllPersistentData();
1327 $cookie_name = $this->getSignedRequestCookieName();
1328 if (array_key_exists($cookie_name,
$_COOKIE)) {
1330 if (!headers_sent()) {
1331 $base_domain = $this->getBaseDomain();
1332 setcookie($cookie_name,
'', 1,
'/',
'.'.$base_domain);
1336 'There exists a cookie that we wanted to clear that we couldn\'t '.
1337 'clear because headers was already sent. Make sure to do the first '.
1338 'API call before outputing anything.' 1351 $cookie_name = $this->getMetadataCookieName();
1352 if (!array_key_exists($cookie_name,
$_COOKIE)) {
1357 $cookie_value = trim(
$_COOKIE[$cookie_name],
'"');
1359 if (empty($cookie_value)) {
1363 $parts = explode(
'&', $cookie_value);
1365 foreach ($parts as $part) {
1366 $pair = explode(
'=', $part, 2);
1367 if (!empty($pair[0])) {
1369 (count($pair) > 1) ? urldecode($pair[1]) :
'';
1377 if ($big === $small) {
1380 return self::endsWith($big,
'.'.$small);
1384 $len = strlen($small);
1388 return substr($big, -$len) === $small;
1410 abstract protected function setPersistentData(
$key, $value);
1420 abstract protected function getPersistentData(
$key, $default =
false);
1428 abstract protected function clearPersistentData(
$key);
1435 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.
shouldRetainParam($param)
Returns true if and only if the key or key/value pair should be retained as part of the query string...
$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...
Create styles array
The data for the language used.
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.
Add data(end) time
Method that wraps PHPs time in order to allow simulations with the workflow.
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.