Kohana v3.3.0

This commit is contained in:
Deon George
2013-04-22 14:09:50 +10:00
commit f96694b18f
1280 changed files with 145034 additions and 0 deletions

View File

@@ -0,0 +1,72 @@
<?php defined('SYSPATH') OR die('No direct script access.');
abstract class Kohana_HTTP_Exception extends Kohana_Exception {
/**
* Creates an HTTP_Exception of the specified type.
*
* @param integer $code the http status code
* @param string $message status message, custom content to display with error
* @param array $variables translation variables
* @return HTTP_Exception
*/
public static function factory($code, $message = NULL, array $variables = NULL, Exception $previous = NULL)
{
$class = 'HTTP_Exception_'.$code;
return new $class($message, $variables, $previous);
}
/**
* @var int http status code
*/
protected $_code = 0;
/**
* @var Request Request instance that triggered this exception.
*/
protected $_request;
/**
* Creates a new translated exception.
*
* throw new Kohana_Exception('Something went terrible wrong, :user',
* array(':user' => $user));
*
* @param string $message status message, custom content to display with error
* @param array $variables translation variables
* @return void
*/
public function __construct($message = NULL, array $variables = NULL, Exception $previous = NULL)
{
parent::__construct($message, $variables, $this->_code, $previous);
}
/**
* Store the Request that triggered this exception.
*
* @param Request $request Request object that triggered this exception.
* @return Response
*/
public function request(Request $request = NULL)
{
if ($request === NULL)
return $this->_request;
$this->_request = $request;
return $this;
}
/**
* Generate a Response for the current Exception
*
* @uses Kohana_Exception::response()
* @return Response
*/
public function get_response()
{
return Kohana_Exception::response($this);
}
} // End Kohana_HTTP_Exception

View File

@@ -0,0 +1,10 @@
<?php defined('SYSPATH') OR die('No direct script access.');
class Kohana_HTTP_Exception_300 extends HTTP_Exception_Redirect {
/**
* @var integer HTTP 300 Multiple Choices
*/
protected $_code = 300;
}

View File

@@ -0,0 +1,10 @@
<?php defined('SYSPATH') OR die('No direct script access.');
class Kohana_HTTP_Exception_301 extends HTTP_Exception_Redirect {
/**
* @var integer HTTP 301 Moved Permanently
*/
protected $_code = 301;
}

View File

@@ -0,0 +1,10 @@
<?php defined('SYSPATH') OR die('No direct script access.');
class Kohana_HTTP_Exception_302 extends HTTP_Exception_Redirect {
/**
* @var integer HTTP 302 Found
*/
protected $_code = 302;
}

View File

@@ -0,0 +1,10 @@
<?php defined('SYSPATH') OR die('No direct script access.');
class Kohana_HTTP_Exception_303 extends HTTP_Exception_Redirect {
/**
* @var integer HTTP 303 See Other
*/
protected $_code = 303;
}

View File

@@ -0,0 +1,10 @@
<?php defined('SYSPATH') OR die('No direct script access.');
class Kohana_HTTP_Exception_304 extends HTTP_Exception_Expected {
/**
* @var integer HTTP 304 Not Modified
*/
protected $_code = 304;
}

View File

@@ -0,0 +1,41 @@
<?php defined('SYSPATH') OR die('No direct script access.');
class Kohana_HTTP_Exception_305 extends HTTP_Exception_Expected {
/**
* @var integer HTTP 305 Use Proxy
*/
protected $_code = 305;
/**
* Specifies the proxy to replay this request via
*
* @param string $location URI of the proxy
*/
public function location($uri = NULL)
{
if ($uri === NULL)
return $this->headers('Location');
$this->headers('Location', $uri);
return $this;
}
/**
* Validate this exception contains everything needed to continue.
*
* @throws Kohana_Exception
* @return bool
*/
public function check()
{
if ($location = $this->headers('location') === NULL)
throw new Kohana_Exception('A \'location\' must be specified for a redirect');
if (strpos($location, '://') === FALSE)
throw new Kohana_Exception('An absolute URI to the proxy server must be specified');
return TRUE;
}
}

View File

@@ -0,0 +1,10 @@
<?php defined('SYSPATH') OR die('No direct script access.');
class Kohana_HTTP_Exception_307 extends HTTP_Exception_Redirect {
/**
* @var integer HTTP 307 Temporary Redirect
*/
protected $_code = 307;
}

View File

@@ -0,0 +1,10 @@
<?php defined('SYSPATH') OR die('No direct script access.');
class Kohana_HTTP_Exception_400 extends HTTP_Exception {
/**
* @var integer HTTP 400 Bad Request
*/
protected $_code = 400;
}

View File

@@ -0,0 +1,38 @@
<?php defined('SYSPATH') OR die('No direct script access.');
class Kohana_HTTP_Exception_401 extends HTTP_Exception_Expected {
/**
* @var integer HTTP 401 Unauthorized
*/
protected $_code = 401;
/**
* Specifies the WWW-Authenticate challenge.
*
* @param string $challenge WWW-Authenticate challenge (eg `Basic realm="Control Panel"`)
*/
public function authenticate($challenge = NULL)
{
if ($challenge === NULL)
return $this->headers('www-authenticate');
$this->headers('www-authenticate', $challenge);
return $this;
}
/**
* Validate this exception contains everything needed to continue.
*
* @throws Kohana_Exception
* @return bool
*/
public function check()
{
if ($this->headers('www-authenticate') === NULL)
throw new Kohana_Exception('A \'www-authenticate\' header must be specified for a HTTP 401 Unauthorized');
return TRUE;
}
}

