1
0
mirror of https://github.com/php/php-src.git synced 2026-04-26 01:18:19 +02:00
Files
archived-php-src/ext/mbstring/tests/gh9535b.phpt
T
NathanFreeman fa0401b0b5 Fix GH-9535 (unintended behavior change for mb_strcut in PHP 8.1)
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.
2022-11-13 14:37:55 +02:00

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) "??"