Report a clear error when a string cannot be split into characters

This commit is contained in:
Fabien Potencier
2026-09-18 17:15:50 +01:00
parent 8eb76e2b7e
commit 5c1be030c5
6 changed files with 53 additions and 8 deletions
+1
View File
@@ -1,5 +1,6 @@
# 3.30.0 (2026-XX-XX)
* Report a clear error when `random`, `reverse`, `shuffle`, and `split` receive a string that is not valid UTF-8
* Fix the deprecation about omitting parentheses when calling a macro being reported twice for the same call
* Add the macro name to the deprecation about omitting parentheses when calling a macro
* Deprecate cloning a `Twig\Environment` instance
+20 -8
View File
@@ -495,9 +495,7 @@ final class CoreExtension extends AbstractExtension
$values = self::convertEncoding($values, 'UTF-8', $charset);
}
// unicode version of str_split()
// split at all positions, but not after the start and not before the end
$values = preg_split('/(?<!^)(?!$)/u', $values);
$values = self::splitIntoCharacters($values, 'random');
if ('UTF-8' !== $charset) {
foreach ($values as $i => $value) {
@@ -897,7 +895,7 @@ final class CoreExtension extends AbstractExtension
}
if ($limit <= 1) {
return preg_split('/(?<!^)(?!$)/u', $value);
return self::splitIntoCharacters($value, 'split');
}
$length = mb_strlen($value, $charset);
@@ -1004,9 +1002,7 @@ final class CoreExtension extends AbstractExtension
$string = self::convertEncoding($string, 'UTF-8', $charset);
}
preg_match_all('/./us', $string, $matches);
$string = implode('', array_reverse($matches[0]));
$string = implode('', array_reverse(self::splitIntoCharacters($string, 'reverse')));
if ('UTF-8' !== $charset) {
$string = self::convertEncoding($string, $charset, 'UTF-8');
@@ -1030,7 +1026,7 @@ final class CoreExtension extends AbstractExtension
$item = self::convertEncoding($item, 'UTF-8', $charset);
}
$item = preg_split('/(?<!^)(?!$)/u', $item, -1);
$item = self::splitIntoCharacters($item, 'shuffle');
shuffle($item);
$item = implode('', $item);
@@ -1274,6 +1270,22 @@ final class CoreExtension extends AbstractExtension
return iconv($from, $to, $string ?? '');
}
/**
* Unicode version of str_split(): splits at every position except after the start and before the end.
*
* @return list<string>
*
* @throws RuntimeError When the string cannot be split into characters
*/
private static function splitIntoCharacters(string $string, string $name): array
{
if (false === $characters = preg_split('/(?<!^)(?!$)/u', $string)) {
throw new RuntimeError(\sprintf('Unable to split the string passed to "%s" into characters: %s.', $name, preg_last_error_msg()));
}
return $characters;
}
/**
* Returns the length of a variable.
*
@@ -0,0 +1,8 @@
--TEST--
"reverse" filter with a string that is not valid UTF-8
--TEMPLATE--
{{ value|reverse }}
--DATA--
return ['value' => "\xC3\x28abc"]
--EXCEPTION--
Twig\Error\RuntimeError: Unable to split the string passed to "reverse" into characters: Malformed UTF-8 characters, possibly incorrectly encoded in "index.twig" at line 2.
@@ -0,0 +1,8 @@
--TEST--
"shuffle" filter with a string that is not valid UTF-8
--TEMPLATE--
{{ value|shuffle }}
--DATA--
return ['value' => "\xC3\x28abc"]
--EXCEPTION--
Twig\Error\RuntimeError: Unable to split the string passed to "shuffle" into characters: Malformed UTF-8 characters, possibly incorrectly encoded in "index.twig" at line 2.
@@ -0,0 +1,8 @@
--TEST--
"split" filter with an empty delimiter and a string that is not valid UTF-8
--TEMPLATE--
{{ value|split('')|join('-') }}
--DATA--
return ['value' => "\xC3\x28abc"]
--EXCEPTION--
Twig\Error\RuntimeError: Unable to split the string passed to "split" into characters: Malformed UTF-8 characters, possibly incorrectly encoded in "index.twig" at line 2.
@@ -0,0 +1,8 @@
--TEST--
"random" function with a string that is not valid UTF-8
--TEMPLATE--
{{ random(value) }}
--DATA--
return ['value' => "\xC3\x28abc"]
--EXCEPTION--
Twig\Error\RuntimeError: Unable to split the string passed to "random" into characters: Malformed UTF-8 characters, possibly incorrectly encoded in "index.twig" at line 2.