PHP の mbstring で encoding_translation 有効の時,.htaccess の internal_encoding の値が反映されない事がある
というのを調べてました.[php-dev 1445], [php-dev 1446] で書いたのと同じ.
設定
php.ini で
mbstring.language = Japanese mbstring.internal_encoding = EUC-JP mbstring.http_input = auto mbstring.http_output = pass mbstring.encoding_translation = On mbstring.detect_order = auto mbstring.substitute_character = none; mbstring.func_overload = 0 mbstring.strict_encoding = Off
という感じで設定して,.htaccess で
php_value mbstring.language Japanese php_value output_handler mb_output_handler php_flag mbstring.encoding_translation 1 php_flag magic_quotes_gpc 0 php_value mbstring.internal_encoding UTF-8
という設定の環境でした.
UTF-8 で書いた以下のようなソース.
<html> <body> <?php if($_REQUEST["message"] != ""){ print "メッセージ<br>\n"; print "encoding: " . mb_detect_encoding($_REQUEST["message"]) . "<br>\n"; print htmlspecialchars($_REQUEST["message"]) . "<br>\n"; } ?> <form action="test.php"> <input type="text" name="message" value="<?php htmlspecialchars($_REQUEST["message"])?>"> <input type="submit" value="送信"> </form> <pre> <?php print_r(mb_get_info()); echo phpversion(); ?> <pre> </body> </html>
というようなプログラム.
再現その1
再現その2
- (再現その1) の 6 でその後は何度アクセスしても正常と書きましたが,ここで一度,.htaccess のないディレクトリへ遷移して,その後,この .htaccess の存在するディレクトリへ戻ります.つまり.htaccess 有 -> 無 -> 有 と一度外に出て戻ります.
- 戻る際は http://server/dir/test.php?message=あいうえお みたいに直接パラメータ指定します.
- 文字化け.mb_detect_encoding は EUC-JP,mb_get_info は UTF-8.
つまり
という動きで,internal_encoding の値が反映されてないことが予想されました.
調査
デバッガで追ってみました.全部追ってないので,かなりこう動くのだろう,という想像が入ってますが..
Apache 起動後,
- .htaccess が検出されると,mbstring.c の OnUpdate_mbstring_internal_encoding へ飛ぶ.ここで new_value の値をデバッガで見てみると,"UTF-8" となっており,確かに .htaccess は読み込まれているっぽいです.先の例で mb_get_info() で設定を取得すると,internal_encoding の値は "UTF-8" と正常になっていたのは,ちゃんと値が取得出来ているからだと思います.
- ここで encoding_translation が指定されていると,mb_gpc.c 内の mbstr_treat_data に処理が飛びます.ここで変換先のエンコーディングを決定しているっぽいですが,ここではグローバル変数 mbstring_globals の internal_encoding を見ているようで,この時点ではmbstring_globals.internal_encoding の値は更新されておらず,例だと "EUC-JP" のままなので,その後の変換処理で(?) 入力文字列が EUC-JP へ変換されてしまい,結果,出力が文字化けしてしまうようです.
- その後,グローバル変数は更新されるので,次にアクセスした際は,正常にエンコード変換される (or されない) のですが,一度,別設定が効いてくる他のディレクトリに移動すると,グローバル変数の値がそのディレクトリの値にリセットされるので,再度文字化けが発症します (= internal_encoding の値が .htaccess 以外の値に).
以上のように想像なのですが,エンコーディング変換が有効な際,.htaccess の値でグローバル変数が更新されていれば,問題なく動くのだと思います.とりあえず,きっと他の動きがめちゃめちゃになるのでしょうけど,以下のようにしてみると,文字化けは解消しています.ただし,これはグローバル変数に値を代入したら動くという確認のためのもので,これで問題がないわけではありません.mbstring の全体を見てませんので.(mbstring 難しい...)
--- mbstring.c.orig 2009-03-02 13:02:25.000000000 +0900 +++ mbstring.c 2009-03-02 13:03:03.000000000 +0900 @@ -745,6 +745,7 @@ * 2. mbstring.language directive is processed in per-dir or runtime * context and 3. call to the handler for mbstring.language is done * after mbstring.internal_encoding is handled. */ + _php_mb_ini_mbstring_internal_encoding_set(new_value, new_value_length TSRMLS_CC); return SUCCESS; } }