PHP には fgetcsv() とか str_getcsv() なんつー無精くさい関数があって, 名前を見た通りファイルや文字列を CSV として解釈して配列で返してくれるというもの.
「CSV」って特に定まった規格のあるフォーマットじゃないんで, しかも一番有名なのが一番変則的な Excel 出力 CSV なんで, マトモに読み書きしようとすると地味にけっこう大変だったりする.
んが,この getcsv() はなぜかよく出来ていて, Excel 形式の(データ中に " を含む場合は "" と書く,とかの)データもさくさく読めちゃったりするので, カジュアルに使うには楽出来て良かったりする.
(もちろん悪意ユーザからの攻撃データとかが想定される場面ではもっと注意して制限したフォーマットを使うべき).
んが,こういう文字列を解析する系の関数では必ず出て来る日本語エンコーディング問題.
自分の手元のサーバでは普通に読めたファイルが, 他の場所ではなぜか日本語を含む一部の項目だけ読み落とされた,なんて事象が起きた. しかも,他の場所で Web から動かした場合のみで, 同じ処理をコマンドラインから動かすと期待通りに動く.
しかも日本語が全てダメというのでもなく,読めてる項目と読めてない項目がある……
コマンドラインと Web で挙動が違うというと, 例えば違う設定ファイルを読んで動いてるかーとかなんだけど, 全部の日本語が文字化けってわけではなく一部は正常に読めてるので, エンコーディング設定とかってわけではなさそう.
これが単に文字化けしてるとかなら, PHP 設定の mbstring.internal_encoding とファイル自体のエンコーディングが合ってないとか, そういう原因だろうと推測できるのだけど……
というわけで軽く Web で調べると, 「fgetcsv() は日本語に対応していない」とかってのを見かける. いえそんなことはありません.うちの環境では読めてるし.
次に「日本語は "" で括らないとダメ」というのが. いえそんな(ry ……でも読めてる項目は "" で括られていて読めてない項目は括られていないってのが判明.
"" の有無で,内部で違う処理になってるんだろうか. たしかに,"" がなければ単に次の , を探せばいいだけだが, "" の場合は \" とかのエスケーピングを避けながら終端 " を探さないといけないので, 内部処理は違ってそうだ.
というわけでソースを読んでみると,確かに処理が違っている. php-5.3.2/ext/standard/file.c の 2150行目あたり. mblen() で日本語を意識しながらぽちぽちと文字を飛ばして行っている処理がありました.
で,この mblen() はどういうエンコーディングを扱ってるんだ? と思ったら, これが標準ライブラリをそのまま読んでるだけで( ext/standard/php_string.h ), 環境設定の mbstring.internal_encoding とかを意識してはいないみたい.
ってことで,そっち方面のエンコーディングを変更するために, setlocale() を使うってわけなのねー.
setlocale( LC_ALL, 'ja_JP.UTF-8' );
で,期待通りの動作になりました. Web と CLI で挙動が違うのは,環境変数 LANG によるものでした,ということで.
答えが判ってから探すと情報が見つかる罠. はっはっは.
それにしても mbstring 設定があるけどそっちは mbstring モジュール用で, 他の箇所では locale 設定をきちんとしとけ,とか, いかにも PHP らしいテキトーっぷりです.ははは.