[PHP-dev 1488] mbstring の文字列変換、文字エンコーディング検出の関連の問題について

komura komura.db2r1e @ gmail.com
2009年 8月 16日 (日) 22:31:44 JST


komura です。

mbstring の文字列変換、文字エンコーディング検出の関連でいくつか気になる問題を
見つけましたので、参考までに Patch を作成してみました(4. を除く)。

PHP 5.3.0 で確認した挙動ですが、PHP 5.2.10 以前でも同じです。
いくつかは仕様かもしれませんが・・・。

1. mb_convert_encoding() で UTF-16(Little Endian) の文字列を変換すると結果が
   壊れる

   UTF-16(Little Endian) で作成した文字列を変換すると、1文字目が壊れます。
   Shift_JIS や EUC-JP に変換した場合でも、1文字目が壊れます。
   変換元文字列が UTF-16(Big Endian) の場合は問題ありません。

   * 再現コード
     // UTF-16(Little Endian) で「テスト」を UTF-16 から UTF-16 へ変換
     $str = "\xFF\xFE\xC6\x30\xB9\x30\xC8\x30";
     var_dump( bin2hex( mb_convert_encoding( $str, "UTF-16", "UTF-16" ) ) );

   * 結果
     string(16) "feffffc630b930c8"
                     ^^←違い
   * 期待する結果
     string(16) "feff30c630b930c8"

   * Patch
     php530_mbfilter_utf16_invalid_conversion.patch

   BOM(\xfe\xff) の次の1バイトが必ず \xff になります。

   期待する結果としては、エンディアンが変換されない "fffec630b930c830" の方が
   都合が良いかもしれませんが、UTF-16 から UTF-16 への変換は、変換元文字列が
   UTF-16(Little Endian) の場合、UTF-16(Big Endian) に変換されます。

2. mb_detect_encoding() で、第3引数(strict)を TRUE にしている場合、最後の
   1文字(バイト)が不正でも不正にならない

   mb_detect_encoding() で、第3引数(strict)を TRUE を指定している場合、
   先行バイト(例:Shift_JIS の \x81)に続く文字列が不正な場合、返り値は
   不正になります。

   しかし、最後の1文字(バイト)が先行バイトのみで終わるバイト列の場合、
   不正になりません。例は Shift_JIS ですが、EUC-JP や UTF-8 でも同様です。

   * 再現コード
     // \x81 で終っているため、不正(false)を期待
     var_dump( mb_detect_encoding( "A\x81", "SJIS", TRUE ) );

   * 結果
     string(4) "SJIS"

   * 期待する結果
     bool(false)

   * Patch
     php530_mbfilter_mb_detect_encoding_strict.patch

   mb_detect_encoding( "\x81 A", "SJIS", TRUE ) のようにすれば不正(false)に
   なります。最後の1文字(バイト)が不正な場合に関数が false を返さないという
   問題です。

3. mb_detect_encoding() で、strict 指定しているのに、一部の不正な UTF-8 の
   文字列が不正にならない

   2. の Patch を当てた場合に顕在化する問題です。mb_detect_encoding() で、
   第3引数(strict)を TRUE にしている場合でも、UTF-8 の不正な文字列
   (\xc0\x00, \xd0\x00, ..., \xfd\x00) が不正になりません。

   * 再現コード
     var_dump( mb_detect_encoding( "\xc0\x00", "UTF-8", TRUE );

   * 結果
     string(5) "UTF-8"

   * 期待する結果
     bool(false)

   * Patch
     php530_mbfilter_utf8_pass_invalid_character.patch

4. mb_check_encoding() で UTF-16(Little Endian) を判定すると、必ず false を
   返す

   mb_check_encoding() では、変換前の文字列と変換後の文字列が同じだった場合
   に true を返しますが、内部的に、UTF-16(Little Endian) が UTF-16(Big Endian)
   に変換されるため、必ず false になります。

   UTF-16(Big Endian) や、UTF-16(Little Endian) の文字列を UTF-16LE として
   判定した場合には、問題ありません。

   * 再現コード
     // UTF-16(Little Endian) で「テスト」
     $str = "\xFF\xFE\xC6\x30\xB9\x30\xC8\x30";
     var_dump( mb_check_encoding( $str, "UTF-16" ) );

   * 結果
     bool(false)

   * 期待する結果
     bool(true)

   * Patch
     作成していません

-- 
komura <komura.db2r1e @ gmail.com>
-------------- next part --------------
A non-text attachment was scrubbed...
Name: php530_mbfilter_utf16_invalid_conversion.patch
Type: text/x-patch
Size: 588 bytes
Desc: 無し
URL: <http://ml.php.gr.jp/pipermail/php-dev/attachments/20090816/f98024cb/attachment.bin>
-------------- next part --------------
A non-text attachment was scrubbed...
Name: php530_mbfilter_mb_detect_encoding_strict.patch
Type: text/x-patch
Size: 515 bytes
Desc: 無し
URL: <http://ml.php.gr.jp/pipermail/php-dev/attachments/20090816/f98024cb/attachment-0001.bin>
-------------- next part --------------
A non-text attachment was scrubbed...
Name: php530_mbfilter_utf8_pass_invalid_character.patch
Type: text/x-patch
Size: 540 bytes
Desc: 無し
URL: <http://ml.php.gr.jp/pipermail/php-dev/attachments/20090816/f98024cb/attachment-0002.bin>


PHP-dev メーリングリストの案内