Back home and almost caught up on email…
One of the many things on my to-do list for Grand Rapids WiFi is to switch from the current system of having separate URLs for the RDF representations of resources to something based on content negotiation.
In order to do that using PHP I needed a couple of functions to extract the relevant headers. A quick web search didn’t turn up any appropriate libraries, so I threw together a couple of functions that should cover everything. For those not familiar with content-negotiation it works by having a user agent (web browser, newsreader, etc.) add an “Accept:” header specifying what types of content it can manage and which it prefers. For example, Firefox might send something like:
Accept: text/xml, application/xml, application/xhtml+xml, text/html;q=0.9,
text/plain;q=0.8, image/png, image/jpeg, image/gif;q=0.2, */*;q=0.1
The ‘q’ value is a measure of preference, and where none is specified we can assume a value of 1.0. So this browser prefers XML/XHTML over plain HTML, and HTML over plain text, but will accept anything (*/*).
So without more ado, the code:
/*
* To be used with array_map. For a given content-type string
* from an Accept: header this returns an array with the
* content-type name and the preference level associated with
* it.
*
* @author James Stewart <james@jystewart.net>
* @version 0.1
* @license http://creativecommons.org/licenses/by-sa/2.0/ Creative Commons Attribution ShareALike 2.0
* @param string $type
* @return array types with associated weighting
*/
function getValue($type)
{
if (strstr($type, ";q=")) {
$values = explode(";q=", $type);
} else {
$values = array($type, 1.0);
}
return $values;
}
/*
* Given an HTTP Accept: header and an array of options, this function
* returns an array of the overlap, ordered by the level of preference
* sorted in order of descending preference.
*
* @author James Stewart <james@jystewart.net>
* @version 0.1
* @license http://creativecommons.org/licenses/by-sa/2.0/ Creative Commons Attribution ShareALike 2.0
* @param string $header
* @param array $options
*/
function parseAccept($header, $options)
{
/* Explode types into an array */
$types = explode(",", $header);
$types = array_map("getValue", $types);
/* Select only the requested options and sort by preference */
$accepted = array();
$accepted_type = array();
$accepted_val = array();
foreach ($types as $type) {
if (in_array($type[0], $options)) {
array_push($accepted, $type);
array_push($accepted_type, $type[0]);
array_push($accepted_val, $type[1]);
}
}
array_multisort($accepted_val, SORT_DESC, $accepted_type, SORT_ASC, $accepted);
return $accepted;
}
Update (03/09/07): Removed a space in the explode call. See comments for more details.