環境
PHP:7.1.21
内容
下記のような処理がありました(簡素化してます)。
入力された文字列を、mb_substr() で1文字ずつ切り取ってチェックしていました。
処理速度に問題はありませんでした。
//住所・氏名など
$src = '入力された文字列';
$count = mb_strlen($src);
//1文字ずつチェック
for ($i=0; $i<$count; $i++) {
$moji = mb_substr($src,$i,1);
}
ある日、上記処理に、100KB以上の文字列を渡しました。
するとタイムアウトが発生しました。
Fatal error: Maximum execution time of xx seconds exceeded in /foo/bar/xxxx.php on line xxx
何でそんなに時間かかるの? と思いましたが、
原因を推測すると、こういうことなんじゃないかと。
mb_substr() は何かしら文字コード変換をおこなうのですが、
「1文字 切り取って文字コード変換」ではなくて、
「文字列全部をコード変換して、1文字 切り取る」なのでしょう。
つまり、上記処理にマルチバイトの 100,000文字が渡されたら、
100,000文字 x 100,000ループ = 10,000,000,000文字
100億文字のコード変換をおこなうことになります(推測ですが)。
(100億文字は Shift_JIS だと約20GBかな?)
文字数の2乗に比例して遅くなる計算です。
そりゃタイムアウトになります
ちなみに上記処理の mb_substr() を substr() にすると、一瞬で完了します。
やはりマルチバイトの処理で時間がかかっているようです。
さいごに
mb_substr() が遅いというより、私の使い方に問題があるとも言えます。
ちなみに100KB以上の文字列を、分割して数回に分けて渡したら、
タイムアウトはしなくなりました。