PageRenderTime 65ms CodeModel.GetById 27ms RepoModel.GetById 1ms app.codeStats 0ms

/xandra.org/www/system/classes/Kohana/Request.php

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