View File

@@ -0,0 +1,10 @@
<?php defined('SYSPATH') OR die('No direct script access.');
class Kohana_HTTP_Exception_402 extends HTTP_Exception {
/**
* @var integer HTTP 402 Payment Required
*/
protected $_code = 402;
}

View File

@@ -0,0 +1,10 @@
<?php defined('SYSPATH') OR die('No direct script access.');
class Kohana_HTTP_Exception_403 extends HTTP_Exception {
/**
* @var integer HTTP 403 Forbidden
*/
protected $_code = 403;
}

View File

@@ -0,0 +1,10 @@
<?php defined('SYSPATH') OR die('No direct script access.');
class Kohana_HTTP_Exception_404 extends HTTP_Exception {
/**
* @var integer HTTP 404 Not Found
*/
protected $_code = 404;
}

View File

@@ -0,0 +1,41 @@
<?php defined('SYSPATH') OR die('No direct script access.');
class Kohana_HTTP_Exception_405 extends HTTP_Exception_Expected {
/**
* @var integer HTTP 405 Method Not Allowed
*/
protected $_code = 405;
/**
* Specifies the list of allowed HTTP methods
*
* @param array $methods List of allowed methods
*/
public function allowed($methods)
{
if (is_array($methods))
{
$methods = implode(',', $methods);
}
$this->headers('allow', $methods);
return $this;
}
/**
* Validate this exception contains everything needed to continue.
*
* @throws Kohana_Exception
* @return bool
*/
public function check()
{
if ($location = $this->headers('allow') === NULL)
throw new Kohana_Exception('A list of allowed methods must be specified');
return TRUE;
}
}

View File

@@ -0,0 +1,10 @@
<?php defined('SYSPATH') OR die('No direct script access.');
class Kohana_HTTP_Exception_406 extends HTTP_Exception {
/**
* @var integer HTTP 406 Not Acceptable
*/
protected $_code = 406;
}

View File

@@ -0,0 +1,10 @@
<?php defined('SYSPATH') OR die('No direct script access.');
class Kohana_HTTP_Exception_407 extends HTTP_Exception {
/**
* @var integer HTTP 407 Proxy Authentication Required
*/
protected $_code = 407;
}

View File

@@ -0,0 +1,10 @@
<?php defined('SYSPATH') OR die('No direct script access.');
class Kohana_HTTP_Exception_408 extends HTTP_Exception {
/**
* @var integer HTTP 408 Request Timeout
*/
protected $_code = 408;
}

View File

@@ -0,0 +1,10 @@
<?php defined('SYSPATH') OR die('No direct script access.');
class Kohana_HTTP_Exception_409 extends HTTP_Exception {
/**
* @var integer HTTP 409 Conflict
*/
protected $_code = 409;
}

View File

@@ -0,0 +1,10 @@
<?php defined('SYSPATH') OR die('No direct script access.');
class Kohana_HTTP_Exception_410 extends HTTP_Exception {
/**
* @var integer HTTP 410 Gone
*/
protected $_code = 410;
}

View File

@@ -0,0 +1,10 @@
<?php defined('SYSPATH') OR die('No direct script access.');
class Kohana_HTTP_Exception_411 extends HTTP_Exception {
/**
* @var integer HTTP 411 Length Required
*/
protected $_code = 411;
}

View File

@@ -0,0 +1,10 @@
<?php defined('SYSPATH') OR die('No direct script access.');
class Kohana_HTTP_Exception_412 extends HTTP_Exception {
/**
* @var integer HTTP 412 Precondition Failed
*/
protected $_code = 412;
}

View File

@@ -0,0 +1,10 @@
<?php defined('SYSPATH') OR die('No direct script access.');
class Kohana_HTTP_Exception_413 extends HTTP_Exception {
/**
* @var integer HTTP 413 Request Entity Too Large
*/
protected $_code = 413;
}

View File

@@ -0,0 +1,10 @@
<?php defined('SYSPATH') OR die('No direct script access.');
class Kohana_HTTP_Exception_414 extends HTTP_Exception {
/**
* @var integer HTTP 414 Request-URI Too Long
*/
protected $_code = 414;
}

View File

@@ -0,0 +1,10 @@
<?php defined('SYSPATH') OR die('No direct script access.');
class Kohana_HTTP_Exception_415 extends HTTP_Exception {
/**
* @var integer HTTP 415 Unsupported Media Type
*/
protected $_code = 415;
}

View File

@@ -0,0 +1,10 @@
<?php defined('SYSPATH') OR die('No direct script access.');
class Kohana_HTTP_Exception_416 extends HTTP_Exception {
/**
* @var integer HTTP 416 Request Range Not Satisfiable
*/
protected $_code = 416;
}

View File

