XHTML 1.1
Pages on this site used to be written to conform to both XHTML 1.0 and XHTML 1.1. The specifications for the two different versions make this very easy, as there are very few changes from 1.0 to 1.1. See the improvements section below to read about my current solution.
However, while it is legal to serve so-called Appendix C compliant XHTML 1.0 as media type text/html, the W3C states that authors should not serve 1.1 as text/html.
The complication is that not all user agents (browsers) are able to work with documents of the recommended application/xhtml+xml media type. Microsoft’s Internet Explorer prompts the user to download the document instead of viewing it!
Simple solution
A solution is to use the HTTP Accept header which every user agent sends to detail what types it can use. This can be achieved with a small piece of PHP code that looks for application/xhtml+xml in the header, and alters the Content-Type output header and document DOCTYPE to match.
The code
This bit of PHP code should be in a separate file, referenced by a one-liner at the top of each page:
if ( stristr($_SERVER["HTTP_ACCEPT"],"application/xhtml+xml")) {
header("Content-Type: application/xhtml+xml");
printf("<?xml version=\"1.0\" encoding=\"iso-8859-1\"?>");
printf("<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.1//EN\"
\"http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd\">");
}
else {
header("Content-Type: text/html");
printf("<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Strict//EN\"
\"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd\">");
}
?>
…referenced by this line at the top of each file:
Improvements
I have been through a few iterations of improvements since this page was first written. The current solution is detailed here.
Using Multiviews
The solution presented above is rather simplistic, as it does not allow for varying q values in the Accept header, nor wildcards like application/*.
An improved solution uses Apache 2.x’s MultiViews content negotiation. Specify your files without extension, and let MultiViews do the hard work of parsing the Accept header properly, Then modify your document depending on its choice of file. Read my in-depth description of this technique.
It is possible to use PHP functions to dynamically generate code that must differ between the two versions. To close an empty tag (the break tag in this example), you could write:
and provide an emtag() function that supplies the required slash as appropriate, to give <br> for HTML and <br /> for XHTML. Similar functions would need to be written for the lang attribute; and for choosing between id and name for anchors.