PageRenderTime 60ms CodeModel.GetById 24ms RepoModel.GetById 0ms app.codeStats 0ms

/lib/ZendGdata-1.8.4PL1/library/Zend/Http/Client/Adapter/Curl.php

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