日報にも書いているとおり、現在 PHP のバージョンアップ作業中(僕はサポート)なのだが、その中で「スマホからは POST できる」けど「ガラケーからは POST できない」という問題が発生した(ガラケーにもサービス展開してるようなアレなサービス…)。
まぁ発生している事象から察するに文字エンコードの問題だろうということは容易に想像出来たのだが、最初あれこれ迷走していたら時間がかかってしまった。
最初にやったこと
時系列的に書いていく。
該当処理の頭で$_POST
を見る
まず最初はform
のaction
で指定している処理のど頭で$_POST
をみた。すると来てない(来てないというか値が空になっている)。もちろんマルチバイトじゃなければ問題なく渡せる。
accept-charset
を指定
form
のaccept-charset
を指定してどうなるかを試した。するとちゃんと渡せることがわかった。ここで文字エンコードの問題だということが確定した。
accept-charset
=“文字セット”CGI プログラム等のデータの受け側で、受付可能な文字セットを指定する
たださすがに全てのform
にaccept-charset
を埋め込んでいくには物量がすごいことになるので、他の方法を模索する必要がある。
デベロッパーツールで見る
次にデベロッパーツールで POST している値を見た。ここは問題なく POST 出来てた。まぁそらそうだ。
httpd.conf
、php.ini
の見直し
とりあえずここまでで、HTTP リクエストとしては渡せていて、どこかの処理で欠損しているということがわかった。まず最初に思い浮かんだのがhttpd.conf
、php.ini
。
httpd.conf
は特にこれと言って怪しそうな部分はなかったが、php.ini
はmbstring
ディレクティブにそれっぽい設定があった。既存で動いてる環境といくつか差分があったのでそれを修正してみるも、変化なし。さて困った。
ロゴを仕込んでいく
ここからは愚直にログを仕込んでいった。
で、最終的に行き着いたのが、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
でも投げてくれてれば一瞬で解決したのに…。まぁそう嘆いても仕方ない。
とりあえずhtmlspecialchars
でgrep
して、encoding
を指定して終了。
余談
これにて一件落着、、といきたいところだが、最初に処理のど頭でやった$_POST
が欠損しているってのがどうにも納得行かず。$_POST
ってスーパーグローバルじゃないんかいと。イミュータブルじゃないんかいと。
で、まぁ冷静になって考えるとイミュータブルなんてことはない(当たり前)のだけど、$_POST
をオーバーライドするのは、完全にお作法違反だと思う。フレームワークは Codeigniter なのだが、Codeigniter では POST 値の取得は$this->input->post('foo')
みたいな形で取得できるので、htmlspecialchars
したものはこっちに入れるべきじゃないのか…?
入力クラス : CodeIgniter ユーザガイド 日本語版
ちなみに実処理では、$_POST
を参照渡し(!)でhtmlspecialchars
な処理をする関数に渡してあった。参照渡し、滅すべし。