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は無効以外ありえない。

*1:文字エンコーディングを考慮してバイト数ではなく文字数を返す関数