protocol($protocol) ->query($_GET) ->post($_POST); if (isset($secure)) { // Set the request security $request->secure($secure); } if (isset($method)) { // Set the request method $request->method($method); } if (isset($referrer)) { // Set the referrer $request->referrer($referrer); } if (isset($requested_with)) { // Apply the requested with variable $request->requested_with($requested_with); } if (isset($body)) { // Set the request body (probably a PUT type) $request->body($body); } if (isset($cookies)) { $request->cookie($cookies); } } else { $request = new Request($uri, $client_params, $allow_external, $injected_routes); } return $request; } /** * Automatically detects the URI of the main request using PATH_INFO, * REQUEST_URI, PHP_SELF or REDIRECT_URL. * * $uri = Request::detect_uri(); * * @return string URI of the main request * @throws Kohana_Exception * @since 3.0.8 */ public static function detect_uri() { if ( ! empty($_SERVER['PATH_INFO'])) { // PATH_INFO does not contain the docroot or index $uri = $_SERVER['PATH_INFO']; } else { // REQUEST_URI and PHP_SELF include the docroot and index if (isset($_SERVER['REQUEST_URI'])) { /** * We use REQUEST_URI as the fallback value. The reason * for this is we might have a malformed URL such as: * * http://localhost/http://example.com/judge.php * * which parse_url can't handle. So rather than leave empty * handed, we'll use this. */ $uri = $_SERVER['REQUEST_URI']; if ($request_uri = parse_url($_SERVER['REQUEST_URI'], PHP_URL_PATH)) { // Valid URL path found, set it. $uri = $request_uri; } // Decode the request URI $uri = rawurldecode($uri); } elseif (isset($_SERVER['PHP_SELF'])) { $uri = $_SERVER['PHP_SELF']; } elseif (isset($_SERVER['REDIRECT_URL'])) { $uri = $_SERVER['REDIRECT_URL']; } else { // If you ever see this error, please report an issue at http://dev.kohanaphp.com/projects/kohana3/issues // along with any relevant information about your web server setup. Thanks! throw new Kohana_Exception('Unable to detect the URI using PATH_INFO, REQUEST_URI, PHP_SELF or REDIRECT_URL'); } // Get the path from the base URL, including the index file $base_url = parse_url(Kohana::$base_url, PHP_URL_PATH); if (strpos($uri, $base_url) === 0) { // Remove the base URL from the URI $uri = (string) substr($uri, strlen($base_url)); } if (Kohana::$index_file AND strpos($uri, Kohana::$index_file) === 0) { // Remove the index file from the URI $uri = (string) substr($uri, strlen(Kohana::$index_file)); } } return $uri; } /** * Return the currently executing request. This is changed to the current * request when [Request::execute] is called and restored when the request * is completed. * * $request = Request::current(); * * @return Request * @since 3.0.5 */ public static function current() { return Request::$current; } /** * Returns the first request encountered by this framework. This will should * only be set once during the first [Request::factory] invocation. * * // Get the first request * $request = Request::initial(); * * // Test whether the current request is the first request * if (Request::initial() === Request::current()) * // Do something useful * * @return Request * @since 3.1.0 */ public static function initial() { return Request::$initial; } /** * Returns information about the initial user agent. * * @param mixed $value array or string to return: browser, version, robot, mobile, platform * @return mixed requested information, FALSE if nothing is found * @uses Request::$user_agent * @uses Text::user_agent */ public static function user_agent($value) { return Text::user_agent(Request::$user_agent, $value); } /** * Returns the accepted content types. If a specific type is defined, * the quality of that type will be returned. * * $types = Request::accept_type(); * * [!!] Deprecated in favor of using [HTTP_Header::accepts_at_quality]. * * @deprecated since version 3.3.0 * @param string $type Content MIME type * @return mixed An array of all types or a specific type as a string * @uses Request::_parse_accept */ public static function accept_type($type = NULL) { static $accepts; if ($accepts === NULL) { // Parse the HTTP_ACCEPT header $accepts = Request::_parse_accept($_SERVER['HTTP_ACCEPT'], array('*/*' => 1.0)); } if (isset($type)) { // Return the quality setting for this type return isset($accepts[$type]) ? $accepts[$type] : $accepts['*/*']; } return $accepts; } /** * Returns the accepted languages. If a specific language is defined, * the quality of that language will be returned. If the language is not * accepted, FALSE will be returned. * * $langs = Request::accept_lang(); * * [!!] Deprecated in favor of using [HTTP_Header::accepts_language_at_quality]. * * @deprecated since version 3.3.0 * @param string $lang Language code * @return mixed An array of all types or a specific type as a string * @uses Request::_parse_accept */ public static function accept_lang($lang = NULL) { static $accepts; if ($accepts === NULL) { // Parse the HTTP_ACCEPT_LANGUAGE header $accepts = Request::_parse_accept($_SERVER['HTTP_ACCEPT_LANGUAGE']); } if (isset($lang)) { // Return the quality setting for this lang return isset($accepts[$lang]) ? $accepts[$lang] : FALSE; } return $accepts; } /** * Returns the accepted encodings. If a specific encoding is defined, * the quality of that encoding will be returned. If the encoding is not * accepted, FALSE will be returned. * * $encodings = Request::accept_encoding(); * * [!!] Deprecated in favor of using [HTTP_Header::accepts_encoding_at_quality]. * * @deprecated since version 3.3.0 * @param string $type Encoding type * @return mixed An array of all types or a specific type as a string * @uses Request::_parse_accept */ public static function accept_encoding($type = NULL) { static $accepts; if ($accepts === NULL) { // Parse the HTTP_ACCEPT_LANGUAGE header $accepts = Request::_parse_accept($_SERVER['HTTP_ACCEPT_ENCODING']); } if (isset($type)) { // Return the quality setting for this type return isset($accepts[$type]) ? $accepts[$type] : FALSE; } return $accepts; } /** * Determines if a file larger than the post_max_size has been uploaded. PHP * does not handle this situation gracefully on its own, so this method * helps to solve that problem. * * @return boolean * @uses Num::bytes * @uses Arr::get */ public static function post_max_size_exceeded() { // Make sure the request method is POST if (Request::$initial->method() !== HTTP_Request::POST) return FALSE; // Get the post_max_size in bytes $max_bytes = Num::bytes(ini_get('post_max_size')); // Error occurred if method is POST, and content length is too long return (Arr::get($_SERVER, 'CONTENT_LENGTH') > $max_bytes); } /** * Process a request to find a matching route * * @param object $request Request * @param array $routes Route * @return array */ public static function process(Request $request, $routes = NULL) { // Load routes $routes = (empty($routes)) ? Route::all() : $routes; $params = NULL; foreach ($routes as $name => $route) { // Use external routes for reverse routing only if ($route->is_external()) { continue; } // We found something suitable if ($params = $route->matches($request)) { return array( 'params' => $params, 'route' => $route, ); } } return NULL; } /** * Parses an accept header and returns an array (type => quality) of the * accepted types, ordered by quality. * * $accept = Request::_parse_accept($header, $defaults); * * @param string $header Header to parse * @param array $accepts Default values * @return array */ protected static function _parse_accept( & $header, array $accepts = NULL) { if ( ! empty($header)) { // Get all of the types $types = explode(',', $header); foreach ($types as $type) { // Split the type into parts $parts = explode(';', $type); // Make the type only the MIME $type = trim(array_shift($parts)); // Default quality is 1.0 $quality = 1.0; foreach ($parts as $part) { // Prevent undefined $value notice below if (strpos($part, '=') === FALSE) continue; // Separate the key and value list ($key, $value) = explode('=', trim($part)); if ($key === 'q') { // There is a quality for this type $quality = (float) trim($value); } } // Add the accept type and quality $accepts[$type] = $quality; } } // Make sure that accepts is an array $accepts = (array) $accepts; // Order by quality arsort($accepts); return $accepts; } /** * @var string the x-requested-with header which most likely * will be xmlhttprequest */ protected $_requested_with; /** * @var string method: GET, POST, PUT, DELETE, HEAD, etc */ protected $_method = 'GET'; /** * @var string protocol: HTTP/1.1, FTP, CLI, etc */ protected $_protocol; /** * @var boolean */ protected $_secure = FALSE; /** * @var string referring URL */ protected $_referrer; /** * @var Route route matched for this request */ protected $_route; /** * @var Route array of routes to manually look at instead of the global namespace */ protected $_routes; /** * @var Kohana_HTTP_Header headers to sent as part of the request */ protected $_header; /** * @var string the body */ protected $_body; /** * @var string controller directory */ protected $_directory = ''; /** * @var string controller to be executed */ protected $_controller; /** * @var string action to be executed in the controller */ protected $_action; /** * @var string the URI of the request */ protected $_uri; /** * @var boolean external request */ protected $_external = FALSE; /** * @var array parameters from the route */ protected $_params = array(); /** * @var array query parameters */ protected $_get = array(); /** * @var array post parameters */ protected $_post = array(); /** * @var array cookies to send with the request */ protected $_cookies = array(); /** * @var Kohana_Request_Client */ protected $_client; /** * Creates a new request object for the given URI. New requests should be * Created using the [Request::factory] method. * * $request = new Request($uri); * * If $cache parameter is set, the response for the request will attempt to * be retrieved from the cache. * * @param string $uri URI of the request * @param array $client_params Array of params to pass to the request client * @param bool $allow_external Allow external requests? (deprecated in 3.3) * @param array $injected_routes An array of routes to use, for testing * @return void * @throws Request_Exception * @uses Route::all * @uses Route::matches */ public function __construct($uri, $client_params = array(), $allow_external = TRUE, $injected_routes = array()) { $client_params = is_array($client_params) ? $client_params : array(); // Initialise the header $this->_header = new HTTP_Header(array()); // Assign injected routes $this->_routes = $injected_routes; // Cleanse query parameters from URI (faster that parse_url()) $split_uri = explode('?', $uri); $uri = array_shift($split_uri); // Initial request has global $_GET already applied if (Request::$initial === NULL) { if ($split_uri) { parse_str($split_uri[0], $this->_get); } } // Detect protocol (if present) // $allow_external = FALSE prevents the default index.php from // being able to proxy external pages. if ( ! $allow_external OR strpos($uri, '://') === FALSE) { // Remove leading and trailing slashes from the URI $this->_uri = trim($uri, '/'); // Apply the client $this->_client = new Request_Client_Internal($client_params); } else { // Create a route $this->_route = new Route($uri); // Store the URI $this->_uri = $uri; // Set the security setting if required if (strpos($uri, 'https://') === 0) { $this->secure(TRUE); } // Set external state $this->_external = TRUE; // Setup the client $this->_client = Request_Client_External::factory($client_params); } } /** * Returns the response as the string representation of a request. * * echo $request; * * @return string */ public function __toString() { return $this->render(); } /** * Sets and gets the uri from the request. * * @param string $uri * @return mixed */ public function uri($uri = NULL) { if ($uri === NULL) { // Act as a getter return ($this->_uri === '') ? '/' : $this->_uri; } // Act as a setter $this->_uri = $uri; return $this; } /** * Create a URL string from the current request. This is a shortcut for: * * echo URL::site($this->request->uri(), $protocol); * * @param mixed $protocol protocol string or Request object * @return string * @since 3.0.7 * @uses URL::site */ public function url($protocol = NULL) { if ($this->is_external()) { // If it's an external request return the URI return $this->uri(); } // Create a URI with the current route, convert to a URL and returns return URL::site($this->uri(), $protocol); } /** * Retrieves a value from the route parameters. * * $id = $request->param('id'); * * @param string $key Key of the value * @param mixed $default Default value if the key is not set * @return mixed */ public function param($key = NULL, $default = NULL) { if ($key === NULL) { // Return the full array return $this->_params; } return isset($this->_params[$key]) ? $this->_params[$key] : $default; } /** * Sets and gets the referrer from the request. * * @param string $referrer * @return mixed */ public function referrer($referrer = NULL) { if ($referrer === NULL) { // Act as a getter return $this->_referrer; } // Act as a setter $this->_referrer = (string) $referrer; return $this; } /** * Sets and gets the route from the request. * * @param string $route * @return mixed */ public function route(Route $route = NULL) { if ($route === NULL) { // Act as a getter return $this->_route; } // Act as a setter $this->_route = $route; return $this; } /** * Sets and gets the directory for the controller. * * @param string $directory Directory to execute the controller from * @return mixed */ public function directory($directory = NULL) { if ($directory === NULL) { // Act as a getter return $this->_directory; } // Act as a setter $this->_directory = (string) $directory; return $this; } /** * Sets and gets the controller for the matched route. * * @param string $controller Controller to execute the action * @return mixed */ public function controller($controller = NULL) { if ($controller === NULL) { // Act as a getter return $this->_controller; } // Act as a setter $this->_controller = (string) $controller; return $this; } /** * Sets and gets the action for the controller. * * @param string $action Action to execute the controller from * @return mixed */ public function action($action = NULL) { if ($action === NULL) { // Act as a getter return $this->_action; } // Act as a setter $this->_action = (string) $action; return $this; } /** * Provides access to the [Request_Client]. * * @return Request_Client * @return self */ public function client(Request_Client $client = NULL) { if ($client === NULL) return $this->_client; else { $this->_client = $client; return $this; } } /** * Gets and sets the requested with property, which should * be relative to the x-requested-with pseudo header. * * @param string $requested_with Requested with value * @return mixed */ public function requested_with($requested_with = NULL) { if ($requested_with === NULL) { // Act as a getter return $this->_requested_with; } // Act as a setter $this->_requested_with = strtolower($requested_with); return $this; } /** * Processes the request, executing the controller action that handles this * request, determined by the [Route]. * * 1. Before the controller action is called, the [Controller::before] method * will be called. * 2. Next the controller action will be called. * 3. After the controller action is called, the [Controller::after] method * will be called. * * By default, the output from the controller is captured and returned, and * no headers are sent. * * $request->execute(); * * @return Response * @throws Request_Exception * @throws HTTP_Exception_404 * @uses [Kohana::$profiling] * @uses [Profiler] */ public function execute() { if ( ! $this->_external) { $processed = Request::process($this, $this->_routes); if ($processed) { // Store the matching route $this->_route = $processed['route']; $params = $processed['params']; // Is this route external? $this->_external = $this->_route->is_external(); if (isset($params['directory'])) { // Controllers are in a sub-directory $this->_directory = $params['directory']; } // Store the controller $this->_controller = $params['controller']; // Store the action $this->_action = (isset($params['action'])) ? $params['action'] : Route::$default_action; // These are accessible as public vars and can be overloaded unset($params['controller'], $params['action'], $params['directory']); // Params cannot be changed once matched $this->_params = $params; } } if ( ! $this->_route instanceof Route) { return HTTP_Exception::factory(404, 'Unable to find a route to match the URI: :uri', array( ':uri' => $this->_uri, ))->request($this) ->get_response(); } if ( ! $this->_client instanceof Request_Client) { throw new Request_Exception('Unable to execute :uri without a Kohana_Request_Client', array( ':uri' => $this->_uri, )); } return $this->_client->execute($this); } /** * Returns whether this request is the initial request Kohana received. * Can be used to test for sub requests. * * if ( ! $request->is_initial()) * // This is a sub request * * @return boolean */ public function is_initial() { return ($this === Request::$initial); } /** * Readonly access to the [Request::$_external] property. * * if ( ! $request->is_external()) * // This is an internal request * * @return boolean */ public function is_external() { return $this->_external; } /** * Returns whether this is an ajax request (as used by JS frameworks) * * @return boolean */ public function is_ajax() { return ($this->requested_with() === 'xmlhttprequest'); } /** * Gets or sets the HTTP method. Usually GET, POST, PUT or DELETE in * traditional CRUD applications. * * @param string $method Method to use for this request * @return mixed */ public function method($method = NULL) { if ($method === NULL) { // Act as a getter return $this->_method; } // Act as a setter $this->_method = strtoupper($method); return $this; } /** * Gets or sets the HTTP protocol. If there is no current protocol set, * it will use the default set in HTTP::$protocol * * @param string $protocol Protocol to set to the request * @return mixed */ public function protocol($protocol = NULL) { if ($protocol === NULL) { if ($this->_protocol) return $this->_protocol; else return $this->_protocol = HTTP::$protocol; } // Act as a setter $this->_protocol = strtoupper($protocol); return $this; } /** * Getter/Setter to the security settings for this request. This * method should be treated as immutable. * * @param boolean $secure is this request secure? * @return mixed */ public function secure($secure = NULL) { if ($secure === NULL) return $this->_secure; // Act as a setter $this->_secure = (bool) $secure; return $this; } /** * Gets or sets HTTP headers oo the request. All headers * are included immediately after the HTTP protocol definition during * transmission. This method provides a simple array or key/value * interface to the headers. * * @param mixed $key Key or array of key/value pairs to set * @param string $value Value to set to the supplied key * @return mixed */ public function headers($key = NULL, $value = NULL) { if ($key instanceof HTTP_Header) { // Act a setter, replace all headers $this->_header = $key; return $this; } if (is_array($key)) { // Act as a setter, replace all headers $this->_header->exchangeArray($key); return $this; } if ($this->_header->count() === 0 AND $this->is_initial()) { // Lazy load the request headers $this->_header = HTTP::request_headers(); } if ($key === NULL) { // Act as a getter, return all headers return $this->_header; } elseif ($value === NULL) { // Act as a getter, single header return ($this->_header->offsetExists($key)) ? $this->_header->offsetGet($key) : NULL; } // Act as a setter for a single header $this->_header[$key] = $value; return $this; } /** * Set and get cookies values for this request. * * @param mixed $key Cookie name, or array of cookie values * @param string $value Value to set to cookie * @return string * @return mixed */ public function cookie($key = NULL, $value = NULL) { if (is_array($key)) { // Act as a setter, replace all cookies $this->_cookies = $key; return $this; } elseif ($key === NULL) { // Act as a getter, all cookies return $this->_cookies; } elseif ($value === NULL) { // Act as a getting, single cookie return isset($this->_cookies[$key]) ? $this->_cookies[$key] : NULL; } // Act as a setter for a single cookie $this->_cookies[$key] = (string) $value; return $this; } /** * Gets or sets the HTTP body of the request. The body is * included after the header, separated by a single empty new line. * * @param string $content Content to set to the object * @return mixed */ public function body($content = NULL) { if ($content === NULL) { // Act as a getter return $this->_body; } // Act as a setter $this->_body = $content; return $this; } /** * Returns the length of the body for use with * content header * * @return integer */ public function content_length() { return strlen($this->body()); } /** * Renders the HTTP_Interaction to a string, producing * * - Protocol * - Headers * - Body * * If there are variables set to the `Kohana_Request::$_post` * they will override any values set to body. * * @return string */ public function render() { if ( ! $post = $this->post()) { $body = $this->body(); } else { $body = http_build_query($post, NULL, '&'); $this->body($body) ->headers('content-type', 'application/x-www-form-urlencoded; charset='.Kohana::$charset); } // Set the content length $this->headers('content-length', (string) $this->content_length()); // If Kohana expose, set the user-agent if (Kohana::$expose) { $this->headers('user-agent', Kohana::version()); } // Prepare cookies if ($this->_cookies) { $cookie_string = array(); // Parse each foreach ($this->_cookies as $key => $value) { $cookie_string[] = $key.'='.$value; } // Create the cookie string $this->_header['cookie'] = implode('; ', $cookie_string); } $output = $this->method().' '.$this->uri().' '.$this->protocol()."\r\n"; $output .= (string) $this->_header; $output .= $body; return $output; } /** * Gets or sets HTTP query string. * * @param mixed $key Key or key value pairs to set * @param string $value Value to set to a key * @return mixed * @uses Arr::path */ public function query($key = NULL, $value = NULL) { if (is_array($key)) { // Act as a setter, replace all query strings $this->_get = $key; return $this; } if ($key === NULL) { // Act as a getter, all query strings return $this->_get; } elseif ($value === NULL) { // Act as a getter, single query string return Arr::path($this->_get, $key); } // Act as a setter, single query string $this->_get[$key] = $value; return $this; } /** * Gets or sets HTTP POST parameters to the request. * * @param mixed $key Key or key value pairs to set * @param string $value Value to set to a key * @return mixed * @uses Arr::path */ public function post($key = NULL, $value = NULL) { if (is_array($key)) { // Act as a setter, replace all fields $this->_post = $key; return $this; } if ($key === NULL) { // Act as a getter, all fields return $this->_post; } elseif ($value === NULL) { // Act as a getter, single field return Arr::path($this->_post, $key); } // Act as a setter, single field $this->_post[$key] = $value; return $this; } }