CurlClient.php 6.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188
  1. <?php
  2. namespace Twilio\Http;
  3. use Twilio\Exceptions\EnvironmentException;
  4. class CurlClient implements Client {
  5. const DEFAULT_TIMEOUT = 60;
  6. protected $curlOptions = array();
  7. protected $debugHttp = false;
  8. public $lastRequest = null;
  9. public $lastResponse = null;
  10. public function __construct(array $options = array()) {
  11. $this->curlOptions = $options;
  12. $this->debugHttp = getenv('DEBUG_HTTP_TRAFFIC') === 'true';
  13. }
  14. public function request($method, $url, $params = array(), $data = array(),
  15. $headers = array(), $user = null, $password = null,
  16. $timeout = null) {
  17. $options = $this->options($method, $url, $params, $data, $headers,
  18. $user, $password, $timeout);
  19. $this->lastRequest = $options;
  20. $this->lastResponse = null;
  21. try {
  22. if (!$curl = curl_init()) {
  23. throw new EnvironmentException('Unable to initialize cURL');
  24. }
  25. if (!curl_setopt_array($curl, $options)) {
  26. throw new EnvironmentException(curl_error($curl));
  27. }
  28. if (!$response = curl_exec($curl)) {
  29. throw new EnvironmentException(curl_error($curl));
  30. }
  31. $parts = explode("\r\n\r\n", $response, 3);
  32. list($head, $body) = ($parts[0] == 'HTTP/1.1 100 Continue')
  33. ? array($parts[1], $parts[2])
  34. : array($parts[0], $parts[1]);
  35. if ($this->debugHttp) {
  36. $u = parse_url($url);
  37. $hdrLine = $method . ' ' . $u['path'];
  38. if (isset($u['query']) && strlen($u['query']) > 0 ) {
  39. $hdrLine = $hdrLine . '?' . $u['query'];
  40. }
  41. error_log($hdrLine);
  42. foreach ($headers as $key => $value) {
  43. error_log("$key: $value");
  44. }
  45. if ($method === 'POST') {
  46. error_log("\n" . $options[CURLOPT_POSTFIELDS] . "\n");
  47. }
  48. }
  49. $statusCode = curl_getinfo($curl, CURLINFO_HTTP_CODE);
  50. $responseHeaders = array();
  51. $headerLines = explode("\r\n", $head);
  52. array_shift($headerLines);
  53. foreach ($headerLines as $line) {
  54. list($key, $value) = explode(':', $line, 2);
  55. $responseHeaders[$key] = $value;
  56. }
  57. curl_close($curl);
  58. if (isset($buffer) && is_resource($buffer)) {
  59. fclose($buffer);
  60. }
  61. if ($this->debugHttp) {
  62. error_log("HTTP/1.1 $statusCode");
  63. foreach ($responseHeaders as $key => $value) {
  64. error_log("$key: $value");
  65. }
  66. error_log("\n$body");
  67. }
  68. $this->lastResponse = new Response($statusCode, $body, $responseHeaders);
  69. return $this->lastResponse;
  70. } catch (\ErrorException $e) {
  71. if (isset($curl) && is_resource($curl)) {
  72. curl_close($curl);
  73. }
  74. if (isset($buffer) && is_resource($buffer)) {
  75. fclose($buffer);
  76. }
  77. throw $e;
  78. }
  79. }
  80. public function options($method, $url, $params = array(), $data = array(),
  81. $headers = array(), $user = null, $password = null,
  82. $timeout = null) {
  83. $timeout = is_null($timeout)
  84. ? self::DEFAULT_TIMEOUT
  85. : $timeout;
  86. $options = $this->curlOptions + array(
  87. CURLOPT_URL => $url,
  88. CURLOPT_HEADER => true,
  89. CURLOPT_RETURNTRANSFER => true,
  90. CURLOPT_INFILESIZE => Null,
  91. CURLOPT_HTTPHEADER => array(),
  92. CURLOPT_TIMEOUT => $timeout,
  93. CURLOPT_SSL_VERIFYHOST => 0,
  94. CURLOPT_SSL_VERIFYPEER => 0
  95. );
  96. foreach ($headers as $key => $value) {
  97. $options[CURLOPT_HTTPHEADER][] = "$key: $value";
  98. }
  99. if ($user && $password) {
  100. $options[CURLOPT_HTTPHEADER][] = 'Authorization: Basic ' . base64_encode("$user:$password");
  101. }
  102. $body = $this->buildQuery($params);
  103. if ($body) {
  104. $options[CURLOPT_URL] .= '?' . $body;
  105. }
  106. switch (strtolower(trim($method))) {
  107. case 'get':
  108. $options[CURLOPT_HTTPGET] = true;
  109. break;
  110. case 'post':
  111. $options[CURLOPT_POST] = true;
  112. $options[CURLOPT_POSTFIELDS] = $this->buildQuery($data);
  113. break;
  114. case 'put':
  115. $options[CURLOPT_PUT] = true;
  116. if ($data) {
  117. if ($buffer = fopen('php://memory', 'w+')) {
  118. $dataString = $this->buildQuery($data);
  119. fwrite($buffer, $dataString);
  120. fseek($buffer, 0);
  121. $options[CURLOPT_INFILE] = $buffer;
  122. $options[CURLOPT_INFILESIZE] = strlen($dataString);
  123. } else {
  124. throw new EnvironmentException('Unable to open a temporary file');
  125. }
  126. }
  127. break;
  128. case 'head':
  129. $options[CURLOPT_NOBODY] = true;
  130. break;
  131. default:
  132. $options[CURLOPT_CUSTOMREQUEST] = strtoupper($method);
  133. }
  134. return $options;
  135. }
  136. public function buildQuery($params) {
  137. $parts = array();
  138. if (is_string($params)) {
  139. return $params;
  140. }
  141. $params = $params ?: array();
  142. foreach ($params as $key => $value) {
  143. if (is_array($value)) {
  144. foreach ($value as $item) {
  145. $parts[] = urlencode((string)$key) . '=' . urlencode((string)$item);
  146. }
  147. } else {
  148. $parts[] = urlencode((string)$key) . '=' . urlencode((string)$value);
  149. }
  150. }
  151. return implode('&', $parts);
  152. }
  153. }