Merge branch '3.x' into 4.x

* 3.x:
  Add missing is_iterable() checks
  Fix tests
  Add quotes on filters/functions in CoreExtension
  Use get_debug_type() everywhere
This commit is contained in:
Fabien Potencier
2024-09-29 18:26:00 +02:00
6 changed files with 34 additions and 20 deletions
+2 -2
View File
@@ -97,7 +97,7 @@ final class HtmlExtension extends AbstractExtension
} elseif (\is_array($arg)) {
foreach ($arg as $class => $condition) {
if (!\is_string($class)) {
throw new RuntimeError(\sprintf('The html_classes function argument %d (key %d) should be a string, got "%s".', $i, $class, \gettype($class)));
throw new RuntimeError(\sprintf('The html_classes function argument %d (key %d) should be a string, got "%s".', $i, $class, get_debug_type($class)));
}
if (!$condition) {
continue;
@@ -105,7 +105,7 @@ final class HtmlExtension extends AbstractExtension
$classes[] = $class;
}
} else {
throw new RuntimeError(\sprintf('The html_classes function argument %d should be either a string or an array, got "%s".', $i, \gettype($arg)));
throw new RuntimeError(\sprintf('The html_classes function argument %d should be either a string or an array, got "%s".', $i, get_debug_type($arg)));
}
}
+28 -14
View File
@@ -435,7 +435,7 @@ final class CoreExtension extends AbstractExtension
$values = self::toArray($values);
if (0 === \count($values)) {
throw new RuntimeError('The random function cannot pick from an empty sequence/mapping.');
throw new RuntimeError('The "random" function cannot pick from an empty sequence/mapping.');
}
return $values[array_rand($values, 1)];
@@ -594,7 +594,7 @@ final class CoreExtension extends AbstractExtension
}
if ('ceil' !== $method && 'floor' !== $method) {
throw new RuntimeError('The round filter only supports the "common", "ceil", and "floor" methods.');
throw new RuntimeError('The "round" filter only supports the "common", "ceil", and "floor" methods.');
}
return $method($value * 10 ** $precision) / 10 ** $precision;
@@ -665,7 +665,7 @@ final class CoreExtension extends AbstractExtension
foreach ($arrays as $argNumber => $array) {
if (!is_iterable($array)) {
throw new RuntimeError(\sprintf('The merge filter only works with sequences/mappings or "Traversable", got "%s" for argument %d.', \gettype($array), $argNumber + 1));
throw new RuntimeError(\sprintf('The "merge" filter only works with sequences/mappings or "Traversable", got "%s" for argument %d.', get_debug_type($array), $argNumber + 1));
}
$result = [...$result, ...$array];
@@ -968,7 +968,7 @@ final class CoreExtension extends AbstractExtension
if ($array instanceof \Traversable) {
$array = iterator_to_array($array);
} elseif (!\is_array($array)) {
throw new RuntimeError(\sprintf('The sort filter only works with sequences/mappings or "Traversable", got "%s".', \gettype($array)));
throw new RuntimeError(\sprintf('The "sort" filter only works with sequences/mappings or "Traversable", got "%s".', get_debug_type($array)));
}
if (null !== $arrow) {
@@ -1515,7 +1515,7 @@ final class CoreExtension extends AbstractExtension
}
if ('::class' === strtolower(substr($constant, -7))) {
throw new RuntimeError(\sprintf('You cannot use the Twig function "constant()" to access "%s". You could provide an object and call constant("class", $object) or use the class name directly as a string.', $constant));
throw new RuntimeError(\sprintf('You cannot use the Twig function "constant" to access "%s". You could provide an object and call constant("class", $object) or use the class name directly as a string.', $constant));
}
throw new RuntimeError(\sprintf('Constant "%s" is undefined.', $constant));
@@ -1611,12 +1611,12 @@ final class CoreExtension extends AbstractExtension
if (null === $object) {
$message = \sprintf('Impossible to access a key ("%s") on a null variable.', $item);
} else {
$message = \sprintf('Impossible to access a key ("%s") on a %s variable ("%s").', $item, \gettype($object), $object);
$message = \sprintf('Impossible to access a key ("%s") on a %s variable ("%s").', $item, get_debug_type($object), $object);
}
} elseif (null === $object) {
$message = \sprintf('Impossible to access an attribute ("%s") on a null variable.', $item);
} else {
$message = \sprintf('Impossible to access an attribute ("%s") on a %s variable ("%s").', $item, \gettype($object), $object);
$message = \sprintf('Impossible to access an attribute ("%s") on a %s variable ("%s").', $item, get_debug_type($object), $object);
}
throw new RuntimeError($message, $lineno, $source);
@@ -1637,7 +1637,7 @@ final class CoreExtension extends AbstractExtension
} elseif (\is_array($object)) {
$message = \sprintf('Impossible to invoke a method ("%s") on a sequence/mapping.', $item);
} else {
$message = \sprintf('Impossible to invoke a method ("%s") on a %s variable ("%s").', $item, \gettype($object), $object);
$message = \sprintf('Impossible to invoke a method ("%s") on a %s variable ("%s").', $item, get_debug_type($object), $object);
}
throw new RuntimeError($message, $lineno, $source);
@@ -1782,10 +1782,12 @@ final class CoreExtension extends AbstractExtension
*/
public static function column($array, $name, $index = null): array
{
if (!is_iterable($array)) {
throw new RuntimeError(\sprintf('The "column" filter only works with sequences/mappings or "Traversable", got "%s" as first argument.', get_debug_type($array)));
}
if ($array instanceof \Traversable) {
$array = iterator_to_array($array);
} elseif (!\is_array($array)) {
throw new RuntimeError(\sprintf('The column filter only works with sequences/mappings or "Traversable", got "%s" as first argument.', \gettype($array)));
}
return array_column($array, $name, $index);
@@ -1815,6 +1817,10 @@ final class CoreExtension extends AbstractExtension
*/
public static function find(Environment $env, $array, $arrow)
{
if (!is_iterable($array)) {
throw new RuntimeError(\sprintf('The "find" filter expects a sequence/mapping or "Traversable", got "%s".', get_debug_type($array)));
}
self::checkArrowInSandbox($env, $arrow, 'find', 'filter');
foreach ($array as $k => $v) {
@@ -1850,12 +1856,12 @@ final class CoreExtension extends AbstractExtension
*/
public static function reduce(Environment $env, $array, $arrow, $initial = null)
{
self::checkArrowInSandbox($env, $arrow, 'reduce', 'filter');
if (!\is_array($array) && !$array instanceof \Traversable) {
throw new RuntimeError(\sprintf('The "reduce" filter only works with sequences/mappings or "Traversable", got "%s" as first argument.', \gettype($array)));
if (!is_iterable($array)) {
throw new RuntimeError(\sprintf('The "reduce" filter only works with sequences/mappings or "Traversable", got "%s" as first argument.', get_debug_type($array)));
}
self::checkArrowInSandbox($env, $arrow, 'reduce', 'filter');
$accumulator = $initial;
foreach ($array as $key => $value) {
$accumulator = $arrow($accumulator, $value, $key);
@@ -1869,6 +1875,10 @@ final class CoreExtension extends AbstractExtension
*/
public static function arraySome(Environment $env, $array, $arrow)
{
if (!is_iterable($array)) {
throw new RuntimeError(\sprintf('The "has some" filter only works with sequences/mappings or "Traversable", got "%s" as first argument.', get_debug_type($array)));
}
self::checkArrowInSandbox($env, $arrow, 'has some', 'operator');
foreach ($array as $k => $v) {
@@ -1885,6 +1895,10 @@ final class CoreExtension extends AbstractExtension
*/
public static function arrayEvery(Environment $env, $array, $arrow)
{
if (!is_iterable($array)) {
throw new RuntimeError(\sprintf('The "has every" filter only works with sequences/mappings or "Traversable", got "%s" as first argument.', get_debug_type($array)));
}
self::checkArrowInSandbox($env, $arrow, 'has every', 'operator');
foreach ($array as $k => $v) {
+1 -1
View File
@@ -50,7 +50,7 @@ abstract class Node implements \Countable, \IteratorAggregate
{
foreach ($nodes as $name => $node) {
if (!$node instanceof self) {
throw new \InvalidArgumentException(\sprintf('Using "%s" for the value of node "%s" of "%s" is not supported. You must pass a \Twig\Node\Node instance.', \is_object($node) ? $node::class : (null === $node ? 'null' : \gettype($node)), $name, static::class));
throw new \InvalidArgumentException(\sprintf('Using "%s" for the value of node "%s" of "%s" is not supported. You must pass a \Twig\Node\Node instance.', get_debug_type($node), $name, static::class));
}
}
$this->nodes = $nodes;
@@ -9,4 +9,4 @@ Exception thrown from a child for an extension error
--DATA--
return []
--EXCEPTION--
Twig\Error\RuntimeError: The random function cannot pick from an empty sequence/mapping in "base.twig" at line 4.
Twig\Error\RuntimeError: The "random" function cannot pick from an empty sequence/mapping in "base.twig" at line 4.
@@ -9,4 +9,4 @@ Exception thrown from an include for an extension error
--DATA--
return []
--EXCEPTION--
Twig\Error\RuntimeError: The random function cannot pick from an empty sequence/mapping in "content.twig" at line 4.
Twig\Error\RuntimeError: The "random" function cannot pick from an empty sequence/mapping in "content.twig" at line 4.
+1 -1
View File
@@ -363,7 +363,7 @@ class TemplateTest extends TestCase
// tests when input is not an array or object
$tests = array_merge($tests, [
[false, null, 42, 'a', [], $anyType, 'Impossible to access an attribute ("a") on a integer variable ("42") in "index.twig".'],
[false, null, 42, 'a', [], $anyType, 'Impossible to access an attribute ("a") on a int variable ("42") in "index.twig".'],
[false, null, 'string', 'a', [], $anyType, 'Impossible to access an attribute ("a") on a string variable ("string") in "index.twig".'],
[false, null, [], 'a', [], $anyType, 'Key "a" does not exist as the sequence/mapping is empty in "index.twig".'],
]);