入門Ajaxその2

問題を整理してみよう。

まず、htmlファイルの文字コードutf-8なので、記述されている文字列リテラルutf-8だと思う。つまり、送信される「あいうえお」はutf-8である。それをURIエンコードしたものが実際にPHPに送信される。PHPでは_POST['data']などとしてそれを取り出す。おそらくこのとき自動でURIデコードはされてるだろう(推測)。そこから、さらにmb_convert_stringでutf-8に変換している(これは必要ない)。さらに、mb_convert_stringでPHPプログラムに記述された文字列リテラル(でもphpファイルの文字コードutf-8にしたから本当は必要ないと思う)をutf-8に変更し、さらにmb_http_outputで出力がutf-8に変換されるよう設定している。これらから、無駄が多すぎ*1なことは置いておいてもPHPプログラムの出力はutf-8のはずである。これはブラウザの表示でも確認できた。

しかし、PHPプログラムの実行結果をWebブラウザで表示したときに、EUC-JPに文字コードが推論されている。つまり、どこかで誰かがEUC-JPであるとしてHTTPヘッダーを出力しており、それを信じたブラウザおよびMSXMLEUC-JPとして解釈した結果文字化けしているに違いない。

ひとまず、IEでは得られる情報が少なすぎなのでFirefoxとLiveHTTPHeadersをインストールさせ、PHPプログラムにアクセスすると、案の定HTTPヘッダーで文字コードとしてEUC-JPが指定されていた。

さあ、犯人は誰だ。

最初に疑うべきは、PHPである(偏見)。私はPHPについてほとんどなにも知らないので、ちょっとは知ってそうな4年生を召還する。助けてー

もしかしてmb_http_outputは、ヘッダーを修正したりしてくれるのだろうかと考え、直してみたが変わらない。php.iniにmbstring.http_outputといういかにもな項目があったのでutf-8に設定してみたが、やっぱりダメ。もちろんapacheはちゃんと再起動している。

じゃあ、犯人はApacheだろうか。

httpd.confを見てみると、

AddDefaultCharset = none

なる項目が見つかった。いかにも怪しい。Apacheのマニュアルを確認すると

レスポンスのコンテントタイプが text/plain あるいは text/html の場合に限りますが、レスポンスに追加するメディアタイプの文字セットパラメータ (文字エンコーディングの名前) のデフォルト値を、このディレクティブで指定します。

とある。なるほど。ひとまずnoneなる項目はしていできないのでOffにしてみた。ふつーのHTMLをみると、確かにHTTPヘッダからcharsetへの言及が消えている。よしよし。

しかし、PHPプログラムの出力には、しっかりEUC-JPであるとのヘッダがあった。

埒が明かないので、てきとうなキーワードでぐぐってみると「headerなる関数でHTTPヘッダを出力すべし」というページが見つかった(URL失念)。指定してみたが、PHPに「なにも出力されていない時点でheader呼ばないとダメよ」といわれる。phpファイルの先頭でやってるんだけどなあ…しかたがないので、php.iniでバッファリングを有効にしてみたら、HTTPヘッダーにutf-8である旨が出力され、めでたくPHPプログラムの出力の文字コードをブラウザが解釈できるようになった。もちろんAjaxプログラムでもOK!

最終的には、php.iniのdefault_charsetで、text/htmlの出力する文字列をutf-8であると指定できて、わりと幸せになれたような気がする。これならheaderとかでいちいちHTTPヘッダを出力する必要が無く、また出力のバッファリングも不要である。


長い戦いだった。

教訓

  • ややこしいことを考えたくなかったら日本語文字列を使ってはいけない
  • ApacheにしろPHPにしろ、きちんとマニュアルを読まなくてはいけない
  • でも、どこから手をつけたらいいかわからないときは、ぐぐってみるのも有効
  • ぐちゃぐちゃやってみたけれども「Ajaxで受信するXMLURIエンコーディングされている」というのが常識だったらどうしよう
    • 泣く

*1:ソースコードutf-8にしたら、mb_convert_stringもmb_http_outputの不要になった