application\/xhtml+xml<\/code> and<\/p>\ntext\/html;q=0.9<\/code>. The part after the semi-colon, q=0.9<\/code>, is called a quality value<\/dfn> and is a value between 0 and 1, inclusive, with up to three decimal places. The higher the quality value, the more the user agent prefers that media type. If no quality value is specified for a particular media type, it means q=1.0<\/code>. The example thus shows that Mozilla prefers application\/xhtml+xml<\/code> to text\/html<\/code>.<\/p>\nThe usual meaning of content negotiation is that the HTTP<\/abbr> server itself decides which media type the user agent prefers, and then automatically chooses between a number of different documents. Normally the file suffix is used to associate to different media types, so the server might choose between<\/p>\nindex.xhtml<\/samp> and index.html<\/samp>.<\/p>\nThis article describes another type of content negotiation; one that is performed through a server-side script. Most web hosts offer some kind of server-side scripting, usually PHP<\/abbr> or ASP<\/acronym>. Our example uses PHP<\/abbr>, since it is available for more platforms and is open source, while ASP<\/acronym> is Microsoft-specific. We don’t delve into the finer details here, but presume that you are sufficiently familiar with PHP<\/abbr>.<\/p>\nTo round off this explanation of what content negotiation means, we want to emphasise that it’s not merely an issue of deciding which media type<\/em> to send. When you have chosen a media type, you should also serve the document with a content<\/em> that corresponds to the chosen media type. You either serve XHTML<\/abbr> as application\/xhtml+xml<\/code>, or you serve HTML<\/abbr> as text\/html<\/code>.<\/p>\nAbout the Examples<\/h2>\n
The code samples in this article are written for PHP<\/abbr> 4.1.0 or higher. For older versions you need to replace $_SERVER<\/code> with $HTTP_SERVER_VARS<\/code>. If the code is executed in a function, you then need to declare the array as a global (global $HTTP_SERVER_VARS;<\/code>).<\/p>\nThis article presumes that the document’s content is marked up as XHTML<\/abbr> 1.1, and that it doesn’t contain anything that cannot be converted into HTML<\/abbr> 4.01 Strict, for instance element from other XML<\/abbr> namespaces, or CDATA<\/code> sections.<\/p>\nParsing the Accept Header<\/h2>\n
First of all we need to find out whether or not the user agent supports the application\/xhtml+xml<\/code> media type and, if so, whether it prefers that to text\/html<\/code>.<\/p>\n\n$xhtml = false;<\/code><\/li>\nif (preg_match('\/application\\\/xhtml\\+xml(;q=(\\d+\\.\\d+))?\/i', $_SERVER['HTTP_ACCEPT'], $matches)) {<\/code><\/li>\n $xhtmlQ = isset($matches[2]) ? $matches[2] : 1;<\/code><\/li>\n if (preg_match('\/text\\\/html(;q=(\\d+\\.\\d+))?\/i', $_SERVER['HTTP_ACCEPT'], $matches)) {<\/code><\/li>\n $htmlQ = isset($matches[2]) ? $matches[2] : 1;<\/code><\/li>\n $xhtml = ($xhtmlQ >= $htmlQ);<\/code><\/li>\n } else {<\/code><\/li>\n $xhtml = true;<\/code><\/li>\n }<\/code><\/li>\n}<\/code><\/li>\n<\/ol>\nThe $xhtml<\/code> variable indicates whether or not we will serve the document as XHTML<\/abbr>. The initial value is false<\/code>, since many older browsers lack support for XHTML<\/abbr>.<\/p>\nOn line 2 we check whether the Accept<\/code> header contains
\napplication\/xhtml+xml<\/code> plus an optional quality value. This regular expression isn’t 100% fool-proof, since it doesn’t limit the value range to [0,1], nor does it limit the number of decimal places to 3. For all intents and purposes, however, it doesn’t matter.<\/p>\nOn line 3 we extract the quality value, if present. If not, we set the quality value for application\/xhtml+xml<\/code> to 1.<\/p>\nOn lines 4 and 5 we perform the corresponding check for text\/html<\/code>. Line 6 compares the quality values and sets $xhtml=true<\/code> if the user agent prefers application\/xhtml+xml<\/code> to text\/html<\/code>. Line 8 handles the case of a user agent that specifies application\/xhtml+xml<\/code> in the<\/p>\nAccept<\/code> header, but not text\/html<\/code>.<\/p>\nAfter these lines of code we thus have a Boolean variable, $xhtml<\/code>, which indicates whether the document will be served as XHTML<\/abbr>.<\/p>\nPrepare HTML<\/abbr> Conversion<\/h2>\nIf the user agent doesn’t support XHTML<\/abbr>, or if it prefers HTML<\/abbr>, we have to convert the document’s content from XHTML<\/abbr> 1.1 to HTML<\/abbr> 4.01. We do this with a simple function:<\/p>\n\nfunction xml2html($buffer)<\/code><\/li>\n{<\/code><\/li>\n $xml = array('\/>', 'xml:lang=');<\/code><\/li>\n $html = array('>', 'lang=');<\/code><\/li>\n return str_replace($xml, $html, $buffer);<\/code><\/li>\n}<\/code><\/li>\n<\/ol>\nLines 3 and 4 declare two arrays, where the elements in the $xml<\/code> array will be replaced by the corresponding element in the $html<\/code> array.<\/p>\nOn line 5 each occurrence of \/><\/code> is replaced by ><\/code> in the $buffer<\/code> string. At the same time, each occurrence of xml:lang<\/code> is replaced by lang<\/code>.<\/p>\nAnd Finally\u00e2\u20ac\u00a6<\/h2>\n
Only a few details now remain. If the $xhtml<\/code> variable is true, we need to write the document type declaration for XHTML<\/abbr> 1.1 and a <html><\/code> element with the proper XML<\/abbr> namespace. Most likely we also want to start with an XML<\/abbr><\/p>\ndeclaration, and link to our style sheets through processing instructions.<\/p>\n
If the user agent doesn’t want XHTML<\/abbr>, we need to write a document type declaration for HTML<\/abbr> 4.01 Strict and a <html><\/code> element without an XML<\/abbr> namespace. Style sheets should be linked through ordinary <link><\/code> elements (or be imported in a <style><\/code> element). Furthermore, we need to instruct the PHP<\/abbr> interpreter to buffer all output to the response stream, and to call our conversion function on the result before sending it back to the user agent.<\/p>\nBefore we write anything at all, however, we must send a couple of HTTP<\/abbr> headers: one that says which media type we use, and one that informs proxy servers that content negotiation has taken place so that they can consider that in their caching algorithms.<\/p>\n\nif ($xhtml) {<\/code><\/li>\n header('Content-Type: application\/xhtml+xml; charset=utf-8');<\/code><\/li>\n header('Vary: Accept');<\/code><\/li>\n echo '<?xml version=\"1.0\" encoding=\"utf-8\"?>', \"\\n\";<\/code><\/li>\n echo '<?xml-stylesheet type=\"text\/css\" xhref=\"\/css\/screen.css\" media=\"screen\"?>', \"\\n\";<\/code><\/li>\n echo '<?xml-stylesheet type=\"text\/css\" xhref=\"\/css\/print.css\" media=\"print\"?>', \"\\n\";<\/code><\/li>\n echo '<!DOCTYPE html PUBLIC \"-\/\/W3C\/\/DTD XHTML 1.1\/\/EN\" \"http:\/\/www.w3.org\/TR\/xhtml11\/DTD\/xhtml11.dtd\">', \"\\n\";<\/code><\/li>\n echo '<html xmlns=\"http:\/\/www.w3.org\/1999\/xhtml\" xml:lang=\"en\">', \"\\n\";<\/code><\/li>\n} else {<\/code><\/li>\n header('Content-Type: text\/html; charset=utf-8');<\/code><\/li>\n header('Vary: Accept');<\/code><\/li>\n ob_start('xml2html');<\/code><\/li>\n echo '<!DOCTYPE html PUBLIC \"-\/\/W3C\/\/DTD HTML 4.01\/\/EN\" \"http:\/\/www.w3.org\/TR\/html4\/strict.dtd\">', \"\\n\";<\/code><\/li>\n echo '<html lang=\"en\">', \"\\n\";<\/code><\/li>\n}<\/code><\/li>\n<\/ol>\nDon’t forget to link to the style sheets in the <head><\/code> if the document is served as HTML<\/abbr>.<\/p>\nThere is a blatant shortcoming in the example shown in this article: the W3C validator<\/a>. It doesn’t send application\/xhtml+xml<\/code> in its Accept<\/code><\/p>\nheader, so it’s impossible to validate the document as XHTML<\/abbr>. It is trivial to let a query parameter control the choice of media type, but that is left as an exercise for the reader.<\/p><\/blockquote>\n(note: We are aware of some possible copyright issues, and we have attempted to contact the original owner to get permission to repost it verbatim here. At the time of this post, no replies had been received and we can only assume the original source is no longer on line. If you are the original source and would like this post removed please contact us and we will take this post down immediately)<\/p>\n