I'm trying to build a standard compliant website framework which serves XHTML 1.1 as application/xhtml+xml or HTML 4.01 as text/html depending on the browser support. Currently it just looks for "application/xhtml+xml" anywhere in the accept header, and uses that if it exists, but that's not flexible - text/html might have a higher score. Also, it will become more complex when other formats (WAP, SVG, XForms etc.) are added. So, does anyone know of a tried and tested piece of PHP code to select, from a string array given by the server, either the one best supported by the client or an ordered list based on the client score?
http://www.dev-archive.net/articles/xhtml.html#content-negotiation is written in Perl, but it is clearly laid out and just consists of some if/else and regex. Porting it to PHP should be trivial.
Little snippet from my library:
function getBestSupportedMimeType($mimeTypes = null) {
// Values will be stored in this array
$AcceptTypes = Array ();
// Accept header is case insensitive, and whitespace isn’t important
$accept = strtolower(str_replace(' ', '', $_SERVER['HTTP_ACCEPT']));
// divide it into parts in the place of a ","
$accept = explode(',', $accept);
foreach ($accept as $a) {
// the default quality is 1.
$q = 1;
// check if there is a different quality
if (strpos($a, ';q=')) {
// divide "mime/type;q=X" into two parts: "mime/type" i "X"
list($a, $q) = explode(';q=', $a);
}
// mime-type $a is accepted with the quality $q
// WARNING: $q == 0 means, that mime-type isn’t supported!
$AcceptTypes[$a] = $q;
}
arsort($AcceptTypes);
// if no parameter was passed, just return parsed data
if (!$mimeTypes) return $AcceptTypes;
$mimeTypes = array_map('strtolower', (array)$mimeTypes);
// let’s check our supported types:
foreach ($AcceptTypes as $mime => $q) {
if ($q && in_array($mime, $mimeTypes)) return $mime;
}
// no mime-type found
return null;
}
example usage:
$mime = getBestSupportedMimeType(Array ('application/xhtml+xml', 'text/html'));
Pear::HTTP 1.4.1 has a method string negotiateMimeType( array $supported, string $default)
<?php
require 'HTTP.php';
foreach(
array(
'text/*;q=0.3, text/html;q=0.7, text/html;level=1, text/html;level=2;q=0.4, */*;q=0.5',
'text/*;q=0.3, text/html;q=0.8, application/xhtml+xml;q=0.7, */*;q=0.2',
'text/*;q=0.3, text/html;q=0.7, */*;q=0.8',
'text/*, application/xhtml+xml',
'text/html, application/xhtml+xml'
) as $testheader) {
$_SERVER['HTTP_ACCEPT'] = $testheader;
$http = new HTTP;
echo $testheader, ' -> ',
$http->negotiateMimeType( array('application/xhtml+xml', 'text/html'), 'application/xhtml+xml'),
"\n";
}
prints
text/*;q=0.3, text/html;q=0.7, text/html;level=1, text/html;level=2;q=0.4, /;q=0.5 -> application/xhtml+xml text/*;q=0.3, text/html;q=0.8, application/xhtml+xml;q=0.7, */*;q=0.2 -> text/html text/*;q=0.3, text/html;q=0.7, */*;q=0.8 -> application/xhtml+xml text/*, application/xhtml+xml -> application/xhtml+xml text/html, application/xhtml+xml -> text/html
edit: this might not be so good after all...
My firefox sends Accept: text/html,application/xhtml+xml,application/xml;q=0.9,/;q=0.8
text/html and application/xhtml+xml have q=1.0 but PEAR::HTTP (afaik) doesn't let you chose which one you prefer, it returns text/html no matter what you pass as $supported. This may or may not be sufficient for you. see my other answer(s).
You can leverage apache's mod_negotiation module. This way you can use the full range of negotiation capabilities the module offers, including your own preferences for the content type (e,g, "I really want to deliver application/xhtml+xml, unless the client very much prefers something else"). basic solution:
- create a .htaccess file with
AddHandler type-map .var
as contents - create a file foo.var with
URI: foo
as contents
URI: foo.php/html Content-type: text/html; qs=0.7
URI: foo.php/xhtml Content-type: application/xhtml+xml; qs=0.8 - create a file foo.php with
<?php echo 'selected type: ', substr($_SERVER['PATH_INFO'], 1);
as contents. - request http://localhost/whatever/foo.var
For this to work you need mod_negotiation enabled, the appropriate AllowOverride privileges for AddHandler and AcceptPathInfo not being disabled for $_SERVER['PATH_INFO'].
With my Firefox sending "Accept: text/html,application/xhtml+xml,application/xml;q=0.9,/;q=0.8" and the example .var map the result is "selected type: xhtml".
You can use other "tweaks" to get rid of PATH_INFO or the need to request foo.var, but the basic concept is: let mod_negotiation redirect the request to your php script in a way that the script can "read" the selected content-type.
So, does anyone know of a tried and tested piece of PHP code to selectIt's not a pure php solution but I'd say mod_negotiation has been tried and tested ;-)