mirror of
https://github.com/twigphp/Twig.git
synced 2026-09-02 21:46:48 +00:00
feature #4735 Add the = assignment operator (fabpot)
This PR was merged into the 3.x branch.
Discussion
----------
Add the = assignment operator
Commits
-------
bfbbef05f2 Add the = assignment operator
This commit is contained in:
@@ -1,5 +1,6 @@
|
||||
# 3.23.0 (2026-XX-XX)
|
||||
|
||||
* Add `=` assignment operator (allows to set variables in expression or to replace the short-form of the set tag)
|
||||
* Add `?.` null-safe operator
|
||||
* Add `===` and `!==` operators (equivalent to the `same as` and `not same as` tests)
|
||||
|
||||
|
||||
@@ -96,6 +96,8 @@
|
||||
+------------+------------------+---------+---------------+-------------------------------------------------------------------+
|
||||
| | ``?`` | infix | Left | Conditional operator (a ? b : c) |
|
||||
+------------+------------------+---------+---------------+-------------------------------------------------------------------+
|
||||
| | ``=`` | | Right | Assignment operator |
|
||||
+------------+------------------+---------+---------------+-------------------------------------------------------------------+
|
||||
|
||||
When a precedence will change in 4.0, the new precedence is indicated by the arrow ``=>``.
|
||||
|
||||
@@ -198,3 +200,5 @@ Here is the same table for Twig 4.0 with adjusted precedences:
|
||||
+------------+------------------+---------+---------------+-------------------------------------------------------------------+
|
||||
| | ``?`` | infix | Left | Conditional operator (a ? b : c) |
|
||||
+------------+------------------+---------+---------------+-------------------------------------------------------------------+
|
||||
| | ``=`` | | Right | Assignment operator |
|
||||
+------------+------------------+---------+---------------+-------------------------------------------------------------------+
|
||||
|
||||
@@ -27,6 +27,16 @@ The assigned value can be any valid :ref:`Twig expression
|
||||
{% set user = {'name': 'Fabien'} %}
|
||||
{% set name = 'Fabien' ~ ' ' ~ 'Potencier' %}
|
||||
|
||||
.. tip::
|
||||
|
||||
To assign a value within an expression, use the :ref:`= operator
|
||||
<assignment-operator>`:
|
||||
|
||||
.. code-block:: twig
|
||||
|
||||
{# use assignment within a larger expression #}
|
||||
{{ (result = fetch_data()) ? result : 'default' }}
|
||||
|
||||
Several variables can be assigned in one block:
|
||||
|
||||
.. code-block:: twig
|
||||
|
||||
+50
-2
@@ -131,8 +131,8 @@ The following variables are always available in templates:
|
||||
Setting Variables
|
||||
~~~~~~~~~~~~~~~~~
|
||||
|
||||
You can assign values to variables inside code blocks. Assignments use the
|
||||
:doc:`set<tags/set>` tag:
|
||||
You can assign values to variables inside code blocks using either the
|
||||
:doc:`set<tags/set>` tag or the :ref:`= operator <assignment-operator>`:
|
||||
|
||||
.. code-block:: twig
|
||||
|
||||
@@ -140,6 +140,31 @@ You can assign values to variables inside code blocks. Assignments use the
|
||||
{% set numbers = [1, 2] %}
|
||||
{% set map = {'city': 'Paris'} %}
|
||||
|
||||
{# or #}
|
||||
|
||||
{% do name = 'Fabien' %}
|
||||
{% do numbers = [1, 2] %}
|
||||
{% do map = {'city': 'Paris'} %}
|
||||
|
||||
For simple assignments, both are equivalent. However, the ``set`` tag provides
|
||||
additional features:
|
||||
|
||||
* **Multi-target assignment**: Assign multiple variables at once:
|
||||
|
||||
.. code-block:: twig
|
||||
|
||||
{% set first, last = 'Fabien', 'Potencier' %}
|
||||
|
||||
* **Block capture**: Capture chunks of template content into a variable:
|
||||
|
||||
.. code-block:: html+twig
|
||||
|
||||
{% set content %}
|
||||
<div id="pagination">...</div>
|
||||
{% endset %}
|
||||
|
||||
See the :doc:`set<tags/set>` tag documentation for more details.
|
||||
|
||||
Filters
|
||||
-------
|
||||
|
||||
@@ -994,6 +1019,29 @@ 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.
|
||||
|
||||
.. _assignment-operator:
|
||||
|
||||
* ``=``: The assignment operator assigns a value to a variable within an
|
||||
expression:
|
||||
|
||||
.. code-block:: twig
|
||||
|
||||
{# assign #}
|
||||
{% do b = 1 + 3 %}
|
||||
|
||||
{# assign and output the result #}
|
||||
{{ b = 1 + 3 }}
|
||||
|
||||
{# assignments can be chained #}
|
||||
{% do a = b = 'foo' %}
|
||||
|
||||
{# assignment can be used inside other expressions #}
|
||||
{% do a = (b = 4) + 5 %}
|
||||
|
||||
.. versionadded:: 3.23
|
||||
|
||||
The ``=`` assignment operator was added in Twig 3.23.
|
||||
|
||||
* ``=>``: 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
|
||||
|
||||
@@ -13,6 +13,7 @@ namespace Twig\ExpressionParser\Infix;
|
||||
|
||||
use Twig\Error\SyntaxError;
|
||||
use Twig\Node\Expression\ArrayExpression;
|
||||
use Twig\Node\Expression\Binary\SetBinary;
|
||||
use Twig\Node\Expression\Unary\SpreadUnary;
|
||||
use Twig\Node\Expression\Variable\ContextVariable;
|
||||
use Twig\Node\Expression\Variable\LocalVariable;
|
||||
@@ -58,7 +59,10 @@ trait ArgumentsTrait
|
||||
}
|
||||
|
||||
$name = null;
|
||||
if (($token = $stream->nextIf(Token::OPERATOR_TYPE, '=')) || ($token = $stream->nextIf(Token::PUNCTUATION_TYPE, ':'))) {
|
||||
if ($value instanceof SetBinary) {
|
||||
$name = $value->getNode('left')->getAttribute('name');
|
||||
$value = $value->getNode('right');
|
||||
} elseif (($token = $stream->nextIf(Token::OPERATOR_TYPE, '=')) || ($token = $stream->nextIf(Token::PUNCTUATION_TYPE, ':'))) {
|
||||
if (!$value instanceof ContextVariable) {
|
||||
throw new SyntaxError(\sprintf('A parameter name must be a string, "%s" given.', $value::class), $token->getLine(), $stream->getSourceContext());
|
||||
}
|
||||
|
||||
@@ -0,0 +1,55 @@
|
||||
<?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\ExpressionParser\Infix;
|
||||
|
||||
use Twig\Error\SyntaxError;
|
||||
use Twig\ExpressionParser\InfixAssociativity;
|
||||
use Twig\Node\Expression\AbstractExpression;
|
||||
use Twig\Node\Expression\Binary\AbstractBinary;
|
||||
use Twig\Node\Expression\Binary\SetBinary;
|
||||
use Twig\Node\Expression\Variable\ContextVariable;
|
||||
use Twig\Parser;
|
||||
use Twig\Token;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
class AssignmentExpressionParser extends BinaryOperatorExpressionParser
|
||||
{
|
||||
public function __construct(
|
||||
string $name,
|
||||
) {
|
||||
parent::__construct(SetBinary::class, $name, 0, InfixAssociativity::Right);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return AbstractBinary
|
||||
*/
|
||||
public function parse(Parser $parser, AbstractExpression $left, Token $token): AbstractExpression
|
||||
{
|
||||
if (!$left instanceof ContextVariable) {
|
||||
throw new SyntaxError(\sprintf('Cannot assign to "%s", only variables can be assigned.', $left::class), $token->getLine(), $parser->getStream()->getSourceContext());
|
||||
}
|
||||
$right = $parser->parseExpression(InfixAssociativity::Left === $this->getAssociativity() ? $this->getPrecedence() + 1 : $this->getPrecedence());
|
||||
$right = match ($this->getName()) {
|
||||
'=' => $right,
|
||||
default => throw new \LogicException(\sprintf('Unknown operator: %s.', $this->getName())),
|
||||
};
|
||||
|
||||
return new SetBinary($left, $right, $token->getLine());
|
||||
}
|
||||
|
||||
public function getDescription(): string
|
||||
{
|
||||
return 'Assignment operator';
|
||||
}
|
||||
}
|
||||
@@ -17,6 +17,7 @@ use Twig\Error\LoaderError;
|
||||
use Twig\Error\RuntimeError;
|
||||
use Twig\Error\SyntaxError;
|
||||
use Twig\ExpressionParser\Infix\ArrowExpressionParser;
|
||||
use Twig\ExpressionParser\Infix\AssignmentExpressionParser;
|
||||
use Twig\ExpressionParser\Infix\BinaryOperatorExpressionParser;
|
||||
use Twig\ExpressionParser\Infix\ConditionalTernaryExpressionParser;
|
||||
use Twig\ExpressionParser\Infix\DotExpressionParser;
|
||||
@@ -377,6 +378,9 @@ final class CoreExtension extends AbstractExtension
|
||||
// ternary operator
|
||||
new ConditionalTernaryExpressionParser(),
|
||||
|
||||
// assignment operator
|
||||
new AssignmentExpressionParser('='),
|
||||
|
||||
// Twig callables
|
||||
new IsExpressionParser(),
|
||||
new IsNotExpressionParser(),
|
||||
|
||||
+1
-1
@@ -525,7 +525,7 @@ class Lexer
|
||||
|
||||
private function getOperatorRegex(): string
|
||||
{
|
||||
$expressionParsers = ['='];
|
||||
$expressionParsers = [];
|
||||
foreach ($this->env->getExpressionParsers() as $expressionParser) {
|
||||
$expressionParsers = array_merge($expressionParsers, [$expressionParser->getName()], $expressionParser->getAliases());
|
||||
}
|
||||
|
||||
@@ -0,0 +1,44 @@
|
||||
<?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;
|
||||
use Twig\Node\Expression\AbstractExpression;
|
||||
use Twig\Node\Expression\Variable\AssignContextVariable;
|
||||
use Twig\Node\Expression\Variable\ContextVariable;
|
||||
use Twig\Node\Node;
|
||||
|
||||
/**
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
*/
|
||||
class SetBinary extends AbstractBinary
|
||||
{
|
||||
/**
|
||||
* @param ContextVariable $left
|
||||
* @param AbstractExpression $right
|
||||
*/
|
||||
public function __construct(Node $left, Node $right, int $lineno)
|
||||
{
|
||||
$name = $left->getAttribute('name');
|
||||
if (!\is_string($name)) {
|
||||
throw new \LogicException('The "name" attribute must be a string.');
|
||||
}
|
||||
$left = new AssignContextVariable($name, $left->getTemplateLine());
|
||||
|
||||
parent::__construct($left, $right, $lineno);
|
||||
}
|
||||
|
||||
public function operator(Compiler $compiler): Compiler
|
||||
{
|
||||
return $compiler->raw('=');
|
||||
}
|
||||
}
|
||||
@@ -735,6 +735,13 @@ class ExpressionParserTest extends TestCase
|
||||
|
||||
// ?? stronger than ()
|
||||
// yield '?? vs ()' => ['{{ (1 ?? "a") }}', '{{ ((1 ?? "a")) }}', eval("return 1;")];
|
||||
|
||||
// = stronger than anything else
|
||||
yield '= same as literal' => ['{% do c = "a" %}{{ c }}', '{% do c = ("a") %}{{ c }}', eval("return 'a';")];
|
||||
yield '= stronger than .' => ['{% do c = a.b %}{{ c }}', '{% do c = (a.b) %}{{ c }}', eval("\$a = ['b' => 1]; return \$a['b'];"), $context];
|
||||
yield '= stronger than math' => ['{% do a = 1 + 3 %}{{ a }}', '{% do a = (1 + 3) %}{{ a }}', eval('$a = 1 + 3; return $a;')];
|
||||
yield '= stronger than logical' => ['{% do a = false or true %}{{ a }}', '{% do a = (false or true) %}{{ a }}', eval('$a = false || true; return $a;')];
|
||||
yield '= stronger than ternary' => ['{% do c = 4 ? 0 : -1 %}{{ c }}', '{% do c = (4 ? 0 : -1) %}{{ c }}', eval('return 4 ? 0 : -1;')];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@ Exception for syntax error in reused template
|
||||
{% use 'foo.twig' %}
|
||||
--TEMPLATE(foo.twig)--
|
||||
{% block bar %}
|
||||
{% do node.data = 5 %}
|
||||
{% do node.data 5 %}
|
||||
{% endblock %}
|
||||
--EXCEPTION--
|
||||
Twig\Error\SyntaxError: Unexpected token "operator" of value "=" ("end of statement block" expected) in "foo.twig" at line 3.
|
||||
Twig\Error\SyntaxError: Unexpected token "number" of value "5" ("end of statement block" expected) in "foo.twig" at line 3.
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
--TEST--
|
||||
Twig supports the "=" operator (assignment)
|
||||
--TEMPLATE--
|
||||
{# stores #}
|
||||
{% do b = 1 + 3 %}{{ b }}
|
||||
{# stores and displays #}
|
||||
{{ b = 1 + 3 }}{{ b }}
|
||||
{# stores and returns #}
|
||||
{% do (c = 4) ? 0 : -1 %}{{ c }}
|
||||
{# = can be chained #}
|
||||
{% do c = d = "a" %}{{ c }}{{ d }}
|
||||
{% do a = (b = 4) + 5 %}{{ a }}{{ b }}
|
||||
--DATA--
|
||||
return []
|
||||
--EXPECT--
|
||||
4
|
||||
44
|
||||
4
|
||||
aa
|
||||
94
|
||||
Reference in New Issue
Block a user