mirror of
https://github.com/twigphp/Twig.git
synced 2026-09-02 05:26:43 +00:00
Allow arrow functions everywhere
This commit is contained in:
@@ -1,5 +1,6 @@
|
||||
# 3.15.0 (2024-XX-XX)
|
||||
|
||||
* Allow arrow functions everywhere
|
||||
* Deprecate passing a string or an array to Twig callable arguments accepting arrow functions (pass a `\Closure`)
|
||||
* Add support for triggering deprecations for future operator precedence changes
|
||||
* Deprecate using the `not` unary operator in an expression with ``*``, ``/``, ``//``, or ``%`` without using explicit parentheses to clarify precedence
|
||||
|
||||
@@ -960,6 +960,32 @@ The following operators don't fit into any of the other categories:
|
||||
Support for expanding the arguments of a function call was introduced in
|
||||
Twig 3.15.
|
||||
|
||||
* ``=>``: The arrow operator allows the creation of functions. A function is
|
||||
made of arguments (use parentheses for multiple arguments) and an arrow
|
||||
(``=>``) followed by an expression to execute. The expression has access to
|
||||
all passed arguments. Arrow functions are supported as arguments for filters,
|
||||
functions, tests, macros, and method calls.
|
||||
|
||||
For instance, the built-in ``map``, ``reduce``, ``sort``, ``filter``, and
|
||||
``find`` filters accept arrow functions as arguments:
|
||||
|
||||
.. code-block:: twig
|
||||
|
||||
{{ people|map(p => p.first_name)|join(', ') }}
|
||||
|
||||
Arrow functions can be stored in variables:
|
||||
|
||||
.. code-block:: twig
|
||||
|
||||
{% set first_name_fn = (p) => p.first_name %}
|
||||
|
||||
{{ people|map(first_name_fn)|join(', ') }}
|
||||
|
||||
.. versionadded:: 3.15
|
||||
|
||||
Arrow function support for functions, macros, and method calls was added in
|
||||
Twig 3.15 (filters and tests were already supported).
|
||||
|
||||
Operators
|
||||
~~~~~~~~~
|
||||
|
||||
|
||||
@@ -87,9 +87,13 @@ class ExpressionParser
|
||||
}
|
||||
}
|
||||
|
||||
public function parseExpression($precedence = 0, $allowArrow = false)
|
||||
public function parseExpression($precedence = 0)
|
||||
{
|
||||
if ($allowArrow && $arrow = $this->parseArrow()) {
|
||||
if (func_num_args() > 1) {
|
||||
trigger_deprecation('twig/twig', '3.15', 'Passing a second argument ($allowArrow) to "%s()" is deprecated.', __METHOD__);
|
||||
}
|
||||
|
||||
if ($arrow = $this->parseArrow()) {
|
||||
return $arrow;
|
||||
}
|
||||
|
||||
@@ -108,7 +112,7 @@ class ExpressionParser
|
||||
} else {
|
||||
$previous = $this->setDeprecationCheck(true);
|
||||
try {
|
||||
$expr1 = $this->parseExpression(self::OPERATOR_LEFT === $op['associativity'] ? $op['precedence'] + 1 : $op['precedence'], true);
|
||||
$expr1 = $this->parseExpression(self::OPERATOR_LEFT === $op['associativity'] ? $op['precedence'] + 1 : $op['precedence']);
|
||||
} finally {
|
||||
$this->setDeprecationCheck($previous);
|
||||
}
|
||||
@@ -672,7 +676,7 @@ class ExpressionParser
|
||||
if (!$this->parser->getStream()->test(Token::PUNCTUATION_TYPE, '(')) {
|
||||
$arguments = new EmptyNode();
|
||||
} else {
|
||||
$arguments = $this->parseArguments(true, false, true);
|
||||
$arguments = $this->parseArguments(true);
|
||||
}
|
||||
|
||||
$filter = $this->getFilter($token->getValue(), $token->getLine());
|
||||
@@ -708,8 +712,12 @@ class ExpressionParser
|
||||
*
|
||||
* @throws SyntaxError
|
||||
*/
|
||||
public function parseArguments($namedArguments = false, $definition = false, $allowArrow = false)
|
||||
public function parseArguments($namedArguments = false, $definition = false)
|
||||
{
|
||||
if (func_num_args() > 2) {
|
||||
trigger_deprecation('twig/twig', '3.15', 'Passing a third argument ($allowArrow) to "%s()" is deprecated.', __METHOD__);
|
||||
}
|
||||
|
||||
$args = [];
|
||||
$stream = $this->parser->getStream();
|
||||
|
||||
@@ -731,11 +739,11 @@ class ExpressionParser
|
||||
} else {
|
||||
if ($stream->nextIf(Token::SPREAD_TYPE)) {
|
||||
$hasSpread = true;
|
||||
$value = new SpreadUnary($this->parseExpression(0, $allowArrow), $stream->getCurrent()->getLine());
|
||||
$value = new SpreadUnary($this->parseExpression(), $stream->getCurrent()->getLine());
|
||||
} elseif ($hasSpread) {
|
||||
throw new SyntaxError('Normal arguments must be placed before argument unpacking.', $stream->getCurrent()->getLine(), $stream->getSourceContext());
|
||||
} else {
|
||||
$value = $this->parseExpression(0, $allowArrow);
|
||||
$value = $this->parseExpression();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -753,7 +761,7 @@ class ExpressionParser
|
||||
throw new SyntaxError('A default value for an argument must be a constant (a boolean, a string, a number, a sequence, or a mapping).', $token->getLine(), $stream->getSourceContext());
|
||||
}
|
||||
} else {
|
||||
$value = $this->parseExpression(0, $allowArrow);
|
||||
$value = $this->parseExpression();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
--TEST--
|
||||
macro
|
||||
--TEMPLATE--
|
||||
{% set people = [
|
||||
{first: "Bob", last: "Smith"},
|
||||
{first: "Alice", last: "Dupond"},
|
||||
] %}
|
||||
|
||||
{% set first_name_fn = (p) => p.first %}
|
||||
|
||||
{{ _self.display_people(people, first_name_fn) }}
|
||||
|
||||
{% macro display_people(people, fn) %}
|
||||
{{ people|map(fn)|join(', ') }}
|
||||
{% endmacro %}
|
||||
--DATA--
|
||||
return []
|
||||
--EXPECT--
|
||||
Bob, Alice
|
||||
Reference in New Issue
Block a user