Local macros auto-import

This commit is contained in:
Fabien Potencier
2019-05-18 07:31:04 +02:00
parent 5a56076659
commit 3fcbc44205
6 changed files with 130 additions and 1 deletions
+1
View File
@@ -1,5 +1,6 @@
* 2.11.0 (2019-XX-XX)
* macros are now auto-imported in the template they are defined (under the ``_self`` variable)
* 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
+14 -1
View File
@@ -74,7 +74,20 @@ via the ``from`` tag:
.. tip::
To import macros from the current file, use the special ``_self`` variable:
When macro usages and definitions are in the same template, you don't need to
import the macros as they are automatically available under the special
``_self`` variable:
.. code-block:: twig
<p>{{ _self.input('password', '', 'password') }}</p>
{% macro input(name, value, type = "text", size = 20) %}
<input type="{{ type }}" name="{{ name }}" value="{{ value|e }}" size="{{ size }}" />
{% endmacro %}
Auto-import is only available as of Twig 2.11. For older versions, import
macros using the special ``_self`` variable for the template name:
.. code-block:: twig
+6
View File
@@ -48,6 +48,7 @@ use Twig\Node\Expression\Test\SameasTest;
use Twig\Node\Expression\Unary\NegUnary;
use Twig\Node\Expression\Unary\NotUnary;
use Twig\Node\Expression\Unary\PosUnary;
use Twig\NodeVisitor\MacroAutoImportNodeVisitor;
use Twig\TokenParser\ApplyTokenParser;
use Twig\TokenParser\BlockTokenParser;
use Twig\TokenParser\DeprecatedTokenParser;
@@ -281,6 +282,11 @@ final class CoreExtension extends AbstractExtension
];
}
public function getNodeVisitors()
{
return [new MacroAutoImportNodeVisitor()];
}
public function getOperators()
{
return [
@@ -0,0 +1,75 @@
<?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\NodeVisitor;
use Twig\Environment;
use Twig\Node\Expression\AssignNameExpression;
use Twig\Node\Expression\ConstantExpression;
use Twig\Node\Expression\GetAttrExpression;
use Twig\Node\Expression\MethodCallExpression;
use Twig\Node\Expression\NameExpression;
use Twig\Node\ImportNode;
use Twig\Node\ModuleNode;
use Twig\Node\Node;
/**
* @author Fabien Potencier <fabien@symfony.com>
*/
final class MacroAutoImportNodeVisitor implements NodeVisitorInterface
{
private $inAModule = false;
private $hasMacroCalls = false;
public function enterNode(Node $node, Environment $env)
{
if ($node instanceof ModuleNode) {
$this->inAModule = true;
}
return $node;
}
public function leaveNode(Node $node, Environment $env)
{
if ($node instanceof ModuleNode) {
$this->inAModule = false;
if ($this->hasMacroCalls) {
$body = [new ImportNode(new NameExpression('_self', 0), new AssignNameExpression('_self', 0), 0, 'import', true)];
foreach ($node->getNode('body') as $n) {
$body[] = $n;
}
$node->setNode('body', new Node($body));
}
} elseif ($this->inAModule) {
if (
$node instanceof GetAttrExpression &&
$node->getNode('node') instanceof NameExpression &&
'_self' === $node->getNode('node')->getAttribute('name') &&
$node->getNode('attribute') instanceof ConstantExpression
) {
$this->hasMacroCalls = true;
$name = $node->getNode('attribute')->getAttribute('value');
$node = new MethodCallExpression($node->getNode('node'), 'macro_'.$name, $node->getNode('arguments'), $node->getTemplateLine());
$node->setAttribute('safe', true);
}
}
return $node;
}
public function getPriority()
{
// we must be ran before auto-escaping
return -10;
}
}
@@ -0,0 +1,16 @@
--TEST--
"macro" tag
--TEMPLATE--
{{ _self.hello('Fabien') }}
{% macro hello(name) -%}
Hello {{ _self.up(name) }}
{% endmacro %}
{% macro up(name) -%}
{{ name|upper }}
{% endmacro %}
--DATA--
return []
--EXPECT--
Hello FABIEN
@@ -0,0 +1,18 @@
--TEST--
"macro" tag
--TEMPLATE--
{% block content %}
{{ _self.hello('Fabien') }}
{% endblock %}
{% macro hello(name) -%}
Hello {{ _self.up(name) }}
{% endmacro %}
{% macro up(name) -%}
{{ name|upper }}
{% endmacro %}
--DATA--
return []
--EXPECT--
Hello FABIEN