mb_convert_kanaの中身を見てみる
「mb_convert_kanaで文字コード指定しないと誤変換する場合がある」d:id:maru_cc:20080212:1202813263 という現象があり、文字コード周りで久々にはまった。
そんな話を同僚に話したら、デフォルトでinternal_encodingの設定にしたがってくれればいいのに。という意見が。
確かにそうだなーと思った。
で、実際に中身がどうなっているのか見てみた。
php5.2.5をcscopeで見てみる。
まずは、mb_convert_kanaで検索。目印は「PHP_FUNCTION」
mbstring.c line:2887
/* {{{ proto string mb_convert_kana(string str [, string option] [, string encoding]) Conversion between full-width character and half-width character (Japanese) */ PHP_FUNCTION(mb_convert_kana) {
このあたり。
まず、宣言周りがある
int opt, i; mbfl_string string, result, *ret; char *optstr = NULL; int optstr_len; char *encname = NULL; int encname_len; mbfl_string_init(&string); string.no_language = MBSTRG(current_language); string.no_encoding = MBSTRG(current_internal_encoding);
で、次にパラメータ取得らしき動作が。
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|ss", (char **)&string.val, &string.len, &optstr, &optstr_len, &encname, &encname_len) == FAILURE) { return; }
zend_parse_parametersを調べてみたところ、zend_API.cにそれらしい関数がある。
パラメータ取得の動作だと思う。たぶん。
で、次。
/* option */ if (optstr != NULL) { char *p = optstr; int n = optstr_len; i = 0; opt = 0; while (i < n) { i++; switch (*p++) { case 'A': opt |= 0x1; break; (略) case 'm': opt |= 0x200000; break; } } } else { opt = 0x900; }
延々とパラメータのcase文が。
次にそれらしい目的のところが。
/* encoding */ if (encname != NULL) { string.no_encoding = mbfl_name2no_encoding(encname); if (string.no_encoding == mbfl_no_encoding_invalid) { php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unknown encoding \"%s\"", encname); RETURN_FALSE; } }
ただし、よく見るとencnameが指定された場合の挙動のようだ。
最後に、変換部分と戻り値あたり。
ret = mbfl_ja_jp_hantozen(&string, &result, opt); if (ret != NULL) { RETVAL_STRINGL((char *)ret->val, ret->len, 0); /* the string is already strdup()'ed */ } else { RETVAL_FALSE; } } /* }}} */
mbfilter.cが変換の挙動みたいだ。
全角半角変換以外にカタカナひらがな変換とかもしているのにmbfl_ja_jp_hantozenなんて関数名は昔のなごりだろうか。
ちょっと面白い。
で、よくよく見ると…
最初の宣言部分で
string.no_encoding = MBSTRG(current_internal_encoding);
internal_encodingを参照しているようだ。
ここで、ん?と。
今回問題が発生した該当のサーバで、phpinfoを書いたスクリプトをコマンドラインで動かしてみたところ、
mbstring.internal_encoding => EUC-JP => EUC-JP mbstring.language => Japanese => Japanese
EUC-JPだ。。。
どうやら、UTF-8の設定になっていると思い込んでいたのは、httpd.confレベルでphp_valueを使用し設定していたようだ。
つまり、internal_encodingがEUC-JPのまま、UTF-8の文字列を変換にかけてしまっているようだった。
そーいえば、こんな問題が前にもあったなぁ。。。
その時の対応は、cli実行時用のUTF-8を指定したphp.iniを別途用意し、実行時にコマンドラインオプションで指定する方法で回避できる。
/path/to/php -c /path/to/cli_php.ini /path/to/script.php
--php-ini という長いオプションもある。
http://jp.php.net/manual/ja/features.commandline.php
前に、同じ問題ではまったことがあるのに、すっかり忘れてた。
追記(2008-02-15)
「phpのコマンドラインオプション」でいろいろ調べてみました
d:id:maru_cc:20080214:1203007176