1<?php 2/** 3 * Copyright 2017 Facebook, Inc. 4 * 5 * You are hereby granted a non-exclusive, worldwide, royalty-free license to 6 * use, copy, modify, and distribute this software in source code or binary 7 * form for use in connection with the web services and APIs provided by 8 * Facebook. 9 * 10 * As with any software that integrates with the Facebook platform, your use 11 * of this software is subject to the Facebook Developer Principles and 12 * Policies [http://developers.facebook.com/policy/]. This copyright notice 13 * shall be included in all copies or substantial portions of the software. 14 * 15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 18 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 20 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 21 * DEALINGS IN THE SOFTWARE. 22 * 23 */ 24namespace Facebook; 25 26use Facebook\HttpClients\FacebookHttpClientInterface; 27use Facebook\HttpClients\FacebookCurlHttpClient; 28use Facebook\HttpClients\FacebookStreamHttpClient; 29use Facebook\Exceptions\FacebookSDKException; 30 31/** 32 * Class FacebookClient 33 * 34 * @package Facebook 35 */ 36class FacebookClient 37{ 38 /** 39 * @const string Production Graph API URL. 40 */ 41 const BASE_GRAPH_URL = 'https://graph.facebook.com'; 42 43 /** 44 * @const string Graph API URL for video uploads. 45 */ 46 const BASE_GRAPH_VIDEO_URL = 'https://graph-video.facebook.com'; 47 48 /** 49 * @const string Beta Graph API URL. 50 */ 51 const BASE_GRAPH_URL_BETA = 'https://graph.beta.facebook.com'; 52 53 /** 54 * @const string Beta Graph API URL for video uploads. 55 */ 56 const BASE_GRAPH_VIDEO_URL_BETA = 'https://graph-video.beta.facebook.com'; 57 58 /** 59 * @const int The timeout in seconds for a normal request. 60 */ 61 const DEFAULT_REQUEST_TIMEOUT = 60; 62 63 /** 64 * @const int The timeout in seconds for a request that contains file uploads. 65 */ 66 const DEFAULT_FILE_UPLOAD_REQUEST_TIMEOUT = 3600; 67 68 /** 69 * @const int The timeout in seconds for a request that contains video uploads. 70 */ 71 const DEFAULT_VIDEO_UPLOAD_REQUEST_TIMEOUT = 7200; 72 73 /** 74 * @var bool Toggle to use Graph beta url. 75 */ 76 protected $enableBetaMode = false; 77 78 /** 79 * @var FacebookHttpClientInterface HTTP client handler. 80 */ 81 protected $httpClientHandler; 82 83 /** 84 * @var int The number of calls that have been made to Graph. 85 */ 86 public static $requestCount = 0; 87 88 /** 89 * Instantiates a new FacebookClient object. 90 * 91 * @param FacebookHttpClientInterface|null $httpClientHandler 92 * @param boolean $enableBeta 93 */ 94 public function __construct(FacebookHttpClientInterface $httpClientHandler = null, $enableBeta = false) 95 { 96 $this->httpClientHandler = $httpClientHandler ?: $this->detectHttpClientHandler(); 97 $this->enableBetaMode = $enableBeta; 98 } 99 100 /** 101 * Sets the HTTP client handler. 102 * 103 * @param FacebookHttpClientInterface $httpClientHandler 104 */ 105 public function setHttpClientHandler(FacebookHttpClientInterface $httpClientHandler) 106 { 107 $this->httpClientHandler = $httpClientHandler; 108 } 109 110 /** 111 * Returns the HTTP client handler. 112 * 113 * @return FacebookHttpClientInterface 114 */ 115 public function getHttpClientHandler() 116 { 117 return $this->httpClientHandler; 118 } 119 120 /** 121 * Detects which HTTP client handler to use. 122 * 123 * @return FacebookHttpClientInterface 124 */ 125 public function detectHttpClientHandler() 126 { 127 return extension_loaded('curl') ? new FacebookCurlHttpClient() : new FacebookStreamHttpClient(); 128 } 129 130 /** 131 * Toggle beta mode. 132 * 133 * @param boolean $betaMode 134 */ 135 public function enableBetaMode($betaMode = true) 136 { 137 $this->enableBetaMode = $betaMode; 138 } 139 140 /** 141 * Returns the base Graph URL. 142 * 143 * @param boolean $postToVideoUrl Post to the video API if videos are being uploaded. 144 * 145 * @return string 146 */ 147 public function getBaseGraphUrl($postToVideoUrl = false) 148 { 149 if ($postToVideoUrl) { 150 return $this->enableBetaMode ? static::BASE_GRAPH_VIDEO_URL_BETA : static::BASE_GRAPH_VIDEO_URL; 151 } 152 153 return $this->enableBetaMode ? static::BASE_GRAPH_URL_BETA : static::BASE_GRAPH_URL; 154 } 155 156 /** 157 * Prepares the request for sending to the client handler. 158 * 159 * @param FacebookRequest $request 160 * 161 * @return array 162 */ 163 public function prepareRequestMessage(FacebookRequest $request) 164 { 165 $postToVideoUrl = $request->containsVideoUploads(); 166 $url = $this->getBaseGraphUrl($postToVideoUrl) . $request->getUrl(); 167 168 // If we're sending files they should be sent as multipart/form-data 169 if ($request->containsFileUploads()) { 170 $requestBody = $request->getMultipartBody(); 171 $request->setHeaders([ 172 'Content-Type' => 'multipart/form-data; boundary=' . $requestBody->getBoundary(), 173 ]); 174 } else { 175 $requestBody = $request->getUrlEncodedBody(); 176 $request->setHeaders([ 177 'Content-Type' => 'application/x-www-form-urlencoded', 178 ]); 179 } 180 181 return [ 182 $url, 183 $request->getMethod(), 184 $request->getHeaders(), 185 $requestBody->getBody(), 186 ]; 187 } 188 189 /** 190 * Makes the request to Graph and returns the result. 191 * 192 * @param FacebookRequest $request 193 * 194 * @return FacebookResponse 195 * 196 * @throws FacebookSDKException 197 */ 198 public function sendRequest(FacebookRequest $request) 199 { 200 if (get_class($request) === 'Facebook\FacebookRequest') { 201 $request->validateAccessToken(); 202 } 203 204 list($url, $method, $headers, $body) = $this->prepareRequestMessage($request); 205 206 // Since file uploads can take a while, we need to give more time for uploads 207 $timeOut = static::DEFAULT_REQUEST_TIMEOUT; 208 if ($request->containsFileUploads()) { 209 $timeOut = static::DEFAULT_FILE_UPLOAD_REQUEST_TIMEOUT; 210 } elseif ($request->containsVideoUploads()) { 211 $timeOut = static::DEFAULT_VIDEO_UPLOAD_REQUEST_TIMEOUT; 212 } 213 214 // Should throw `FacebookSDKException` exception on HTTP client error. 215 // Don't catch to allow it to bubble up. 216 $rawResponse = $this->httpClientHandler->send($url, $method, $body, $headers, $timeOut); 217 218 static::$requestCount++; 219 220 $returnResponse = new FacebookResponse( 221 $request, 222 $rawResponse->getBody(), 223 $rawResponse->getHttpResponseCode(), 224 $rawResponse->getHeaders() 225 ); 226 227 if ($returnResponse->isError()) { 228 throw $returnResponse->getThrownException(); 229 } 230 231 return $returnResponse; 232 } 233 234 /** 235 * Makes a batched request to Graph and returns the result. 236 * 237 * @param FacebookBatchRequest $request 238 * 239 * @return FacebookBatchResponse 240 * 241 * @throws FacebookSDKException 242 */ 243 public function sendBatchRequest(FacebookBatchRequest $request) 244 { 245 $request->prepareRequestsForBatch(); 246 $facebookResponse = $this->sendRequest($request); 247 248 return new FacebookBatchResponse($request, $facebookResponse); 249 } 250} 251