mirror of
https://github.com/twigphp/Twig.git
synced 2026-09-13 10:56:38 +00:00
Allow arrow functions everywhere
This commit is contained in:
@@ -1,5 +1,6 @@
|
|||||||
# 3.15.0 (2024-XX-XX)
|
# 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`)
|
* 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
|
* 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
|
* 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
|
Support for expanding the arguments of a function call was introduced in
|
||||||
Twig 3.15.
|
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
|
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;
|
return $arrow;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -108,7 +112,7 @@ class ExpressionParser
|
|||||||
} else {
|
} else {
|
||||||
$previous = $this->setDeprecationCheck(true);
|
$previous = $this->setDeprecationCheck(true);
|
||||||
try {
|
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 {
|
} finally {
|
||||||
$this->setDeprecationCheck($previous);
|
$this->setDeprecationCheck($previous);
|
||||||
}
|
}
|
||||||
@@ -672,7 +676,7 @@ class ExpressionParser
|
|||||||
if (!$this->parser->getStream()->test(Token::PUNCTUATION_TYPE, '(')) {
|
if (!$this->parser->getStream()->test(Token::PUNCTUATION_TYPE, '(')) {
|
||||||
$arguments = new EmptyNode();
|
$arguments = new EmptyNode();
|
||||||
} else {
|
} else {
|
||||||
$arguments = $this->parseArguments(true, false, true);
|
$arguments = $this->parseArguments(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
$filter = $this->getFilter($token->getValue(), $token->getLine());
|
$filter = $this->getFilter($token->getValue(), $token->getLine());
|
||||||
@@ -708,8 +712,12 @@ class ExpressionParser
|
|||||||
*
|
*
|
||||||
* @throws SyntaxError
|
* @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 = [];
|
$args = [];
|
||||||
$stream = $this->parser->getStream();
|
$stream = $this->parser->getStream();
|
||||||
|
|
||||||
@@ -731,11 +739,11 @@ class ExpressionParser
|
|||||||
} else {
|
} else {
|
||||||
if ($stream->nextIf(Token::SPREAD_TYPE)) {
|
if ($stream->nextIf(Token::SPREAD_TYPE)) {
|
||||||
$hasSpread = true;
|
$hasSpread = true;
|
||||||
$value = new SpreadUnary($this->parseExpression(0, $allowArrow), $stream->getCurrent()->getLine());
|
$value = new SpreadUnary($this->parseExpression(), $stream->getCurrent()->getLine());
|
||||||
} elseif ($hasSpread) {
|
} elseif ($hasSpread) {
|
||||||
throw new SyntaxError('Normal arguments must be placed before argument unpacking.', $stream->getCurrent()->getLine(), $stream->getSourceContext());
|
throw new SyntaxError('Normal arguments must be placed before argument unpacking.', $stream->getCurrent()->getLine(), $stream->getSourceContext());
|
||||||
} else {
|
} 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());
|
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 {
|
} 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