mbstring.func_overloadの罠
mbstringを愛用している日本のPHPerならphp.iniのmbstringセクションで必ずと言っていいほど目にする設定、mbstring.func_overload。
これを有効にするとmail()やstrほげほげ()がmb_send_mail()やmb_strほげほげ()にオーバーロードされて、mail()関数を呼び出すと自動的にmb_send_mail()で処理する、ってなことをやってくれるわけだけど、オリジナルの方を呼び出す術はないんだろうか。
例えばデータのバイト数を得るために$len = strlen($data)
とかやると思うんだけど、これがmb_strlen()*1で処理されたらダメじゃね?
mbstring.func_overloadはPHP_INI_PERDIRなので、スクリプト実行中にini_set()で変えることは出来ない。
んー、どうすんだろ。
bin2hex()してみる
ぐぐってみたらこんな解があった。
日本語文字列のバイト数取得にstrlenだけではダメな理由-PHP - CPA-LABテクニカル
php:バイト数の取得(strlen は mb_strlen にオーバーロードされる): Script雑感
<?php $volm = strlen(bin2hex($data)) / 2; $data:バイト数を取得したいデータ $volm:データ長(byte) ?>
なるほど、2桁の16進数にして2で割ることでバイト数を出しているようだ。
16進数に変換された文字列自体はシングルバイト文字なので大丈夫なんだろう。きっと。
ASCIIとして解釈させる
さらに別解を見つけた。
http://www.revulo.com/blog/20070420.html
<?php if (ini_get('mbstring.func_overload') & 2 && function_exists('mb_strlen')) { $size = mb_strlen($data, 'ASCII'); } else { $size = strlen($data); } ?>
無理矢理ASCII文字列とみなして処理させるということだろうか。
おかしくなったりしないのか不安ではある。
ちなみに、この手法は公式マニュアルのUser Contributed Notesにも書かれている。
PHP: mb_strlen - Manual
http://jp.php.net/manual/ja/function.strlen.php
それぞれどうなるのか試してみた
mbstring.func_overloadは無効で、UTF-8で書いたコード。
<?php $str = 'マルチバイト文字列'; echo strlen($str) . PHP_EOL; echo mb_strlen($str) . PHP_EOL; echo mb_strlen($str, 'ASCII') . PHP_EOL; echo (mb_strlen(bin2hex($str)) / 2) . PHP_EOL; ?>
27 # func_overloadに2が立っていると18になってしまう 18 27 27
ただまぁ、バイト数ぐらいならまだいいけど現実的にはstrpos()とか色々あるし、既存のライブラリ等を考えるとmbstring.func_overloadは無効以外ありえない。