@@ -0,0 +1,10 @@
<?php defined('SYSPATH') OR die('No direct script access.');
class Kohana_HTTP_Exception_417 extends HTTP_Exception {
/**
* @var integer HTTP 417 Expectation Failed
*/
protected $_code = 417;
}

View File

@@ -0,0 +1,10 @@
<?php defined('SYSPATH') OR die('No direct script access.');
class Kohana_HTTP_Exception_500 extends HTTP_Exception {
/**
* @var integer HTTP 500 Internal Server Error
*/
protected $_code = 500;
}

View File

@@ -0,0 +1,10 @@
<?php defined('SYSPATH') OR die('No direct script access.');
class Kohana_HTTP_Exception_501 extends HTTP_Exception {
/**
* @var integer HTTP 501 Not Implemented
*/
protected $_code = 501;
}

View File

@@ -0,0 +1,10 @@
<?php defined('SYSPATH') OR die('No direct script access.');
class Kohana_HTTP_Exception_502 extends HTTP_Exception {
/**
* @var integer HTTP 502 Bad Gateway
*/
protected $_code = 502;
}

View File

@@ -0,0 +1,10 @@
<?php defined('SYSPATH') OR die('No direct script access.');
class Kohana_HTTP_Exception_503 extends HTTP_Exception {
/**
* @var integer HTTP 503 Service Unavailable
*/
protected $_code = 503;
}

View File

@@ -0,0 +1,10 @@
<?php defined('SYSPATH') OR die('No direct script access.');
class Kohana_HTTP_Exception_504 extends HTTP_Exception {
/**
* @var integer HTTP 504 Gateway Timeout
*/
protected $_code = 504;
}

View File

@@ -0,0 +1,10 @@
<?php defined('SYSPATH') OR die('No direct script access.');
class Kohana_HTTP_Exception_505 extends HTTP_Exception {
/**
* @var integer HTTP 505 HTTP Version Not Supported
*/
protected $_code = 505;
}

View File

@@ -0,0 +1,82 @@
<?php defined('SYSPATH') OR die('No direct script access.');
/**
* "Expected" HTTP exception class. Used for all [HTTP_Exception]'s where a standard
* Kohana error page should never be shown.
*
* Eg [HTTP_Exception_301], [HTTP_Exception_302] etc
*
* @package Kohana
* @category Exceptions
* @author Kohana Team
* @copyright (c) 2008-2012 Kohana Team
* @license http://kohanaframework.org/license
*/
abstract class Kohana_HTTP_Exception_Expected extends HTTP_Exception {
/**
* @var Response Response Object
*/
protected $_response;
/**
* Creates a new translated exception.
*
* throw new Kohana_Exception('Something went terrible wrong, :user',
* array(':user' => $user));
*
* @param string $message status message, custom content to display with error
* @param array $variables translation variables
* @return void
*/
public function __construct($message = NULL, array $variables = NULL, Exception $previous = NULL)
{
parent::__construct($message, $variables, $previous);
// Prepare our response object and set the correct status code.
$this->_response = Response::factory()
->status($this->_code);
}
/**
* Gets and sets headers to the [Response].
*
* @see [Response::headers]
* @param mixed $key
* @param string $value
* @return mixed
*/
public function headers($key = NULL, $value = NULL)
{
$result = $this->_response->headers($key, $value);
if ( ! $result instanceof Response)
return $result;
return $this;
}
/**
* Validate this exception contains everything needed to continue.
*
* @throws Kohana_Exception
* @return bool
*/
public function check()
{
return TRUE;
}
/**
* Generate a Response for the current Exception
*
* @uses Kohana_Exception::response()
* @return Response
*/
public function get_response()
{
$this->check();
return $this->_response;
}
} // End Kohana_HTTP_Exception

View File

@@ -0,0 +1,51 @@
<?php defined('SYSPATH') OR die('No direct script access.');
/**
* Redirect HTTP exception class. Used for all [HTTP_Exception]'s where the status
* code indicates a redirect.
*
* Eg [HTTP_Exception_301], [HTTP_Exception_302] and most of the other 30x's
*
* @package Kohana
* @category Exceptions
* @author Kohana Team
* @copyright (c) 2008-2012 Kohana Team
* @license http://kohanaframework.org/license
*/
abstract class Kohana_HTTP_Exception_Redirect extends HTTP_Exception_Expected {
/**
* Specifies the URI to redirect to.
*
* @param string $location URI of the proxy
*/
public function location($uri = NULL)
{
if ($uri === NULL)
return $this->headers('Location');
if (strpos($uri, '://') === FALSE)
{
// Make the URI into a URL
$uri = URL::site($uri, TRUE, ! empty(Kohana::$index_file));
}
$this->headers('Location', $uri);
return $this;
}
/**
* Validate this exception contains everything needed to continue.
*
* @throws Kohana_Exception
* @return bool
*/
public function check()
{
if ($this->headers('location') === NULL)
throw new Kohana_Exception('A \'location\' must be specified for a redirect');
return TRUE;
}
} // End Kohana_HTTP_Exception_Redirect

View File

