DOMDocument->loadHTML()でパースすると文字化けする

大体HTMLに原因があるのでそこを見ればよい。

  • 原因1: CP932なのにmeta charset=Shift_JISになっている
  • 原因2: meta charsetは正しいが、それより前にマルチバイト文字がある
  • 原因3: そもそもmeta charsetがない

ブラウザが「いい感じ」に処理してくれるのもダメHTMLがいまだに駆逐されない原因の一つじゃないだろうか。

正しく処理するには、HTMLの頭に正しいmeta charset指定を補うのが一番速いだろうと思う。
ただ(3)のmeta charsetがない場合、mb_detect_encoding()で見当をつけるにも言語を絞り込まないと限界があるので、結構難しい。

ちなみにコンストラクタで文字コードを指定できるが、HTML処理では全く使われない。

$doc = new DOMDocument(null, 'UTF-8');

$doc->loadHTMLFile() の場合PHPはlibxmlの htmlCreateFileParserCtxt()エンコーディングを渡しておらず、$doc->loadHTML() の場合 htmlCreateMemoryParserCtxt() にはエンコーディングを指定できないようになっている。

$doc->saveHTML() で出力する

パースしたHTMLを変形して出力することもあると思うが、libxml2 2.9.8 時点では <meta charset="..."> で指定していると保存するべきエンコーディングを発見できず、出力がHTMLエンティティだらけになる。
そのため<meta http-equiv="Content-Type" content="text/html; charset=..."> で指定する必要がある。
そもそもHTML4のパースと言っているので、HTML5の記法には対応していないということだろうか。読み込みでは認識しているようにも見えるが深く追っていない。