620 lines
15 KiB
PHP
620 lines
15 KiB
PHP
![]() |
<?php defined('SYSPATH') OR die('No direct access allowed.');
|
||
|
/**
|
||
|
* Request helper class.
|
||
|
*
|
||
|
* $Id: request.php 4692 2009-12-04 15:59:44Z cbandy $
|
||
|
*
|
||
|
* @package Core
|
||
|
* @author Kohana Team
|
||
|
* @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');
|
||
|
|
||
|
// 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, $remove_base = FALSE)
|
||
|
{
|
||
|
if ( ! empty($_SERVER['HTTP_REFERER']))
|
||
|
{
|
||
|
// Set referrer
|
||
|
$ref = $_SERVER['HTTP_REFERER'];
|
||
|
|
||
|
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)));
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return isset($ref) ? $ref : $default;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Returns the current request protocol, based on $_SERVER['https']. In CLI
|
||
|
* mode, NULL will be returned.
|
||
|
*
|
||
|
* @return string
|
||
|
*/
|
||
|
public static function protocol()
|
||
|
{
|
||
|
if (Kohana::$server_api === 'cli')
|
||
|
{
|
||
|
return NULL;
|
||
|
}
|
||
|
elseif ( ! empty($_SERVER['HTTPS']) AND $_SERVER['HTTPS'] === 'on')
|
||
|
{
|
||
|
return 'https';
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
return 'http';
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Tests if the current request is an AJAX request by checking the X-Requested-With HTTP
|
||
|
* request header that most popular JS frameworks now set for AJAX calls.
|
||
|
*
|
||
|
* @return boolean
|
||
|
*/
|
||
|
public static function is_ajax()
|
||
|
{
|
||
|
return (isset($_SERVER['HTTP_X_REQUESTED_WITH']) AND strtolower($_SERVER['HTTP_X_REQUESTED_WITH']) === 'xmlhttprequest');
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Returns current request method.
|
||
|
*
|
||
|
* @throws Kohana_Exception in case of an unknown request method
|
||
|
* @return string
|
||
|
*/
|
||
|
public static function method()
|
||
|
{
|
||
|
$method = strtolower($_SERVER['REQUEST_METHOD']);
|
||
|
|
||
|
if ( ! in_array($method, request::$http_methods))
|
||
|
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.
|
||
|
*
|
||
|
* @param string content type
|
||
|
* @param boolean set to TRUE to disable wildcard checking
|
||
|
* @return boolean
|
||
|
*/
|
||
|
public static function accepts($type = NULL, $explicit_check = FALSE)
|
||
|
{
|
||
|
request::parse_accept_content_header();
|
||
|
|
||
|
if ($type === NULL)
|
||
|
return request::$accept_types;
|
||
|
|
||
|
return (request::accepts_at_quality($type, $explicit_check) > 0);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* 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.
|
||
|
*
|
||
|
* @param array content types
|
||
|
* @param boolean set to TRUE to disable wildcard checking
|
||
|
* @return mixed string mime type with highest q value, FALSE if none of the given types are accepted
|
||
|
*/
|
||
|
public static function preferred_accept($types, $explicit_check = FALSE)
|
||
|
{
|
||
|
$max_q = 0;
|
||
|
$preferred = FALSE;
|
||
|
|
||
|
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)
|
||
|
{
|
||
|
$q = request::accepts_encoding_at_quality($encoding, $explicit_check);
|
||
|
|
||
|
if ($q > $max_q)
|
||
|
{
|
||
|
$max_q = $q;
|
||
|
$preferred = $encoding;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
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 = $tag;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return $preferred;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* 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, $explicit_check = FALSE)
|
||
|
{
|
||
|
request::parse_accept_content_header();
|
||
|
|
||
|
// Normalize type
|
||
|
$type = strtolower($type);
|
||
|
|
||
|
// General content type (e.g. "jpg")
|
||
|
if (strpos($type, '/') === FALSE)
|
||
|
{
|
||
|
// Don't accept anything by default
|
||
|
$q = 0;
|
||
|
|
||
|
// Look up relevant mime types
|
||
|
foreach ((array) Kohana::config('mimes.'.$type) as $type)
|
||
|
{
|
||
|
$q2 = request::accepts_at_quality($type, $explicit_check);
|
||
|
$q = ($q2 > $q) ? $q2 : $q;
|
||
|
}
|
||
|
|
||
|
return $q;
|
||
|
}
|
||
|
|
||
|
// Content type with subtype given (e.g. "image/jpg")
|
||
|
$type = explode('/', $type, 2);
|
||
|
|
||
|
// Exact match
|
||
|
if (isset(request::$accept_types[$type[0]][$type[1]]))
|
||
|
return request::$accept_types[$type[0]][$type[1]];
|
||
|
|
||
|
if ($explicit_check === FALSE)
|
||
|
{
|
||
|
// Wildcard match
|
||
|
if (isset(request::$accept_types[$type[0]]['*']))
|
||
|
return request::$accept_types[$type[0]]['*'];
|
||
|
|
||
|
// Catch-all wildcard match
|
||
|
if (isset(request::$accept_types['*']['*']))
|
||
|
return request::$accept_types['*']['*'];
|
||
|
}
|
||
|
|
||
|
// Content type not accepted
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* 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
|
||
|
*
|
||
|
* @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_charset_header()
|
||
|
{
|
||
|
// Run this function just once
|
||
|
if (request::$accept_charsets !== NULL)
|
||
|
return;
|
||
|
|
||
|
// 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;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
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;
|
||
|
|
||
|
// 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']);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* 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;
|
||
|
|
||
|
// No HTTP Accept-Language header found
|
||
|
if (empty($_SERVER['HTTP_ACCEPT_LANGUAGE']))
|
||
|
{
|
||
|
// Accept everything
|
||
|
request::$accept_languages['*'] = 1;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
request::$accept_languages = array();
|
||
|
|
||
|
foreach (request::parse_accept_header($_SERVER['HTTP_ACCEPT_LANGUAGE']) as $tag => $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
|