tohokuaikiのチラシの裏

技術的ネタとか。

PHPのDOMDocumentが内部エンコードで何をしているか

要するに、次のようなスクリプトを書いたら

<?php
$html = file_get_contents('sample.html');
$html = mb_convert_encoding($html, 'HTML-ENTITIES', 'UTF-8');
$dom = new DOMDocument();
@$dom->loadHTML($html);
$html = $dom->saveHTML();
file_put_contents('sample_ddtest.html', $html);
exit;

sample_ddtest.htmlには、HTMLエンティティで記述された本文のHTMLができてるかなと思ったのですが、そんなことはなく普通にUTF8の人間が読めるHTMLがあったのでびっくりと。


ということは、DOMDocumentはloadとsaveの間でmb_convert_encoding($html, 'UTF-8', 'HTML-ENTITIES');が勝手に行われているわけです。

勝手にというか、おそらくですが、loadHTMLで読み込むときに自動的にmb_convert_encoding($html, 'HTML-ENTITIES', 'UTF-8');してsaveHTMLするときに自動的にmb_convert_encoding($html, 'UTF-8', 'HTML-ENTITIES');しているのではないか疑惑。

しかし、loadHTML時のはやってないかもしれません。というのは、実はloadHTMLしたものの一部を別のHTMLのDOMDocumentにcloneしてInsertしたのですが、文字化けしてしまったのです。

この人は私の遭遇した文字化けに似ている感じがします。
PHP DomDocument saveHTML not encoding Japanese properly

それでは困ると思い、mb_convert_encoding($html, 'HTML-ENTITIES', 'UTF-8');を最初にかけたわけです。それをしたので、saveHTML()した時に戻さないとなと思ったら、なんと戻さなくてもよかったのでびっくりということです。


DOMDocumentのソースコード読めば分かるのかなぁと思いつつ、mbstringに頼るような挙動はしてないだろうし・・・・。謎です。> ソース読めよって感じですが。

更なる疑問

もしかして、更に<a>のhrefアトリビュートはURLENCODEを掛けている可能性があります。

HTML-ENTITY化したHTMLをDOMDocumentに通して戻すと、

<a href="https://support.google.com/androidmarket/developer/bin/answer.py?hl=ja&#65286;&#65345;&#65357;&#65360;&#65307;answer=113466&#65286;&#65345;&#65357;&#65360;&#65307;topic=2365624&#65286;&#65345;&#65357;&#65360;&#65307;ctx=topic">https://support.google.com/androidmarket/developer/bin/answer.py?hl=ja&#65286;&#65345;&#65357;&#65360;&#65307;answer=113466&#65286;&#65345;&#65357;&#65360;&#65307;topic=2365624&#65286;&#65345;&#65357;&#65360;&#65307;ctx=topic</a>

というのが、

<a href="https://support.google.com/androidmarket/developer/bin/answer.py?hl=ja%EF%BC%86%EF%BD%81%EF%BD%8D%EF%BD%90%EF%BC%9Banswer=113466%EF%BC%86%EF%BD%81%EF%BD%8D%EF%BD%90%EF%BC%9Btopic=2365624%EF%BC%86%EF%BD%81%EF%BD%8D%EF%BD%90%EF%BC%9Bctx=topic">https://support.google.com/androidmarket/developer/bin/answer.py?hl=ja&amp;answer=113466&amp;topic=2365624&amp;ctx=topic</a>

となって帰ってくるのでおかしいなぁと。
上記で注目すべきなのは、HREFでして
&#65286;&#65345;&#65357;&#65360;&#65307;

%EF%BC%86%EF%BD%81%EF%BD%8D%EF%BD%90%EF%BC%9B(urldecodeで &amp; となる)
となってるんですね。&が&amp;なことは意図してやってるので突っ込みなしで。

URLENCODEしてるのはHREFだけじゃなくて

URLを対象にしうるものだけのようです。imgのsrcとかは変換されます。altに入れても変わらないので。