Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
* Fix duplicated macro argument names triggering a PHP fatal error instead of a `SyntaxError`
* Deprecate defining a macro more than once in the same template
* Deprecate `TemplateVariable` and `AssignTemplateVariable`; use `MacroVariable` and `AssignMacroVariable` instead
* Deprecate omitting parentheses when calling a macro; it will throw a `SyntaxError` in 4.0

# 3.28.0 (2026-07-03)

Expand Down
6 changes: 6 additions & 0 deletions doc/deprecated.rst
Original file line number Diff line number Diff line change
Expand Up @@ -318,6 +318,12 @@ Macros
3.29 and will throw a ``SyntaxError`` in Twig 4.0. Give each macro a unique
name.

* Omitting parentheses when calling a macro (e.g. ``macros.input`` or
``macros.(name)``) is deprecated as of Twig 3.29 and will throw a
``SyntaxError`` in Twig 4.0. Add parentheses after the macro name (e.g.
``macros.input()`` or ``macros.(name)()``). Parentheses remain optional when
testing whether a macro is defined.

Filters
-------

Expand Down
10 changes: 8 additions & 2 deletions src/ExpressionParser/Infix/DotExpressionParser.php
Original file line number Diff line number Diff line change
Expand Up @@ -77,11 +77,17 @@ public function parse(Parser $parser, AbstractExpression $expr, Token $token): A
&& \is_string($name = $attribute->getAttribute('value'))
&& preg_match('#^[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*$#D', $name)
) {
return new MacroReferenceExpression(new MacroVariable($expr->getAttribute('name'), $expr->getTemplateLine()), 'macro_'.$name, $arguments, $expr->getTemplateLine());
$node = new MacroReferenceExpression(new MacroVariable($expr->getAttribute('name'), $expr->getTemplateLine()), 'macro_'.$name, $arguments, $expr->getTemplateLine());
$node->setHasParentheses(Template::METHOD_CALL === $type);

return $node;
}

if ($isMacroTarget && !$attribute instanceof ConstantExpression) {
return new MacroReferenceExpression(new MacroVariable($expr->getAttribute('name'), $expr->getTemplateLine()), $attribute, $arguments, $expr->getTemplateLine());
$node = new MacroReferenceExpression(new MacroVariable($expr->getAttribute('name'), $expr->getTemplateLine()), $attribute, $arguments, $expr->getTemplateLine());
$node->setHasParentheses(Template::METHOD_CALL === $type);

return $node;
}

return new GetAttrExpression($expr, $attribute, $arguments, $type, $lineno, $nullSafe);
Expand Down
14 changes: 14 additions & 0 deletions src/Node/Expression/MacroReferenceExpression.php
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@ class MacroReferenceExpression extends AbstractExpression implements SupportDefi
use SupportDefinedTestDeprecationTrait;
use SupportDefinedTestTrait;

private bool $hasParentheses = true;

/**
* @param string|AbstractExpression $name A static macro method name (e.g. "macro_foo") or, for a dynamic
* call, an expression resolving to the macro name (without the
Expand All @@ -51,6 +53,14 @@ public function __construct(MacroVariable $template, string|AbstractExpression $
parent::__construct($nodes, $attributes, $lineno);
}

/**
* @internal
*/
public function setHasParentheses(bool $hasParentheses): void
Comment thread
fabpot marked this conversation as resolved.
{
$this->hasParentheses = $hasParentheses;
}

public function __clone()
{
// The template node must not be deep-cloned because its name is
Expand All @@ -63,6 +73,10 @@ public function __clone()

public function compile(Compiler $compiler): void
{
if (!$this->hasParentheses && !$this->definedTest) {
trigger_deprecation('twig/twig', '3.29', 'Omitting parentheses when calling a macro is deprecated and will throw a SyntaxError in Twig 4.0; add parentheses after the macro name in "%s" at line %d.', $this->getTemplateName(), $this->getTemplateLine());
}

if ($this->hasNode('name')) {
$this->compileDynamic($compiler);

Expand Down
16 changes: 16 additions & 0 deletions tests/Fixtures/macros/call_without_parentheses.legacy.test
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
--TEST--
Omitting parentheses when calling macros is deprecated
--DEPRECATION--
Since twig/twig 3.29: Omitting parentheses when calling a macro is deprecated and will throw a SyntaxError in Twig 4.0; add parentheses after the macro name in "index.twig" at line 4.
Since twig/twig 3.29: Omitting parentheses when calling a macro is deprecated and will throw a SyntaxError in Twig 4.0; add parentheses after the macro name in "index.twig" at line 5.
--TEMPLATE--
{% import _self as macros %}
{% set name = 'hello' %}
{{ macros.hello }}
{{ _self.(name) }}
{% macro hello() %}Hello{% endmacro %}
--DATA--
return []
--EXPECT--
Hello
Hello
Loading