Moodle  2.2.1
http://www.collinsharper.com
C:/xampp/htdocs/moodle/lib/zend/Zend/Http/Client/Adapter/Curl.php
Go to the documentation of this file.
00001 <?php
00002 
00027 require_once 'Zend/Uri/Http.php';
00028 
00032 require_once 'Zend/Http/Client/Adapter/Interface.php';
00036 require_once 'Zend/Http/Client/Adapter/Stream.php';
00037 
00048 class Zend_Http_Client_Adapter_Curl implements Zend_Http_Client_Adapter_Interface, Zend_Http_Client_Adapter_Stream
00049 {
00055     protected $_config = array();
00056 
00062     protected $_connected_to = array(null, null);
00063 
00069     protected $_curl = null;
00070 
00076     protected $_invalidOverwritableCurlOptions;
00077 
00083     protected $_response = null;
00084 
00090     protected $out_stream;
00091 
00100     public function __construct()
00101     {
00102         if (!extension_loaded('curl')) {
00103             require_once 'Zend/Http/Client/Adapter/Exception.php';
00104             throw new Zend_Http_Client_Adapter_Exception('cURL extension has to be loaded to use this Zend_Http_Client adapter.');
00105         }
00106         $this->_invalidOverwritableCurlOptions = array(
00107             CURLOPT_HTTPGET,
00108             CURLOPT_POST,
00109             CURLOPT_PUT,
00110             CURLOPT_CUSTOMREQUEST,
00111             CURLOPT_HEADER,
00112             CURLOPT_RETURNTRANSFER,
00113             CURLOPT_HTTPHEADER,
00114             CURLOPT_POSTFIELDS,
00115             CURLOPT_INFILE,
00116             CURLOPT_INFILESIZE,
00117             CURLOPT_PORT,
00118             CURLOPT_MAXREDIRS,
00119             CURLOPT_CONNECTTIMEOUT,
00120             CURL_HTTP_VERSION_1_1,
00121             CURL_HTTP_VERSION_1_0,
00122         );
00123     }
00124 
00132     public function setConfig($config = array())
00133     {
00134         if ($config instanceof Zend_Config) {
00135             $config = $config->toArray();
00136 
00137         } elseif (! is_array($config)) {
00138             require_once 'Zend/Http/Client/Adapter/Exception.php';
00139             throw new Zend_Http_Client_Adapter_Exception(
00140                 'Array or Zend_Config object expected, got ' . gettype($config)
00141             );
00142         }
00143 
00144         if(isset($config['proxy_user']) && isset($config['proxy_pass'])) {
00145             $this->setCurlOption(CURLOPT_PROXYUSERPWD, $config['proxy_user'].":".$config['proxy_pass']);
00146             unset($config['proxy_user'], $config['proxy_pass']);
00147         }
00148 
00149         foreach ($config as $k => $v) {
00150             $option = strtolower($k);
00151             switch($option) {
00152                 case 'proxy_host':
00153                     $this->setCurlOption(CURLOPT_PROXY, $v);
00154                     break;
00155                 case 'proxy_port':
00156                     $this->setCurlOption(CURLOPT_PROXYPORT, $v);
00157                     break;
00158                 default:
00159                     $this->_config[$option] = $v;
00160                     break;
00161             }
00162         }
00163 
00164         return $this;
00165     }
00166 
00172      public function getConfig()
00173      {
00174          return $this->_config;
00175      }
00176 
00184     public function setCurlOption($option, $value)
00185     {
00186         if (!isset($this->_config['curloptions'])) {
00187             $this->_config['curloptions'] = array();
00188         }
00189         $this->_config['curloptions'][$option] = $value;
00190         return $this;
00191     }
00192 
00202     public function connect($host, $port = 80, $secure = false)
00203     {
00204         // If we're already connected, disconnect first
00205         if ($this->_curl) {
00206             $this->close();
00207         }
00208 
00209         // If we are connected to a different server or port, disconnect first
00210         if ($this->_curl
00211             && is_array($this->_connected_to)
00212             && ($this->_connected_to[0] != $host
00213             || $this->_connected_to[1] != $port)
00214         ) {
00215             $this->close();
00216         }
00217 
00218         // Do the actual connection
00219         $this->_curl = curl_init();
00220         if ($port != 80) {
00221             curl_setopt($this->_curl, CURLOPT_PORT, intval($port));
00222         }
00223 
00224         // Set timeout
00225         curl_setopt($this->_curl, CURLOPT_CONNECTTIMEOUT, $this->_config['timeout']);
00226 
00227         // Set Max redirects
00228         curl_setopt($this->_curl, CURLOPT_MAXREDIRS, $this->_config['maxredirects']);
00229 
00230         if (!$this->_curl) {
00231             $this->close();
00232 
00233             require_once 'Zend/Http/Client/Adapter/Exception.php';
00234             throw new Zend_Http_Client_Adapter_Exception('Unable to Connect to ' .  $host . ':' . $port);
00235         }
00236 
00237         if ($secure !== false) {
00238             // Behave the same like Zend_Http_Adapter_Socket on SSL options.
00239             if (isset($this->_config['sslcert'])) {
00240                 curl_setopt($this->_curl, CURLOPT_SSLCERT, $this->_config['sslcert']);
00241             }
00242             if (isset($this->_config['sslpassphrase'])) {
00243                 curl_setopt($this->_curl, CURLOPT_SSLCERTPASSWD, $this->_config['sslpassphrase']);
00244             }
00245         }
00246 
00247         // Update connected_to
00248         $this->_connected_to = array($host, $port);
00249     }
00250 
00262     public function write($method, $uri, $httpVersion = 1.1, $headers = array(), $body = '')
00263     {
00264         // Make sure we're properly connected
00265         if (!$this->_curl) {
00266             require_once 'Zend/Http/Client/Adapter/Exception.php';
00267             throw new Zend_Http_Client_Adapter_Exception("Trying to write but we are not connected");
00268         }
00269 
00270         if ($this->_connected_to[0] != $uri->getHost() || $this->_connected_to[1] != $uri->getPort()) {
00271             require_once 'Zend/Http/Client/Adapter/Exception.php';
00272             throw new Zend_Http_Client_Adapter_Exception("Trying to write but we are connected to the wrong host");
00273         }
00274 
00275         // set URL
00276         curl_setopt($this->_curl, CURLOPT_URL, $uri->__toString());
00277 
00278         // ensure correct curl call
00279         $curlValue = true;
00280         switch ($method) {
00281             case Zend_Http_Client::GET:
00282                 $curlMethod = CURLOPT_HTTPGET;
00283                 break;
00284 
00285             case Zend_Http_Client::POST:
00286                 $curlMethod = CURLOPT_POST;
00287                 break;
00288 
00289             case Zend_Http_Client::PUT:
00290                 // There are two different types of PUT request, either a Raw Data string has been set
00291                 // or CURLOPT_INFILE and CURLOPT_INFILESIZE are used.
00292                 if(is_resource($body)) {
00293                     $this->_config['curloptions'][CURLOPT_INFILE] = $body;
00294                 }
00295                 if (isset($this->_config['curloptions'][CURLOPT_INFILE])) {
00296                     // Now we will probably already have Content-Length set, so that we have to delete it
00297                     // from $headers at this point:
00298                     foreach ($headers AS $k => $header) {
00299                         if (preg_match('/Content-Length:\s*(\d+)/i', $header, $m)) {
00300                             if(is_resource($body)) {
00301                                 $this->_config['curloptions'][CURLOPT_INFILESIZE] = (int)$m[1];
00302                             }
00303                             unset($headers[$k]);
00304                         }
00305                     }
00306 
00307                     if (!isset($this->_config['curloptions'][CURLOPT_INFILESIZE])) {
00308                         require_once 'Zend/Http/Client/Adapter/Exception.php';
00309                         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.");
00310                     }
00311 
00312                     if(is_resource($body)) {
00313                         $body = '';
00314                     }
00315 
00316                     $curlMethod = CURLOPT_PUT;
00317                 } else {
00318                     $curlMethod = CURLOPT_CUSTOMREQUEST;
00319                     $curlValue = "PUT";
00320                 }
00321                 break;
00322 
00323             case Zend_Http_Client::DELETE:
00324                 $curlMethod = CURLOPT_CUSTOMREQUEST;
00325                 $curlValue = "DELETE";
00326                 break;
00327 
00328             case Zend_Http_Client::OPTIONS:
00329                 $curlMethod = CURLOPT_CUSTOMREQUEST;
00330                 $curlValue = "OPTIONS";
00331                 break;
00332 
00333             case Zend_Http_Client::TRACE:
00334                 $curlMethod = CURLOPT_CUSTOMREQUEST;
00335                 $curlValue = "TRACE";
00336                 break;
00337             
00338             case Zend_Http_Client::HEAD:
00339                 $curlMethod = CURLOPT_CUSTOMREQUEST;
00340                 $curlValue = "HEAD";
00341                 break;
00342 
00343             default:
00344                 // For now, through an exception for unsupported request methods
00345                 require_once 'Zend/Http/Client/Adapter/Exception.php';
00346                 throw new Zend_Http_Client_Adapter_Exception("Method currently not supported");
00347         }
00348 
00349         if(is_resource($body) && $curlMethod != CURLOPT_PUT) {
00350             require_once 'Zend/Http/Client/Adapter/Exception.php';
00351             throw new Zend_Http_Client_Adapter_Exception("Streaming requests are allowed only with PUT");
00352         }
00353 
00354         // get http version to use
00355         $curlHttp = ($httpVersion == 1.1) ? CURL_HTTP_VERSION_1_1 : CURL_HTTP_VERSION_1_0;
00356 
00357         // mark as HTTP request and set HTTP method
00358         curl_setopt($this->_curl, $curlHttp, true);
00359         curl_setopt($this->_curl, $curlMethod, $curlValue);
00360 
00361         if($this->out_stream) {
00362             // headers will be read into the response
00363             curl_setopt($this->_curl, CURLOPT_HEADER, false);
00364             curl_setopt($this->_curl, CURLOPT_HEADERFUNCTION, array($this, "readHeader"));
00365             // and data will be written into the file
00366             curl_setopt($this->_curl, CURLOPT_FILE, $this->out_stream);
00367         } else {
00368             // ensure headers are also returned
00369             curl_setopt($this->_curl, CURLOPT_HEADER, true);
00370 
00371             // ensure actual response is returned
00372             curl_setopt($this->_curl, CURLOPT_RETURNTRANSFER, true);
00373         }
00374 
00375         // set additional headers
00376         $headers['Accept'] = '';
00377         curl_setopt($this->_curl, CURLOPT_HTTPHEADER, $headers);
00378 
00383         if ($method == Zend_Http_Client::POST) {
00384             curl_setopt($this->_curl, CURLOPT_POSTFIELDS, $body);
00385         } elseif ($curlMethod == CURLOPT_PUT) {
00386             // this covers a PUT by file-handle:
00387             // Make the setting of this options explicit (rather than setting it through the loop following a bit lower)
00388             // to group common functionality together.
00389             curl_setopt($this->_curl, CURLOPT_INFILE, $this->_config['curloptions'][CURLOPT_INFILE]);
00390             curl_setopt($this->_curl, CURLOPT_INFILESIZE, $this->_config['curloptions'][CURLOPT_INFILESIZE]);
00391             unset($this->_config['curloptions'][CURLOPT_INFILE]);
00392             unset($this->_config['curloptions'][CURLOPT_INFILESIZE]);
00393         } elseif ($method == Zend_Http_Client::PUT) {
00394             // This is a PUT by a setRawData string, not by file-handle
00395             curl_setopt($this->_curl, CURLOPT_POSTFIELDS, $body);
00396         }
00397 
00398         // set additional curl options
00399         if (isset($this->_config['curloptions'])) {
00400             foreach ((array)$this->_config['curloptions'] as $k => $v) {
00401                 if (!in_array($k, $this->_invalidOverwritableCurlOptions)) {
00402                     if (curl_setopt($this->_curl, $k, $v) == false) {
00403                         require_once 'Zend/Http/Client/Exception.php';
00404                         throw new Zend_Http_Client_Exception(sprintf("Unknown or erroreous cURL option '%s' set", $k));
00405                     }
00406                 }
00407             }
00408         }
00409 
00410         // send the request
00411         $response = curl_exec($this->_curl);
00412 
00413         // if we used streaming, headers are already there
00414         if(!is_resource($this->out_stream)) {
00415             $this->_response = $response;
00416         }
00417 
00418         $request  = curl_getinfo($this->_curl, CURLINFO_HEADER_OUT);
00419         $request .= $body;
00420 
00421         if (empty($this->_response)) {
00422             require_once 'Zend/Http/Client/Exception.php';
00423             throw new Zend_Http_Client_Exception("Error in cURL request: " . curl_error($this->_curl));
00424         }
00425 
00426         // cURL automatically decodes chunked-messages, this means we have to disallow the Zend_Http_Response to do it again
00427         if (stripos($this->_response, "Transfer-Encoding: chunked\r\n")) {
00428             $this->_response = str_ireplace("Transfer-Encoding: chunked\r\n", '', $this->_response);
00429         }
00430 
00431         // Eliminate multiple HTTP responses.
00432         do {
00433             $parts  = preg_split('|(?:\r?\n){2}|m', $this->_response, 2);
00434             $again  = false;
00435 
00436             if (isset($parts[1]) && preg_match("|^HTTP/1\.[01](.*?)\r\n|mi", $parts[1])) {
00437                 $this->_response    = $parts[1];
00438                 $again              = true;
00439             }
00440         } while ($again);
00441 
00442         // cURL automatically handles Proxy rewrites, remove the "HTTP/1.0 200 Connection established" string:
00443         if (stripos($this->_response, "HTTP/1.0 200 Connection established\r\n\r\n") !== false) {
00444             $this->_response = str_ireplace("HTTP/1.0 200 Connection established\r\n\r\n", '', $this->_response);
00445         }
00446 
00447         return $request;
00448     }
00449 
00455     public function read()
00456     {
00457         return $this->_response;
00458     }
00459 
00464     public function close()
00465     {
00466         if(is_resource($this->_curl)) {
00467             curl_close($this->_curl);
00468         }
00469         $this->_curl         = null;
00470         $this->_connected_to = array(null, null);
00471     }
00472 
00478     public function getHandle()
00479     {
00480         return $this->_curl;
00481     }
00482 
00489     public function setOutputStream($stream)
00490     {
00491         $this->out_stream = $stream;
00492         return $this;
00493     }
00494 
00502     public function readHeader($curl, $header)
00503     {
00504         $this->_response .= $header;
00505         return strlen($header);
00506     }
00507 }
 All Data Structures Namespaces Files Functions Variables Enumerations