mirror of
https://github.com/php/php-src.git
synced 2026-04-26 01:18:19 +02:00
fa0401b0b5
The existing implementation of mb_strcut extracts part of a multi-byte encoded string by pulling out raw bytes and then running them through a conversion filter to ensure that the output is valid in the requested encoding. If the conversion filter emits error markers when doing the final 'flush' operation which ends the conversion of the extracted bytes, these error markers may (in some cases) be included in the output. The conversion operation does not respect the value of mb_substitute_character; rather, it always uses '?' as an error marker. So this issue manifests itself as unwanted '?' characters being inserted into the output. This issue has existed for a long time, but became noticeable in PHP 8.1 because for at least some of the supported text encodings, mbstring is now more strict about emitting error markers when strings end in an illegal state. The simplest fix is to suppress error markers during the final flush operation. While working on a fix for this problem, another problem with mb_strcut was discovered; since it decides when to stop consuming bytes from the input by looking at the byte length of its OUTPUT, anything which causes extra bytes to be emitted to the output may cause mb_strcut to not consume all the bytes in the requested range. The one case where we DO emit extra output bytes is for encodings which have a selectable mode, like ISO-2022-JP; if a string in such an encoding ends in a mode which is not the default, we emit an ending escape sequence which changes back to the default mode. This is done so that concatenating strings in such encodings is safe. However, as mentioned, this can cause the output of mb_strcut to be shorter than it logically should be. This bug has existed for a long time, and fixing it now will be a BC break, so we may not fix it right away. Therefore, tests for THIS fix which don't pass because of that OTHER bug have been split out into a separate test file (gh9535b.phpt), and that file has been marked XFAIL.
107 lines
3.3 KiB
PHP
107 lines
3.3 KiB
PHP
--TEST--
|
|
Output of mb_strcut covers requested range of bytes even when output contains ending escape sequences
|
|
--EXTENSIONS--
|
|
mbstring
|
|
--FILE--
|
|
<?php
|
|
// The existing behavior of mb_strcut is wrong for these encodings, when they add an extra closing
|
|
// escape sequence to a string which would otherwise end in a non-default conversion mode
|
|
// See https://github.com/php/php-src/pull/9562 for details on the bug
|
|
|
|
// These tests were developed when fixing a different bug, but they don't pass because of
|
|
// the bug involving the added closing escape sequences
|
|
// When that bug is fixed, we can remove XFAIL (or combine this file with gh9535.phpt)
|
|
|
|
$encodings = [
|
|
'JIS',
|
|
'ISO-2022-JP',
|
|
'ISO-2022-JP-2004',
|
|
];
|
|
|
|
$input = '宛如繁星般宛如皎月般';
|
|
$bytes_length = 15;
|
|
foreach($encodings as $encoding) {
|
|
$converted_str = mb_convert_encoding($input, $encoding, mb_internal_encoding());
|
|
$cut_str = mb_strcut($converted_str, 0, $bytes_length, $encoding);
|
|
$reconverted_str = mb_convert_encoding($cut_str, mb_internal_encoding(), $encoding);
|
|
echo $encoding.': '.$reconverted_str.PHP_EOL;
|
|
}
|
|
|
|
echo PHP_EOL;
|
|
|
|
$input = '星のように月のように';
|
|
$bytes_length = 20;
|
|
foreach($encodings as $encoding) {
|
|
$converted_str = mb_convert_encoding($input, $encoding, mb_internal_encoding());
|
|
$cut_str = mb_strcut($converted_str, 0, $bytes_length, $encoding);
|
|
$reconverted_str = mb_convert_encoding($cut_str, mb_internal_encoding(), $encoding);
|
|
echo $encoding.': '.$reconverted_str.PHP_EOL;
|
|
}
|
|
|
|
echo PHP_EOL;
|
|
|
|
$input = 'あaいb';
|
|
$bytes_length = 10;
|
|
foreach($encodings as $encoding) {
|
|
$converted_str = mb_convert_encoding($input, $encoding, mb_internal_encoding());
|
|
$cut_str = mb_strcut($converted_str, 0, $bytes_length, $encoding);
|
|
$reconverted_str = mb_convert_encoding($cut_str, mb_internal_encoding(), $encoding);
|
|
echo $encoding.': '.$reconverted_str.PHP_EOL;
|
|
}
|
|
|
|
echo PHP_EOL;
|
|
|
|
$input = 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA';
|
|
$bytes_length = 10;
|
|
foreach($encodings as $encoding) {
|
|
$converted_str = mb_convert_encoding($input, $encoding, mb_internal_encoding());
|
|
$cut_str = mb_strcut($converted_str, 0, $bytes_length, $encoding);
|
|
$reconverted_str = mb_convert_encoding($cut_str, mb_internal_encoding(), $encoding);
|
|
echo $encoding.': '.$reconverted_str.PHP_EOL;
|
|
}
|
|
|
|
echo PHP_EOL;
|
|
|
|
$input = '???';
|
|
$bytes_length = 2;
|
|
foreach($encodings as $encoding) {
|
|
$converted_str = mb_convert_encoding($input, $encoding, mb_internal_encoding());
|
|
$cut_str = mb_strcut($converted_str, 0, $bytes_length, $encoding);
|
|
$reconverted_str = mb_convert_encoding($cut_str, mb_internal_encoding(), $encoding);
|
|
echo $encoding.': '.$reconverted_str.PHP_EOL;
|
|
}
|
|
|
|
echo PHP_EOL;
|
|
|
|
foreach($encodings as $encoding) {
|
|
var_dump(mb_strcut($input, 0, $bytes_length, $encoding));
|
|
}
|
|
|
|
?>
|
|
--XFAIL--
|
|
Discussion: https://github.com/php/php-src/pull/9562
|
|
--EXPECTF--
|
|
JIS: 宛如繁星般
|
|
ISO-2022-JP: 宛如繁星般
|
|
ISO-2022-JP-2004: 宛如繁星
|
|
|
|
JIS: 星のように月の
|
|
ISO-2022-JP: 星のように月の
|
|
ISO-2022-JP-2004: 星のように月の
|
|
|
|
JIS: あa
|
|
ISO-2022-JP: あa
|
|
ISO-2022-JP-2004: あa
|
|
|
|
JIS: AAAAAAAAAA
|
|
ISO-2022-JP: AAAAAAAAAA
|
|
ISO-2022-JP-2004: AAAAAAAAAA
|
|
|
|
JIS: ??
|
|
ISO-2022-JP: ??
|
|
ISO-2022-JP-2004: ??
|
|
|
|
string(2) "??"
|
|
string(2) "??"
|
|
string(2) "??"
|