PHPのhtmlentities()とhtmlspecialchars()の違いと適切なエンティティ変換

公開
更新

HTML に用いられる文字列には「特殊文字」と呼ばれるものがあります。代表的なものは「<」大なりや「>」小なり、「”」ダブルクオートや「’」シングルクオート、そして「&」アンパサンドなどの一般的な記号がありますが、他にも、数学記号やギリシャ文字など、OS や Web ブラウザの種類によっては正しく表示できないものもこれに当たります。

特殊文字を HTML 上に展開する場合、そのまま出力すると文字化けを起こしたり HTML 構造が壊れたりしてしまう場合があるので、適宜エンティティ変換を行う事が推奨されます。

PHP ではこれらのエンティティ変換用に htmlentities 関数と htmlspecialchars 関数が用意されています

htmlentities

適用可能な文字を全て HTML エンティティに変換する(PHP 4, PHP 5, PHP 7, PHP 8)

htmlentities(string $string, int $flags, string $encoding, bool $double_encode);
  • $string
    • 変換対象文字列
  • $flags
    • フラグ定数(デフォルト: ENT_QUOTES | ENT_SUBSTITUTE | ENT_HTML401。PHP 8.0 以前は ENT_COMPAT | ENT_HTML401)
  • $encoding
    • エンコーディング(デフォルト: ini_get("default_charset"))
  • $double_encode
    • 既存の html エンティティ取扱い(デフォルト: TRUE)

http://php.net/manual/ja/function.htmlentities.php

htmlspecialchars

特殊文字を HTML エンティティに変換する(PHP 4, PHP 5, PHP 7, PHP 8)

htmlspecialchars(string $string, int $flags, string $encoding, bool $double_encode);
  • $string
    • 変換対象文字列
  • $flags
    • フラグ定数(デフォルト: ENT_QUOTES | ENT_SUBSTITUTE | ENT_HTML401。PHP 8.0 以前は ENT_COMPAT | ENT_HTML401)
  • $encoding
    • エンコーディング(デフォルト: ini_get("default_charset"))
  • $double_encode
    • 既存の html エンティティ取扱い(デフォルト: TRUE)

http://php.net/manual/ja/function.htmlspecialchars.php

共通オプション

htmlentities関数と htmlspecialchars関数の引数項目は全て共通になっています。

フラグ定数(第二引数オプション)

ダブルクオート変換 シングルクオート変換
1. ENT_COMPAT ○ ×
2. ENT_QUOTES ○ ○
3. ENT_NOQUOTES × ×

※デフォルトは ENT_QUOTES(PHP 8.0 以前は ENT_COMPAT)

無効な符号単位シーケンスを含む文字列 備考
4. ENT_IGNORE 無効な部分を切り捨て セキュリティに問題があり使用は非推奨
5. ENT_SUBSTITUTE Unicode の置換文字に置き換える UTF-8 なら「U+FFFD 」それ以外は「� 」
  1. ENT_DISALLOWED
    • 無効な符号位置を Unicode の代替文字である U+FFFD (UTF-8) もしくは � で置き換える。
    • 設定しない場合は無効な符号位置をそのまま残す。
    • 外部コンテンツを埋め込んだ XML 文書を整形式に保つために有効。
コード処理書式
7. ENT_HTML401 HTML 4.01
8. ENT_XML1 XML 1
9. ENT_XHTML XHTML
10. ENT_HTML5 HTML 5

エンコーディング(第三引数オプション)

エンコーディングは、セットしない場合デフォルトで php.ini の default_charset で設定されている文字セットが適用されます。(設定値は公式ページを参照してください。)

既存の html エンティティ取扱い(第四引数オプション)

このオプションは、エンティティ変換に二重処理を防止する為のオプションで、デフォルトでは ON(true) に設定されています。つまりは、既にエンティティ変換が行われているものについては変換処理を行わない為の設定です。

例えばこのオプションを OFF(false) に設定した場合、重ねてエンティティ変換が行われると以下のようになります。

&
↓   // 1 回目のエンティティ変換
&
↓   // 2 回目のエンティティ変換
&
↓   // 3 回目のエンティティ変換
&

「&」部分が何重にもエンティティ変換されていってしまい、文字参照・実体参照として成立しなくなります。

htmlentities関数と htmlspecialchars関数の違い

結論から言うと、両者の違いは「エンティティ変換として取り扱う対象の違い」です。

htmlspecialchars() で変換対象となる文字は以下になります。

  • 「& (アンパサンド) 」
  • 「" (ダブルクォート) 」(※オプションによる)
  • 「' (シングルクォート) 」(※オプションによる)
  • 「< (小なり) 」
  • 「> (大なり) 」

HTML タグなどコードの一部として機能するものが変換対象となっています。

一方、htmlentities() に関しては、エンティティ変換可能なもの全てが変換対象となっています。(こちらもオプションによって変動があります)

以下に、両者の処理の違いを示しますが、まず前提として、変換対象として渡すテキストは以下のようになっています。

// 元となるテキスト
$text = '<あいう&"えおか"&\'きくけ\'> 記号 ⇒「©」「≠」「£」「→」「⇔」';

わかりやすいように、ひらがな・漢字、そして「」が全角で、あとは全て半角です。 シングルクオーテーションのみ、変数への代入の関係でエスケープシーケンスで記述しています。

デフォルト処理

オプションは設定せず、デフォルトでの変換の場合です。

htmlentities
echo htmlentities($text);
// => &lt;あいう&amp;&quot;えおか&quot;&amp;&#039;きくけ&#039;&gt; 記号 &rArr;「&copy;」「&ne;」「&pound;」「&rarr;」「&hArr;」
htmlspecialchars
echo htmlspecialchars($text);
// => &lt;あいう&amp;&quot;えおか&quot;&amp;&#039;きくけ&#039;&gt; 記号 ⇒「©」「≠」「£」「→」「⇔」

