Request.php 30 KB

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