Joomla Platform  13.1
Documentation des API du framework Joomla Platform
 Tout Classes Espaces de nommage Fichiers Fonctions Variables Pages
curl.php
Aller à la documentation de ce fichier.
1 <?php
2 /**
3  * @package Joomla.Platform
4  * @subpackage HTTP
5  *
6  * @copyright Copyright (C) 2005 - 2013 Open Source Matters, Inc. All rights reserved.
7  * @license GNU General Public License version 2 or later; see LICENSE
8  */
9 
10 defined('JPATH_PLATFORM') or die;
11 
12 /**
13  * HTTP transport class for using cURL.
14  *
15  * @package Joomla.Platform
16  * @subpackage HTTP
17  * @since 11.3
18  */
20 {
21  /**
22  * @var JRegistry The client options.
23  * @since 11.3
24  */
25  protected $options;
26 
27  /**
28  * Constructor. CURLOPT_FOLLOWLOCATION must be disabled when open_basedir or safe_mode are enabled.
29  *
30  * @param JRegistry $options Client options object.
31  *
32  * @see http://www.php.net/manual/en/function.curl-setopt.php
33  * @since 11.3
34  * @throws RuntimeException
35  */
36  public function __construct(JRegistry $options)
37  {
38  if (!function_exists('curl_init') || !is_callable('curl_init'))
39  {
40  throw new RuntimeException('Cannot use a cURL transport when curl_init() is not available.');
41  }
42 
43  $this->options = $options;
44  }
45 
46  /**
47  * Send a request to the server and return a JHttpResponse object with the response.
48  *
49  * @param string $method The HTTP method for sending the request.
50  * @param JUri $uri The URI to the resource to request.
51  * @param mixed $data Either an associative array or a string to be sent with the request.
52  * @param array $headers An array of request headers to send with the request.
53  * @param integer $timeout Read timeout in seconds.
54  * @param string $userAgent The optional user agent string to send with the request.
55  *
56  * @return JHttpResponse
57  *
58  * @since 11.3
59  * @throws RuntimeException
60  */
61  public function request($method, JUri $uri, $data = null, array $headers = null, $timeout = null, $userAgent = null)
62  {
63  // Setup the cURL handle.
64  $ch = curl_init();
65 
66  // Set the request method.
67  $options[CURLOPT_CUSTOMREQUEST] = strtoupper($method);
68 
69  // Don't wait for body when $method is HEAD
70  $options[CURLOPT_NOBODY] = ($method === 'HEAD');
71 
72  // Initialize the certificate store
73  $options[CURLOPT_CAINFO] = $this->options->get('curl.certpath', __DIR__ . '/cacert.pem');
74 
75  // If data exists let's encode it and make sure our Content-type header is set.
76  if (isset($data))
77  {
78  // If the data is a scalar value simply add it to the cURL post fields.
79  if (is_scalar($data) || (isset($headers['Content-Type']) && strpos($headers['Content-Type'], 'multipart/form-data') === 0))
80  {
81  $options[CURLOPT_POSTFIELDS] = $data;
82  }
83 
84  // Otherwise we need to encode the value first.
85  else
86  {
87  $options[CURLOPT_POSTFIELDS] = http_build_query($data);
88  }
89 
90  if (!isset($headers['Content-Type']))
91  {
92  $headers['Content-Type'] = 'application/x-www-form-urlencoded; charset=utf-8';
93  }
94 
95  // Add the relevant headers.
96  if (is_scalar($options[CURLOPT_POSTFIELDS]))
97  {
98  $headers['Content-Length'] = strlen($options[CURLOPT_POSTFIELDS]);
99  }
100  }
101 
102  // Build the headers string for the request.
103  $headerArray = array();
104 
105  if (isset($headers))
106  {
107  foreach ($headers as $key => $value)
108  {
109  $headerArray[] = $key . ': ' . $value;
110  }
111 
112  // Add the headers string into the stream context options array.
113  $options[CURLOPT_HTTPHEADER] = $headerArray;
114  }
115 
116  // If an explicit timeout is given user it.
117  if (isset($timeout))
118  {
119  $options[CURLOPT_TIMEOUT] = (int) $timeout;
120  $options[CURLOPT_CONNECTTIMEOUT] = (int) $timeout;
121  }
122 
123  // If an explicit user agent is given use it.
124  if (isset($userAgent))
125  {
126  $options[CURLOPT_USERAGENT] = $userAgent;
127  }
128 
129  // Set the request URL.
130  $options[CURLOPT_URL] = (string) $uri;
131 
132  // We want our headers. :-)
133  $options[CURLOPT_HEADER] = true;
134 
135  // Return it... echoing it would be tacky.
136  $options[CURLOPT_RETURNTRANSFER] = true;
137 
138  // Override the Expect header to prevent cURL from confusing itself in its own stupidity.
139  // Link: http://the-stickman.com/web-development/php-and-curl-disabling-100-continue-header/
140  $options[CURLOPT_HTTPHEADER][] = 'Expect:';
141 
142  // Follow redirects.
143  $options[CURLOPT_FOLLOWLOCATION] = (bool) $this->options->get('follow_location', true);
144 
145  // Set the cURL options.
146  curl_setopt_array($ch, $options);
147 
148  // Execute the request and close the connection.
149  $content = curl_exec($ch);
150 
151  // Check if the content is a string. If it is not, it must be an error.
152  if (!is_string($content))
153  {
154  $message = curl_error($ch);
155 
156  if (empty($message))
157  {
158  // Error but nothing from cURL? Create our own
159  $message = 'No HTTP response received';
160  }
161 
162  throw new RuntimeException($message);
163  }
164 
165  // Get the request information.
166  $info = curl_getinfo($ch);
167 
168  // Close the connection.
169  curl_close($ch);
170 
171  return $this->getResponse($content, $info);
172  }
173 
174  /**
175  * Method to get a response object from a server response.
176  *
177  * @param string $content The complete server response, including headers
178  * as a string if the response has no errors.
179  * @param array $info The cURL request information.
180  *
181  * @return JHttpResponse
182  *
183  * @since 11.3
184  * @throws UnexpectedValueException
185  */
186  protected function getResponse($content, $info)
187  {
188  // Create the response object.
189  $return = new JHttpResponse;
190 
191  // Get the number of redirects that occurred.
192  $redirects = isset($info['redirect_count']) ? $info['redirect_count'] : 0;
193 
194  /*
195  * Split the response into headers and body. If cURL encountered redirects, the headers for the redirected requests will
196  * also be included. So we split the response into header + body + the number of redirects and only use the last two
197  * sections which should be the last set of headers and the actual body.
198  */
199  $response = explode("\r\n\r\n", $content, 2 + $redirects);
200 
201  // Set the body for the response.
202  $return->body = array_pop($response);
203 
204  // Get the last set of response headers as an array.
205  $headers = explode("\r\n", array_pop($response));
206 
207  // Get the response code from the first offset of the response headers.
208  preg_match('/[0-9]{3}/', array_shift($headers), $matches);
209 
210  $code = count($matches) ? $matches[0] : null;
211 
212  if (is_numeric($code))
213  {
214  $return->code = (int) $code;
215  }
216 
217  // No valid response code was detected.
218  else
219  {
220  throw new UnexpectedValueException('No HTTP response code found.');
221  }
222 
223  // Add the response headers to the response object.
224  foreach ($headers as $header)
225  {
226  $pos = strpos($header, ':');
227  $return->headers[trim(substr($header, 0, $pos))] = trim(substr($header, ($pos + 1)));
228  }
229 
230  return $return;
231  }
232 
233  /**
234  * Method to check if HTTP transport cURL is available for use
235  *
236  * @return boolean true if available, else false
237  *
238  * @since 12.1
239  */
240  public static function isSupported()
241  {
242  return function_exists('curl_version') && curl_version();
243  }
244 }