Implements: IteratorAggregate | Traversable | ArrayAccess | Serializable | Countable
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.
Class declared in SYSPATH/classes/http/header.php on line 3.
integer 1
integer 1
integer 2
array
$_accept_charsetlink to thisAccept-Charset: parsed header
array
$_accept_contentlink to thisAccept: (content) types
array
$_accept_encodinglink to thisAccept-Encoding: parsed header
array
$_accept_languagelink to thisAccept-Language: parsed header
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'
=>
'...'
));
mixed
$input
= array(0) - Input arrayint
$flags
= NULL - Flagsstring
$iterator_class
= string(13) "ArrayIterator" - The iterator class to use
public
function
__construct(
array
$input
=
array
(),
$flags
= NULL,
$iterator_class
=
'ArrayIterator'
)
{
/**
*
* 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();
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"
;
}
Parses an Accept(-*) header and detects the quality
array
$parts
required - Accept header partsarray
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
;
}
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);
string
$type
required - $typeboolean
$explicit
= bool FALSE - Explicit check, excludes `*`mixed
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 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
string
$charset
required - Charset to examinefloat
- The quality of the charset
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 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
string
$encoding
required - Encoding type to interrogateboolean
$explicit
= bool FALSE - Explicit check, ignoring wildcards and `identity`float
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 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
string
$language
required - Language to interrogateboolean
$explicit
= bool FALSE - Explicit interrogation, `TRUE` ignores wildcardsfloat
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;
}
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
);
array
$cache_control
required - Cache-Control to render to stringstring
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
);
}
Overloads the ArrayObject::exchangeArray()
method to ensure that
all keys are changed to lowercase.
mixed
$input
required - $inputarray
public
function
exchangeArray(
$input
)
{
/**
*
* HTTP header declarations should be treated as case-insensitive
*/
$input
=
array_change_key_case
((
array
)
$input
, CASE_LOWER);
return
parent::exchangeArray(
$input
);
}
Overloads the ArrayObject::offsetExists()
method to ensure keys
are lowercase.
string
$index
required - $indexboolean
public
function
offsetExists(
$index
)
{
return
parent::offsetExists(
strtolower
(
$index
));
}
Overload the ArrayObject::offsetGet()
method to ensure that all
keys passed to it are formatted correctly for this object.
string
$index
required - Index to retrievemixed
public
function
offsetGet(
$index
)
{
return
parent::offsetGet(
strtolower
(
$index
));
}
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.
mixed
$index
required - Index to set `$newval` tomixed
$newval
required - New value to setboolean
$replace
= bool TRUE - Replace existing valuevoid
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::offsetUnset()
method to ensure keys
are lowercase.
string
$index
required - $indexvoid
public
function
offsetUnset(
$index
)
{
return
parent::offsetUnset(
strtolower
(
$index
));
}
Parses the accept header to provide the correct quality values for each supplied accept type.
string
$accepts
= NULL - Accept content header string to parsearray
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 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'
];
}
array
$cache_control
required - Array of headersmixed
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
;
}
Parses the Accept-Charset:
HTTP header and returns an array containing
the charset and associated quality.
string
$charset
= NULL - Charset string to parsearray
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.
string
$encoding
= NULL - Charset string to parsearray
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 a HTTP Message header line and applies it to this HTTP_Header
$header
=
$response
->headers();
$header
->parse_header_string(NULL,
'content-type: application/json'
);
resource
$resource
required - The resource (required by Curl API)string
$header_line
required - The line from the header to parseint
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
);
}
Parses the Accept-Language:
HTTP header and returns an array containing
the languages and associated quality.
string
$language
= NULL - Charset string to parsearray
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
;
}
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)
array
$types
required - The content types to examineboolean
$explicit
= bool FALSE - Only allow explicit references, no wildcardsstring
- Name of the preferred content type
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 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'
array
$charsets
required - Charsets to testmixed
- Preferred charset or `FALSE`
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 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';
array
$encodings
required - Encodings to test againstboolean
$explicit
= bool FALSE - Explicit check, if `TRUE` wildcards are excludedmixed
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 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'
array
$languages
required - $languagesboolean
$explicit
= bool FALSE - $explicitmixed
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
HTTP_Response
$response
= NULL - Header to sendboolean
$replace
= bool FALSE - Replace existing valuecallback
$callback
= NULL - Optional callback to replace PHP header functionmixed
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 Framework '
.
Kohana::VERSION.
' ('
.Kohana::CODENAME.
')'
;
}
// 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.
array
$headers
required - Headers to send to phpboolean
$replace
required - Replace existing headersself
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
;
}