Another HTML-lintでXHTML1.1をチェックするとHTML内のmeta
要素とHTTPレスポンスヘッダ内のContent-typeフィールドの値が「text/html」だった場合に減点されるようになった。これはXHTMLのメディアタイプに関するW3Cのノートに基づいた判定で、本来ならXHTML1.1では「application/xhtml+xml」が最も適した値とされている。
以前Mozillaの仕様とIE6の仕様(「application/xhtml+xml」だとダウンロードダイアログが表示されてしまう)を両立するためにUAが送信してきたAcceptフィールドに合わせて変化させる方法を紹介したが、HTML-lintはAcceptフィールドに「application/xhtml+xml」を含めていないため、困ったことにXHTML1.1の文章が「text/html」で送信されてしまう。
Content-typeの条件分岐をもう少し高度にしても対応できるがXHTML1.0なら「text/html」で送信してもよいことになっているので、Acceptフィールドに「application/xhtml+xml」が含まれていない場合はXSLTでXHTML1.0に変換してから送信すればよい。XHTMLがXMLである利点を使わない手はない。
XHTML1.1のXHTML1.0からの変更点は以下の通りだ。マイナーバージョンアップなのでそれほど変更点は多くない。
lang
属性が廃止されxml:lang
属性に統一された。a
要素とmap
要素からname
属性が廃止されid
属性に統一された。ruby
系の要素が追加された。以上よりXHTML1.1をXHTML1.0へ変換する際に必要なことは
xml:lang
属性と同じ値のlang
属性の追加(XHTML1.0では対に使うことが促されている)a
及びmap
要素にid
属性と同じ値のname
属性の追加(同上)ruby
系要素を削除である。まずはPHP側のコードを書く。
$ha = explode(",", $_SERVER["HTTP_ACCEPT"]);
if (in_array("application/xhtml+xml", $ha)) {
$version_xhtml = 1.1;
} else {
$version_xhtml = 1.0;
}
単純にAcceptフィールドを取得してカンマで分割し「application/xhtml+xml」が含まれていれば使用するXHTMLのバージョンを1.1に、それ以外の場合は1.0として変数に格納している。Content-typeの送信やXSL変換の条件分岐にはこの数字を使うと後々XHTMLがバージョンアップを重ねたときも便利だと思われる。
xslt_process()
関数を呼び出すときは5番目の引数(配列)にバージョンを格納しておくとXSLの中で<xsl:param name="version_xhtml"/>
で呼び出せる。
$info_xml["version_xhtml"] = $version_xhtml;
$handle_xslt = xslt_create();
$data_proceeded = xslt_process($handle_xslt, "foo.xml", "bar.xsl", null, null, $info_xml);
xslt_free($handle_xslt);
呼び出されるbar.xslには以下のようなコードを追加しておく。これだけでは動かないので注意。
<xsl:param name="version_xhtml"/>
<xsl:template match="a/@id|map/@id">
<xsl:attribute name="id"><xsl:value-of select="."/></xsl:attribute>
<xsl:if test="$version_xhtml = 1.0">
<xsl:attribute name="name"><xsl:value-of select="."/></xsl:attribute>
</xsl:if>
</xsl:template>
<xsl:template match="@xml:lang">
<xsl:attribute name="xml:lang"><xsl:value-of select="."/></xsl:attribute>
<xsl:if test="$version_xhtml = 1.0">
<xsl:attribute name="lang"><xsl:value-of select="."/></xsl:attribute>
</xsl:if>
</xsl:template>
<xsl:template match="ruby">
<xsl:choose>
<xsl:when test="$version_xhtml = 1.0">
<xsl:value-of select="."/>
</xsl:when>
<xsl:otherwise>
<xsl:copy>
<xsl:apply-templates select="@*|node()"/>
</xsl:copy>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
一応DOCTYPE宣言とContent-typeフィールドの送信方法も書いておく。
if ($version_xhtml == 1.1) {
$data_proceeded = '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">' . "\n" . $data_proceeded;
} elseif ($version_xhtml == 1.0) {
$data_proceeded = '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">' . "\n" . $data_proceeded;
}
if ($version_xhtml == 1.1) {
header("Content-Type: application/xhtml+xml; charset=UTF-8");
} elseif ($version_xhtml == 1.0) {
header("Content-Type: text/html; charset=UTF-8");
}
DOCTYPEはバージョンに合わせて単純に先頭に追加するだけなのでXSLTで出力しても良いかもしれない。
元々PHP+XML+XSLTという組み合わせでサイトを構築していた場合は数行のコードを追加すればいいだけなのでこういった処理も比較的簡単だ。