PageRenderTime 51ms CodeModel.GetById 18ms RepoModel.GetById 0ms app.codeStats 0ms

/system/classes/kohana/request.php

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