diff options
Diffstat (limited to 'system/helpers/request.php')
-rw-r--r-- | system/helpers/request.php | 482 |
1 files changed, 431 insertions, 51 deletions
diff --git a/system/helpers/request.php b/system/helpers/request.php index 4203d0e5..31afee4e 100644 --- a/system/helpers/request.php +++ b/system/helpers/request.php @@ -2,35 +2,48 @@ /** * Request helper class. * - * $Id: request.php 4355 2009-05-15 17:18:28Z kiall $ + * $Id: request.php 4692 2009-12-04 15:59:44Z cbandy $ * * @package Core * @author Kohana Team - * @copyright (c) 2007-2008 Kohana Team - * @license http://kohanaphp.com/license.html + * @copyright (c) 2007-2009 Kohana Team + * @license http://kohanaphp.com/license */ class request_Core { // Possible HTTP methods protected static $http_methods = array('get', 'head', 'options', 'post', 'put', 'delete'); - // Content types from client's HTTP Accept request header (array) + // Character sets from client's HTTP Accept-Charset request header + protected static $accept_charsets; + + // Content codings from client's HTTP Accept-Encoding request header + protected static $accept_encodings; + + // Language tags from client's HTTP Accept-Language request header + protected static $accept_languages; + + // Content types from client's HTTP Accept request header protected static $accept_types; + // The current user agent and its parsed attributes + protected static $user_agent; + /** * Returns the HTTP referrer, or the default if the referrer is not set. * * @param mixed default to return + * @param bool Remove base URL * @return string */ - public static function referrer($default = FALSE) + public static function referrer($default = FALSE, $remove_base = FALSE) { if ( ! empty($_SERVER['HTTP_REFERER'])) { // Set referrer $ref = $_SERVER['HTTP_REFERER']; - if (strpos($ref, url::base(FALSE)) === 0) + if ($remove_base === TRUE AND (strpos($ref, url::base(FALSE)) === 0)) { // Remove the base URL from the referrer $ref = substr($ref, strlen(url::base(FALSE))); @@ -48,7 +61,7 @@ class request_Core { */ public static function protocol() { - if (PHP_SAPI === 'cli') + if (Kohana::$server_api === 'cli') { return NULL; } @@ -84,10 +97,59 @@ class request_Core { $method = strtolower($_SERVER['REQUEST_METHOD']); if ( ! in_array($method, request::$http_methods)) - throw new Kohana_Exception('request.unknown_method', $method); + throw new Kohana_Exception('Invalid request method :method:', array(':method:' => $method)); return $method; - } + } + + /** + * Retrieves current user agent information + * keys: browser, version, platform, mobile, robot + * + * @param string key + * @return mixed NULL or the parsed value + */ + public static function user_agent($key = 'agent') + { + // Retrieve raw user agent without parsing + if ($key === 'agent') + { + if (request::$user_agent === NULL) + return request::$user_agent = isset($_SERVER['HTTP_USER_AGENT']) ? trim($_SERVER['HTTP_USER_AGENT']) : ''; + + if (is_array(request::$user_agent)) + return request::$user_agent['agent']; + + return request::$user_agent; + } + + if ( ! is_array(request::$user_agent)) + { + request::$user_agent['agent'] = isset($_SERVER['HTTP_USER_AGENT']) ? trim($_SERVER['HTTP_USER_AGENT']) : ''; + + // Parse the user agent and extract basic information + foreach (Kohana::config('user_agents') as $type => $data) + { + foreach ($data as $fragment => $name) + { + if (stripos(request::$user_agent['agent'], $fragment) !== FALSE) + { + if ($type === 'browser' AND preg_match('|'.preg_quote($fragment).'[^0-9.]*+([0-9.][0-9.a-z]*)|i', request::$user_agent['agent'], $match)) + { + // Set the browser version + request::$user_agent['version'] = $match[1]; + } + + // Set the agent name + request::$user_agent[$type] = $name; + break; + } + } + } + } + + return isset(request::$user_agent[$key]) ? request::$user_agent[$key] : NULL; + } /** * Returns boolean of whether client accepts content type. @@ -98,7 +160,7 @@ class request_Core { */ public static function accepts($type = NULL, $explicit_check = FALSE) { - request::parse_accept_header(); + request::parse_accept_content_header(); if ($type === NULL) return request::$accept_types; @@ -107,6 +169,56 @@ class request_Core { } /** + * Returns boolean indicating if the client accepts a charset + * + * @param string + * @return boolean + */ + public static function accepts_charset($charset = NULL) + { + request::parse_accept_charset_header(); + + if ($charset === NULL) + return request::$accept_charsets; + + return (request::accepts_charset_at_quality($charset) > 0); + } + + /** + * Returns boolean indicating if the client accepts an encoding + * + * @param string + * @param boolean set to TRUE to disable wildcard checking + * @return boolean + */ + public static function accepts_encoding($encoding = NULL, $explicit_check = FALSE) + { + request::parse_accept_encoding_header(); + + if ($encoding === NULL) + return request::$accept_encodings; + + return (request::accepts_encoding_at_quality($encoding, $explicit_check) > 0); + } + + /** + * Returns boolean indicating if the client accepts a language tag + * + * @param string language tag + * @param boolean set to TRUE to disable prefix and wildcard checking + * @return boolean + */ + public static function accepts_language($tag = NULL, $explicit_check = FALSE) + { + request::parse_accept_language_header(); + + if ($tag === NULL) + return request::$accept_languages; + + return (request::accepts_language_at_quality($tag, $explicit_check) > 0); + } + + /** * Compare the q values for given array of content types and return the one with the highest value. * If items are found to have the same q value, the first one encountered in the given array wins. * If all items in the given array have a q value of 0, FALSE is returned. @@ -117,24 +229,103 @@ class request_Core { */ public static function preferred_accept($types, $explicit_check = FALSE) { - // Initialize - $mime_types = array(); $max_q = 0; $preferred = FALSE; - // Load q values for all given content types - foreach (array_unique($types) as $type) + foreach ($types as $type) + { + $q = request::accepts_at_quality($type, $explicit_check); + + if ($q > $max_q) + { + $max_q = $q; + $preferred = $type; + } + } + + return $preferred; + } + + /** + * Compare the q values for a given array of character sets and return the + * one with the highest value. If items are found to have the same q value, + * the first one encountered takes precedence. If all items in the given + * array have a q value of 0, FALSE is returned. + * + * @param array character sets + * @return mixed + */ + public static function preferred_charset($charsets) + { + $max_q = 0; + $preferred = FALSE; + + foreach ($charsets as $charset) + { + $q = request::accepts_charset_at_quality($charset); + + if ($q > $max_q) + { + $max_q = $q; + $preferred = $charset; + } + } + + return $preferred; + } + + /** + * Compare the q values for a given array of encodings and return the one with + * the highest value. If items are found to have the same q value, the first + * one encountered takes precedence. If all items in the given array have + * a q value of 0, FALSE is returned. + * + * @param array encodings + * @param boolean set to TRUE to disable wildcard checking + * @return mixed + */ + public static function preferred_encoding($encodings, $explicit_check = FALSE) + { + $max_q = 0; + $preferred = FALSE; + + foreach ($encodings as $encoding) { - $mime_types[$type] = request::accepts_at_quality($type, $explicit_check); + $q = request::accepts_encoding_at_quality($encoding, $explicit_check); + + if ($q > $max_q) + { + $max_q = $q; + $preferred = $encoding; + } } - // Look for the highest q value - foreach ($mime_types as $type => $q) + return $preferred; + } + + /** + * Compare the q values for a given array of language tags and return the + * one with the highest value. If items are found to have the same q value, + * the first one encountered takes precedence. If all items in the given + * array have a q value of 0, FALSE is returned. + * + * @param array language tags + * @param boolean set to TRUE to disable prefix and wildcard checking + * @return mixed + */ + public static function preferred_language($tags, $explicit_check = FALSE) + { + $max_q = 0; + $preferred = FALSE; + + foreach ($tags as $tag) { + $q = request::accepts_language_at_quality($tag, $explicit_check); + if ($q > $max_q) { $max_q = $q; - $preferred = $type; + $preferred = $tag; } } @@ -142,18 +333,18 @@ class request_Core { } /** - * Returns quality factor at which the client accepts content type. + * Returns quality factor at which the client accepts content type * * @param string content type (e.g. "image/jpg", "jpg") * @param boolean set to TRUE to disable wildcard checking * @return integer|float */ - public static function accepts_at_quality($type = NULL, $explicit_check = FALSE) + public static function accepts_at_quality($type, $explicit_check = FALSE) { - request::parse_accept_header(); + request::parse_accept_content_header(); // Normalize type - $type = strtolower((string) $type); + $type = strtolower($type); // General content type (e.g. "jpg") if (strpos($type, '/') === FALSE) @@ -178,62 +369,251 @@ class request_Core { if (isset(request::$accept_types[$type[0]][$type[1]])) return request::$accept_types[$type[0]][$type[1]]; - // Wildcard match (if not checking explicitly) - if ($explicit_check === FALSE AND isset(request::$accept_types[$type[0]]['*'])) - return request::$accept_types[$type[0]]['*']; + if ($explicit_check === FALSE) + { + // Wildcard match + if (isset(request::$accept_types[$type[0]]['*'])) + return request::$accept_types[$type[0]]['*']; - // Catch-all wildcard match (if not checking explicitly) - if ($explicit_check === FALSE AND isset(request::$accept_types['*']['*'])) - return request::$accept_types['*']['*']; + // Catch-all wildcard match + if (isset(request::$accept_types['*']['*'])) + return request::$accept_types['*']['*']; + } // Content type not accepted return 0; } /** - * Parses client's HTTP Accept request header, and builds array structure representing it. + * Returns quality factor at which the client accepts a charset + * + * @param string charset (e.g., "ISO-8859-1", "utf-8") + * @return integer|float + */ + public static function accepts_charset_at_quality($charset) + { + request::parse_accept_charset_header(); + + // Normalize charset + $charset = strtolower($charset); + + // Exact match + if (isset(request::$accept_charsets[$charset])) + return request::$accept_charsets[$charset]; + + if (isset(request::$accept_charsets['*'])) + return request::$accept_charsets['*']; + + if ($charset === 'iso-8859-1') + return 1; + + return 0; + } + + /** + * Returns quality factor at which the client accepts an encoding + * + * @param string encoding (e.g., "gzip", "deflate") + * @param boolean set to TRUE to disable wildcard checking + * @return integer|float + */ + public static function accepts_encoding_at_quality($encoding, $explicit_check = FALSE) + { + request::parse_accept_encoding_header(); + + // Normalize encoding + $encoding = strtolower($encoding); + + // Exact match + if (isset(request::$accept_encodings[$encoding])) + return request::$accept_encodings[$encoding]; + + if ($explicit_check === FALSE) + { + if (isset(request::$accept_encodings['*'])) + return request::$accept_encodings['*']; + + if ($encoding === 'identity') + return 1; + } + + return 0; + } + + /** + * Returns quality factor at which the client accepts a language + * + * @param string tag (e.g., "en", "en-us", "fr-ca") + * @param boolean set to TRUE to disable prefix and wildcard checking + * @return integer|float + */ + public static function accepts_language_at_quality($tag, $explicit_check = FALSE) + { + request::parse_accept_language_header(); + + $tag = explode('-', strtolower($tag), 2); + + if (isset(request::$accept_languages[$tag[0]])) + { + if (isset($tag[1])) + { + // Exact match + if (isset(request::$accept_languages[$tag[0]][$tag[1]])) + return request::$accept_languages[$tag[0]][$tag[1]]; + + // A prefix matches + if ($explicit_check === FALSE AND isset(request::$accept_languages[$tag[0]]['*'])) + return request::$accept_languages[$tag[0]]['*']; + } + else + { + // No subtags + if (isset(request::$accept_languages[$tag[0]]['*'])) + return request::$accept_languages[$tag[0]]['*']; + } + } + + if ($explicit_check === FALSE AND isset(request::$accept_languages['*'])) + return request::$accept_languages['*']; + + return 0; + } + + /** + * Parses a HTTP Accept or Accept-* header for q values * - * @return void + * @param string header data + * @return array + */ + protected static function parse_accept_header($header) + { + $result = array(); + + // Remove linebreaks and parse the HTTP Accept header + foreach (explode(',', str_replace(array("\r", "\n"), '', strtolower($header))) as $entry) + { + // Explode each entry in content type and possible quality factor + $entry = explode(';', trim($entry), 2); + + $q = (isset($entry[1]) AND preg_match('~\bq\s*+=\s*+([.0-9]+)~', $entry[1], $match)) ? (float) $match[1] : 1; + + // Overwrite entries with a smaller q value + if ( ! isset($result[$entry[0]]) OR $q > $result[$entry[0]]) + { + $result[$entry[0]] = $q; + } + } + + return $result; + } + + /** + * Parses a client's HTTP Accept-Charset header */ - protected static function parse_accept_header() + protected static function parse_accept_charset_header() { // Run this function just once - if (request::$accept_types !== NULL) + if (request::$accept_charsets !== NULL) return; - // Initialize accept_types array - request::$accept_types = array(); + // No HTTP Accept-Charset header found + if (empty($_SERVER['HTTP_ACCEPT_CHARSET'])) + { + // Accept everything + request::$accept_charsets['*'] = 1; + } + else + { + request::$accept_charsets = request::parse_accept_header($_SERVER['HTTP_ACCEPT_CHARSET']); + } + } + + /** + * Parses a client's HTTP Accept header + */ + protected static function parse_accept_content_header() + { + // Run this function just once + if (request::$accept_types !== NULL) + return; // No HTTP Accept header found if (empty($_SERVER['HTTP_ACCEPT'])) { // Accept everything request::$accept_types['*']['*'] = 1; - return; } - - // Remove linebreaks and parse the HTTP Accept header - foreach (explode(',', str_replace(array("\r", "\n"), '', $_SERVER['HTTP_ACCEPT'])) as $accept_entry) + else { - // Explode each entry in content type and possible quality factor - $accept_entry = explode(';', trim($accept_entry), 2); + request::$accept_types = array(); + + foreach (request::parse_accept_header($_SERVER['HTTP_ACCEPT']) as $type => $q) + { + // Explode each content type (e.g. "text/html") + $type = explode('/', $type, 2); + + // Skip invalid content types + if ( ! isset($type[1])) + continue; + + request::$accept_types[$type[0]][$type[1]] = $q; + } + } + } + + /** + * Parses a client's HTTP Accept-Encoding header + */ + protected static function parse_accept_encoding_header() + { + // Run this function just once + if (request::$accept_encodings !== NULL) + return; - // Explode each content type (e.g. "text/html") - $type = explode('/', $accept_entry[0], 2); + // No HTTP Accept-Encoding header found + if ( ! isset($_SERVER['HTTP_ACCEPT_ENCODING'])) + { + // Accept everything + request::$accept_encodings['*'] = 1; + } + elseif ($_SERVER['HTTP_ACCEPT_ENCODING'] === '') + { + // Accept only identity + request::$accept_encodings['identity'] = 1; + } + else + { + request::$accept_encodings = request::parse_accept_header($_SERVER['HTTP_ACCEPT_ENCODING']); + } + } - // Skip invalid content types - if ( ! isset($type[1])) - continue; + /** + * Parses a client's HTTP Accept-Language header + */ + protected static function parse_accept_language_header() + { + // Run this function just once + if (request::$accept_languages !== NULL) + return; - // Assume a default quality factor of 1 if no custom q value found - $q = (isset($accept_entry[1]) AND preg_match('~\bq\s*+=\s*+([.0-9]+)~', $accept_entry[1], $match)) ? (float) $match[1] : 1; + // No HTTP Accept-Language header found + if (empty($_SERVER['HTTP_ACCEPT_LANGUAGE'])) + { + // Accept everything + request::$accept_languages['*'] = 1; + } + else + { + request::$accept_languages = array(); - // Populate accept_types array - if ( ! isset(request::$accept_types[$type[0]][$type[1]]) OR $q > request::$accept_types[$type[0]][$type[1]]) + foreach (request::parse_accept_header($_SERVER['HTTP_ACCEPT_LANGUAGE']) as $tag => $q) { - request::$accept_types[$type[0]][$type[1]] = $q; + // Explode each language (e.g. "en-us") + $tag = explode('-', $tag, 2); + + request::$accept_languages[$tag[0]][isset($tag[1]) ? $tag[1] : '*'] = $q; } } } -} // End request
\ No newline at end of file +} // End request |