[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 メーリングリストの案内