PHP7.4 で HTML を DOMDocument を使って変換する処理をしていたが
PHP8.2 に上げたところ、 mb_convert_encoding
の Deprecated エラーが出るようになった。
PHP Deprecated: mb_convert_encoding(): Handling HTML entities via mbstring is deprecated; use htmlspecialchars, htmlentities, or mb_encode_numericentity/mb_decode_numericentity
エラーを解消するのに手間取ったので記録として残しておく。
従来の方法
これまでは以下のような変換をしていた。
<?php // 1. DOMDocument を使いたいが、単独の & を渡すと warning が出るので事前に & に変換する $escaped = str_replace('&', '&', $html); // 2. DOMDocument に utf-8 の文字列を渡すとそのままでは文字化けするので、mb_convert_encoding でエンティティに変換する $encoded = mb_convert_encoding($escaped, 'HTML-ENTITIES', 'utf-8'); // 3. DOM操作をして変換後の HTML を取得する $doc = new DOMDocument(); $doc->loadHTML($encoded); $savedHtml = $doc->saveHTML(); // 4. 変換後のHTMLはエンティティのままなので、再度 mb_convert_encoding で元に戻す $result = mb_convert_encoding($savedHtml, 'utf-8', 'HTML-ENTITIES');
新しい方法
最終的に以下の形にした。
<?php // 1.事前に & → & に変換する処理は同じ $escaped = str_replace('&', '&', $html); // 2. mb_convert_encoding の代わりに mb_encode_numericentity を使う $map = [0x80, 0x10ffff, 0, 0x1fffff]; // ascii を除くユニコード文字の範囲 $encoded = mb_encode_numericentity($escaped, $map, 'utf-8'); // 3. DOM操作をして変換後の HTML を取得する $doc = new DOMDocument(); $doc->loadHTML($encoded); $savedHtml = $doc->saveHTML(); // 4. html_entity_decode でエンティティを元の文字に戻す $result = html_entity_decode($string, ENT_QUOTES | ENT_SUBSTITUTE | ENT_HTML401, 'utf-8');
変換処理について
mb_convert_encoding
の代わりに、 mb_encode_numericentity
と html_entity_decode
を使用したが、それぞれどのように変換されるか実験した。
例として、元の HTML が以下の形だったとする。
<p>あ & ♥</p>
<p>
タグの中は以下のように変化した。
旧 | 新 | |
---|---|---|
初期状態 | あ & ♥ |
あ & ♥ |
1. & に変換 | あ & ♥ |
あ & ♥ |
2. エンティティに変換 | あ & ♥ |
あ & ♥ |
3. DOM操作 | あ & ♥ |
あ & ♥ |
4. デコード | あ & ♥ |
あ & ♥ |
&
→&
の置き換えは DOMDocument に渡すために必要なので、どちらの方式も同じmb_convert_encoding
では「♥」が名前付きエンティティ (♥
) に変換されていたが、mb_encode_numericentity
では数値エンティティに置き換わっている点が異なる- DOMDocument にエンティティに変換した文字列を渡すと、名前付きエンティティを持っている数値エンティティは名前付きに変換されるため、新方式ではこのタイミングで
♥
に置き換わっている - 旧方式 (
mb_convert_encoding
) では名前付き・数値エンティティのどちらも変換されるので、元の文字列に戻る。
新方式は 2. で使ったmb_encode_numericentity
の対になる関数はmb_decode_numericentity
だが、数値エンティティのみデコードするため、&
や♥
は残ってしまう。html_entity_decode
であればどちらも変換するので、今回はデコードにhtml_entity_decode
を使う形にした。