source: extensions/Google2Piwigo/include/Zend/Http/Client/Adapter/Curl.php @ 17475

Last change on this file since 17475 was 17475, checked in by mistic100, 12 years ago

new extension: Google2Piwigo

File size: 16.8 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 24594 2012-01-05 21:27:01Z matthew $
20 * @copyright  Copyright (c) 2005-2012 Zend Technologies USA Inc. (http://www.zend.com)
21 * @license    http://framework.zend.com/license/new-bsd     New BSD License
22 */
23
24/**
25 * @see Zend_Uri_Http
26 */
27require_once 'Zend/Uri/Http.php';
28
29/**
30 * @see Zend_Http_Client_Adapter_Interface
31 */
32require_once 'Zend/Http/Client/Adapter/Interface.php';
33/**
34 * @see Zend_Http_Client_Adapter_Stream
35 */
36require_once 'Zend/Http/Client/Adapter/Stream.php';
37
38/**
39 * An adapter class for Zend_Http_Client based on the curl extension.
40 * Curl requires libcurl. See for full requirements the PHP manual: http://php.net/curl
41 *
42 * @category   Zend
43 * @package    Zend_Http
44 * @subpackage Client_Adapter
45 * @copyright  Copyright (c) 2005-2012 Zend Technologies USA Inc. (http://www.zend.com)
46 * @license    http://framework.zend.com/license/new-bsd     New BSD License
47 */
48class Zend_Http_Client_Adapter_Curl implements Zend_Http_Client_Adapter_Interface, Zend_Http_Client_Adapter_Stream
49{
50    /**
51     * Parameters array
52     *
53     * @var array
54     */
55    protected $_config = array();
56
57    /**
58     * What host/port are we connected to?
59     *
60     * @var array
61     */
62    protected $_connected_to = array(null, null);
63
64    /**
65     * The curl session handle
66     *
67     * @var resource|null
68     */
69    protected $_curl = null;
70
71    /**
72     * List of cURL options that should never be overwritten
73     *
74     * @var array
75     */
76    protected $_invalidOverwritableCurlOptions;
77
78    /**
79     * Response gotten from server
80     *
81     * @var string
82     */
83    protected $_response = null;
84
85    /**
86     * Stream for storing output
87     *
88     * @var resource
89     */
90    protected $out_stream;
91
92    /**
93     * Adapter constructor
94     *
95     * Config is set using setConfig()
96     *
97     * @return void
98     * @throws Zend_Http_Client_Adapter_Exception
99     */
100    public function __construct()
101    {
102        if (!extension_loaded('curl')) {
103            require_once 'Zend/Http/Client/Adapter/Exception.php';
104            throw new Zend_Http_Client_Adapter_Exception('cURL extension has to be loaded to use this Zend_Http_Client adapter.');
105        }
106        $this->_invalidOverwritableCurlOptions = array(
107            CURLOPT_HTTPGET,
108            CURLOPT_POST,
109            CURLOPT_PUT,
110            CURLOPT_CUSTOMREQUEST,
111            CURLOPT_HEADER,
112            CURLOPT_RETURNTRANSFER,
113            CURLOPT_HTTPHEADER,
114            CURLOPT_POSTFIELDS,
115            CURLOPT_INFILE,
116            CURLOPT_INFILESIZE,
117            CURLOPT_PORT,
118            CURLOPT_MAXREDIRS,
119            CURLOPT_CONNECTTIMEOUT,
120            CURL_HTTP_VERSION_1_1,
121            CURL_HTTP_VERSION_1_0,
122        );
123    }
124
125    /**
126     * Set the configuration array for the adapter
127     *
128     * @throws Zend_Http_Client_Adapter_Exception
129     * @param  Zend_Config | array $config
130     * @return Zend_Http_Client_Adapter_Curl
131     */
132    public function setConfig($config = array())
133    {
134        if ($config instanceof Zend_Config) {
135            $config = $config->toArray();
136
137        } elseif (! is_array($config)) {
138            require_once 'Zend/Http/Client/Adapter/Exception.php';
139            throw new Zend_Http_Client_Adapter_Exception(
140                'Array or Zend_Config object expected, got ' . gettype($config)
141            );
142        }
143
144        if(isset($config['proxy_user']) && isset($config['proxy_pass'])) {
145            $this->setCurlOption(CURLOPT_PROXYUSERPWD, $config['proxy_user'].":".$config['proxy_pass']);
146            unset($config['proxy_user'], $config['proxy_pass']);
147        }
148
149        foreach ($config as $k => $v) {
150            $option = strtolower($k);
151            switch($option) {
152                case 'proxy_host':
153                    $this->setCurlOption(CURLOPT_PROXY, $v);
154                    break;
155                case 'proxy_port':
156                    $this->setCurlOption(CURLOPT_PROXYPORT, $v);
157                    break;
158                default:
159                    $this->_config[$option] = $v;
160                    break;
161            }
162        }
163
164        return $this;
165    }
166
167    /**
168      * Retrieve the array of all configuration options
169      *
170      * @return array
171      */
172     public function getConfig()
173     {
174         return $this->_config;
175     }
176
177    /**
178     * Direct setter for cURL adapter related options.
179     *
180     * @param  string|int $option
181     * @param  mixed $value
182     * @return Zend_Http_Adapter_Curl
183     */
184    public function setCurlOption($option, $value)
185    {
186        if (!isset($this->_config['curloptions'])) {
187            $this->_config['curloptions'] = array();
188        }
189        $this->_config['curloptions'][$option] = $value;
190        return $this;
191    }
192
193    /**
194     * Initialize curl
195     *
196     * @param  string  $host
197     * @param  int     $port
198     * @param  boolean $secure
199     * @return void
200     * @throws Zend_Http_Client_Adapter_Exception if unable to connect
201     */
202    public function connect($host, $port = 80, $secure = false)
203    {
204        // If we're already connected, disconnect first
205        if ($this->_curl) {
206            $this->close();
207        }
208
209        // If we are connected to a different server or port, disconnect first
210        if ($this->_curl
211            && is_array($this->_connected_to)
212            && ($this->_connected_to[0] != $host
213            || $this->_connected_to[1] != $port)
214        ) {
215            $this->close();
216        }
217
218        // Do the actual connection
219        $this->_curl = curl_init();
220        if ($port != 80) {
221            curl_setopt($this->_curl, CURLOPT_PORT, intval($port));
222        }
223
224        // Set timeout
225        curl_setopt($this->_curl, CURLOPT_CONNECTTIMEOUT, $this->_config['timeout']);
226
227        // Set Max redirects
228        curl_setopt($this->_curl, CURLOPT_MAXREDIRS, $this->_config['maxredirects']);
229
230        if (!$this->_curl) {
231            $this->close();
232
233            require_once 'Zend/Http/Client/Adapter/Exception.php';
234            throw new Zend_Http_Client_Adapter_Exception('Unable to Connect to ' .  $host . ':' . $port);
235        }
236
237        if ($secure !== false) {
238            // Behave the same like Zend_Http_Adapter_Socket on SSL options.
239            if (isset($this->_config['sslcert'])) {
240                curl_setopt($this->_curl, CURLOPT_SSLCERT, $this->_config['sslcert']);
241            }
242            if (isset($this->_config['sslpassphrase'])) {
243                curl_setopt($this->_curl, CURLOPT_SSLCERTPASSWD, $this->_config['sslpassphrase']);
244            }
245        }
246
247        // Update connected_to
248        $this->_connected_to = array($host, $port);
249    }
250
251    /**
252     * Send request to the remote server
253     *
254     * @param  string        $method
255     * @param  Zend_Uri_Http $uri
256     * @param  float         $http_ver
257     * @param  array         $headers
258     * @param  string        $body
259     * @return string        $request
260     * @throws Zend_Http_Client_Adapter_Exception If connection fails, connected to wrong host, no PUT file defined, unsupported method, or unsupported cURL option
261     */
262    public function write($method, $uri, $httpVersion = 1.1, $headers = array(), $body = '')
263    {
264        // Make sure we're properly connected
265        if (!$this->_curl) {
266            require_once 'Zend/Http/Client/Adapter/Exception.php';
267            throw new Zend_Http_Client_Adapter_Exception("Trying to write but we are not connected");
268        }
269
270        if ($this->_connected_to[0] != $uri->getHost() || $this->_connected_to[1] != $uri->getPort()) {
271            require_once 'Zend/Http/Client/Adapter/Exception.php';
272            throw new Zend_Http_Client_Adapter_Exception("Trying to write but we are connected to the wrong host");
273        }
274
275        // set URL
276        curl_setopt($this->_curl, CURLOPT_URL, $uri->__toString());
277
278        // ensure correct curl call
279        $curlValue = true;
280        switch ($method) {
281            case Zend_Http_Client::GET:
282                $curlMethod = CURLOPT_HTTPGET;
283                break;
284
285            case Zend_Http_Client::POST:
286                $curlMethod = CURLOPT_POST;
287                break;
288
289            case Zend_Http_Client::PUT:
290                // There are two different types of PUT request, either a Raw Data string has been set
291                // or CURLOPT_INFILE and CURLOPT_INFILESIZE are used.
292                if(is_resource($body)) {
293                    $this->_config['curloptions'][CURLOPT_INFILE] = $body;
294                }
295                if (isset($this->_config['curloptions'][CURLOPT_INFILE])) {
296                    // Now we will probably already have Content-Length set, so that we have to delete it
297                    // from $headers at this point:
298                    foreach ($headers AS $k => $header) {
299                        if (preg_match('/Content-Length:\s*(\d+)/i', $header, $m)) {
300                            if(is_resource($body)) {
301                                $this->_config['curloptions'][CURLOPT_INFILESIZE] = (int)$m[1];
302                            }
303                            unset($headers[$k]);
304                        }
305                    }
306
307                    if (!isset($this->_config['curloptions'][CURLOPT_INFILESIZE])) {
308                        require_once 'Zend/Http/Client/Adapter/Exception.php';
309                        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.");
310                    }
311
312                    if(is_resource($body)) {
313                        $body = '';
314                    }
315
316                    $curlMethod = CURLOPT_PUT;
317                } else {
318                    $curlMethod = CURLOPT_CUSTOMREQUEST;
319                    $curlValue = "PUT";
320                }
321                break;
322
323            case Zend_Http_Client::DELETE:
324                $curlMethod = CURLOPT_CUSTOMREQUEST;
325                $curlValue = "DELETE";
326                break;
327
328            case Zend_Http_Client::OPTIONS:
329                $curlMethod = CURLOPT_CUSTOMREQUEST;
330                $curlValue = "OPTIONS";
331                break;
332
333            case Zend_Http_Client::TRACE:
334                $curlMethod = CURLOPT_CUSTOMREQUEST;
335                $curlValue = "TRACE";
336                break;
337
338            case Zend_Http_Client::HEAD:
339                $curlMethod = CURLOPT_CUSTOMREQUEST;
340                $curlValue = "HEAD";
341                break;
342
343            default:
344                // For now, through an exception for unsupported request methods
345                require_once 'Zend/Http/Client/Adapter/Exception.php';
346                throw new Zend_Http_Client_Adapter_Exception("Method currently not supported");
347        }
348
349        if(is_resource($body) && $curlMethod != CURLOPT_PUT) {
350            require_once 'Zend/Http/Client/Adapter/Exception.php';
351            throw new Zend_Http_Client_Adapter_Exception("Streaming requests are allowed only with PUT");
352        }
353
354        // get http version to use
355        $curlHttp = ($httpVersion == 1.1) ? CURL_HTTP_VERSION_1_1 : CURL_HTTP_VERSION_1_0;
356
357        // mark as HTTP request and set HTTP method
358        curl_setopt($this->_curl, $curlHttp, true);
359        curl_setopt($this->_curl, $curlMethod, $curlValue);
360
361        if($this->out_stream) {
362            // headers will be read into the response
363            curl_setopt($this->_curl, CURLOPT_HEADER, false);
364            curl_setopt($this->_curl, CURLOPT_HEADERFUNCTION, array($this, "readHeader"));
365            // and data will be written into the file
366            curl_setopt($this->_curl, CURLOPT_FILE, $this->out_stream);
367        } else {
368            // ensure headers are also returned
369            curl_setopt($this->_curl, CURLOPT_HEADER, true);
370
371            // ensure actual response is returned
372            curl_setopt($this->_curl, CURLOPT_RETURNTRANSFER, true);
373        }
374
375        // set additional headers
376        $headers['Accept'] = '';
377        curl_setopt($this->_curl, CURLOPT_HTTPHEADER, $headers);
378
379        /**
380         * Make sure POSTFIELDS is set after $curlMethod is set:
381         * @link http://de2.php.net/manual/en/function.curl-setopt.php#81161
382         */
383        if ($method == Zend_Http_Client::POST) {
384            curl_setopt($this->_curl, CURLOPT_POSTFIELDS, $body);
385        } elseif ($curlMethod == CURLOPT_PUT) {
386            // this covers a PUT by file-handle:
387            // Make the setting of this options explicit (rather than setting it through the loop following a bit lower)
388            // to group common functionality together.
389            curl_setopt($this->_curl, CURLOPT_INFILE, $this->_config['curloptions'][CURLOPT_INFILE]);
390            curl_setopt($this->_curl, CURLOPT_INFILESIZE, $this->_config['curloptions'][CURLOPT_INFILESIZE]);
391            unset($this->_config['curloptions'][CURLOPT_INFILE]);
392            unset($this->_config['curloptions'][CURLOPT_INFILESIZE]);
393        } elseif ($method == Zend_Http_Client::PUT) {
394            // This is a PUT by a setRawData string, not by file-handle
395            curl_setopt($this->_curl, CURLOPT_POSTFIELDS, $body);
396        } elseif ($method == Zend_Http_Client::DELETE) {
397            // This is a DELETE by a setRawData string
398            curl_setopt($this->_curl, CURLOPT_POSTFIELDS, $body);
399        }
400
401        // set additional curl options
402        if (isset($this->_config['curloptions'])) {
403            foreach ((array)$this->_config['curloptions'] as $k => $v) {
404                if (!in_array($k, $this->_invalidOverwritableCurlOptions)) {
405                    if (curl_setopt($this->_curl, $k, $v) == false) {
406                        require_once 'Zend/Http/Client/Exception.php';
407                        throw new Zend_Http_Client_Exception(sprintf("Unknown or erroreous cURL option '%s' set", $k));
408                    }
409                }
410            }
411        }
412
413        // send the request
414        $response = curl_exec($this->_curl);
415
416        // if we used streaming, headers are already there
417        if(!is_resource($this->out_stream)) {
418            $this->_response = $response;
419        }
420
421        $request  = curl_getinfo($this->_curl, CURLINFO_HEADER_OUT);
422        $request .= $body;
423
424        if (empty($this->_response)) {
425            require_once 'Zend/Http/Client/Exception.php';
426            throw new Zend_Http_Client_Exception("Error in cURL request: " . curl_error($this->_curl));
427        }
428
429        // cURL automatically decodes chunked-messages, this means we have to disallow the Zend_Http_Response to do it again
430        if (stripos($this->_response, "Transfer-Encoding: chunked\r\n")) {
431            $this->_response = str_ireplace("Transfer-Encoding: chunked\r\n", '', $this->_response);
432        }
433
434        // Eliminate multiple HTTP responses.
435        do {
436            $parts  = preg_split('|(?:\r?\n){2}|m', $this->_response, 2);
437            $again  = false;
438
439            if (isset($parts[1]) && preg_match("|^HTTP/1\.[01](.*?)\r\n|mi", $parts[1])) {
440                $this->_response    = $parts[1];
441                $again              = true;
442            }
443        } while ($again);
444
445        // cURL automatically handles Proxy rewrites, remove the "HTTP/1.0 200 Connection established" string:
446        if (stripos($this->_response, "HTTP/1.0 200 Connection established\r\n\r\n") !== false) {
447            $this->_response = str_ireplace("HTTP/1.0 200 Connection established\r\n\r\n", '', $this->_response);
448        }
449
450        return $request;
451    }
452
453    /**
454     * Return read response from server
455     *
456     * @return string
457     */
458    public function read()
459    {
460        return $this->_response;
461    }
462
463    /**
464     * Close the connection to the server
465     *
466     */
467    public function close()
468    {
469        if(is_resource($this->_curl)) {
470            curl_close($this->_curl);
471        }
472        $this->_curl         = null;
473        $this->_connected_to = array(null, null);
474    }
475
476    /**
477     * Get cUrl Handle
478     *
479     * @return resource
480     */
481    public function getHandle()
482    {
483        return $this->_curl;
484    }
485
486    /**
487     * Set output stream for the response
488     *
489     * @param resource $stream
490     * @return Zend_Http_Client_Adapter_Socket
491     */
492    public function setOutputStream($stream)
493    {
494        $this->out_stream = $stream;
495        return $this;
496    }
497
498    /**
499     * Header reader function for CURL
500     *
501     * @param resource $curl
502     * @param string $header
503     * @return int
504     */
505    public function readHeader($curl, $header)
506    {
507        $this->_response .= $header;
508        return strlen($header);
509    }
510}
Note: See TracBrowser for help on using the repository browser.