1<?php 2 3namespace OAuth\Common\Http\Client; 4 5use OAuth\Common\Http\Exception\TokenResponseException; 6use OAuth\Common\Http\Uri\UriInterface; 7 8/** 9 * Client implementation for cURL 10 */ 11class CurlClient extends AbstractClient 12{ 13 /** 14 * If true, explicitly sets cURL to use SSL version 3. Use this if cURL 15 * compiles with GnuTLS SSL. 16 * 17 * @var bool 18 */ 19 private $forceSSL3 = false; 20 21 /** 22 * Additional parameters (as `key => value` pairs) to be passed to `curl_setopt` 23 * 24 * @var array 25 */ 26 private $parameters = array(); 27 28 /** 29 * Additional `curl_setopt` parameters 30 * 31 * @param array $parameters 32 */ 33 public function setCurlParameters(array $parameters) 34 { 35 $this->parameters = $parameters; 36 } 37 38 /** 39 * @param bool $force 40 * 41 * @return CurlClient 42 */ 43 public function setForceSSL3($force) 44 { 45 $this->forceSSL3 = $force; 46 47 return $this; 48 } 49 50 /** 51 * Any implementing HTTP providers should send a request to the provided endpoint with the parameters. 52 * They should return, in string form, the response body and throw an exception on error. 53 * 54 * @param UriInterface $endpoint 55 * @param mixed $requestBody 56 * @param array $extraHeaders 57 * @param string $method 58 * 59 * @return string 60 * 61 * @throws TokenResponseException 62 * @throws \InvalidArgumentException 63 */ 64 public function retrieveResponse( 65 UriInterface $endpoint, 66 $requestBody, 67 array $extraHeaders = array(), 68 $method = 'POST' 69 ) { 70 // Normalize method name 71 $method = strtoupper($method); 72 73 $this->normalizeHeaders($extraHeaders); 74 75 if ($method === 'GET' && !empty($requestBody)) { 76 throw new \InvalidArgumentException('No body expected for "GET" request.'); 77 } 78 79 if (!isset($extraHeaders['Content-Type']) && $method === 'POST' && is_array($requestBody)) { 80 $extraHeaders['Content-Type'] = 'Content-Type: application/x-www-form-urlencoded'; 81 } 82 83 $extraHeaders['Host'] = 'Host: '.$endpoint->getHost(); 84 $extraHeaders['Connection'] = 'Connection: close'; 85 86 $ch = curl_init(); 87 88 curl_setopt($ch, CURLOPT_URL, $endpoint->getAbsoluteUri()); 89 90 if ($method === 'POST' || $method === 'PUT') { 91 if ($requestBody && is_array($requestBody)) { 92 $requestBody = http_build_query($requestBody, '', '&'); 93 } 94 95 if ($method === 'PUT') { 96 curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'PUT'); 97 } else { 98 curl_setopt($ch, CURLOPT_POST, true); 99 } 100 101 curl_setopt($ch, CURLOPT_POSTFIELDS, $requestBody); 102 } else { 103 curl_setopt($ch, CURLOPT_CUSTOMREQUEST, $method); 104 } 105 106 if ($this->maxRedirects > 0) { 107 curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true); 108 curl_setopt($ch, CURLOPT_MAXREDIRS, $this->maxRedirects); 109 } 110 111 curl_setopt($ch, CURLOPT_TIMEOUT, $this->timeout); 112 curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); 113 curl_setopt($ch, CURLOPT_HEADER, false); 114 curl_setopt($ch, CURLOPT_HTTPHEADER, $extraHeaders); 115 curl_setopt($ch, CURLOPT_USERAGENT, $this->userAgent); 116 117 foreach ($this->parameters as $key => $value) { 118 curl_setopt($ch, $key, $value); 119 } 120 121 if ($this->forceSSL3) { 122 curl_setopt($ch, CURLOPT_SSLVERSION, 3); 123 } 124 125 $response = curl_exec($ch); 126 $responseCode = curl_getinfo($ch, CURLINFO_HTTP_CODE); 127 128 if (false === $response) { 129 $errNo = curl_errno($ch); 130 $errStr = curl_error($ch); 131 curl_close($ch); 132 if (empty($errStr)) { 133 throw new TokenResponseException('Failed to request resource.', $responseCode); 134 } 135 throw new TokenResponseException('cURL Error # '.$errNo.': '.$errStr, $responseCode); 136 } 137 138 curl_close($ch); 139 140 return $response; 141 } 142} 143