@@ -0,0 +1,949 @@
<?php defined('SYSPATH') OR die('No direct script access.');
/**
* The Kohana_HTTP_Header class provides an Object-Orientated interface
* to HTTP headers. This can parse header arrays returned from the
* PHP functions `apache_request_headers()` or the `http_parse_headers()`
* function available within the PECL HTTP library.
*
* @package Kohana
* @category HTTP
* @author Kohana Team
* @since 3.1.0
* @copyright (c) 2008-2012 Kohana Team
* @license http://kohanaphp.com/license
*/
class Kohana_HTTP_Header extends ArrayObject {
// Default Accept-* quality value if none supplied
const DEFAULT_QUALITY = 1;
/**
* Parses an Accept(-*) header and detects the quality
*
* @param array $parts accept header parts
* @return array
* @since 3.2.0
*/
public static function accept_quality(array $parts)
{
$parsed = array();
// Resource light iteration
$parts_keys = array_keys($parts);
foreach ($parts_keys as $key)
{
$value = trim(str_replace(array("\r", "\n"), '', $parts[$key]));
$pattern = '~\b(\;\s*+)?q\s*+=\s*+([.0-9]+)~';
// If there is no quality directive, return default
if ( ! preg_match($pattern, $value, $quality))
{
$parsed[$value] = (float) HTTP_Header::DEFAULT_QUALITY;
}
else
{
$quality = $quality[2];
if ($quality[0] === '.')
{
$quality = '0'.$quality;
}
// Remove the quality value from the string and apply quality
$parsed[trim(preg_replace($pattern, '', $value, 1), '; ')] = (float) $quality;
}
}
return $parsed;
}
/**
* Parses the accept header to provide the correct quality values
* for each supplied accept type.
*
* @link http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.1
* @param string $accepts accept content header string to parse
* @return array
* @since 3.2.0
*/
public static function parse_accept_header($accepts = NULL)
{
$accepts = explode(',', (string) $accepts);
// If there is no accept, lets accept everything
if ($accepts === NULL)
return array('*' => array('*' => (float) HTTP_Header::DEFAULT_QUALITY));
// Parse the accept header qualities
$accepts = HTTP_Header::accept_quality($accepts);
$parsed_accept = array();
// This method of iteration uses less resource
$keys = array_keys($accepts);
foreach ($keys as $key)
{
// Extract the parts
$parts = explode('/', $key, 2);
// Invalid content type- bail
if ( ! isset($parts[1]))
continue;
// Set the parsed output
$parsed_accept[$parts[0]][$parts[1]] = $accepts[$key];
}
return $parsed_accept;
}
/**
* Parses the `Accept-Charset:` HTTP header and returns an array containing
* the charset and associated quality.
*
* @link http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.2
* @param string $charset charset string to parse
* @return array
* @since 3.2.0
*/
public static function parse_charset_header($charset = NULL)
{
if ($charset === NULL)
{
return array('*' => (float) HTTP_Header::DEFAULT_QUALITY);
}
return HTTP_Header::accept_quality(explode(',', (string) $charset));
}
/**
* Parses the `Accept-Encoding:` HTTP header and returns an array containing
* the charsets and associated quality.
*
* @link http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.3
* @param string $encoding charset string to parse
* @return array
* @since 3.2.0
*/
public static function parse_encoding_header($encoding = NULL)
{
// Accept everything
if ($encoding === NULL)
{
return array('*' => (float) HTTP_Header::DEFAULT_QUALITY);
}
elseif ($encoding === '')
{
return array('identity' => (float) HTTP_Header::DEFAULT_QUALITY);
}
else
{
return HTTP_Header::accept_quality(explode(',', (string) $encoding));
}
}
/**
* Parses the `Accept-Language:` HTTP header and returns an array containing
* the languages and associated quality.
*
* @link http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.4
* @param string $language charset string to parse
* @return array
* @since 3.2.0
*/
public static function parse_language_header($language = NULL)
{
if ($language === NULL)
{
return array('*' => array('*' => (float) HTTP_Header::DEFAULT_QUALITY));
}
$language = HTTP_Header::accept_quality(explode(',', (string) $language));
$parsed_language = array();
$keys = array_keys($language);
foreach ($keys as $key)
{
// Extract the parts
$parts = explode('-', $key, 2);
// Invalid content type- bail
if ( ! isset($parts[1]))
{
$parsed_language[$parts[0]]['*'] = $language[$key];
}
else
{
// Set the parsed output
$parsed_language[$parts[0]][$parts[1]] = $language[$key];
}
}
return $parsed_language;
}
/**
* Generates a Cache-Control HTTP header based on the supplied array.
*
* // Set the cache control headers you want to use
* $cache_control = array(
* 'max-age' => 3600,
* 'must-revalidate',
* 'public'
* );
*
* // Create the cache control header, creates :
* // cache-control: max-age=3600, must-revalidate, public
* $response->headers('Cache-Control', HTTP_Header::create_cache_control($cache_control);
*
* @link http://www.w3.org/Protocols/rfc2616/rfc2616-sec13.html#sec13
* @param array $cache_control Cache-Control to render to string
* @return string
*/
public static function create_cache_control(array $cache_control)
{
$parts = array();
foreach ($cache_control as $key => $value)
{
$parts[] = (is_int($key)) ? $value : ($key.'='.$value);
}
return implode(', ', $parts);
}
/**
* Parses the Cache-Control header and returning an array representation of the Cache-Control
* header.
*
* // Create the cache control header
* $response->headers('cache-control', 'max-age=3600, must-revalidate, public');
*
* // Parse the cache control header
* if ($cache_control = HTTP_Header::parse_cache_control($response->headers('cache-control')))
* {
* // Cache-Control header was found
* $maxage = $cache_control['max-age'];
* }
*
* @param array $cache_control Array of headers
* @return mixed
*/
public static function parse_cache_control($cache_control)
{
$directives = explode(',', strtolower($cache_control));
if ($directives === FALSE)
return FALSE;
$output = array();
foreach ($directives as $directive)
{
if (strpos($directive, '=') !== FALSE)
{
list($key, $value) = explode('=', trim($directive), 2);
$output[$key] = ctype_digit($value) ? (int) $value : $value;
}
else
{
$output[] = trim($directive);
}
}
return $output;
}
/**
* @var array Accept: (content) types
*/
protected $_accept_content;
/**
* @var array Accept-Charset: parsed header
*/
protected $_accept_charset;
/**
* @var array Accept-Encoding: parsed header
*/
protected $_accept_encoding;
/**
* @var array Accept-Language: parsed header
*/
protected $_accept_language;
/**
* Constructor method for [Kohana_HTTP_Header]. Uses the standard constructor
* of the parent `ArrayObject` class.
*
* $header_object = new HTTP_Header(array('x-powered-by' => 'Kohana 3.1.x', 'expires' => '...'));
*
* @param mixed $input Input array
* @param int $flags Flags
* @param string $iterator_class The iterator class to use
*/
public function __construct(array $input = array(), $flags = NULL, $iterator_class = 'ArrayIterator')
{
/**
* @link http://www.w3.org/Protocols/rfc2616/rfc2616.html
*
* HTTP header declarations should be treated as case-insensitive
*/
$input = array_change_key_case( (array) $input, CASE_LOWER);
parent::__construct($input, $flags, $iterator_class);
}
/**
* Returns the header object as a string, including
* the terminating new line
*
* // Return the header as a string
* echo (string) $request->headers();
*
* @return string
*/
public function __toString()
{
$header = '';
foreach ($this as $key => $value)
{
// Put the keys back the Case-Convention expected
$key = Text::ucfirst($key);
if (is_array($value))
{
$header .= $key.': '.(implode(', ', $value))."\r\n";
}
else
{
$header .= $key.': '.$value."\r\n";
}
}
return $header."\r\n";
}
/**
* Overloads `ArrayObject::offsetSet()` to enable handling of header
* with multiple instances of the same directive. If the `$replace` flag
* is `FALSE`, the header will be appended rather than replacing the
* original setting.
*
* @param mixed $index index to set `$newval` to
* @param mixed $newval new value to set
* @param boolean $replace replace existing value
* @return void
* @since 3.2.0
*/
public function offsetSet($index, $newval, $replace = TRUE)
{
// Ensure the index is lowercase
$index = strtolower($index);
if ($replace OR ! $this->offsetExists($index))
{
return parent::offsetSet($index, $newval);
}
$current_value = $this->offsetGet($index);
if (is_array($current_value))
{
$current_value[] = $newval;
}
else
{
$current_value = array($current_value, $newval);
}
return parent::offsetSet($index, $current_value);
}
/**
* Overloads the `ArrayObject::offsetExists()` method to ensure keys
* are lowercase.
*
* @param string $index
* @return boolean
* @since 3.2.0
*/
public function offsetExists($index)
{
return parent::offsetExists(strtolower($index));
}
/**
* Overloads the `ArrayObject::offsetUnset()` method to ensure keys
* are lowercase.
*
* @param string $index
* @return void
* @since 3.2.0
*/
public function offsetUnset($index)
{
return parent::offsetUnset(strtolower($index));
}
/**
* Overload the `ArrayObject::offsetGet()` method to ensure that all
* keys passed to it are formatted correctly for this object.
*
* @param string $index index to retrieve
* @return mixed
* @since 3.2.0
*/
public function offsetGet($index)
{
return parent::offsetGet(strtolower($index));
}
/**
* Overloads the `ArrayObject::exchangeArray()` method to ensure that
* all keys are changed to lowercase.
*
* @param mixed $input
* @return array
* @since 3.2.0
*/
public function exchangeArray($input)
{
/**
* @link http://www.w3.org/Protocols/rfc2616/rfc2616.html
*
* HTTP header declarations should be treated as case-insensitive
*/
$input = array_change_key_case( (array) $input, CASE_LOWER);
return parent::exchangeArray($input);
}
/**
* Parses a HTTP Message header line and applies it to this HTTP_Header
*
* $header = $response->headers();
* $header->parse_header_string(NULL, 'content-type: application/json');
*
* @param resource $resource the resource (required by Curl API)
* @param string $header_line the line from the header to parse
* @return int
* @since 3.2.0
*/
public function parse_header_string($resource, $header_line)
{
$headers = array();
if (preg_match_all('/(\w[^\s:]*):[ ]*([^\r\n]*(?:\r\n[ \t][^\r\n]*)*)/', $header_line, $matches))
{
foreach ($matches[0] as $key => $value)
{
$this->offsetSet($matches[1][$key], $matches[2][$key], FALSE);
}
}
return strlen($header_line);
}
/**
* Returns the accept quality of a submitted mime type based on the
* request `Accept:` header. If the `$explicit` argument is `TRUE`,
* only precise matches will be returned, excluding all wildcard (`*`)
* directives.
*
* // Accept: application/xml; application/json; q=.5; text/html; q=.2, text/*
* // Accept quality for application/json
*
* // $quality = 0.5
* $quality = $request->headers()->accepts_at_quality('application/json');
*
* // $quality_explicit = FALSE
* $quality_explicit = $request->headers()->accepts_at_quality('text/plain', TRUE);
*
* @param string $type
* @param boolean $explicit explicit check, excludes `*`
* @return mixed
* @since 3.2.0
*/
public function accepts_at_quality($type, $explicit = FALSE)
{
// Parse Accept header if required
if ($this->_accept_content === NULL)
{
if ($this->offsetExists('Accept'))
{
$accept = $this->offsetGet('Accept');
}
else
{
$accept = '*/*';
}
$this->_accept_content = HTTP_Header::parse_accept_header($accept);
}
// If not a real mime, try and find it in config
if (strpos($type, '/') === FALSE)
{
$mime = Kohana::$config->load('mimes.'.$type);
if ($mime === NULL)
return FALSE;
$quality = FALSE;
foreach ($mime as $_type)
{
$quality_check = $this->accepts_at_quality($_type, $explicit);
$quality = ($quality_check > $quality) ? $quality_check : $quality;
}
return $quality;
}
$parts = explode('/', $type, 2);
if (isset($this->_accept_content[$parts[0]][$parts[1]]))
{
return $this->_accept_content[$parts[0]][$parts[1]];
}
elseif ($explicit === TRUE)
{
return FALSE;
}
else
{
if (isset($this->_accept_content[$parts[0]]['*']))
{
return $this->_accept_content[$parts[0]]['*'];
}
elseif (isset($this->_accept_content['*']['*']))
{
return $this->_accept_content['*']['*'];
}
else
{
return FALSE;
}
}
}
/**
* Returns the preferred response content type based on the accept header
* quality settings. If items have the same quality value, the first item
* found in the array supplied as `$types` will be returned.
*
* // Get the preferred acceptable content type
* // Accept: text/html, application/json; q=.8, text/*
* $result = $header->preferred_accept(array(
* 'text/html'
* 'text/rtf',
* 'application/json'
* )); // $result = 'application/json'
*
* $result = $header->preferred_accept(array(
* 'text/rtf',
* 'application/xml'
* ), TRUE); // $result = FALSE (none matched explicitly)
*
*
* @param array $types the content types to examine
* @param boolean $explicit only allow explicit references, no wildcards
* @return string name of the preferred content type
* @since 3.2.0
*/
public function preferred_accept(array $types, $explicit = FALSE)
{
$preferred = FALSE;
$ceiling = 0;
foreach ($types as $type)
{
$quality = $this->accepts_at_quality($type, $explicit);
if ($quality > $ceiling)
{
$preferred = $type;
$ceiling = $quality;
}
}
return $preferred;
}
/**
* Returns the quality of the supplied `$charset` argument. This method
* will automatically parse the `Accept-Charset` header if present and
* return the associated resolved quality value.
*
* // Accept-Charset: utf-8, utf-16; q=.8, iso-8859-1; q=.5
* $quality = $header->accepts_charset_at_quality('utf-8');
* // $quality = (float) 1
*
* @param string $charset charset to examine
* @return float the quality of the charset
* @since 3.2.0
*/
public function accepts_charset_at_quality($charset)
{
if ($this->_accept_charset === NULL)
{
if ($this->offsetExists('Accept-Charset'))
{
$charset_header = strtolower($this->offsetGet('Accept-Charset'));
$this->_accept_charset = HTTP_Header::parse_charset_header($charset_header);
}
else
{
$this->_accept_charset = HTTP_Header::parse_charset_header(NULL);
}
}
$charset = strtolower($charset);
if (isset($this->_accept_charset[$charset]))
{
return $this->_accept_charset[$charset];
}
elseif (isset($this->_accept_charset['*']))
{
return $this->_accept_charset['*'];
}
elseif ($charset === 'iso-8859-1')
{
return (float) 1;
}
return (float) 0;
}
/**
* Returns the preferred charset from the supplied array `$charsets` based
* on the `Accept-Charset` header directive.
*
* // Accept-Charset: utf-8, utf-16; q=.8, iso-8859-1; q=.5
* $charset = $header->preferred_charset(array(
* 'utf-10', 'ascii', 'utf-16', 'utf-8'
* )); // $charset = 'utf-8'
*
* @param array $charsets charsets to test
* @return mixed preferred charset or `FALSE`
* @since 3.2.0
*/
public function preferred_charset(array $charsets)
{
$preferred = FALSE;
$ceiling = 0;
foreach ($charsets as $charset)
{
$quality = $this->accepts_charset_at_quality($charset);
if ($quality > $ceiling)
{
$preferred = $charset;
$ceiling = $quality;
}
}
return $preferred;
}
/**
* Returns the quality of the `$encoding` type passed to it. Encoding
* is usually compression such as `gzip`, but could be some other
* message encoding algorithm. This method allows explicit checks to be
* done ignoring wildcards.
*
* // Accept-Encoding: compress, gzip, *; q=.5
* $encoding = $header->accepts_encoding_at_quality('gzip');
* // $encoding = (float) 1.0s
*
* @param string $encoding encoding type to interrogate
* @param boolean $explicit explicit check, ignoring wildcards and `identity`
* @return float
* @since 3.2.0
*/
public function accepts_encoding_at_quality($encoding, $explicit = FALSE)
{
if ($this->_accept_encoding === NULL)
{
if ($this->offsetExists('Accept-Encoding'))
{
$encoding_header = $this->offsetGet('Accept-Encoding');
}
else
{
$encoding_header = NULL;
}
$this->_accept_encoding = HTTP_Header::parse_encoding_header($encoding_header);
}
// Normalize the encoding
$encoding = strtolower($encoding);
if (isset($this->_accept_encoding[$encoding]))
{
return $this->_accept_encoding[$encoding];
}
if ($explicit === FALSE)
{
if (isset($this->_accept_encoding['*']))
{
return $this->_accept_encoding['*'];
}
elseif ($encoding === 'identity')
{
return (float) HTTP_Header::DEFAULT_QUALITY;
}
}
return (float) 0;
}
/**
* Returns the preferred message encoding type based on quality, and can
* optionally ignore wildcard references. If two or more encodings have the
* same quality, the first listed in `$encodings` will be returned.
*
* // Accept-Encoding: compress, gzip, *; q.5
* $encoding = $header->preferred_encoding(array(
* 'gzip', 'bzip', 'blowfish'
* ));
* // $encoding = 'gzip';
*
* @param array $encodings encodings to test against
* @param boolean $explicit explicit check, if `TRUE` wildcards are excluded
* @return mixed
* @since 3.2.0
*/
public function preferred_encoding(array $encodings, $explicit = FALSE)
{
$ceiling = 0;
$preferred = FALSE;
foreach ($encodings as $encoding)
{
$quality = $this->accepts_encoding_at_quality($encoding, $explicit);
if ($quality > $ceiling)
{
$ceiling = $quality;
$preferred = $encoding;
}
}
return $preferred;
}
/**
* Returns the quality of `$language` supplied, optionally ignoring
* wildcards if `$explicit` is set to a non-`FALSE` value. If the quality
* is not found, `0.0` is returned.
*
* // Accept-Language: en-us, en-gb; q=.7, en; q=.5
* $lang = $header->accepts_language_at_quality('en-gb');
* // $lang = (float) 0.7
*
* $lang2 = $header->accepts_language_at_quality('en-au');
* // $lang2 = (float) 0.5
*
* $lang3 = $header->accepts_language_at_quality('en-au', TRUE);
* // $lang3 = (float) 0.0
*
* @param string $language language to interrogate
* @param boolean $explicit explicit interrogation, `TRUE` ignores wildcards
* @return float
* @since 3.2.0
*/
public function accepts_language_at_quality($language, $explicit = FALSE)
{
if ($this->_accept_language === NULL)
{
if ($this->offsetExists('Accept-Language'))
{
$language_header = strtolower($this->offsetGet('Accept-Language'));
}
else
{
$language_header = NULL;
}
$this->_accept_language = HTTP_Header::parse_language_header($language_header);
}
// Normalize the language
$language_parts = explode('-', strtolower($language), 2);
if (isset($this->_accept_language[$language_parts[0]]))
{
if (isset($language_parts[1]))
{
if (isset($this->_accept_language[$language_parts[0]][$language_parts[1]]))
{
return $this->_accept_language[$language_parts[0]][$language_parts[1]];
}
elseif ($explicit === FALSE AND isset($this->_accept_language[$language_parts[0]]['*']))
{
return $this->_accept_language[$language_parts[0]]['*'];
}
}
elseif (isset($this->_accept_language[$language_parts[0]]['*']))
{
return $this->_accept_language[$language_parts[0]]['*'];
}
}
if ($explicit === FALSE AND isset($this->_accept_language['*']))
{
return $this->_accept_language['*'];
}
return (float) 0;
}
/**
* Returns the preferred language from the supplied array `$languages` based
* on the `Accept-Language` header directive.
*
* // Accept-Language: en-us, en-gb; q=.7, en; q=.5
* $lang = $header->preferred_language(array(
* 'en-gb', 'en-au', 'fr', 'es'
* )); // $lang = 'en-gb'
*
* @param array $languages
* @param boolean $explicit
* @return mixed
* @since 3.2.0
*/
public function preferred_language(array $languages, $explicit = FALSE)
{
$ceiling = 0;
$preferred = FALSE;
foreach ($languages as $language)
{
$quality = $this->accepts_language_at_quality($language, $explicit);
if ($quality > $ceiling)
{
$ceiling = $quality;
$preferred = $language;
}
}
return $preferred;
}
/**
* Sends headers to the php processor, or supplied `$callback` argument.
* This method formats the headers correctly for output, re-instating their
* capitalization for transmission.
*
* [!!] if you supply a custom header handler via `$callback`, it is
* recommended that `$response` is returned
*
* @param HTTP_Response $response header to send
* @param boolean $replace replace existing value
* @param callback $callback optional callback to replace PHP header function
* @return mixed
* @since 3.2.0
*/
public function send_headers(HTTP_Response $response = NULL, $replace = FALSE, $callback = NULL)
{
if ($response === NULL)
{
// Default to the initial request message
$response = Request::initial()->response();
}
$protocol = $response->protocol();
$status = $response->status();
// Create the response header
$processed_headers = array($protocol.' '.$status.' '.Response::$messages[$status]);
// Get the headers array
$headers = $response->headers()->getArrayCopy();
foreach ($headers as $header => $value)
{
if (is_array($value))
{
$value = implode(', ', $value);
}
$processed_headers[] = Text::ucfirst($header).': '.$value;
}
if ( ! isset($headers['content-type']))
{
$processed_headers[] = 'Content-Type: '.Kohana::$content_type.'; charset='.Kohana::$charset;
}
if (Kohana::$expose AND ! isset($headers['x-powered-by']))
{
$processed_headers[] = 'X-Powered-By: '.Kohana::version();
}
// Get the cookies and apply
if ($cookies = $response->cookie())
{
$processed_headers['Set-Cookie'] = $cookies;
}
if (is_callable($callback))
{
// Use the callback method to set header
return call_user_func($callback, $response, $processed_headers, $replace);
}
else
{
$this->_send_headers_to_php($processed_headers, $replace);
return $response;
}
}
/**
* Sends the supplied headers to the PHP output buffer. If cookies
* are included in the message they will be handled appropriately.
*
* @param array $headers headers to send to php
* @param boolean $replace replace existing headers
* @return self
* @since 3.2.0
*/
protected function _send_headers_to_php(array $headers, $replace)
{
// If the headers have been sent, get out
if (headers_sent())
return $this;
foreach ($headers as $key => $line)
{
if ($key == 'Set-Cookie' AND is_array($line))
{
// Send cookies
foreach ($line as $name => $value)
{
Cookie::set($name, $value['value'], $value['expiration']);
}
continue;
}
header($line, $replace);
}
return $this;
}
} // End Kohana_HTTP_Header

