PageRenderTime 58ms CodeModel.GetById 19ms RepoModel.GetById 1ms app.codeStats 0ms

/rxwandc/system/classes/kohana/request.php

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