diff --git a/CHANGELOG b/CHANGELOG index bd327f589..ed237df2d 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -19,6 +19,7 @@ * 2.11.0 (2019-XX-XX) + * added support for macros on "is defined" tests * fixed macros "import" when using the same name in the parent and child templates * fixed recursive macros * macros imported "globally" in a template are now available in macros without re-importing them diff --git a/src/ExpressionParser.php b/src/ExpressionParser.php index b68adcdc5..bfe77c059 100644 --- a/src/ExpressionParser.php +++ b/src/ExpressionParser.php @@ -674,6 +674,11 @@ class ExpressionParser $arguments = $this->parseArguments(true); } + if ('defined' === $name && $node instanceof NameExpression && null !== $alias = $this->parser->getImportedSymbol('function', $node->getAttribute('name'))) { + $node = new MethodCallExpression($alias['node'], $alias['name'], new ArrayExpression([], $node->getTemplateLine()), $node->getTemplateLine()); + $node->setAttribute('safe', true); + } + return new $class($node, $name, $arguments, $this->parser->getCurrentToken()->getLine()); } diff --git a/src/Node/Expression/MethodCallExpression.php b/src/Node/Expression/MethodCallExpression.php index 3adfe8aea..127d3c50e 100644 --- a/src/Node/Expression/MethodCallExpression.php +++ b/src/Node/Expression/MethodCallExpression.php @@ -17,7 +17,7 @@ class MethodCallExpression extends AbstractExpression { public function __construct(AbstractExpression $node, string $method, ArrayExpression $arguments, int $lineno) { - parent::__construct(['node' => $node, 'arguments' => $arguments], ['method' => $method, 'safe' => false], $lineno); + parent::__construct(['node' => $node, 'arguments' => $arguments], ['method' => $method, 'safe' => false, 'is_defined_test' => false], $lineno); if ($node instanceof NameExpression) { $node->setAttribute('always_defined', true); @@ -26,6 +26,18 @@ class MethodCallExpression extends AbstractExpression public function compile(Compiler $compiler): void { + if ($this->getAttribute('is_defined_test')) { + $compiler + ->raw('method_exists($macros[') + ->repr($this->getNode('node')->getAttribute('name')) + ->raw('], ') + ->repr($this->getAttribute('method')) + ->raw(')') + ; + + return; + } + $compiler ->raw('$macros[') ->repr($this->getNode('node')->getAttribute('name')) diff --git a/src/Node/Expression/Test/DefinedTest.php b/src/Node/Expression/Test/DefinedTest.php index f4c7b90af..8ccfc8e65 100644 --- a/src/Node/Expression/Test/DefinedTest.php +++ b/src/Node/Expression/Test/DefinedTest.php @@ -18,6 +18,7 @@ use Twig\Node\Expression\BlockReferenceExpression; use Twig\Node\Expression\ConstantExpression; use Twig\Node\Expression\FunctionExpression; use Twig\Node\Expression\GetAttrExpression; +use Twig\Node\Expression\MethodCallExpression; use Twig\Node\Expression\NameExpression; use Twig\Node\Expression\TestExpression; use Twig\Node\Node; @@ -47,6 +48,8 @@ class DefinedTest extends TestExpression $node->setAttribute('is_defined_test', true); } elseif ($node instanceof ConstantExpression || $node instanceof ArrayExpression) { $node = new ConstantExpression(true, $node->getTemplateLine()); + } elseif ($node instanceof MethodCallExpression) { + $node->setAttribute('is_defined_test', true); } else { throw new SyntaxError('The "defined" test only works with simple variables.', $lineno); } diff --git a/test/Twig/Tests/Fixtures/tests/defined_for_macros.test b/test/Twig/Tests/Fixtures/tests/defined_for_macros.test new file mode 100644 index 000000000..1aa45fc82 --- /dev/null +++ b/test/Twig/Tests/Fixtures/tests/defined_for_macros.test @@ -0,0 +1,41 @@ +--TEST-- +"defined" support for macros +--TEMPLATE-- +{% import _self as macros %} +{% from _self import hello, bar %} + +{% if macros.hello is defined -%} + OK +{% endif %} + +{% if macros.foo is not defined -%} + OK +{% endif %} + +{% if hello is defined -%} + OK +{% endif %} + +{% if bar is not defined -%} + OK +{% endif %} + +{% if foo is not defined -%} + OK +{% endif %} + +{% macro hello(name) %} + Hello {{ name }} +{% endmacro %} +--DATA-- +return [] +--EXPECT-- +OK + +OK + +OK + +OK + +OK