mirror of
https://github.com/twigphp/Twig.git
synced 2026-09-01 04:57:23 +00:00
bug #3341 In |filter(), |map(), and |reduce(), throw a RuntimeError instead of a TypeError (thijzert)
This PR was squashed before being merged into the 1.x branch.
Discussion
----------
In |filter(), |map(), and |reduce(), throw a RuntimeError instead of a TypeError
If a template accidentally performs `|filter(...)` on a variable that isn't traversable (e.g. `null`), the calling code will encouter the raw `TypeError` that results from Twig trying to construct an `IteratorIterator` on that variable. This means that context information (e.g., in which exact template the error occurred, and on which line) is discarded, which makes it very difficult to debug your templates.
This commit adds code to throw a `RuntimeError` if Twig encounters a situation where there's no valid way to filter an array.
Commits
-------
567b1e2a In |filter(), |map(), and |reduce(), throw a RuntimeError instead of a TypeError
This commit is contained in:
@@ -1736,6 +1736,10 @@ function twig_array_reduce(Environment $env, $array, $arrow, $initial = null)
|
||||
}
|
||||
|
||||
if (!\is_array($array)) {
|
||||
if (!$array instanceof \Traversable) {
|
||||
throw new RuntimeError(sprintf('The "reduce" filter only works with arrays or "Traversable", got "%s" as first argument.', \gettype($array)));
|
||||
}
|
||||
|
||||
$array = iterator_to_array($array);
|
||||
}
|
||||
|
||||
|
||||
@@ -161,6 +161,85 @@ EOHTML
|
||||
}
|
||||
}
|
||||
|
||||
public function testTwigArrayFilterThrowsRuntimeExceptions()
|
||||
{
|
||||
$loader = new ArrayLoader([
|
||||
'filter-null.html' => <<<EOHTML
|
||||
{# Argument 1 passed to IteratorIterator::__construct() must implement interface Traversable, null given: #}
|
||||
{% for n in variable|filter(x => x > 3) %}
|
||||
This list contains {{n}}.
|
||||
{% endfor %}
|
||||
EOHTML
|
||||
]);
|
||||
|
||||
$twig = new Environment($loader, ['debug' => true, 'cache' => false]);
|
||||
|
||||
$template = $twig->load('filter-null.html');
|
||||
$out = $template->render(['variable' => [1, 2, 3, 4]]);
|
||||
$this->assertEquals('This list contains 4.', trim($out));
|
||||
|
||||
try {
|
||||
$template->render(['variable' => null]);
|
||||
|
||||
$this->fail();
|
||||
} catch (RuntimeError $e) {
|
||||
$this->assertEquals(2, $e->getTemplateLine());
|
||||
$this->assertEquals('filter-null.html', $e->getSourceContext()->getName());
|
||||
}
|
||||
}
|
||||
|
||||
public function testTwigArrayMapThrowsRuntimeExceptions()
|
||||
{
|
||||
$loader = new ArrayLoader([
|
||||
'map-null.html' => <<<EOHTML
|
||||
{# We expect a runtime error if `variable` is not traversable #}
|
||||
{% for n in variable|map(x => x * 3) %}
|
||||
{{- n -}}
|
||||
{% endfor %}
|
||||
EOHTML
|
||||
]);
|
||||
|
||||
$twig = new Environment($loader, ['debug' => true, 'cache' => false]);
|
||||
|
||||
$template = $twig->load('map-null.html');
|
||||
$out = $template->render(['variable' => [1, 2, 3, 4]]);
|
||||
$this->assertEquals('36912', trim($out));
|
||||
|
||||
try {
|
||||
$template->render(['variable' => null]);
|
||||
|
||||
$this->fail();
|
||||
} catch (RuntimeError $e) {
|
||||
$this->assertEquals(2, $e->getTemplateLine());
|
||||
$this->assertEquals('map-null.html', $e->getSourceContext()->getName());
|
||||
}
|
||||
}
|
||||
|
||||
public function testTwigArrayReduceThrowsRuntimeExceptions()
|
||||
{
|
||||
$loader = new ArrayLoader([
|
||||
'reduce-null.html' => <<<EOHTML
|
||||
{# We expect a runtime error if `variable` is not traversable #}
|
||||
{{ variable|reduce((carry, x) => carry + x) }}
|
||||
EOHTML
|
||||
]);
|
||||
|
||||
$twig = new Environment($loader, ['debug' => true, 'cache' => false]);
|
||||
|
||||
$template = $twig->load('reduce-null.html');
|
||||
$out = $template->render(['variable' => [1, 2, 3, 4]]);
|
||||
$this->assertEquals('10', trim($out));
|
||||
|
||||
try {
|
||||
$template->render(['variable' => null]);
|
||||
|
||||
$this->fail();
|
||||
} catch (RuntimeError $e) {
|
||||
$this->assertEquals(2, $e->getTemplateLine());
|
||||
$this->assertEquals('reduce-null.html', $e->getSourceContext()->getName());
|
||||
}
|
||||
}
|
||||
|
||||
public function getErroredTemplates()
|
||||
{
|
||||
return [
|
||||
|
||||
Reference in New Issue
Block a user