PageRenderTime 47ms CodeModel.GetById 19ms RepoModel.GetById 0ms app.codeStats 0ms

/system/classes/kohana/request/client/external.php

https://github.com/pratikdhaboo/kodelearn
PHP | 427 lines | 232 code | 73 blank | 122 comment | 22 complexity | c47787bfc60f2ce3f29ded94fc8e229c MD5 | raw file
  1. <?php defined('SYSPATH') or die('No direct script access.');
  2. /**
  3. *
  4. * @package Kohana
  5. * @category Base
  6. * @author Kohana Team
  7. * @copyright (c) 2008-2011 Kohana Team
  8. * @license http://kohanaframework.org/license
  9. */
  10. class Kohana_Request_Client_External extends Request_Client {
  11. /**
  12. * @var array internal header cache for curl processing
  13. * @todo remove in PHP 5.3, use Lambda instead
  14. */
  15. protected static $_processed_headers = array();
  16. /**
  17. * Parses the returned headers from the remote
  18. * request
  19. *
  20. * @param resource $remote The curl resource
  21. * @param string $header The full header string
  22. * @return int
  23. */
  24. protected static function _parse_headers($remote, $header)
  25. {
  26. $headers = array();
  27. if (preg_match_all('/(\w[^\s:]*):[ ]*([^\r\n]*(?:\r\n[ \t][^\r\n]*)*)/', $header, $matches))
  28. {
  29. foreach ($matches[0] as $key => $value)
  30. $headers[$matches[1][$key]] = $matches[2][$key];
  31. }
  32. // If there are headers to apply
  33. if ($headers)
  34. {
  35. Request_Client_External::$_processed_headers += $headers;
  36. }
  37. return strlen($header);
  38. }
  39. /**
  40. * @var array additional curl options to use on execution
  41. */
  42. protected $_options = array();
  43. /**
  44. * Processes the request, executing the controller action that handles this
  45. * request, determined by the [Route].
  46. *
  47. * 1. Before the controller action is called, the [Controller::before] method
  48. * will be called.
  49. * 2. Next the controller action will be called.
  50. * 3. After the controller action is called, the [Controller::after] method
  51. * will be called.
  52. *
  53. * By default, the output from the controller is captured and returned, and
  54. * no headers are sent.
  55. *
  56. * $request->execute();
  57. *
  58. * @param Request $request A request object
  59. * @return Response
  60. * @throws Kohana_Exception
  61. * @uses [Kohana::$profiling]
  62. * @uses [Profiler]
  63. */
  64. public function execute(Request $request)
  65. {
  66. // Check for cache existance
  67. if ($this->_cache instanceof Cache AND ($response = $this->cache_response($request)) instanceof Response)
  68. return $response;
  69. if (Kohana::$profiling)
  70. {
  71. // Set the benchmark name
  72. $benchmark = '"'.$request->uri().'"';
  73. if ($request !== Request::$initial AND Request::$current)
  74. {
  75. // Add the parent request uri
  76. $benchmark .= ' « "'.Request::$current->uri().'"';
  77. }
  78. // Start benchmarking
  79. $benchmark = Profiler::start('Requests', $benchmark);
  80. }
  81. // Store the current active request and replace current with new request
  82. $previous = Request::$current;
  83. Request::$current = $request;
  84. // Resolve the POST fields
  85. if ($post = $request->post())
  86. {
  87. $request->body(http_build_query($post, NULL, '&'))
  88. ->headers('content-type', 'application/x-www-form-urlencoded');
  89. }
  90. try
  91. {
  92. // If PECL_HTTP is present, use extension to complete request
  93. if (extension_loaded('http'))
  94. {
  95. $this->_http_execute($request);
  96. }
  97. // Else if CURL is present, use extension to complete request
  98. elseif (extension_loaded('curl'))
  99. {
  100. $this->_curl_execute($request);
  101. }
  102. // Else use the sloooow method
  103. else
  104. {
  105. $this->_native_execute($request);
  106. }
  107. }
  108. catch (Exception $e)
  109. {
  110. // Restore the previous request
  111. Request::$current = $previous;
  112. if (isset($benchmark))
  113. {
  114. // Delete the benchmark, it is invalid
  115. Profiler::delete($benchmark);
  116. }
  117. // Re-throw the exception
  118. throw $e;
  119. }
  120. // Restore the previous request
  121. Request::$current = $previous;
  122. if (isset($benchmark))
  123. {
  124. // Stop the benchmark
  125. Profiler::stop($benchmark);
  126. }
  127. // Cache the response if cache is available
  128. if ($this->_cache instanceof Cache)
  129. {
  130. $this->cache_response($request, $request->response());
  131. }
  132. // Return the response
  133. return $request->response();
  134. }
  135. /**
  136. * Set and get options for this request.
  137. *
  138. * @param mixed $key Option name, or array of options
  139. * @param mixed $value Option value
  140. * @return mixed
  141. * @return Request_Client_External
  142. */
  143. public function options($key = NULL, $value = NULL)
  144. {
  145. if ($key === NULL)
  146. return $this->_options;
  147. if (is_array($key))
  148. {
  149. $this->_options = $key;
  150. }
  151. elseif ( ! $value)
  152. {
  153. return Arr::get($this->_options, $key);
  154. }
  155. else
  156. {
  157. $this->_options[$key] = $value;
  158. }
  159. return $this;
  160. }
  161. /**
  162. * Execute the request using the PECL HTTP extension. (recommended)
  163. *
  164. * @param Request $request Request to execute
  165. * @return Response
  166. */
  167. protected function _http_execute(Request $request)
  168. {
  169. $http_method_mapping = array(
  170. HTTP_Request::GET => HTTPRequest::METH_GET,
  171. HTTP_Request::HEAD => HTTPRequest::METH_HEAD,
  172. HTTP_Request::POST => HTTPRequest::METH_POST,
  173. HTTP_Request::PUT => HTTPRequest::METH_PUT,
  174. HTTP_Request::DELETE => HTTPRequest::METH_DELETE,
  175. HTTP_Request::OPTIONS => HTTPRequest::METH_OPTIONS,
  176. HTTP_Request::TRACE => HTTPRequest::METH_TRACE,
  177. HTTP_Request::CONNECT => HTTPRequest::METH_CONNECT,
  178. );
  179. // Create an http request object
  180. $http_request = new HTTPRequest($request->uri(), $http_method_mapping[$request->method()]);
  181. if ($this->_options)
  182. {
  183. // Set custom options
  184. $http_request->setOptions($this->_options);
  185. }
  186. // Set headers
  187. $http_request->setHeaders($request->headers()->getArrayCopy());
  188. // Set cookies
  189. $http_request->setCookies($request->cookie());
  190. // Set the body
  191. $http_request->setBody($request->body());
  192. // Set the query
  193. $http_request->setQueryData($request->query());
  194. try
  195. {
  196. $http_request->send();
  197. }
  198. catch (HTTPRequestException $e)
  199. {
  200. throw new Kohana_Request_Exception($e->getMessage());
  201. }
  202. catch (HTTPMalformedHeaderException $e)
  203. {
  204. throw new Kohana_Request_Exception($e->getMessage());
  205. }
  206. catch (HTTPEncodingException $e)
  207. {
  208. throw new Kohana_Request_Exception($e->getMessage());
  209. }
  210. // Create the response
  211. $response = $request->create_response();
  212. // Build the response
  213. $response->status($http_request->getResponseCode())
  214. ->headers($http_request->getResponseHeader())
  215. ->cookie($http_request->getResponseCookies())
  216. ->body($http_request->getResponseBody());
  217. return $response;
  218. }
  219. /**
  220. * Execute the request using the CURL extension. (recommended)
  221. *
  222. * @param Request $request Request to execute
  223. * @return Response
  224. */
  225. protected function _curl_execute(Request $request)
  226. {
  227. // Reset the headers
  228. Request_Client_External::$_processed_headers = array();
  229. // Set the request method
  230. $options[CURLOPT_CUSTOMREQUEST] = $request->method();
  231. // Set the request body. This is perfectly legal in CURL even
  232. // if using a request other than POST. PUT does support this method
  233. // and DOES NOT require writing data to disk before putting it, if
  234. // reading the PHP docs you may have got that impression. SdF
  235. $options[CURLOPT_POSTFIELDS] = $request->body();
  236. // Process headers
  237. if ($headers = $request->headers())
  238. {
  239. $http_headers = array();
  240. foreach ($headers as $key => $value)
  241. {
  242. $http_headers[] = $key.': '.$value;
  243. }
  244. $options[CURLOPT_HTTPHEADER] = $http_headers;
  245. }
  246. // Process cookies
  247. if ($cookies = $request->cookie())
  248. {
  249. $options[CURLOPT_COOKIE] = http_build_query($cookies, NULL, '; ');
  250. }
  251. // The transfer must always be returned
  252. $options[CURLOPT_RETURNTRANSFER] = TRUE;
  253. // Apply any additional options set to Request_Client_External::$_options
  254. $options += $this->_options;
  255. $uri = $request->uri();
  256. if ($query = $request->query())
  257. {
  258. $uri .= '?'.http_build_query($query, NULL, '&');
  259. }
  260. // Open a new remote connection
  261. $curl = curl_init($uri);
  262. // Set connection options
  263. if ( ! curl_setopt_array($curl, $options))
  264. {
  265. throw new Kohana_Request_Exception('Failed to set CURL options, check CURL documentation: :url',
  266. array(':url' => 'http://php.net/curl_setopt_array'));
  267. }
  268. // Get the response body
  269. $body = curl_exec($curl);
  270. // Get the response information
  271. $code = curl_getinfo($curl, CURLINFO_HTTP_CODE);
  272. if ($body === FALSE)
  273. {
  274. $error = curl_error($curl);
  275. }
  276. // Close the connection
  277. curl_close($curl);
  278. if (isset($error))
  279. {
  280. throw new Kohana_Request_Exception('Error fetching remote :url [ status :code ] :error',
  281. array(':url' => $request->url(), ':code' => $code, ':error' => $error));
  282. }
  283. // Create response
  284. $response = $request->create_response();
  285. $response->status($code)
  286. ->headers(Request_Client_External::$_processed_headers)
  287. ->body($body);
  288. return $response;
  289. }
  290. /**
  291. * Execute the request using PHP stream. (not recommended)
  292. *
  293. * @param Request $request Request to execute
  294. * @return Response
  295. */
  296. protected function _native_execute(Request $request)
  297. {
  298. // Reset the headers
  299. Request_Client_External::$_processed_headers = array();
  300. // Calculate stream mode
  301. $mode = ($request->method() === HTTP_Request::GET) ? 'r' : 'r+';
  302. // Process cookies
  303. if ($cookies = $request->cookie())
  304. {
  305. $request->headers('cookie', http_build_query($cookies, NULL, '; '));
  306. }
  307. // Get the message body
  308. $body = $request->body();
  309. // Set the content length
  310. $request->headers('content-length', strlen($body));
  311. // Create the context
  312. $options = array(
  313. $request->protocol() => array(
  314. 'method' => $request->method(),
  315. 'header' => (string) $request->headers(),
  316. 'content' => $body,
  317. 'user-agent' => 'Kohana Framework '.Kohana::VERSION.' ('.Kohana::CODENAME.')'
  318. )
  319. );
  320. // Create the context stream
  321. $context = stream_context_create($options);
  322. stream_context_set_option($context, $this->_options);
  323. $uri = $request->uri();
  324. if ($query = $request->query())
  325. {
  326. $uri .= '?'.http_build_query($query, NULL, '&');
  327. }
  328. $stream = fopen($uri, $mode, FALSE, $context);
  329. $meta_data = stream_get_meta_data($stream);
  330. // Get the HTTP response code
  331. $http_response = array_shift($meta_data['wrapper_data']);
  332. if (preg_match_all('/(\w+\/\d\.\d) (\d{3})/', $http_response, $matches) !== FALSE)
  333. {
  334. $protocol = $matches[1][0];
  335. $status = (int) $matches[2][0];
  336. }
  337. else
  338. {
  339. $protocol = NULL;
  340. $status = NULL;
  341. }
  342. // Process headers
  343. array_map(array('Request_Client_External', '_parse_headers'), array(), $meta_data['wrapper_data']);
  344. // Create a response
  345. $response = $request->create_response();
  346. $response->status($status)
  347. ->protocol($protocol)
  348. ->headers(Request_Client_External::$_processed_headers)
  349. ->body(stream_get_contents($stream));
  350. // Close the stream after use
  351. fclose($stream);
  352. return $response;
  353. }
  354. } // End Kohana_Request_Client_External