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\HttpClients; 25 26use Facebook\Http\GraphRawResponse; 27use Facebook\Exceptions\FacebookSDKException; 28 29/** 30 * Class FacebookCurlHttpClient 31 * 32 * @package Facebook 33 */ 34class FacebookCurlHttpClient implements FacebookHttpClientInterface 35{ 36 /** 37 * @var string The client error message 38 */ 39 protected $curlErrorMessage = ''; 40 41 /** 42 * @var int The curl client error code 43 */ 44 protected $curlErrorCode = 0; 45 46 /** 47 * @var string|boolean The raw response from the server 48 */ 49 protected $rawResponse; 50 51 /** 52 * @var FacebookCurl Procedural curl as object 53 */ 54 protected $facebookCurl; 55 56 /** 57 * @param FacebookCurl|null Procedural curl as object 58 */ 59 public function __construct(FacebookCurl $facebookCurl = null) 60 { 61 $this->facebookCurl = $facebookCurl ?: new FacebookCurl(); 62 } 63 64 /** 65 * @inheritdoc 66 */ 67 public function send($url, $method, $body, array $headers, $timeOut) 68 { 69 $this->openConnection($url, $method, $body, $headers, $timeOut); 70 $this->sendRequest(); 71 72 if ($curlErrorCode = $this->facebookCurl->errno()) { 73 throw new FacebookSDKException($this->facebookCurl->error(), $curlErrorCode); 74 } 75 76 // Separate the raw headers from the raw body 77 list($rawHeaders, $rawBody) = $this->extractResponseHeadersAndBody(); 78 79 $this->closeConnection(); 80 81 return new GraphRawResponse($rawHeaders, $rawBody); 82 } 83 84 /** 85 * Opens a new curl connection. 86 * 87 * @param string $url The endpoint to send the request to. 88 * @param string $method The request method. 89 * @param string $body The body of the request. 90 * @param array $headers The request headers. 91 * @param int $timeOut The timeout in seconds for the request. 92 */ 93 public function openConnection($url, $method, $body, array $headers, $timeOut) 94 { 95 $options = [ 96 CURLOPT_CUSTOMREQUEST => $method, 97 CURLOPT_HTTPHEADER => $this->compileRequestHeaders($headers), 98 CURLOPT_URL => $url, 99 CURLOPT_CONNECTTIMEOUT => 10, 100 CURLOPT_TIMEOUT => $timeOut, 101 CURLOPT_RETURNTRANSFER => true, // Return response as string 102 CURLOPT_HEADER => true, // Enable header processing 103 CURLOPT_SSL_VERIFYHOST => 2, 104 CURLOPT_SSL_VERIFYPEER => true, 105 CURLOPT_CAINFO => __DIR__ . '/certs/DigiCertHighAssuranceEVRootCA.pem', 106 ]; 107 108 if ($method !== "GET") { 109 $options[CURLOPT_POSTFIELDS] = $body; 110 } 111 112 $this->facebookCurl->init(); 113 $this->facebookCurl->setoptArray($options); 114 } 115 116 /** 117 * Closes an existing curl connection 118 */ 119 public function closeConnection() 120 { 121 $this->facebookCurl->close(); 122 } 123 124 /** 125 * Send the request and get the raw response from curl 126 */ 127 public function sendRequest() 128 { 129 $this->rawResponse = $this->facebookCurl->exec(); 130 } 131 132 /** 133 * Compiles the request headers into a curl-friendly format. 134 * 135 * @param array $headers The request headers. 136 * 137 * @return array 138 */ 139 public function compileRequestHeaders(array $headers) 140 { 141 $return = []; 142 143 foreach ($headers as $key => $value) { 144 $return[] = $key . ': ' . $value; 145 } 146 147 return $return; 148 } 149 150 /** 151 * Extracts the headers and the body into a two-part array 152 * 153 * @return array 154 */ 155 public function extractResponseHeadersAndBody() 156 { 157 $parts = explode("\r\n\r\n", $this->rawResponse); 158 $rawBody = array_pop($parts); 159 $rawHeaders = implode("\r\n\r\n", $parts); 160 161 return [trim($rawHeaders), trim($rawBody)]; 162 } 163} 164