ねこさんとへびさんの新人技術ブログ

新人エンジニアのねこさんとへびさんの、技術向上のためのブログです。

TS!mb_strlen関数が返してくる文字数が狂ってる - PHP

PHPmb_strlen関数は、シングルバイト文字の一文字もマルチバイト文字の一文字も、同じ1個として文字の長さを返してくれる便利な関数です。
PHP: mb_strlen - Manual

ところが開発中、mb_strlen関数が返してくる文字数が狂ってるがゆえに入力値のチェックが正常に動作しない不具合が発生してしまいました。

何故か

結論から言うと、mb_strlen関数に渡している文字列のエンコードと内部エンコーディングが違っていたから。

対処法

どちらか

具体的にどう修正したか

今回は、CodeIglinerのForm Validationクラスを魔改…流用して自環境のバリデーションとして使用しておりました。
例えば最大文字数のチェックは以下の通り

<?php
public function max_length($str, $val)
{
    if (preg_match("/[^0-9]/", $val))
    {
        return FALSE;
    }

    if (function_exists('mb_strlen'))
    {
        return (mb_strlen($str) > $val) ? FALSE : TRUE;
    }

    return (strlen($str) > $val) ? FALSE : TRUE;
}
?>

$strに$_POSTから取得した入力値、$valに最大文字数が入ります。

入力チェックに使用しているmb_strlenを使っている関数は他にも多々あり、そのため一つ一つmb_strlenに引数を与えるのではなく、$_POSTから受け取った入力値を各関数に渡す前に、内部エンコーディングと同じEUC-JPに変換する方法を選択しました。

<?php
if(is_string($postval) ){
    $detect_array = array(
        'ASCII','UTF-8','UTF-16','EUC-JP','CP51932','SJIS','JIS'
     );
    $postval= mb_convert_encoding($postdata, 'EUC-JP', mb_detect_encoding($postdata, $detect_array, true));
    if(mb_detect_encoding($postdata, $detect_array, true) !== 'EUC-JP'){
        $postdata = mb_convert_encoding($postdata, 'EUC-JP', mb_detect_encoding($postdata, $detect_array, true));
    }   
}
$result = $this->$rule($postdata, $param);
?>

$result = $this->$rule($postdata, $param); が、実際に入力チェックをする個々の関数を呼び出している箇所です。

$this->form_validation->set_rules('username', 'ユーザー名', 'max_length[5]');

と指定すると
function max_length($postdata, 5) が呼ばれます。
そういった入力チェック関数が呼ばれる直前で、POSTデータが文字列であればEUC-JPにしてます。
これで入力値の文字数が指定値を超えたらFALSEを返してくれるようになりました。
ちなみに何で文字エンコーディングに差がでてしまったかというと、EUC-JPの環境でAjaxUTF-8)で値を受け取っていたからです。

まとめ

mb_strlenが狂ったら文字コードを疑え

構文: mixed mb_strlen ( string $str [, string $encoding] )

(byへびさん)