View File

@@ -0,0 +1,56 @@
<?php defined('SYSPATH') OR die('No direct script access.');
/**
* The HTTP Interaction interface providing the core HTTP methods that
* should be implemented by any HTTP request or response class.
*
* @package Kohana
* @category HTTP
* @author Kohana Team
* @since 3.1.0
* @copyright (c) 2008-2012 Kohana Team
* @license http://kohanaphp.com/license
*/
interface Kohana_HTTP_Message {
/**
* Gets or sets the HTTP protocol. The standard protocol to use
* is `HTTP/1.1`.
*
* @param string $protocol Protocol to set to the request/response
* @return mixed
*/
public function protocol($protocol = NULL);
/**
* Gets or sets HTTP headers to the request or response. 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);
/**
* Gets or sets the HTTP body to the request or response. The body is
* included after the header, separated by a single empty new line.
*
* @param string $content Content to set to the object
* @return string
* @return void
*/
public function body($content = NULL);
/**
* Renders the HTTP_Interaction to a string, producing
*
* - Protocol
* - Headers
* - Body
*
* @return string
*/
public function render();
}

View File

@@ -0,0 +1,64 @@
<?php defined('SYSPATH') OR die('No direct script access.');
/**
* A HTTP Request specific interface that adds the methods required
* by HTTP requests. Over and above [Kohana_HTTP_Interaction], this
* interface provides method, uri, get and post methods.
*
* @package Kohana
* @category HTTP
* @author Kohana Team
* @since 3.1.0
* @copyright (c) 2008-2012 Kohana Team
* @license http://kohanaphp.com/license
*/
interface Kohana_HTTP_Request extends HTTP_Message {
// HTTP Methods
const GET = 'GET';
const POST = 'POST';
const PUT = 'PUT';
const DELETE = 'DELETE';
const HEAD = 'HEAD';
const OPTIONS = 'OPTIONS';
const TRACE = 'TRACE';
const CONNECT = 'CONNECT';
/**
* 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);
/**
* Gets the URI of this request, optionally allows setting
* of [Route] specific parameters during the URI generation.
* If no parameters are passed, the request will use the
* default values defined in the Route.
*
* @param array $params Optional parameters to include in uri generation
* @return string
*/
public function uri();
/**
* 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
*/
public function query($key = NULL, $value = NULL);
/**
* 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
*/
public function post($key = NULL, $value = NULL);
}

View File

@@ -0,0 +1,31 @@
<?php defined('SYSPATH') OR die('No direct script access.');
/**
* A HTTP Reponse specific interface that adds the methods required
* by HTTP responses. Over and above [Kohana_HTTP_Interaction], this
* interface provides status.
*
* @package Kohana
* @category HTTP
* @author Kohana Team
* @since 3.1.0
* @copyright (c) 2008-2012 Kohana Team
* @license http://kohanaphp.com/license
*/
interface Kohana_HTTP_Response extends HTTP_Message {
/**
* Sets or gets the HTTP status from this response.
*
* // Set the HTTP status to 404 Not Found
* $response = Response::factory()
* ->status(404);
*
* // Get the current status
* $status = $response->status();
*
* @param integer $code Status to set to this response
* @return mixed
*/
public function status($code = NULL);
}