source: trunk/www.guidonia.net/wp/wp-content/plugins/webtv/Drivers/Zend/Http/Client/Adapter/Curl.php@ 44

Last change on this file since 44 was 44, checked in by luciano, 14 years ago
File size: 13.4 KB
Line 
1<?php
2
3/**
4 * Zend Framework
5 *
6 * LICENSE
7 *
8 * This source file is subject to the new BSD license that is bundled
9 * with this package in the file LICENSE.txt.
10 * It is also available through the world-wide-web at this URL:
11 * http://framework.zend.com/license/new-bsd
12 * If you did not receive a copy of the license and are unable to
13 * obtain it through the world-wide-web, please send an email
14 * to license@zend.com so we can send you a copy immediately.
15 *
16 * @category Zend
17 * @package Zend_Http
18 * @subpackage Client_Adapter
19 * @version $Id: Curl.php 14379 2009-03-19 14:57:23Z matthew $
20 * @copyright Copyright (c) 2005-2008 Zend Technologies USA Inc. (http://www.zend.com)
21 * @license http://framework.zend.com/license/new-bsd New BSD License
22 */
23
24/** Zend_Uri_Http */
25require_once 'Zend/Uri/Http.php';
26
27/** Zend_Http_Client_Adapter_Interface */
28require_once 'Zend/Http/Client/Adapter/Interface.php';
29
30/**
31 * An adapter class for Zend_Http_Client based on the curl extension.
32 * Curl requires libcurl. See for full requirements the PHP manual: http://php.net/curl
33 *
34 * @category Zend
35 * @package Zend_Http
36 * @subpackage Client_Adapter
37 * @copyright Copyright (c) 2005-2008 Zend Technologies USA Inc. (http://www.zend.com)
38 * @license http://framework.zend.com/license/new-bsd New BSD License
39 */
40class Zend_Http_Client_Adapter_Curl implements Zend_Http_Client_Adapter_Interface
41{
42 /**
43 * Parameters array
44 *
45 * @var array
46 */
47 protected $_config = array();
48
49 /**
50 * What host/port are we connected to?
51 *
52 * @var array
53 */
54 protected $_connected_to = array(null, null);
55
56 /**
57 * The curl session handle
58 *
59 * @var resource|null
60 */
61 protected $_curl = null;
62
63 /**
64 * List of cURL options that should never be overwritten
65 *
66 * @var array
67 */
68 protected $_invalidOverwritableCurlOptions = array(
69 CURLOPT_HTTPGET,
70 CURLOPT_POST,
71 CURLOPT_PUT,
72 CURLOPT_CUSTOMREQUEST,
73 CURLOPT_HEADER,
74 CURLOPT_RETURNTRANSFER,
75 CURLOPT_HTTPHEADER,
76 CURLOPT_POSTFIELDS,
77 CURLOPT_INFILE,
78 CURLOPT_INFILESIZE,
79 CURLOPT_PORT,
80 CURLOPT_MAXREDIRS,
81 CURLOPT_TIMEOUT,
82 CURL_HTTP_VERSION_1_1,
83 CURL_HTTP_VERSION_1_0,
84 );
85
86 /**
87 * Response gotten from server
88 *
89 * @var string
90 */
91 protected $_response = null;
92
93 /**
94 * Adapter constructor
95 *
96 * Config is set using setConfig()
97 *
98 * @return void
99 * @throws Zend_Http_Client_Adapter_Exception
100 */
101 public function __construct()
102 {
103 if (!extension_loaded('curl')) {
104 require_once 'Zend/Http/Client/Adapter/Exception.php';
105 throw new Zend_Http_Client_Adapter_Exception('cURL extension has to be loaded to use this Zend_Http_Client adapter.');
106 }
107 }
108
109 /**
110 * Set the configuration array for the adapter
111 *
112 * @throws Zend_Http_Client_Adapter_Exception
113 * @param array $config
114 * @return Zend_Http_Client_Adapter_Curl
115 */
116 public function setConfig($config = array())
117 {
118 if (!is_array($config)) {
119 require_once 'Zend/Http/Client/Adapter/Exception.php';
120 throw new Zend_Http_Client_Adapter_Exception('Http Adapter configuration expects an array, ' . gettype($config) . ' recieved.');
121 }
122
123 foreach ($config as $k => $v) {
124 $this->_config[strtolower($k)] = $v;
125 }
126
127 return $this;
128 }
129
130 /**
131 * Direct setter for cURL adapter related options.
132 *
133 * @param string|int $option
134 * @param mixed $value
135 * @return Zend_Http_Adapter_Curl
136 */
137 public function setCurlOption($option, $value)
138 {
139 if (!isset($this->_config['curloptions'])) {
140 $this->_config['curloptions'] = array();
141 }
142 $this->_config['curloptions'][$option] = $value;
143 return $this;
144 }
145
146 /**
147 * Initialize curl
148 *
149 * @param string $host
150 * @param int $port
151 * @param boolean $secure
152 * @return void
153 * @throws Zend_Http_Client_Adapter_Exception if unable to connect
154 */
155 public function connect($host, $port = 80, $secure = false)
156 {
157 // If we're already connected, disconnect first
158 if ($this->_curl) {
159 $this->close();
160 }
161
162 // If we are connected to a different server or port, disconnect first
163 if ($this->_curl
164 && is_array($this->_connected_to)
165 && ($this->_connected_to[0] != $host
166 || $this->_connected_to[1] != $port)
167 ) {
168 $this->close();
169 }
170
171 // Do the actual connection
172 $this->_curl = curl_init();
173 if ($port != 80) {
174 curl_setopt($this->_curl, CURLOPT_PORT, intval($port));
175 }
176
177 // Set timeout
178 curl_setopt($this->_curl, CURLOPT_TIMEOUT, $this->_config['timeout']);
179
180 // Set Max redirects
181 curl_setopt($this->_curl, CURLOPT_MAXREDIRS, $this->_config['maxredirects']);
182
183 if (!$this->_curl) {
184 $this->close();
185
186 require_once 'Zend/Http/Client/Adapter/Exception.php';
187 throw new Zend_Http_Client_Adapter_Exception('Unable to Connect to ' . $host . ':' . $port);
188 }
189
190 if ($secure !== false) {
191 // Behave the same like Zend_Http_Adapter_Socket on SSL options.
192 if (isset($this->_config['sslcert'])) {
193 curl_setopt($this->_curl, CURLOPT_SSLCERT, $this->_config['sslcert']);
194 }
195 if (isset($this->_config['sslpassphrase'])) {
196 curl_setopt($this->_curl, CURLOPT_SSLCERTPASSWD, $this->_config['sslpassphrase']);
197 }
198 }
199
200 // Update connected_to
201 $this->_connected_to = array($host, $port);
202 }
203
204 /**
205 * Send request to the remote server
206 *
207 * @param string $method
208 * @param Zend_Uri_Http $uri
209 * @param float $http_ver
210 * @param array $headers
211 * @param string $body
212 * @return string $request
213 * @throws Zend_Http_Client_Adapter_Exception If connection fails, connected to wrong host, no PUT file defined, unsupported method, or unsupported cURL option
214 */
215 public function write($method, $uri, $http_ver = '1.1', $headers = array(), $body = '')
216 {
217 // Make sure we're properly connected
218 if (!$this->_curl) {
219 require_once 'Zend/Http/Client/Adapter/Exception.php';
220 throw new Zend_Http_Client_Adapter_Exception("Trying to write but we are not connected");
221 }
222
223 if ($this->_connected_to[0] != $uri->getHost() || $this->_connected_to[1] != $uri->getPort()) {
224 require_once 'Zend/Http/Client/Adapter/Exception.php';
225 throw new Zend_Http_Client_Adapter_Exception("Trying to write but we are connected to the wrong host");
226 }
227
228 // set URL
229 curl_setopt($this->_curl, CURLOPT_URL, $uri->__toString());
230
231 // ensure correct curl call
232 $curlValue = true;
233 switch ($method) {
234 case Zend_Http_Client::GET:
235 $curlMethod = CURLOPT_HTTPGET;
236 break;
237
238 case Zend_Http_Client::POST:
239 $curlMethod = CURLOPT_POST;
240 break;
241
242 case Zend_Http_Client::PUT:
243 // There are two different types of PUT request, either a Raw Data string has been set
244 // or CURLOPT_INFILE and CURLOPT_INFILESIZE are used.
245 if (isset($this->_config['curloptions'][CURLOPT_INFILE])) {
246 if (!isset($this->_config['curloptions'][CURLOPT_INFILESIZE])) {
247 require_once 'Zend/Http/Client/Adapter/Exception.php';
248 throw new Zend_Http_Client_Adapter_Exception("Cannot set a file-handle for cURL option CURLOPT_INFILE without also setting its size in CURLOPT_INFILESIZE.");
249 }
250
251 // Now we will probably already have Content-Length set, so that we have to delete it
252 // from $headers at this point:
253 foreach ($headers AS $k => $header) {
254 if (stristr($header, "Content-Length:") !== false) {
255 unset($headers[$k]);
256 }
257 }
258
259 $curlMethod = CURLOPT_PUT;
260 } else {
261 $curlMethod = CURLOPT_CUSTOMREQUEST;
262 $curlValue = "PUT";
263 }
264 break;
265
266 case Zend_Http_Client::DELETE:
267 $curlMethod = CURLOPT_CUSTOMREQUEST;
268 $curlValue = "DELETE";
269 break;
270
271 case Zend_Http_Client::OPTIONS:
272 $curlMethod = CURLOPT_CUSTOMREQUEST;
273 $curlValue = "OPTIONS";
274 break;
275
276 case Zend_Http_Client::TRACE:
277 $curlMethod = CURLOPT_CUSTOMREQUEST;
278 $curlValue = "TRACE";
279 break;
280
281 default:
282 // For now, through an exception for unsupported request methods
283 require_once 'Zend/Http/Client/Adapter/Exception.php';
284 throw new Zend_Http_Client_Adapter_Exception("Method currently not supported");
285 }
286
287 // get http version to use
288 $curlHttp = ($http_ver = 1.1) ? CURL_HTTP_VERSION_1_1 : CURL_HTTP_VERSION_1_0;
289
290 // mark as HTTP request and set HTTP method
291 curl_setopt($this->_curl, $curlHttp, true);
292 curl_setopt($this->_curl, $curlMethod, $curlValue);
293
294 // ensure headers are also returned
295 curl_setopt($this->_curl, CURLOPT_HEADER, true);
296
297 // ensure actual response is returned
298 curl_setopt($this->_curl, CURLOPT_RETURNTRANSFER, true);
299
300 // set additional headers
301 $headers['Accept'] = '';
302 curl_setopt($this->_curl, CURLOPT_HTTPHEADER, $headers);
303
304 /**
305 * Make sure POSTFIELDS is set after $curlMethod is set:
306 * @link http://de2.php.net/manual/en/function.curl-setopt.php#81161
307 */
308 if ($method == Zend_Http_Client::POST) {
309 curl_setopt($this->_curl, CURLOPT_POSTFIELDS, $body);
310 } elseif ($curlMethod == CURLOPT_PUT) {
311 // this covers a PUT by file-handle:
312 // Make the setting of this options explicit (rather than setting it through the loop following a bit lower)
313 // to group common functionality together.
314 curl_setopt($this->_curl, CURLOPT_INFILE, $this->_config['curloptions'][CURLOPT_INFILE]);
315 curl_setopt($this->_curl, CURLOPT_INFILESIZE, $this->_config['curloptions'][CURLOPT_INFILESIZE]);
316 unset($this->_config['curloptions'][CURLOPT_INFILE]);
317 unset($this->_config['curloptions'][CURLOPT_INFILESIZE]);
318 } elseif ($method == Zend_Http_Client::PUT) {
319 // This is a PUT by a setRawData string, not by file-handle
320 curl_setopt($this->_curl, CURLOPT_POSTFIELDS, $body);
321 }
322
323 // set additional curl options
324 if (isset($this->_config['curloptions'])) {
325 foreach ((array)$this->_config['curloptions'] as $k => $v) {
326 if (!in_array($k, $this->_invalidOverwritableCurlOptions)) {
327 if (curl_setopt($this->_curl, $k, $v) == false) {
328 require_once 'Zend/Http/Client/Exception.php';
329 throw new Zend_Http_Client_Exception(sprintf("Unknown or erroreous cURL option '%s' set", $k));
330 }
331 }
332 }
333 }
334
335 // send the request
336 $this->_response = curl_exec($this->_curl);
337
338 $request = curl_getinfo($this->_curl, CURLINFO_HEADER_OUT);
339 $request .= $body;
340
341 if (empty($this->_response)) {
342 require_once 'Zend/Http/Client/Exception.php';
343 throw new Zend_Http_Client_Exception("Error in cURL request: " . curl_error($this->_curl));
344 }
345
346 // cURL automatically decodes chunked-messages, this means we have to disallow the Zend_Http_Response to do it again
347 if (stripos($this->_response, "Transfer-Encoding: chunked\r\n")) {
348 $this->_response = str_ireplace("Transfer-Encoding: chunked\r\n", '', $this->_response);
349 }
350
351 // TODO: Probably the pattern for multiple handshake requests is always the same, several HTTP codes in the response. Use that information?
352 // cURL automactically handles Expect: 100-continue; and its responses. Delete the HTTP 100 CONTINUE from a response
353 // because it messes up Zend_Http_Response parsing
354 if (stripos($this->_response, "HTTP/1.1 100 Continue\r\n\r\n") !== false) {
355 $this->_response = str_ireplace("HTTP/1.1 100 Continue\r\n\r\n", '', $this->_response);
356 }
357
358 // cURL automatically handles Proxy rewrites, remove the "HTTP/1.0 200 Connection established" string:
359 if (stripos($this->_response, "HTTP/1.0 200 Connection established\r\n\r\n") !== false) {
360 $this->_response = str_ireplace("HTTP/1.0 200 Connection established\r\n\r\n", '', $this->_response);
361 }
362
363 return $request;
364 }
365
366 /**
367 * Return read response from server
368 *
369 * @return string
370 */
371 public function read()
372 {
373 return $this->_response;
374 }
375
376 /**
377 * Close the connection to the server
378 *
379 */
380 public function close()
381 {
382 if(is_resource($this->_curl)) {
383 curl_close($this->_curl);
384 }
385 $this->_curl = null;
386 $this->_connected_to = array(null, null);
387 }
388}
Note: See TracBrowser for help on using the repository browser.