PHPのDOMDocumentを利用すると、HTMLやXMLのドキュメントに対して検索したり、アクセスしたりできるかと思います。
この中の文字列からHTMLを読み込むときに使用するloadHTML
を使っていて、問題が発生した時の原因と解決策のメモを書いた記事になります。
エラー発生
まずは、ドキュメントにも書いてあるサンプルプログラムから以下のようにシンプルな処理を作成しました
<?php $doc = new DOMDocument(); $doc->loadHTML("<html><body>Test<br></body></html>"); echo $doc->saveHTML();
これを実行すると以下のように生成されたHTMLが出力されます。
$ php test.php <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN" "http://www.w3.org/TR/REC-html40/loose.dtd"> <html><body>Test<br></body></html>
これに対して、以下のように&
を入れた文字列に変更してみました。
<?php $doc = new DOMDocument(); $doc->loadHTML("<html><body>Test&<br></body></html>"); echo $doc->saveHTML();
Test&
のところです。
これを実行すると、、、
$ php test.php Warning: DOMDocument::loadHTML(): htmlParseEntityRef: no name in Entity, line: 1 in /Users/pnkts/test.php on line 3 <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN" "http://www.w3.org/TR/REC-html40/loose.dtd"> <html><body>Test&<br></body></html>
このように、Warning: DOMDocument::loadHTML(): htmlParseEntityRef: no name in Entityというワーニングが出力されました。
これは大変。それ以外にも、もしかしたらリンクのURLにクエリパラメータをつけることもあるかもしれないです。
ということで確認です!
<?php $doc = new DOMDocument(); $doc->loadHTML('<html><body><a href="./test?a=1&b=1">Test</a><br></body></html>');
これを実行すると、、、
$ php test.php Warning: DOMDocument::loadHTML(): htmlParseEntityRef: expecting ';' in Entity, line: 1 in /Users/pnkts/test.php on line 3 <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN" "http://www.w3.org/TR/REC-html40/loose.dtd"> <html><body><a href="./test?a=1&b=1">Test</a><br></body></html>
このように、Warning: DOMDocument::loadHTML(): htmlParseEntityRef: expecting ';' in Entityというワーニングメッセージが出力されてしまいました。
解決策
こちらの記事を参考にしました。
libxml_use_internal_errors
を用いて、libxmlエラーを無効にして、後からエラー情報を取得する形に変更します。この記事ではエラー情報の取得は行いませんので、取得したい場合はドキュメントを参考にしてみてください。
戻り値がuse_errors
の前の値が変えるので、処理が終わったらその値を利用して元に戻してあげます。
<?php $doc = new DOMDocument(); // set error level $internalErrors = libxml_use_internal_errors(true); $doc->loadHTML('<html><body><a href="./test?a=1&b=1">Test</a><br></body></html>'); // Restore error level libxml_use_internal_errors($internalErrors); echo $doc->saveHTML();
これを実行すると、、、
$ php test.php <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN" "http://www.w3.org/TR/REC-html40/loose.dtd"> <html><body><a href="./test?a=1&b=1">Test</a><br></body></html>
ワーニングなく実行できたことがわかります。ただし、&
(アンパサンド)のところが&
に置換されてしまっているので注意しなきゃですね。
もちろん、@
でエラーを握りつぶしてしまっても良いかと思います。
<?php $doc = new DOMDocument(); @$doc->loadHTML('<html><body><a href="./test?a=1&b=1">Test</a><br></body></html>'); echo $doc->saveHTML();
php test.php <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN" "http://www.w3.org/TR/REC-html40/loose.dtd"> <html><body><a href="./test?a=1&b=1">Test</a><br></body></html>
無事、ワーニングなく正常に実行することができました。
「&」を「&」に変換する
では、&
を普通の表示にさせたいですよね。と思って考えたのは、htmlspecialchars_decode
の使用です。
<?php $doc = new DOMDocument(); @$doc->loadHTML('<html><body><a href="./test?a=1&b=1">Test</a><br></body></html>'); echo htmlspecialchars_decode($doc->saveHTML());
最後の出力のときに、htmlspecialchars_decode
を挟んでみました。
$ php test.php <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN" "http://www.w3.org/TR/REC-html40/loose.dtd"> <html><body><a href="./test?a=1&b=1">Test</a><br></body></html>
実行してみると、このようにこの例では問題なく良い感じに出力されるようになりました。
ということで、一旦解決としました!