diff --git a/README.md b/README.md index 8e1067376..91dd2b5c1 100644 --- a/README.md +++ b/README.md @@ -70,6 +70,7 @@ Polyfills are provided for: - the `mb_ucfirst` and `mb_lcfirst` functions introduced in PHP 8.4; - the `array_find`, `array_find_key`, `array_any` and `array_all` functions introduced in PHP 8.4; - the `Deprecated` attribute introduced in PHP 8.4; +- the `mb_trim`, `mb_ltrim` and `mb_rtrim` functions introduced in PHP 8.4; It is strongly recommended to upgrade your PHP version and/or install the missing extensions whenever possible. This polyfill should be used only when there is no diff --git a/src/Mbstring/Mbstring.php b/src/Mbstring/Mbstring.php index 1ad33a86b..7be551bb8 100644 --- a/src/Mbstring/Mbstring.php +++ b/src/Mbstring/Mbstring.php @@ -50,6 +50,9 @@ * - mb_substr_count - Count the number of substring occurrences * - mb_ucfirst - Make a string's first character uppercase * - mb_lcfirst - Make a string's first character lowercase + * - mb_trim - Strip whitespace (or other characters) from the beginning and end of a string + * - mb_ltrim - Strip whitespace (or other characters) from the beginning of a string + * - mb_rtrim - Strip whitespace (or other characters) from the end of a string * * Not implemented: * - mb_convert_kana - Convert "kana" one from another ("zen-kaku", "han-kaku" and more) @@ -83,7 +86,7 @@ final class Mbstring public static function mb_convert_encoding($s, $toEncoding, $fromEncoding = null) { if (\is_array($s)) { - if (PHP_VERSION_ID < 70200) { + if (\PHP_VERSION_ID < 70200) { trigger_error('mb_convert_encoding() expects parameter 1 to be string, array given', \E_USER_WARNING); return null; @@ -980,17 +983,75 @@ private static function getEncoding($encoding) return $encoding; } + public static function mb_trim(string $string, ?string $characters = null, ?string $encoding = null): string + { + return self::mb_internal_trim('{^[%s]+|[%1$s]+$}Du', $string, $characters, $encoding, __FUNCTION__); + } + + public static function mb_ltrim(string $string, ?string $characters = null, ?string $encoding = null): string + { + return self::mb_internal_trim('{^[%s]+}Du', $string, $characters, $encoding, __FUNCTION__); + } + + public static function mb_rtrim(string $string, ?string $characters = null, ?string $encoding = null): string + { + return self::mb_internal_trim('{[%s]+$}D', $string, $characters, $encoding, __FUNCTION__); + } + + private static function mb_internal_trim(string $regex, string $string, ?string $characters, ?string $encoding, string $function): string + { + if (null === $encoding) { + $encoding = self::mb_internal_encoding(); + } else { + self::assertEncoding($encoding, $function.'(): Argument #3 ($encoding) must be a valid encoding, "%s" given'); + } + + if ('' === $characters) { + return null === $encoding ? $string : self::mb_convert_encoding($string, $encoding); + } + + if ('UTF-8' === $encoding) { + $encoding = null; + if (!preg_match('//u', $string)) { + $string = @iconv('UTF-8', 'UTF-8//IGNORE', $string); + } + if (null !== $characters && !preg_match('//u', $characters)) { + $characters = @iconv('UTF-8', 'UTF-8//IGNORE', $characters); + } + } else { + $string = iconv($encoding, 'UTF-8//IGNORE', $string); + + if (null !== $characters) { + $characters = iconv($encoding, 'UTF-8//IGNORE', $characters); + } + } + + if (null === $characters) { + $characters = "\\0 \f\n\r\t\v\u{00A0}\u{1680}\u{2000}\u{2001}\u{2002}\u{2003}\u{2004}\u{2005}\u{2006}\u{2007}\u{2008}\u{2009}\u{200A}\u{2028}\u{2029}\u{202F}\u{205F}\u{3000}\u{0085}\u{180E}"; + } else { + $characters = preg_quote($characters); + } + + $string = preg_replace(sprintf($regex, $characters), '', $string); + + if (null === $encoding) { + return $string; + } + + return iconv('UTF-8', $encoding.'//IGNORE', $string); + } + private static function assertEncoding(string $encoding, string $errorFormat): void { try { $validEncoding = @self::mb_check_encoding('', $encoding); } catch (\ValueError $e) { - throw new \ValueError(\sprintf($errorFormat, $encoding)); + throw new \ValueError(sprintf($errorFormat, $encoding)); } // BC for PHP 7.3 and lower if (!$validEncoding) { - throw new \ValueError(\sprintf($errorFormat, $encoding)); + throw new \ValueError(sprintf($errorFormat, $encoding)); } } } diff --git a/src/Mbstring/bootstrap.php b/src/Mbstring/bootstrap.php index 6e4b5fce8..ff51ae079 100644 --- a/src/Mbstring/bootstrap.php +++ b/src/Mbstring/bootstrap.php @@ -144,6 +144,19 @@ function mb_ucfirst(string $string, ?string $encoding = null): string { return p function mb_lcfirst(string $string, ?string $encoding = null): string { return p\Mbstring::mb_lcfirst($string, $encoding); } } +if (!function_exists('mb_trim')) { + function mb_trim(string $string, ?string $characters = null, ?string $encoding = null): string { return p\Mbstring::mb_trim($string, $characters, $encoding); } +} + +if (!function_exists('mb_ltrim')) { + function mb_ltrim(string $string, ?string $characters = null, ?string $encoding = null): string { return p\Mbstring::mb_ltrim($string, $characters, $encoding); } +} + +if (!function_exists('mb_rtrim')) { + function mb_rtrim(string $string, ?string $characters = null, ?string $encoding = null): string { return p\Mbstring::mb_rtrim($string, $characters, $encoding); } +} + + if (extension_loaded('mbstring')) { return; } diff --git a/src/Mbstring/bootstrap80.php b/src/Mbstring/bootstrap80.php index ec2ae4276..8016bdb48 100644 --- a/src/Mbstring/bootstrap80.php +++ b/src/Mbstring/bootstrap80.php @@ -140,6 +140,18 @@ function mb_ucfirst($string, ?string $encoding = null): string { return p\Mbstri function mb_lcfirst($string, ?string $encoding = null): string { return p\Mbstring::mb_lcfirst($string, $encoding); } } +if (!function_exists('mb_trim')) { + function mb_trim(string $string, ?string $characters = null, ?string $encoding = null): string { return p\Mbstring::mb_trim($string, $characters, $encoding); } +} + +if (!function_exists('mb_ltrim')) { + function mb_ltrim(string $string, ?string $characters = null, ?string $encoding = null): string { return p\Mbstring::mb_ltrim($string, $characters, $encoding); } +} + +if (!function_exists('mb_rtrim')) { + function mb_rtrim(string $string, ?string $characters = null, ?string $encoding = null): string { return p\Mbstring::mb_rtrim($string, $characters, $encoding); } +} + if (extension_loaded('mbstring')) { return; } diff --git a/src/Php72/bootstrap.php b/src/Php72/bootstrap.php index b5c92d4c7..00d793c41 100644 --- a/src/Php72/bootstrap.php +++ b/src/Php72/bootstrap.php @@ -46,12 +46,15 @@ function utf8_decode($string) { return p\Php72::utf8_decode($string); } if (!function_exists('spl_object_id')) { function spl_object_id($object) { return p\Php72::spl_object_id($object); } } -if (!function_exists('mb_ord')) { - function mb_ord($string, $encoding = null) { return p\Php72::mb_ord($string, $encoding); } -} -if (!function_exists('mb_chr')) { - function mb_chr($codepoint, $encoding = null) { return p\Php72::mb_chr($codepoint, $encoding); } -} -if (!function_exists('mb_scrub')) { - function mb_scrub($string, $encoding = null) { $encoding = null === $encoding ? mb_internal_encoding() : $encoding; return mb_convert_encoding($string, $encoding, $encoding); } + +if (extension_loaded('mbstring')) { + if (!function_exists('mb_ord')) { + function mb_ord($string, $encoding = null) { return p\Php72::mb_ord($string, $encoding); } + } + if (!function_exists('mb_chr')) { + function mb_chr($codepoint, $encoding = null) { return p\Php72::mb_chr($codepoint, $encoding); } + } + if (!function_exists('mb_scrub')) { + function mb_scrub($string, $encoding = null) { $encoding = null === $encoding ? mb_internal_encoding() : $encoding; return mb_convert_encoding($string, $encoding, $encoding); } + } } diff --git a/src/Php74/bootstrap.php b/src/Php74/bootstrap.php index f6a11f157..c894d99f3 100644 --- a/src/Php74/bootstrap.php +++ b/src/Php74/bootstrap.php @@ -18,9 +18,11 @@ if (!function_exists('get_mangled_object_vars')) { function get_mangled_object_vars($object) { return p\Php74::get_mangled_object_vars($object); } } -if (!function_exists('mb_str_split') && function_exists('mb_substr')) { - function mb_str_split($string, $length = 1, $encoding = null) { return p\Php74::mb_str_split($string, $length, $encoding); } -} if (!function_exists('password_algos')) { function password_algos() { return p\Php74::password_algos(); } } +if (extension_loaded('mbstring')) { + if (!function_exists('mb_str_split')) { + function mb_str_split($string, $length = 1, $encoding = null) { return p\Php74::mb_str_split($string, $length, $encoding); } + } +} diff --git a/src/Php83/bootstrap.php b/src/Php83/bootstrap.php index f43af17e0..a92799cb3 100644 --- a/src/Php83/bootstrap.php +++ b/src/Php83/bootstrap.php @@ -19,8 +19,10 @@ function json_validate(string $json, int $depth = 512, int $flags = 0): bool { return p\Php83::json_validate($json, $depth, $flags); } } -if (!function_exists('mb_str_pad') && function_exists('mb_substr')) { - function mb_str_pad(string $string, int $length, string $pad_string = ' ', int $pad_type = STR_PAD_RIGHT, ?string $encoding = null): string { return p\Php83::mb_str_pad($string, $length, $pad_string, $pad_type, $encoding); } +if (extension_loaded('mbstring')) { + if (!function_exists('mb_str_pad')) { + function mb_str_pad(string $string, int $length, string $pad_string = ' ', int $pad_type = STR_PAD_RIGHT, ?string $encoding = null): string { return p\Php83::mb_str_pad($string, $length, $pad_string, $pad_type, $encoding); } + } } if (!function_exists('stream_context_set_options')) { diff --git a/src/Php84/Php84.php b/src/Php84/Php84.php index 992675574..1bca70b56 100644 --- a/src/Php84/Php84.php +++ b/src/Php84/Php84.php @@ -13,6 +13,7 @@ /** * @author Ayesh Karunaratne + * @author Pierre Ambroise * * @internal */ @@ -107,4 +108,65 @@ public static function array_all(array $array, callable $callback): bool return true; } + + public static function mb_trim(string $string, ?string $characters = null, ?string $encoding = null): string + { + return self::mb_internal_trim('{^[%s]+|[%1$s]+$}Du', $string, $characters, $encoding, __FUNCTION__); + } + + public static function mb_ltrim(string $string, ?string $characters = null, ?string $encoding = null): string + { + return self::mb_internal_trim('{^[%s]+}Du', $string, $characters, $encoding, __FUNCTION__); + } + + public static function mb_rtrim(string $string, ?string $characters = null, ?string $encoding = null): string + { + return self::mb_internal_trim('{[%s]+$}Du', $string, $characters, $encoding, __FUNCTION__); + } + + private static function mb_internal_trim(string $regex, string $string, ?string $characters, ?string $encoding, string $function): string + { + if (null === $encoding) { + $encoding = mb_internal_encoding(); + } + + try { + $validEncoding = @mb_check_encoding('', $encoding); + } catch (\ValueError $e) { + throw new \ValueError(sprintf('%s(): Argument #3 ($encoding) must be a valid encoding, "%s" given', $function, $encoding)); + } + + // BC for PHP 7.3 and lower + if (!$validEncoding) { + throw new \ValueError(sprintf('%s(): Argument #3 ($encoding) must be a valid encoding, "%s" given', $function, $encoding)); + } + + if ('' === $characters) { + return null === $encoding ? $string : mb_convert_encoding($string, $encoding); + } + + if ('UTF-8' === $encoding || \in_array(strtolower($encoding), ['utf-8', 'utf8'], true)) { + $encoding = 'UTF-8'; + } + + $string = mb_convert_encoding($string, 'UTF-8', $encoding); + + if (null !== $characters) { + $characters = mb_convert_encoding($characters, 'UTF-8', $encoding); + } + + if (null === $characters) { + $characters = "\\0 \f\n\r\t\v\u{00A0}\u{1680}\u{2000}\u{2001}\u{2002}\u{2003}\u{2004}\u{2005}\u{2006}\u{2007}\u{2008}\u{2009}\u{200A}\u{2028}\u{2029}\u{202F}\u{205F}\u{3000}\u{0085}\u{180E}"; + } else { + $characters = preg_quote($characters); + } + + $string = preg_replace(sprintf($regex, $characters), '', $string); + + if ('UTF-8' === $encoding) { + return $string; + } + + return mb_convert_encoding($string, $encoding, 'UTF-8'); + } } diff --git a/src/Php84/Resources/stubs/Deprecated.php b/src/Php84/Resources/stubs/Deprecated.php index aa039e3e5..f3e6a4f8e 100644 --- a/src/Php84/Resources/stubs/Deprecated.php +++ b/src/Php84/Resources/stubs/Deprecated.php @@ -15,7 +15,7 @@ final class Deprecated { public readonly ?string $message; public readonly ?string $since; - + public function __construct(?string $message = null, ?string $since = null) { $this->message = $message; diff --git a/src/Php84/bootstrap.php b/src/Php84/bootstrap.php index fc337c50d..5a86f206f 100644 --- a/src/Php84/bootstrap.php +++ b/src/Php84/bootstrap.php @@ -15,14 +15,6 @@ return; } -if (!function_exists('mb_ucfirst')) { - function mb_ucfirst($string, ?string $encoding = null): string { return p\Php84::mb_ucfirst($string, $encoding); } -} - -if (!function_exists('mb_lcfirst')) { - function mb_lcfirst($string, ?string $encoding = null): string { return p\Php84::mb_lcfirst($string, $encoding); } -} - if (!function_exists('array_find')) { function array_find(array $array, callable $callback) { return p\Php84::array_find($array, $callback); } } @@ -38,3 +30,25 @@ function array_any(array $array, callable $callback): bool { return p\Php84::arr if (!function_exists('array_all')) { function array_all(array $array, callable $callback): bool { return p\Php84::array_all($array, $callback); } } + +if (extension_loaded('mbstring')) { + if (!function_exists('mb_ucfirst')) { + function mb_ucfirst($string, ?string $encoding = null): string { return p\Php84::mb_ucfirst($string, $encoding); } + } + + if (!function_exists('mb_lcfirst')) { + function mb_lcfirst($string, ?string $encoding = null): string { return p\Php84::mb_lcfirst($string, $encoding); } + } + + if (!function_exists('mb_trim')) { + function mb_trim(string $string, ?string $characters = null, ?string $encoding = null): string { return p\Php84::mb_trim($string, $characters, $encoding); } + } + + if (!function_exists('mb_ltrim')) { + function mb_ltrim(string $string, ?string $characters = null, ?string $encoding = null): string { return p\Php84::mb_ltrim($string, $characters, $encoding); } + } + + if (!function_exists('mb_rtrim')) { + function mb_rtrim(string $string, ?string $characters = null, ?string $encoding = null): string { return p\Php84::mb_rtrim($string, $characters, $encoding); } + } +} diff --git a/tests/Mbstring/MbstringTest.php b/tests/Mbstring/MbstringTest.php index 8aa26d578..aea3f4caf 100644 --- a/tests/Mbstring/MbstringTest.php +++ b/tests/Mbstring/MbstringTest.php @@ -808,4 +808,127 @@ public static function lcFirstDataProvider(): array ['ß', 'ß'], ]; } + + /** + * @covers \Symfony\Polyfill\Php84\Php84::mb_trim + * + * @dataProvider mbTrimProvider + */ + public function testMbTrim(string $expected, string $string, ?string $characters = null, ?string $encoding = null): void + { + $this->assertSame($expected, mb_trim($string, $characters, $encoding)); + } + + /** + * @covers \Symfony\Polyfill\Php84\Php84::mb_ltrim + * + * @dataProvider mbLTrimProvider + */ + public function testMbLTrim(string $expected, string $string, ?string $characters = null, ?string $encoding = null): void + { + $this->assertSame($expected, mb_ltrim($string, $characters, $encoding)); + } + + /** + * @covers \Symfony\Polyfill\Php84\Php84::mb_rtrim + * + * @dataProvider mbRTrimProvider + */ + public function testMbRTrim(string $expected, string $string, ?string $characters = null, ?string $encoding = null): void + { + $this->assertSame($expected, mb_rtrim($string, $characters, $encoding)); + } + + public function testMbTrimException(): void + { + $this->expectException(\ValueError::class); + mb_trim("\u{180F}", '', 'NULL'); + } + + public function testMbTrimEncoding(): void + { + $this->assertSame('あ', mb_convert_encoding(mb_trim("\x81\x40\x82\xa0\x81\x40", "\x81\x40", 'SJIS'), 'UTF-8', 'SJIS')); + $this->assertSame('226f575b', bin2hex(mb_ltrim(mb_convert_encoding("\u{FFFE}漢字", 'UTF-16LE', 'UTF-8'), mb_convert_encoding("\u{FFFE}\u{FEFF}", 'UTF-16LE', 'UTF-8'), 'UTF-16LE'))); + $this->assertSame('6f225b57', bin2hex(mb_ltrim(mb_convert_encoding("\u{FEFF}漢字", 'UTF-16BE', 'UTF-8'), mb_convert_encoding("\u{FFFE}\u{FEFF}", 'UTF-16BE', 'UTF-8'), 'UTF-16BE'))); + } + + public function testMbTrimCharactersEncoding(): void + { + $strUtf8 = "\u{3042}\u{3000}"; + + $this->assertSame(1, mb_strlen(mb_trim($strUtf8))); + $this->assertSame(1, mb_strlen(mb_trim($strUtf8, null, 'UTF-8'))); + + $old = mb_internal_encoding(); + mb_internal_encoding('Shift_JIS'); + $strSjis = mb_convert_encoding($strUtf8, 'Shift_JIS', 'UTF-8'); + + $this->assertSame(1, mb_strlen(mb_trim($strSjis))); + $this->assertSame(1, mb_strlen(mb_trim($strSjis, null, 'Shift_JIS'))); + mb_internal_encoding($old); + } + + public static function mbTrimProvider(): iterable + { + yield ['ABC', 'ABC']; + yield ['ABC', "\0\t\nABC \0\t\n"]; + yield ["\0\t\nABC \0\t\n", "\0\t\nABC \0\t\n", '']; + + yield ['', '']; + + yield ['あいうえおあお', ' あいうえおあお ', ' ', 'UTF-8']; + yield ['foo BAR Spa', 'foo BAR Spaß', 'ß', 'UTF-8']; + yield ['oo BAR Spaß', 'oo BAR Spaß', 'f', 'UTF-8']; + + yield ['oo BAR Spa', 'foo BAR Spaß', 'ßf', 'UTF-8']; + yield ['oo BAR Spa', 'foo BAR Spaß', 'fß', 'UTF-8']; + yield ['いうおえお', ' あいうおえお あ', ' あ', 'UTF-8']; + yield ['いうおえお', ' あいうおえお あ', 'あ ', 'UTF-8']; + yield [' あいうおえお ', ' あいうおえお a', 'あa', 'UTF-8']; + yield [' あいうおえお a', ' あいうおえお a', "\xe3", 'UTF-8']; + + yield ['', str_repeat(' ', 129)]; + yield ['a', str_repeat(' ', 129).'a']; + + yield ['', " \f\n\r\v\x00\u{00A0}\u{1680}\u{2000}\u{2001}\u{2002}\u{2003}\u{2004}\u{2005}\u{2006}\u{2007}\u{2008}\u{2009}\u{200A}\u{2028}\u{2029}\u{202F}\u{205F}\u{3000}\u{0085}\u{180E}"]; + + yield [' abcd ', ' abcd ', '']; + + yield ['f', 'foo', 'oo']; + + yield ["foo\n", "foo\n", 'o']; + } + + public static function mbLTrimProvider(): iterable + { + yield ['ABC', 'ABC']; + yield ["ABC \0\t\n", "\0\t\nABC \0\t\n"]; + yield ["\0\t\nABC \0\t\n", "\0\t\nABC \0\t\n", '']; + + yield ['', '']; + + yield [' test ', ' test ', '']; + + yield ['いああああ', 'あああああああああああああああああああああああああああああああああいああああ', 'あ']; + + yield ['漢字', "\u{FFFE}漢字", "\u{FFFE}\u{FEFF}"]; + yield [' abcd ', ' abcd ', '']; + } + + public static function mbRTrimProvider(): iterable + { + yield ['ABC', 'ABC']; + yield ['ABC', "ABC \0\t\n"]; + yield ["\0\t\nABC \0\t\n", "\0\t\nABC \0\t\n", '']; + + yield ['', '']; + + yield [' a', str_repeat(' ', 129).'a']; + + yield ['あああああああああああああああああああああああああああああああああい', 'あああああああああああああああああああああああああああああああああいああああ', 'あ']; + + yield [' abcd ', ' abcd ', '']; + + yield ["foo\n", "foo\n", 'o']; + } } diff --git a/tests/Php84/Php84Test.php b/tests/Php84/Php84Test.php index 73d919452..0989113a0 100644 --- a/tests/Php84/Php84Test.php +++ b/tests/Php84/Php84Test.php @@ -182,4 +182,127 @@ public static function arrayAllDataProvider(): array [[1 => '1', 2 => '12', 3 => '123', 4 => '1234'], $callableKey, true], ]; } + + /** + * @covers \Symfony\Polyfill\Php84\Php84::mb_trim + * + * @dataProvider mbTrimProvider + */ + public function testMbTrim(string $expected, string $string, ?string $characters = null, ?string $encoding = null): void + { + $this->assertSame($expected, mb_trim($string, $characters, $encoding)); + } + + /** + * @covers \Symfony\Polyfill\Php84\Php84::mb_ltrim + * + * @dataProvider mbLTrimProvider + */ + public function testMbLTrim(string $expected, string $string, ?string $characters = null, ?string $encoding = null): void + { + $this->assertSame($expected, mb_ltrim($string, $characters, $encoding)); + } + + /** + * @covers \Symfony\Polyfill\Php84\Php84::mb_rtrim + * + * @dataProvider mbRTrimProvider + */ + public function testMbRTrim(string $expected, string $string, ?string $characters = null, ?string $encoding = null): void + { + $this->assertSame($expected, mb_rtrim($string, $characters, $encoding)); + } + + public function testMbTrimException(): void + { + $this->expectException(\ValueError::class); + mb_trim("\u{180F}", '', 'NULL'); + } + + public function testMbTrimEncoding(): void + { + $this->assertSame('あ', mb_convert_encoding(mb_trim("\x81\x40\x82\xa0\x81\x40", "\x81\x40", 'SJIS'), 'UTF-8', 'SJIS')); + $this->assertSame('226f575b', bin2hex(mb_ltrim(mb_convert_encoding("\u{FFFE}漢字", 'UTF-16LE', 'UTF-8'), mb_convert_encoding("\u{FFFE}\u{FEFF}", 'UTF-16LE', 'UTF-8'), 'UTF-16LE'))); + $this->assertSame('6f225b57', bin2hex(mb_ltrim(mb_convert_encoding("\u{FEFF}漢字", 'UTF-16BE', 'UTF-8'), mb_convert_encoding("\u{FFFE}\u{FEFF}", 'UTF-16BE', 'UTF-8'), 'UTF-16BE'))); + } + + public function testMbTrimCharactersEncoding(): void + { + $strUtf8 = "\u{3042}\u{3000}"; + + $this->assertSame(1, mb_strlen(mb_trim($strUtf8))); + $this->assertSame(1, mb_strlen(mb_trim($strUtf8, null, 'UTF-8'))); + + $old = mb_internal_encoding(); + mb_internal_encoding('Shift_JIS'); + $strSjis = mb_convert_encoding($strUtf8, 'Shift_JIS', 'UTF-8'); + + $this->assertSame(1, mb_strlen(mb_trim($strSjis))); + $this->assertSame(1, mb_strlen(mb_trim($strSjis, null, 'Shift_JIS'))); + mb_internal_encoding($old); + } + + public static function mbTrimProvider(): iterable + { + yield ['ABC', 'ABC']; + yield ['ABC', "\0\t\nABC \0\t\n"]; + yield ["\0\t\nABC \0\t\n", "\0\t\nABC \0\t\n", '']; + + yield ['', '']; + + yield ['あいうえおあお', ' あいうえおあお ', ' ', 'UTF-8']; + yield ['foo BAR Spa', 'foo BAR Spaß', 'ß', 'UTF-8']; + yield ['oo BAR Spaß', 'oo BAR Spaß', 'f', 'UTF-8']; + + yield ['oo BAR Spa', 'foo BAR Spaß', 'ßf', 'UTF-8']; + yield ['oo BAR Spa', 'foo BAR Spaß', 'fß', 'UTF-8']; + yield ['いうおえお', ' あいうおえお あ', ' あ', 'UTF-8']; + yield ['いうおえお', ' あいうおえお あ', 'あ ', 'UTF-8']; + yield [' あいうおえお ', ' あいうおえお a', 'あa', 'UTF-8']; + yield [' あいうおえお a', ' あいうおえお a', "\xe3", 'UTF-8']; + + yield ['', str_repeat(' ', 129)]; + yield ['a', str_repeat(' ', 129).'a']; + + yield ['', " \f\n\r\v\x00\u{00A0}\u{1680}\u{2000}\u{2001}\u{2002}\u{2003}\u{2004}\u{2005}\u{2006}\u{2007}\u{2008}\u{2009}\u{200A}\u{2028}\u{2029}\u{202F}\u{205F}\u{3000}\u{0085}\u{180E}"]; + + yield [' abcd ', ' abcd ', '']; + + yield ['f', 'foo', 'oo']; + + yield ["foo\n", "foo\n", 'o']; + } + + public static function mbLTrimProvider(): iterable + { + yield ['ABC', 'ABC']; + yield ["ABC \0\t\n", "\0\t\nABC \0\t\n"]; + yield ["\0\t\nABC \0\t\n", "\0\t\nABC \0\t\n", '']; + + yield ['', '']; + + yield [' test ', ' test ', '']; + + yield ['いああああ', 'あああああああああああああああああああああああああああああああああいああああ', 'あ']; + + yield ['漢字', "\u{FFFE}漢字", "\u{FFFE}\u{FEFF}"]; + yield [' abcd ', ' abcd ', '']; + } + + public static function mbRTrimProvider(): iterable + { + yield ['ABC', 'ABC']; + yield ['ABC', "ABC \0\t\n"]; + yield ["\0\t\nABC \0\t\n", "\0\t\nABC \0\t\n", '']; + + yield ['', '']; + + yield [' a', str_repeat(' ', 129).'a']; + + yield ['あああああああああああああああああああああああああああああああああい', 'あああああああああああああああああああああああああああああああああいああああ', 'あ']; + + yield [' abcd ', ' abcd ', '']; + + yield ["foo\n", "foo\n", 'o']; + } }