2 min read

[PHP]PHPのバージョンにおけるhtmlspecialcharsの違い

Table of Contents

日報にも書いているとおり、現在 PHP のバージョンアップ作業中(僕はサポート)なのだが、その中で「スマホからは POST できる」けど「ガラケーからは POST できない」という問題が発生した(ガラケーにもサービス展開してるようなアレなサービス…)。

まぁ発生している事象から察するに文字エンコードの問題だろうということは容易に想像出来たのだが、最初あれこれ迷走していたら時間がかかってしまった。

最初にやったこと

時系列的に書いていく。

該当処理の頭で$_POST を見る

まず最初はformactionで指定している処理のど頭で$_POSTをみた。すると来てない(来てないというか値が空になっている)。もちろんマルチバイトじゃなければ問題なく渡せる。

PHP: $_POST - Manual

accept-charset を指定

formaccept-charsetを指定してどうなるかを試した。するとちゃんと渡せることがわかった。ここで文字エンコードの問題だということが確定した。

<FORM>-HTML タグリファレンス

accept-charset=“文字セット”

CGI プログラム等のデータの受け側で、受付可能な文字セットを指定する

たださすがに全てのformaccept-charsetを埋め込んでいくには物量がすごいことになるので、他の方法を模索する必要がある。

デベロッパーツールで見る

次にデベロッパーツールで POST している値を見た。ここは問題なく POST 出来てた。まぁそらそうだ。

httpd.confphp.ini の見直し

とりあえずここまでで、HTTP リクエストとしては渡せていて、どこかの処理で欠損しているということがわかった。まず最初に思い浮かんだのがhttpd.confphp.ini

httpd.confは特にこれと言って怪しそうな部分はなかったが、php.inimbstringディレクティブにそれっぽい設定があった。既存で動いてる環境といくつか差分があったのでそれを修正してみるも、変化なし。さて困った。

ロゴを仕込んでいく

ここからは愚直にログを仕込んでいった。

で、最終的に行き着いたのが、htmlspecialchars。これの前後で値が欠損していた。

PHP: htmlspecialchars - Manual

encoding

省略した場の encoding のデフォルト値は、varies PHP のバージョンによって異なります。 PHP 5.6.0 以降では、デフォルト値として default_charset の値を使います。PHP 5.4 と PHP 5.5 のデフォルト値は、 UTF-8 で、それより前のバージョンの PHP のデフォルト値は ISO-8859-1 でした。

「それより前のバージョン」に該当するので、ちゃんとencodingを指定しないといけないということだった。「それより前のバージョン ww」とかいうマサカリはやめて。

返り値

変換後の文字列を返します。

入力の string の中に エンコーディング encoding における無効なコードユニットシーケンスが含まれており、 かつ ENT_IGNORE フラグが設定されていなければ、 htmlspecialchars() は空文字列を返します。

ダメだった場合のhtmlspecialcharsの戻り値は空文字になる。もしここでExceptionでも投げてくれてれば一瞬で解決したのに…。まぁそう嘆いても仕方ない。

とりあえずhtmlspecialcharsgrepして、encodingを指定して終了。

余談

これにて一件落着、、といきたいところだが、最初に処理のど頭でやった$_POSTが欠損しているってのがどうにも納得行かず。$_POSTってスーパーグローバルじゃないんかいと。イミュータブルじゃないんかいと。

で、まぁ冷静になって考えるとイミュータブルなんてことはない(当たり前)のだけど、$_POSTをオーバーライドするのは、完全にお作法違反だと思う。フレームワークは Codeigniter なのだが、Codeigniter では POST 値の取得は$this->input->post('foo')みたいな形で取得できるので、htmlspecialcharsしたものはこっちに入れるべきじゃないのか…?

入力クラス : CodeIgniter ユーザガイド 日本語版

ちなみに実処理では、$_POSTを参照渡し(!)でhtmlspecialcharsな処理をする関数に渡してあった。参照渡し、滅すべし。