2010年12月7日火曜日

PHPの htmlSpecialChars()の 第3引数(文字コード)の調査メモ

そもそも

基本的に内部的な文字コードは(ソースファイルもDBもその他の設定も)全てUTF-8にしておき、POSTやGETなどで入力値を受け取る時には入力値をUTF-8に変換してから使うのが良いと思うし、そうすれば漏れがない限りはhtmlSpecialChars()等で出力時に不正文字コードを気にする必要はない。



しかし

現実的にはPOST/GETで受け取る以外にも下記の様に様々な方法でデータが入ってくることもある。

それぞれデータを取り出す時に適切に文字コード変換すべきだが、やっぱりいつか誰かが漏らしそう。

なので、やはり出力時にも不正な文字コードは弾いた方がよい。



で、調べてみた

2009年の後半にいくつかのブログでこのことが論じられていた。(そういえば読んだ記憶が...。詳細は下の方の参考リンクを参照。)
中でも「htmlspecialchars - 第45回PHP勉強会@関東 :: handsOut.jp」が簡潔でわかりやすかった。
PHP5.2.5以降を使い、htmlspecialchars()の第3引数にUTF-8を指定するのが一番堅実のようだ。

蛇足だが、もしUTF-8以外でWebページを入出力したい場合は、mb_http_output()とob_start()を使って出力完了後に文字コードをSJIS-WINなどに変換させるようにしておいて、そこにUTF-8で出力するのが良いと思う。(処理量が多いとオーバーヘッドが気になる?)



以外な落とし穴が

しかし、htmlspecialchars()の文字コード検出には思わぬ落とし穴があった。
htmlspecialchars()は不正な文字コードを見つけても、PHPの設定でdisplay_errorsがfalseの場合しかWarningを出さない
  • display_errors = true → Warningを画面にもログにも出さない
  • display_errors = false → Warningをログにも出す(画面には出さない)
これでは、開発環境では文字コードが不正になりうる箇所に気づかないまま、本番環境でWarningを出しまくるかもしれない。

これは意図的なものなので、修正されない可能性が高そう。(参考:display_errorsが謎の副作用を持っている箇所について - muddy brown thang
また、@htmlspecialchars()に@(アットマーク)を付けてもログへのエラー出力を抑制できなかった。

Warningを出さないようにする対策としては、htmlspecialchars()の手前で強制的に文字コードを変換するという手がある。
$str = mb_convert_encoding($str, 'UTF-8', 'UTF-8');
echo htmlSpecialCahrs($str, ENT_QUOTES, 'UTF-8');
現在のサーバ性能とPHPの速さなら、こんな富豪的な重複処理もあまり問題にならない環境が多い。
mb_convert_encoding()とhtmlSpecialChars()の検出基準(?)が違うというようなことをどこかで読んだ気がするんで、その場合はこれでもWarningが出るのだろう。
でも、どうせmb_convert_encoding()するのなら、そもそもhtmlSpecialChars()での文字コード指定は要らないか。

ただし、本道としては、せっかく出してくれるWarningを手がかりにして、該当箇所で適切な文字コード変換をするようコードを修正するのが筋だろう。
PHPのWarningはset_error_handlerで捕まえられる。



もう1つ

htmlspecialchars/htmlentitiesはBMP外の文字を正しく扱えない - [php] - 徳丸浩の日記」という落とし穴もあるようだが、これはPHPの5.3.2でFixした。(5.2系は?)
また、(線文字Bは置いておいて)一部の漢字を「使えません。仕様です。」で済ませられるなら、こちらの問題は黙殺できそうだ。



参考

0 件のコメント:

ブログ アーカイブ

tags