htmlentities() の方は記号も含め根こそぎエンティティ変換されているのに対し、htmlspecialchars() の方は対象文字以外は変換されていないことが確認できます。

※ 上記は PHP 8.1 以降の出力です。PHP 8.0 以前はフラグ定数のデフォルトが ENT_COMPAT だったため、両者ともシングルクォーテーションは変換されませんでした。

シングルクォーテーションも変換対象に含む

次は、第二引数を指定してシングルクォーテーションも変換対象に含みます。

htmlentities
echo htmlentities($text, ENT_QUOTES);
// => &lt;あいう&amp;&quot;えおか&quot;&amp;&#039;きくけ&#039;&gt; 記号 &rArr;「&copy;」「&ne;」「&pound;」「&rarr;」「&hArr;」
htmlspecialchars
echo htmlspecialchars($text, ENT_QUOTES);
// => &lt;あいう&amp;&quot;えおか&quot;&amp;&#039;きくけ&#039;&gt; 記号 ⇒「©」「≠」「£」「→」「⇔」

第二引数に「ENT_QUOTES 」を指定する事で、両者ともシングルクォーテーションも変換された事が確認できます。(PHP 8.1 以降はデフォルトに ENT_QUOTES が含まれているため、指定しなくても変換されます)

また、第二引数は複数指定する事も可能です。その場合は「|(パイプライン)」で区切ります。

htmlentities
echo htmlentities($text, ENT_QUOTES | ENT_HTML5);
// => &lt;あいう&amp;&quot;えおか&quot;&amp;&apos;きくけ&apos;&gt; 記号 &Implies;「&copy;」「&NotEqual;」「&pound;」「&srarr;」「&hArr;」
htmlspecialchars
echo htmlspecialchars($text, ENT_QUOTES | ENT_HTML5);
// => &lt;あいう&amp;&quot;えおか&quot;&amp;&apos;きくけ&apos;&gt; 記号 ⇒「©」「≠」「£」「→」「⇔」

エンティティ変換をデコードする

一度エンティティ変換を行ったものに対して、デコード(もとに戻す)事も出来ます。それぞれ、デコード用関数が用意されており、htmlentities() の場合は html_entity_decode() を、htmlspecialchars() の場合は htmlspecialchars_decode() を用います。

htmlentities
$entity = htmlentities($text, ENT_QUOTES);
echo html_entity_decode($entity, ENT_QUOTES);
// => <あいう&"えおか"&'きくけ'> 記号 ⇒「©」「≠」「£」「→」「⇔」
htmlspecialchars
$entity = htmlspecialchars($text, ENT_QUOTES);
echo htmlspecialchars_decode($entity, ENT_QUOTES);
// => <あいう&"えおか"&'きくけ'> 記号 ⇒「©」「≠」「£」「→」「⇔」

デコードは両者ともエンティティ変換前の文字列に戻っている事が確認できます。ポイントは、デコードの際に、エンティティ変換を行った時と同じオプション(フラグ定数)を指定する事です。そうする事で、フラグ定数指示に則ってデコードが行われます。

get_html_translation_table()

おまけですが、デコード用関数を使わなくても手動で戻す事も可能です。

PHP には get_html_translation_table() という関数があり、これを用いる事で htmlspecialchars() や htmlentities() で使用される変換テーブルを取得する事が出来るので、これを利用する事で手動でデコードが可能です。

htmlentities
// エンティティ変換
$entity = htmlentities($text, ENT_QUOTES);

// htmlentities の変換テーブルを取得する
$entity_table = get_html_translation_table(HTML_ENTITIES, ENT_QUOTES);

// KEY と VALUE を反転させる
$rev_entity_table = array_flip($entity_table);

// 文字列の置換を行う
echo strtr($entity, $rev_entity_table);
// => <あいう&"えおか"&'きくけ'> 記号 ⇒「©」「≠」「£」「→」「⇔」
htmlspecialchars
// エンティティ変換
$entity = htmlspecialchars($text, ENT_QUOTES);

// htmlspecialchars の変換テーブルを取得する
$entity_table = get_html_translation_table(HTML_SPECIALCHARS, ENT_QUOTES);

// KEY と VALUE を反転させる
$rev_entity_table = array_flip($entity_table);

// 文字列の置換を行う
echo strtr($entity, $rev_entity_table);
// => <あいう&"えおか"&'きくけ'> 記号 ⇒「©」「≠」「£」「→」「⇔」

get_html_translation_table関数の第一引数には変換テーブル指定の為の定数が渡されており、必要に応じてフラグ定数を第二引数に渡す事で、こちらの要求する変換テーブルセットを配列で取得する事が出来ます。 htmlentities の変換テーブルは多いので割愛しますが、htmlspecialchars の変換テーブルは、例えば以下のようになっています。

get_html_translation_table(HTML_SPECIALCHARS, ENT_QUOTES);
 => Array
    (
        ["] => &quot;
        [&] => &amp;
        ['] => &#039;
        [<] => &lt;
        [>] => &gt;
    )

まとめ

昨今では PHP フレームワークの進化でこれらの関数を直接的に使用する機会はかなり減りました。とはいえ、Laravel などで使われている Blade テンプレートの{{}}記法や e ヘルパーなどでは htmlspecialchars や htmlentities が使われており、レガシーながらも現在でも必要不可欠な関数である事には間違いありません。

両者の違いを理解できると、使いどころも迷わずにチョイスできるようになるので、覚えておくとどこかできっと役に立つはずです。

Ritolabo