|
Moodle
2.2.1
http://www.collinsharper.com
|
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 }