Add "some" and "every" filters

This commit is contained in:
hubert.lenoir
2022-03-24 13:50:00 +01:00
parent e33577f1be
commit 009bb4fd82
6 changed files with 135 additions and 1 deletions
+1 -1
View File
@@ -80,7 +80,7 @@ class ExpressionParser
} elseif (isset($op['callable'])) {
$expr = $op['callable']($this->parser, $expr);
} else {
$expr1 = $this->parseExpression(self::OPERATOR_LEFT === $op['associativity'] ? $op['precedence'] + 1 : $op['precedence']);
$expr1 = $this->parseExpression(self::OPERATOR_LEFT === $op['associativity'] ? $op['precedence'] + 1 : $op['precedence'], true);
$class = $op['class'];
$expr = new $class($expr, $expr1, $token->getLine());
}
+30
View File
@@ -23,6 +23,8 @@ use Twig\Node\Expression\Binary\EqualBinary;
use Twig\Node\Expression\Binary\FloorDivBinary;
use Twig\Node\Expression\Binary\GreaterBinary;
use Twig\Node\Expression\Binary\GreaterEqualBinary;
use Twig\Node\Expression\Binary\HasEveryBinary;
use Twig\Node\Expression\Binary\HasSomeBinary;
use Twig\Node\Expression\Binary\InBinary;
use Twig\Node\Expression\Binary\LessBinary;
use Twig\Node\Expression\Binary\LessEqualBinary;
@@ -284,6 +286,8 @@ final class CoreExtension extends AbstractExtension
'matches' => ['precedence' => 20, 'class' => MatchesBinary::class, 'associativity' => ExpressionParser::OPERATOR_LEFT],
'starts with' => ['precedence' => 20, 'class' => StartsWithBinary::class, 'associativity' => ExpressionParser::OPERATOR_LEFT],
'ends with' => ['precedence' => 20, 'class' => EndsWithBinary::class, 'associativity' => ExpressionParser::OPERATOR_LEFT],
'has some' => ['precedence' => 20, 'class' => HasSomeBinary::class, 'associativity' => ExpressionParser::OPERATOR_LEFT],
'has every' => ['precedence' => 20, 'class' => HasEveryBinary::class, 'associativity' => ExpressionParser::OPERATOR_LEFT],
'..' => ['precedence' => 25, 'class' => RangeBinary::class, 'associativity' => ExpressionParser::OPERATOR_LEFT],
'+' => ['precedence' => 30, 'class' => AddBinary::class, 'associativity' => ExpressionParser::OPERATOR_LEFT],
'-' => ['precedence' => 30, 'class' => SubBinary::class, 'associativity' => ExpressionParser::OPERATOR_LEFT],
@@ -1678,6 +1682,32 @@ function twig_array_reduce(Environment $env, $array, $arrow, $initial = null)
return array_reduce($array, $arrow, $initial);
}
function twig_array_some(Environment $env, $array, $arrow)
{
twig_check_arrow_in_sandbox($env, $arrow, 'some', 'filter');
foreach ($array as $k => $v) {
if ($arrow($v, $k)) {
return true;
}
}
return false;
}
function twig_array_every(Environment $env, $array, $arrow)
{
twig_check_arrow_in_sandbox($env, $arrow, 'every', 'filter');
foreach ($array as $k => $v) {
if (!$arrow($v, $k)) {
return false;
}
}
return true;
}
function twig_check_arrow_in_sandbox(Environment $env, $arrow, $thing, $type)
{
if (!$arrow instanceof Closure && $env->hasExtension('\Twig\Extension\SandboxExtension') && $env->getExtension('\Twig\Extension\SandboxExtension')->isSandboxed()) {
@@ -0,0 +1,33 @@
<?php
/*
* This file is part of Twig.
*
* (c) Fabien Potencier
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Twig\Node\Expression\Binary;
use Twig\Compiler;
class HasEveryBinary extends AbstractBinary
{
public function compile(Compiler $compiler): void
{
$compiler
->raw('twig_array_every($this->env, ')
->subcompile($this->getNode('left'))
->raw(', ')
->subcompile($this->getNode('right'))
->raw(')')
;
}
public function operator(Compiler $compiler): Compiler
{
return $compiler->raw('');
}
}
@@ -0,0 +1,33 @@
<?php
/*
* This file is part of Twig.
*
* (c) Fabien Potencier
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Twig\Node\Expression\Binary;
use Twig\Compiler;
class HasSomeBinary extends AbstractBinary
{
public function compile(Compiler $compiler): void
{
$compiler
->raw('twig_array_some($this->env, ')
->subcompile($this->getNode('left'))
->raw(', ')
->subcompile($this->getNode('right'))
->raw(')')
;
}
public function operator(Compiler $compiler): Compiler
{
return $compiler->raw('');
}
}
+19
View File
@@ -0,0 +1,19 @@
--TEST--
Twig supports the "has every" operator
--TEMPLATE--
{% if [0, 2, 4] has every v => 0 == v % 2 %}Every{% else %}Not every{% endif %} items are even in array
{{ ([0, 2, 4] has every v => 0 == v % 2) ? 'Every' : 'Not every' }} items are even in array
{{ ({ a: 0, b: 2, c: 4 } has every v => 0 == v % 2) ? 'Every' : 'Not every' }} items are even in object
{{ ({ a: 0, b: 2, c: 4 } has every (v, k) => "d" > k)? 'Every' : 'Not every' }} keys are before "d" in object
{{ (it has every v => 0 == v % 2) ? 'Every' : 'Not every' }} items are even in iterator
{{ ([0, 1, 2] has every v => 0 == v % 2) ? 'Every' : 'Not every' }} items are even in array
--DATA--
return ['it' => new \ArrayIterator([0, 2, 4])]
--EXPECT--
Every items are even in array
Every items are even in array
Every items are even in object
Every keys are before "d" in object
Every items are even in iterator
Not every items are even in array
+19
View File
@@ -0,0 +1,19 @@
--TEST--
Twig supports the "has some" operator
--TEMPLATE--
{% if [1, 2, 3] has some v => 0 == v % 2 %}At least one{% else %}No{% endif %} item is even in array
{{ ([1, 2, 3] has some v => 0 == v % 2) ? 'At least one' : 'No' }} item is even in array
{{ ({ a: 1, b: 2, c: 3 } has some v => 0 == v % 2) ? 'At least one' : 'No' }} item is even in object
{{ ({ a: 1, b: 2, c: 3 } has some (v, k) => "b" == k)? 'At least one' : 'No' }} key is "b" in object
{{ (it has some v => 0 == v % 2) ? 'At least one' : 'No' }} item is even in iterator
{{ ([1, 3, 5] has some v => 0 == v % 2) ? 'At least one' : 'No' }} item is even in array
--DATA--
return ['it' => new \ArrayIterator([1, 2, 3])]
--EXPECT--
At least one item is even in array
At least one item is even in array
At least one item is even in object
At least one key is "b" in object
At least one item is even in iterator
No item is even in array