PageRenderTime 55ms CodeModel.GetById 22ms RepoModel.GetById 1ms app.codeStats 0ms

/system/classes/kohana/request.php

https://bitbucket.org/rlm3/mrs
PHP | 1501 lines | 682 code | 202 blank | 617 comment | 82 complexity | 0747e37e267d7979479d808bf5888c4d MD5 | raw file
Possible License(s): BSD-3-Clause
  1. <?php defined('SYSPATH') or die('No direct script access.');
  2. /**
  3. * Request and response wrapper. Uses the [Route] class to determine what
  4. * [Controller] to send the request to.
  5. *
  6. * @package Kohana
  7. * @category Base
  8. * @author Kohana Team
  9. * @copyright (c) 2008-2011 Kohana Team
  10. * @license http://kohanaframework.org/license
  11. */
  12. class Kohana_Request implements HTTP_Request {
  13. /**
  14. * @var string client user agent
  15. */
  16. public static $user_agent = '';
  17. /**
  18. * @var string client IP address
  19. */
  20. public static $client_ip = '0.0.0.0';
  21. /**
  22. * @var Request main request instance
  23. */
  24. public static $initial;
  25. /**
  26. * @var Request currently executing request instance
  27. */
  28. public static $current;
  29. /**
  30. * Creates a new request object for the given URI. New requests should be
  31. * created using the [Request::instance] or [Request::factory] methods.
  32. *
  33. * $request = Request::factory($uri);
  34. *
  35. * If $cache parameter is set, the response for the request will attempt to
  36. * be retrieved from the cache.
  37. *
  38. * @param string $uri URI of the request
  39. * @param Cache $cache
  40. * @param array $injected_routes an array of routes to use, for testing
  41. * @return void
  42. * @throws Kohana_Request_Exception
  43. * @uses Route::all
  44. * @uses Route::matches
  45. */
  46. public static function factory($uri = TRUE, Cache $cache = NULL, $injected_routes = array())
  47. {
  48. // If this is the initial request
  49. if ( ! Request::$initial)
  50. {
  51. if (Kohana::$is_cli)
  52. {
  53. // Default protocol for command line is cli://
  54. $protocol = 'cli';
  55. // Get the command line options
  56. $options = CLI::options('uri', 'method', 'get', 'post', 'referrer');
  57. if (isset($options['uri']))
  58. {
  59. // Use the specified URI
  60. $uri = $options['uri'];
  61. }
  62. elseif ($uri === TRUE)
  63. {
  64. $uri = '';
  65. }
  66. if (isset($options['method']))
  67. {
  68. // Use the specified method
  69. $method = strtoupper($options['method']);
  70. }
  71. else
  72. {
  73. // Default to GET requests
  74. $method = HTTP_Request::GET;
  75. }
  76. if (isset($options['get']))
  77. {
  78. // Overload the global GET data
  79. parse_str($options['get'], $_GET);
  80. }
  81. if (isset($options['post']))
  82. {
  83. // Overload the global POST data
  84. parse_str($options['post'], $_POST);
  85. }
  86. if (isset($options['referrer']))
  87. {
  88. $referrer = $options['referrer'];
  89. }
  90. }
  91. else
  92. {
  93. if (isset($_SERVER['REQUEST_METHOD']))
  94. {
  95. // Use the server request method
  96. $method = $_SERVER['REQUEST_METHOD'];
  97. }
  98. else
  99. {
  100. // Default to GET requests
  101. $method = HTTP_Request::GET;
  102. }
  103. if ( ! empty($_SERVER['HTTPS']) AND filter_var($_SERVER['HTTPS'], FILTER_VALIDATE_BOOLEAN))
  104. {
  105. // This request is secure
  106. $protocol = 'https';
  107. }
  108. else
  109. {
  110. $protocol = 'http';
  111. }
  112. if (isset($_SERVER['HTTP_REFERER']))
  113. {
  114. // There is a referrer for this request
  115. $referrer = $_SERVER['HTTP_REFERER'];
  116. }
  117. if (isset($_SERVER['HTTP_USER_AGENT']))
  118. {
  119. // Browser type
  120. Request::$user_agent = $_SERVER['HTTP_USER_AGENT'];
  121. }
  122. if (isset($_SERVER['HTTP_X_REQUESTED_WITH']))
  123. {
  124. // Typically used to denote AJAX requests
  125. $requested_with = $_SERVER['HTTP_X_REQUESTED_WITH'];
  126. }
  127. if (isset($_SERVER['HTTP_X_FORWARDED_FOR']))
  128. {
  129. // Use the forwarded IP address, typically set when the
  130. // client is using a proxy server.
  131. Request::$client_ip = $_SERVER['HTTP_X_FORWARDED_FOR'];
  132. }
  133. elseif (isset($_SERVER['HTTP_CLIENT_IP']))
  134. {
  135. // Use the forwarded IP address, typically set when the
  136. // client is using a proxy server.
  137. Request::$client_ip = $_SERVER['HTTP_CLIENT_IP'];
  138. }
  139. elseif (isset($_SERVER['REMOTE_ADDR']))
  140. {
  141. // The remote IP address
  142. Request::$client_ip = $_SERVER['REMOTE_ADDR'];
  143. }
  144. if ($method !== 'GET')
  145. {
  146. // Ensure the raw body is saved for future use
  147. $body = file_get_contents('php://input');
  148. }
  149. if ($uri === TRUE)
  150. {
  151. // Attempt to guess the proper URI
  152. $uri = Request::detect_uri();
  153. }
  154. }
  155. // Create the instance singleton
  156. Request::$initial = $request = new Request($uri, $cache);
  157. // Store global GET and POST data in the initial request only
  158. $request->query($_GET);
  159. $request->post($_POST);
  160. if (isset($protocol))
  161. {
  162. // Set the request protocol
  163. $request->protocol($protocol);
  164. }
  165. if (isset($method))
  166. {
  167. // Set the request method
  168. $request->method($method);
  169. }
  170. if (isset($referrer))
  171. {
  172. // Set the referrer
  173. $request->referrer($referrer);
  174. }
  175. if (isset($requested_with))
  176. {
  177. // Apply the requested with variable
  178. $request->requested_with($requested_with);
  179. }
  180. if (isset($body))
  181. {
  182. // Set the request body (probably a PUT type)
  183. $request->body($body);
  184. }
  185. }
  186. else
  187. {
  188. $request = new Request($uri, $cache, $injected_routes);
  189. }
  190. return $request;
  191. }
  192. /**
  193. * Automatically detects the URI of the main request using PATH_INFO,
  194. * REQUEST_URI, PHP_SELF or REDIRECT_URL.
  195. *
  196. * $uri = Request::detect_uri();
  197. *
  198. * @return string URI of the main request
  199. * @throws Kohana_Exception
  200. * @since 3.0.8
  201. */
  202. public static function detect_uri()
  203. {
  204. if ( ! empty($_SERVER['PATH_INFO']))
  205. {
  206. // PATH_INFO does not contain the docroot or index
  207. $uri = $_SERVER['PATH_INFO'];
  208. }
  209. else
  210. {
  211. // REQUEST_URI and PHP_SELF include the docroot and index
  212. if (isset($_SERVER['REQUEST_URI']))
  213. {
  214. /**
  215. * We use REQUEST_URI as the fallback value. The reason
  216. * for this is we might have a malformed URL such as:
  217. *
  218. * http://localhost/http://example.com/judge.php
  219. *
  220. * which parse_url can't handle. So rather than leave empty
  221. * handed, we'll use this.
  222. */
  223. $uri = $_SERVER['REQUEST_URI'];
  224. if ($request_uri = parse_url($_SERVER['REQUEST_URI'], PHP_URL_PATH))
  225. {
  226. // Valid URL path found, set it.
  227. $uri = $request_uri;
  228. }
  229. // Decode the request URI
  230. $uri = rawurldecode($uri);
  231. }
  232. elseif (isset($_SERVER['PHP_SELF']))
  233. {
  234. $uri = $_SERVER['PHP_SELF'];
  235. }
  236. elseif (isset($_SERVER['REDIRECT_URL']))
  237. {
  238. $uri = $_SERVER['REDIRECT_URL'];
  239. }
  240. else
  241. {
  242. // If you ever see this error, please report an issue at http://dev.kohanaphp.com/projects/kohana3/issues
  243. // along with any relevant information about your web server setup. Thanks!
  244. throw new Kohana_Exception('Unable to detect the URI using PATH_INFO, REQUEST_URI, PHP_SELF or REDIRECT_URL');
  245. }
  246. // Get the path from the base URL, including the index file
  247. $base_url = parse_url(Kohana::$base_url, PHP_URL_PATH);
  248. if (strpos($uri, $base_url) === 0)
  249. {
  250. // Remove the base URL from the URI
  251. $uri = (string) substr($uri, strlen($base_url));
  252. }
  253. if (Kohana::$index_file AND strpos($uri, Kohana::$index_file) === 0)
  254. {
  255. // Remove the index file from the URI
  256. $uri = (string) substr($uri, strlen(Kohana::$index_file));
  257. }
  258. }
  259. return $uri;
  260. }
  261. /**
  262. * Return the currently executing request. This is changed to the current
  263. * request when [Request::execute] is called and restored when the request
  264. * is completed.
  265. *
  266. * $request = Request::current();
  267. *
  268. * @return Request
  269. * @since 3.0.5
  270. */
  271. public static function current()
  272. {
  273. return Request::$current;
  274. }
  275. /**
  276. * Returns the first request encountered by this framework. This will should
  277. * only be set once during the first [Request::factory] invocation.
  278. *
  279. * // Get the first request
  280. * $request = Request::initial();
  281. *
  282. * // Test whether the current request is the first request
  283. * if (Request::initial() === Request::current())
  284. * // Do something useful
  285. *
  286. * @return Request
  287. * @since 3.1.0
  288. */
  289. public static function initial()
  290. {
  291. return Request::$initial;
  292. }
  293. /**
  294. * Returns information about the client user agent.
  295. *
  296. * // Returns "Chrome" when using Google Chrome
  297. * $browser = Request::user_agent('browser');
  298. *
  299. * Multiple values can be returned at once by using an array:
  300. *
  301. * // Get the browser and platform with a single call
  302. * $info = Request::user_agent(array('browser', 'platform'));
  303. *
  304. * When using an array for the value, an associative array will be returned.
  305. *
  306. * @param mixed $value String to return: browser, version, robot, mobile, platform; or array of values
  307. * @return mixed requested information, FALSE if nothing is found
  308. * @uses Kohana::config
  309. * @uses Request::$user_agent
  310. */
  311. public static function user_agent($value)
  312. {
  313. if (is_array($value))
  314. {
  315. $agent = array();
  316. foreach ($value as $v)
  317. {
  318. // Add each key to the set
  319. $agent[$v] = Request::user_agent($v);
  320. }
  321. return $agent;
  322. }
  323. static $info;
  324. if (isset($info[$value]))
  325. {
  326. // This value has already been found
  327. return $info[$value];
  328. }
  329. if ($value === 'browser' OR $value == 'version')
  330. {
  331. // Load browsers
  332. $browsers = Kohana::config('user_agents')->browser;
  333. foreach ($browsers as $search => $name)
  334. {
  335. if (stripos(Request::$user_agent, $search) !== FALSE)
  336. {
  337. // Set the browser name
  338. $info['browser'] = $name;
  339. if (preg_match('#'.preg_quote($search).'[^0-9.]*+([0-9.][0-9.a-z]*)#i', Request::$user_agent, $matches))
  340. {
  341. // Set the version number
  342. $info['version'] = $matches[1];
  343. }
  344. else
  345. {
  346. // No version number found
  347. $info['version'] = FALSE;
  348. }
  349. return $info[$value];
  350. }
  351. }
  352. }
  353. else
  354. {
  355. // Load the search group for this type
  356. $group = Kohana::config('user_agents')->$value;
  357. foreach ($group as $search => $name)
  358. {
  359. if (stripos(Request::$user_agent, $search) !== FALSE)
  360. {
  361. // Set the value name
  362. return $info[$value] = $name;
  363. }
  364. }
  365. }
  366. // The value requested could not be found
  367. return $info[$value] = FALSE;
  368. }
  369. /**
  370. * Returns the accepted content types. If a specific type is defined,
  371. * the quality of that type will be returned.
  372. *
  373. * $types = Request::accept_type();
  374. *
  375. * @param string $type Content MIME type
  376. * @return mixed An array of all types or a specific type as a string
  377. * @uses Request::_parse_accept
  378. */
  379. public static function accept_type($type = NULL)
  380. {
  381. static $accepts;
  382. if ($accepts === NULL)
  383. {
  384. // Parse the HTTP_ACCEPT header
  385. $accepts = Request::_parse_accept($_SERVER['HTTP_ACCEPT'], array('*/*' => 1.0));
  386. }
  387. if (isset($type))
  388. {
  389. // Return the quality setting for this type
  390. return isset($accepts[$type]) ? $accepts[$type] : $accepts['*/*'];
  391. }
  392. return $accepts;
  393. }
  394. /**
  395. * Returns the accepted languages. If a specific language is defined,
  396. * the quality of that language will be returned. If the language is not
  397. * accepted, FALSE will be returned.
  398. *
  399. * $langs = Request::accept_lang();
  400. *
  401. * @param string $lang Language code
  402. * @return mixed An array of all types or a specific type as a string
  403. * @uses Request::_parse_accept
  404. */
  405. public static function accept_lang($lang = NULL)
  406. {
  407. static $accepts;
  408. if ($accepts === NULL)
  409. {
  410. // Parse the HTTP_ACCEPT_LANGUAGE header
  411. $accepts = Request::_parse_accept($_SERVER['HTTP_ACCEPT_LANGUAGE']);
  412. }
  413. if (isset($lang))
  414. {
  415. // Return the quality setting for this lang
  416. return isset($accepts[$lang]) ? $accepts[$lang] : FALSE;
  417. }
  418. return $accepts;
  419. }
  420. /**
  421. * Returns the accepted encodings. If a specific encoding is defined,
  422. * the quality of that encoding will be returned. If the encoding is not
  423. * accepted, FALSE will be returned.
  424. *
  425. * $encodings = Request::accept_encoding();
  426. *
  427. * @param string $type Encoding type
  428. * @return mixed An array of all types or a specific type as a string
  429. * @uses Request::_parse_accept
  430. */
  431. public static function accept_encoding($type = NULL)
  432. {
  433. static $accepts;
  434. if ($accepts === NULL)
  435. {
  436. // Parse the HTTP_ACCEPT_LANGUAGE header
  437. $accepts = Request::_parse_accept($_SERVER['HTTP_ACCEPT_ENCODING']);
  438. }
  439. if (isset($type))
  440. {
  441. // Return the quality setting for this type
  442. return isset($accepts[$type]) ? $accepts[$type] : FALSE;
  443. }
  444. return $accepts;
  445. }
  446. /**
  447. * Determines if a file larger than the post_max_size has been uploaded. PHP
  448. * does not handle this situation gracefully on its own, so this method
  449. * helps to solve that problem.
  450. *
  451. * @return boolean
  452. * @uses Num::bytes
  453. * @uses Arr::get
  454. */
  455. public static function post_max_size_exceeded()
  456. {
  457. // Make sure the request method is POST
  458. if (Request::$initial->method() !== HTTP_Request::POST)
  459. return FALSE;
  460. // Get the post_max_size in bytes
  461. $max_bytes = Num::bytes(ini_get('post_max_size'));
  462. // Error occurred if method is POST, and content length is too long
  463. return (Arr::get($_SERVER, 'CONTENT_LENGTH') > $max_bytes);
  464. }
  465. /**
  466. * Process URI
  467. *
  468. * @param string $uri URI
  469. * @param array $routes Route
  470. * @return array
  471. */
  472. public static function process_uri($uri, $routes = NULL)
  473. {
  474. // Load routes
  475. $routes = (empty($routes)) ? Route::all() : $routes;
  476. $params = NULL;
  477. foreach ($routes as $name => $route)
  478. {
  479. // We found something suitable
  480. if ($params = $route->matches($uri))
  481. {
  482. return array(
  483. 'params' => $params,
  484. 'route' => $route,
  485. );
  486. }
  487. }
  488. return NULL;
  489. }
  490. /**
  491. * Parses an accept header and returns an array (type => quality) of the
  492. * accepted types, ordered by quality.
  493. *
  494. * $accept = Request::_parse_accept($header, $defaults);
  495. *
  496. * @param string $header Header to parse
  497. * @param array $accepts Default values
  498. * @return array
  499. */
  500. protected static function _parse_accept( & $header, array $accepts = NULL)
  501. {
  502. if ( ! empty($header))
  503. {
  504. // Get all of the types
  505. $types = explode(',', $header);
  506. foreach ($types as $type)
  507. {
  508. // Split the type into parts
  509. $parts = explode(';', $type);
  510. // Make the type only the MIME
  511. $type = trim(array_shift($parts));
  512. // Default quality is 1.0
  513. $quality = 1.0;
  514. foreach ($parts as $part)
  515. {
  516. // Prevent undefined $value notice below
  517. if (strpos($part, '=') === FALSE)
  518. continue;
  519. // Separate the key and value
  520. list ($key, $value) = explode('=', trim($part));
  521. if ($key === 'q')
  522. {
  523. // There is a quality for this type
  524. $quality = (float) trim($value);
  525. }
  526. }
  527. // Add the accept type and quality
  528. $accepts[$type] = $quality;
  529. }
  530. }
  531. // Make sure that accepts is an array
  532. $accepts = (array) $accepts;
  533. // Order by quality
  534. arsort($accepts);
  535. return $accepts;
  536. }
  537. /**
  538. * @var string the x-requested-with header which most likely
  539. * will be xmlhttprequest
  540. */
  541. protected $_requested_with;
  542. /**
  543. * @var string method: GET, POST, PUT, DELETE, HEAD, etc
  544. */
  545. protected $_method = 'GET';
  546. /**
  547. * @var string protocol: HTTP/1.1, FTP, CLI, etc
  548. */
  549. protected $_protocol;
  550. /**
  551. * @var string referring URL
  552. */
  553. protected $_referrer;
  554. /**
  555. * @var Route route matched for this request
  556. */
  557. protected $_route;
  558. /**
  559. * @var Route array of routes to manually look at instead of the global namespace
  560. */
  561. protected $_routes;
  562. /**
  563. * @var Kohana_Response response
  564. */
  565. protected $_response;
  566. /**
  567. * @var Kohana_HTTP_Header headers to sent as part of the request
  568. */
  569. protected $_header;
  570. /**
  571. * @var string the body
  572. */
  573. protected $_body;
  574. /**
  575. * @var string controller directory
  576. */
  577. protected $_directory = '';
  578. /**
  579. * @var string controller to be executed
  580. */
  581. protected $_controller;
  582. /**
  583. * @var string action to be executed in the controller
  584. */
  585. protected $_action;
  586. /**
  587. * @var string the URI of the request
  588. */
  589. protected $_uri;
  590. /**
  591. * @var boolean external request
  592. */
  593. protected $_external = FALSE;
  594. /**
  595. * @var array parameters from the route
  596. */
  597. protected $_params = array();
  598. /**
  599. * @var array query parameters
  600. */
  601. protected $_get = array();
  602. /**
  603. * @var array post parameters
  604. */
  605. protected $_post = array();
  606. /**
  607. * @var array cookies to send with the request
  608. */
  609. protected $_cookies = array();
  610. /**
  611. * @var Kohana_Request_Client
  612. */
  613. protected $_client;
  614. /**
  615. * Creates a new request object for the given URI. New requests should be
  616. * created using the [Request::instance] or [Request::factory] methods.
  617. *
  618. * $request = new Request($uri);
  619. *
  620. * If $cache parameter is set, the response for the request will attempt to
  621. * be retrieved from the cache.
  622. *
  623. * @param string $uri URI of the request
  624. * @param Cache $cache
  625. * @param array $injected_routes an array of routes to use, for testing
  626. * @return void
  627. * @throws Kohana_Request_Exception
  628. * @uses Route::all
  629. * @uses Route::matches
  630. */
  631. public function __construct($uri, Cache $cache = NULL, $injected_routes = array())
  632. {
  633. // Initialise the header
  634. $this->_header = new HTTP_Header(array());
  635. // Assign injected routes
  636. $this->_injected_routes = $injected_routes;
  637. // Cleanse query parameters from URI (faster that parse_url())
  638. $split_uri = explode('?', $uri);
  639. $uri = array_shift($split_uri);
  640. // Initial request has global $_GET already applied
  641. if (Request::$initial !== NULL)
  642. {
  643. if ($split_uri)
  644. {
  645. parse_str($split_uri[0], $this->_get);
  646. }
  647. }
  648. // Detect protocol (if present)
  649. // Always default to an internal request if we don't have an initial.
  650. // This prevents the default index.php from being able to proxy
  651. // external pages.
  652. if (Request::$initial === NULL OR strpos($uri, '://') === FALSE)
  653. {
  654. // Remove trailing slashes from the URI
  655. $uri = trim($uri, '/');
  656. $processed_uri = Request::process_uri($uri, $this->_injected_routes);
  657. if ($processed_uri === NULL)
  658. {
  659. throw new HTTP_Exception_404('Unable to find a route to match the URI: :uri', array(
  660. ':uri' => $uri,
  661. ));
  662. }
  663. // Store the URI
  664. $this->_uri = $uri;
  665. // Store the matching route
  666. $this->_route = $processed_uri['route'];
  667. $params = $processed_uri['params'];
  668. // Is this route external?
  669. $this->_external = $this->_route->is_external();
  670. if (isset($params['directory']))
  671. {
  672. // Controllers are in a sub-directory
  673. $this->_directory = $params['directory'];
  674. }
  675. // Store the controller
  676. $this->_controller = $params['controller'];
  677. if (isset($params['action']))
  678. {
  679. // Store the action
  680. $this->_action = $params['action'];
  681. }
  682. else
  683. {
  684. // Use the default action
  685. $this->_action = Route::$default_action;
  686. }
  687. // These are accessible as public vars and can be overloaded
  688. unset($params['controller'], $params['action'], $params['directory']);
  689. // Params cannot be changed once matched
  690. $this->_params = $params;
  691. // Apply the client
  692. $this->_client = new Request_Client_Internal(array('cache' => $cache));
  693. }
  694. else
  695. {
  696. // Create a route
  697. $this->_route = new Route($uri);
  698. // Store the URI
  699. $this->_uri = $uri;
  700. // Set external state
  701. $this->_external = TRUE;
  702. // Setup the client
  703. $this->_client = new Request_Client_External(array('cache' => $cache));
  704. }
  705. }
  706. /**
  707. * Returns the response as the string representation of a request.
  708. *
  709. * echo $request;
  710. *
  711. * @return string
  712. */
  713. public function __toString()
  714. {
  715. return $this->render();
  716. }
  717. /**
  718. * Generates a relative URI for the current route.
  719. *
  720. * $request->uri($params);
  721. *
  722. * @param array $params Additional route parameters
  723. * @return string
  724. * @uses Route::uri
  725. */
  726. public function uri(array $params = NULL)
  727. {
  728. if ( ! isset($params['directory']))
  729. {
  730. // Add the current directory
  731. $params['directory'] = $this->directory();
  732. }
  733. if ( ! isset($params['controller']))
  734. {
  735. // Add the current controller
  736. $params['controller'] = $this->controller();
  737. }
  738. if ( ! isset($params['action']))
  739. {
  740. // Add the current action
  741. $params['action'] = $this->action();
  742. }
  743. // Add the current parameters
  744. $params += $this->_params;
  745. $uri = $this->_route->uri($params);
  746. return $uri;
  747. }
  748. /**
  749. * Create a URL from the current request. This is a shortcut for:
  750. *
  751. * echo URL::site($this->request->uri($params), $protocol);
  752. *
  753. * @param array $params URI parameters
  754. * @param mixed $protocol protocol string or Request object
  755. * @return string
  756. * @since 3.0.7
  757. * @uses URL::site
  758. */
  759. public function url(array $params = NULL, $protocol = NULL)
  760. {
  761. // Create a URI with the current route and convert it to a URL
  762. $url = URL::site($this->uri($params), $protocol);
  763. return $url;
  764. }
  765. /**
  766. * Retrieves a value from the route parameters.
  767. *
  768. * $id = $request->param('id');
  769. *
  770. * @param string $key Key of the value
  771. * @param mixed $default Default value if the key is not set
  772. * @return mixed
  773. */
  774. public function param($key = NULL, $default = NULL)
  775. {
  776. if ($key === NULL)
  777. {
  778. // Return the full array
  779. return $this->_params;
  780. }
  781. return isset($this->_params[$key]) ? $this->_params[$key] : $default;
  782. }
  783. /**
  784. * Sends the response status and all set headers. The current server
  785. * protocol (HTTP/1.0 or HTTP/1.1) will be used when available. If not
  786. * available, HTTP/1.1 will be used.
  787. *
  788. * $request->send_headers();
  789. *
  790. * @return $this
  791. * @uses Request::$messages
  792. * @deprecated This should not be here, it belongs in\n
  793. * Response::send_headers() where it is implemented correctly.
  794. */
  795. public function send_headers()
  796. {
  797. if ( ! ($response = $this->response()) instanceof Response)
  798. return $this;
  799. $response->send_headers();
  800. return $this;
  801. }
  802. /**
  803. * Redirects as the request response. If the URL does not include a
  804. * protocol, it will be converted into a complete URL.
  805. *
  806. * $request->redirect($url);
  807. *
  808. * [!!] No further processing can be done after this method is called!
  809. *
  810. * @param string $url Redirect location
  811. * @param integer $code Status code: 301, 302, etc
  812. * @return void
  813. * @uses URL::site
  814. * @uses Request::send_headers
  815. */
  816. public function redirect($url = '', $code = 302)
  817. {
  818. if (strpos($url, '://') === FALSE)
  819. {
  820. // Make the URI into a URL
  821. $url = URL::site($url, TRUE);
  822. }
  823. // Redirect
  824. $response = $this->create_response();
  825. // Set the response status
  826. $response->status($code);
  827. // Set the location header
  828. $response->headers('Location', $url);
  829. // Send headers
  830. $response->send_headers();
  831. // Stop execution
  832. exit;
  833. }
  834. /**
  835. * Sets and gets the referrer from the request.
  836. *
  837. * @param string $referrer
  838. * @return mixed
  839. */
  840. public function referrer($referrer = NULL)
  841. {
  842. if ($referrer === NULL)
  843. {
  844. // Act as a getter
  845. return $this->_referrer;
  846. }
  847. // Act as a setter
  848. $this->_referrer = (string) $referrer;
  849. return $this;
  850. }
  851. /**
  852. * Sets and gets the route from the request.
  853. *
  854. * @param string $route
  855. * @return mixed
  856. */
  857. public function route(Route $route = NULL)
  858. {
  859. if ($route === NULL)
  860. {
  861. // Act as a getter
  862. return $this->_route;
  863. }
  864. // Act as a setter
  865. $this->_route = $route;
  866. return $this;
  867. }
  868. /**
  869. * Sets and gets the directory for the controller.
  870. *
  871. * @param string $directory Directory to execute the controller from
  872. * @return mixed
  873. */
  874. public function directory($directory = NULL)
  875. {
  876. if ($directory === NULL)
  877. {
  878. // Act as a getter
  879. return $this->_directory;
  880. }
  881. // Act as a setter
  882. $this->_directory = (string) $directory;
  883. return $this;
  884. }
  885. /**
  886. * Sets and gets the controller for the matched route.
  887. *
  888. * @param string $controller Controller to execute the action
  889. * @return mixed
  890. */
  891. public function controller($controller = NULL)
  892. {
  893. if ($controller === NULL)
  894. {
  895. // Act as a getter
  896. return $this->_controller;
  897. }
  898. // Act as a setter
  899. $this->_controller = (string) $controller;
  900. return $this;
  901. }
  902. /**
  903. * Sets and gets the action for the controller.
  904. *
  905. * @param string $action Action to execute the controller from
  906. * @return mixed
  907. */
  908. public function action($action = NULL)
  909. {
  910. if ($action === NULL)
  911. {
  912. // Act as a getter
  913. return $this->_action;
  914. }
  915. // Act as a setter
  916. $this->_action = (string) $action;
  917. return $this;
  918. }
  919. /**
  920. * Provides readonly access to the [Request_Client],
  921. * useful for accessing the caching methods within the
  922. * request client.
  923. *
  924. * @return Request_Client
  925. */
  926. public function get_client()
  927. {
  928. return $this->_client;
  929. }
  930. /**
  931. * Gets and sets the requested with property, which should
  932. * be relative to the x-requested-with pseudo header.
  933. *
  934. * @param string $requested_with Requested with value
  935. * @return mixed
  936. */
  937. public function requested_with($requested_with = NULL)
  938. {
  939. if ($requested_with === NULL)
  940. {
  941. // Act as a getter
  942. return $this->_requested_with;
  943. }
  944. // Act as a setter
  945. $this->_requested_with = strtolower($requested_with);
  946. return $this;
  947. }
  948. /**
  949. * Processes the request, executing the controller action that handles this
  950. * request, determined by the [Route].
  951. *
  952. * 1. Before the controller action is called, the [Controller::before] method
  953. * will be called.
  954. * 2. Next the controller action will be called.
  955. * 3. After the controller action is called, the [Controller::after] method
  956. * will be called.
  957. *
  958. * By default, the output from the controller is captured and returned, and
  959. * no headers are sent.
  960. *
  961. * $request->execute();
  962. *
  963. * @return Response
  964. * @throws Kohana_Exception
  965. * @uses [Kohana::$profiling]
  966. * @uses [Profiler]
  967. */
  968. public function execute()
  969. {
  970. if ( ! $this->_client instanceof Kohana_Request_Client)
  971. {
  972. throw new Kohana_Request_Exception('Unable to execute :uri without a Kohana_Request_Client', array(
  973. ':uri' => $this->_uri,
  974. ));
  975. }
  976. return $this->_client->execute($this);
  977. }
  978. /**
  979. * Returns whether this request is the initial request Kohana received.
  980. * Can be used to test for sub requests.
  981. *
  982. * if ( ! $request->is_initial())
  983. * // This is a sub request
  984. *
  985. * @return boolean
  986. */
  987. public function is_initial()
  988. {
  989. return ($this === Request::$initial);
  990. }
  991. /**
  992. * Returns whether this is an ajax request (as used by JS frameworks)
  993. *
  994. * @return boolean
  995. */
  996. public function is_ajax()
  997. {
  998. return ($this->requested_with() === 'xmlhttprequest');
  999. }
  1000. /**
  1001. * Generates an [ETag](http://en.wikipedia.org/wiki/HTTP_ETag) from the
  1002. * request response.
  1003. *
  1004. * $etag = $request->generate_etag();
  1005. *
  1006. * [!!] If the request response is empty when this method is called, an
  1007. * exception will be thrown!
  1008. *
  1009. * @return string
  1010. * @throws Kohana_Request_Exception
  1011. */
  1012. public function generate_etag()
  1013. {
  1014. if ($this->_response === NULL)
  1015. {
  1016. throw new Kohana_Request_Exception('No response yet associated with request - cannot auto generate resource ETag');
  1017. }
  1018. // Generate a unique hash for the response
  1019. return '"'.sha1($this->_response).'"';
  1020. }
  1021. /**
  1022. * Set or get the response for this request
  1023. *
  1024. * @param Response $response Response to apply to this request
  1025. * @return Response
  1026. * @return void
  1027. */
  1028. public function response(Response $response = NULL)
  1029. {
  1030. if ($response === NULL)
  1031. {
  1032. // Act as a getter
  1033. return $this->_response;
  1034. }
  1035. // Act as a setter
  1036. $this->_response = $response;
  1037. return $this;
  1038. }
  1039. /**
  1040. * Creates a response based on the type of request, i.e. an
  1041. * Request_HTTP will produce a Response_HTTP, and the same applies
  1042. * to CLI.
  1043. *
  1044. * // Create a response to the request
  1045. * $response = $request->create_response();
  1046. *
  1047. * @param boolean $bind Bind to this request
  1048. * @return Response
  1049. * @since 3.1.0
  1050. */
  1051. public function create_response($bind = TRUE)
  1052. {
  1053. $response = new Response(array('_protocol' => $this->protocol()));
  1054. if ($bind)
  1055. {
  1056. // Bind a new response to the request
  1057. $this->_response = $response;
  1058. }
  1059. return $response;
  1060. }
  1061. /**
  1062. * Gets or sets the HTTP method. Usually GET, POST, PUT or DELETE in
  1063. * traditional CRUD applications.
  1064. *
  1065. * @param string $method Method to use for this request
  1066. * @return mixed
  1067. */
  1068. public function method($method = NULL)
  1069. {
  1070. if ($method === NULL)
  1071. {
  1072. // Act as a getter
  1073. return $this->_method;
  1074. }
  1075. // Act as a setter
  1076. $this->_method = strtoupper($method);
  1077. return $this;
  1078. }
  1079. /**
  1080. * Gets or sets the HTTP protocol. The standard protocol to use
  1081. * is `http`.
  1082. *
  1083. * @param string $protocol Protocol to set to the request/response
  1084. * @return mixed
  1085. */
  1086. public function protocol($protocol = NULL)
  1087. {
  1088. if ($protocol === NULL)
  1089. {
  1090. if ($this->_protocol)
  1091. {
  1092. // Act as a getter
  1093. return $this->_protocol;
  1094. }
  1095. else
  1096. {
  1097. // Get the default protocol
  1098. return HTTP::$protocol;
  1099. }
  1100. }
  1101. // Act as a setter
  1102. $this->_protocol = strtolower($protocol);
  1103. return $this;
  1104. }
  1105. /**
  1106. * Gets or sets HTTP headers to the request or response. All headers
  1107. * are included immediately after the HTTP protocol definition during
  1108. * transmission. This method provides a simple array or key/value
  1109. * interface to the headers.
  1110. *
  1111. * @param mixed $key Key or array of key/value pairs to set
  1112. * @param string $value Value to set to the supplied key
  1113. * @return mixed
  1114. */
  1115. public function headers($key = NULL, $value = NULL)
  1116. {
  1117. if ($key instanceof HTTP_Header)
  1118. {
  1119. // Act a setter, replace all headers
  1120. $this->_header = $key;
  1121. return $this;
  1122. }
  1123. if (is_array($key))
  1124. {
  1125. // Act as a setter, replace all headers
  1126. $this->_header->exchangeArray($key);
  1127. return $this;
  1128. }
  1129. if ($this->_header->count() === 0 AND $this->is_initial())
  1130. {
  1131. // Lazy load the request headers
  1132. $this->_header = HTTP::request_headers();
  1133. }
  1134. if ($key === NULL)
  1135. {
  1136. // Act as a getter, return all headers
  1137. return $this->_header;
  1138. }
  1139. elseif ($value === NULL)
  1140. {
  1141. // Act as a getter, single header
  1142. return ($this->_header->offsetExists($key)) ? $this->_header->offsetGet($key) : NULL;
  1143. }
  1144. // Act as a setter for a single header
  1145. $this->_header[$key] = $value;
  1146. return $this;
  1147. }
  1148. /**
  1149. * Set and get cookies values for this request.
  1150. *
  1151. * @param mixed $key Cookie name, or array of cookie values
  1152. * @param string $value Value to set to cookie
  1153. * @return string
  1154. * @return mixed
  1155. */
  1156. public function cookie($key = NULL, $value = NULL)
  1157. {
  1158. if (is_array($key))
  1159. {
  1160. // Act as a setter, replace all cookies
  1161. $this->_cookies = $key;
  1162. }
  1163. if ($key === NULL)
  1164. {
  1165. // Act as a getter, all cookies
  1166. return $this->_cookies;
  1167. }
  1168. elseif ($value === NULL)
  1169. {
  1170. // Act as a getting, single cookie
  1171. return isset($this->_cookies[$key]) ? $this->_cookies[$key] : NULL;
  1172. }
  1173. // Act as a setter for a single cookie
  1174. $this->_cookies[$key] = (string) $value;
  1175. return $this;
  1176. }
  1177. /**
  1178. * Gets or sets the HTTP body to the request or response. The body is
  1179. * included after the header, separated by a single empty new line.
  1180. *
  1181. * @param string $content Content to set to the object
  1182. * @return mixed
  1183. */
  1184. public function body($content = NULL)
  1185. {
  1186. if ($content === NULL)
  1187. {
  1188. // Act as a getter
  1189. return $this->_body;
  1190. }
  1191. // Act as a setter
  1192. $this->_body = $content;
  1193. return $this;
  1194. }
  1195. /**
  1196. * Renders the HTTP_Interaction to a string, producing
  1197. *
  1198. * - Protocol
  1199. * - Headers
  1200. * - Body
  1201. *
  1202. * If there are variables set to the `Kohana_Request::$_post`
  1203. * they will override any values set to body.
  1204. *
  1205. * @param boolean $response Return the rendered response, else returns the rendered request
  1206. * @return string
  1207. */
  1208. public function render($response = TRUE)
  1209. {
  1210. if ($response)
  1211. {
  1212. // Act as a getter
  1213. return (string) $this->_response;
  1214. }
  1215. if ( ! $post = $this->post())
  1216. {
  1217. $body = $this->body();
  1218. }
  1219. else
  1220. {
  1221. $this->headers('content-type', 'application/x-www-form-urlencoded');
  1222. $body = http_build_query($post, NULL, '&');
  1223. }
  1224. // Prepare cookies
  1225. if ($this->_cookies)
  1226. {
  1227. $cookie_string = array();
  1228. // Parse each
  1229. foreach ($this->_cookies as $key => $value)
  1230. {
  1231. $cookie_string[] = $key.'='.$value;
  1232. }
  1233. // Create the cookie string
  1234. $this->_header['cookie'] = implode('; ', $cookie_string);
  1235. }
  1236. $output = $this->method().' '.$this->uri($this->param()).' '.strtoupper($this->protocol()).'/'.HTTP::$version."\n";
  1237. $output .= (string) $this->_header;
  1238. $output .= $body;
  1239. return $output;
  1240. }
  1241. /**
  1242. * Gets or sets HTTP query string.
  1243. *
  1244. * @param mixed $key Key or key value pairs to set
  1245. * @param string $value Value to set to a key
  1246. * @return mixed
  1247. */
  1248. public function query($key = NULL, $value = NULL)
  1249. {
  1250. if (is_array($key))
  1251. {
  1252. // Act as a setter, replace all query strings
  1253. $this->_get = $key;
  1254. return $this;
  1255. }
  1256. if ($key === NULL)
  1257. {
  1258. // Act as a getter, all query strings
  1259. return $this->_get;
  1260. }
  1261. elseif ($value === NULL)
  1262. {
  1263. // Act as a getter, single query string
  1264. return Arr::get($this->_get, $key);
  1265. }
  1266. // Act as a setter, single query string
  1267. $this->_get[$key] = $value;
  1268. return $this;
  1269. }
  1270. /**
  1271. * Gets or sets HTTP POST parameters to the request.
  1272. *
  1273. * @param mixed $key Key or key value pairs to set
  1274. * @param string $value Value to set to a key
  1275. * @return mixed
  1276. */
  1277. public function post($key = NULL, $value = NULL)
  1278. {
  1279. if (is_array($key))
  1280. {
  1281. // Act as a setter, replace all fields
  1282. $this->_post = $key;
  1283. return $this;
  1284. }
  1285. if ($key === NULL)
  1286. {
  1287. // Act as a getter, all fields
  1288. return $this->_post;
  1289. }
  1290. elseif ($value === NULL)
  1291. {
  1292. // Act as a getter, single field
  1293. return Arr::get($this->_post, $key);
  1294. }
  1295. // Act as a setter, single field
  1296. $this->_post[$key] = $value;
  1297. return $this;
  1298. }
  1299. } // End Request