Changes:
+ * added the "from" tag to import macros as functions
* added support for functions (a function is just syntactic sugar for a getAttribute() call)
* made macros callable when sandbox mode is enabled
* added an exception when a macro uses a reserved name
.. code-block:: jinja
{% macro input(name, value, type, size) %}
- <input type="{{ type|default('text') }}" name="{{ name }}" value="{{ value|e }}" size="{{ size|default(20) }}" />
+ <input type="{{ type|default('text') }}" name="{{ name }}" value="{{ value|e }}" size="{{ size|default(20) }}" />
{% endmacro %}
Macros differs from native PHP functions in a few ways:
Twig supports putting often used code into macros. These macros can go into
different templates and get imported from there.
+There are two ways to import templates. You can import the complete template
+into a variable or request specific macros from it.
+
Imagine we have a helper module that renders forms (called ``forms.html``):
.. code-block:: jinja
{% macro input(name, value, type, size) %}
- <input type="{{ type|default('text') }}" name="{{ name }}" value="{{ value|e }}" size="{{ size|default(20) }}" />
+ <input type="{{ type|default('text') }}" name="{{ name }}" value="{{ value|e }}" size="{{ size|default(20) }}" />
{% endmacro %}
{% macro textarea(name, value, rows) %}
- <textarea name="{{ name }}" rows="{{ rows|default(10) }}" cols="{{ cols|default(40) }}">{{ value|e }}</textarea>
+ <textarea name="{{ name }}" rows="{{ rows|default(10) }}" cols="{{ cols|default(40) }}">{{ value|e }}</textarea>
{% endmacro %}
-Importing these macros in a template is as easy as using the ``import`` tag:
+The easiest and most flexible is importing the whole module into a variable.
+That way you can access the attributes:
.. code-block:: jinja
{% import 'forms.html' as forms %}
<dl>
- <dt>Username</dt>
- <dd>{{ forms.input('username') }}</dd>
- <dt>Password</dt>
- <dd>{{ forms.input('password', none, 'password') }}</dd>
+ <dt>Username</dt>
+ <dd>{{ forms.input('username') }}</dd>
+ <dt>Password</dt>
+ <dd>{{ forms.input('password', none, 'password') }}</dd>
</dl>
<p>{{ forms.textarea('comment') }}</p>
-Importing is not needed if the macros and the template are defined in the file;
-use the special ``_self`` variable instead:
+Alternatively you can import names from the template into the current
+namespace:
+
+.. code-block:: jinja
+
+ {% from 'forms.html' import input as input_field, textarea %}
+
+ <dl>
+ <dt>Username</dt>
+ <dd>{{ input_field('username') }}</dd>
+ <dt>Password</dt>
+ <dd>{{ input_field('password', type='password') }}</dd>
+ </dl>
+ <p>{{ textarea('comment') }}</p>
+
+Importing is not needed if the macros and the template are defined in the same
+file; use the special ``_self`` variable instead:
.. code-block:: jinja
{# index.html template #}
{% macro textarea(name, value, rows) %}
- <textarea name="{{ name }}" rows="{{ rows|default(10) }}" cols="{{ cols|default(40) }}">{{ value|e }}</textarea>
+ <textarea name="{{ name }}" rows="{{ rows|default(10) }}" cols="{{ cols|default(40) }}">{{ value|e }}</textarea>
{% endmacro %}
<p>{{ _self.textarea('comment') }}</p>
{# index.html template #}
{% macro textarea(name, value, rows) %}
- <textarea name="{{ name }}" rows="{{ rows|default(10) }}" cols="{{ cols|default(40) }}">{{ value|e }}</textarea>
+ <textarea name="{{ name }}" rows="{{ rows|default(10) }}" cols="{{ cols|default(40) }}">{{ value|e }}</textarea>
{% endmacro %}
{% import _self as forms %}
new Twig_TokenParser_Filter(),
new Twig_TokenParser_Macro(),
new Twig_TokenParser_Import(),
+ new Twig_TokenParser_From(),
new Twig_TokenParser_Set(),
new Twig_TokenParser_Spaceless(),
);
--- /dev/null
+<?php
+
+/*
+ * This file is part of Twig.
+ *
+ * (c) 2010 Fabien Potencier
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/**
+ * Represents a from node.
+ *
+ * @package twig
+ * @author Fabien Potencier <fabien.potencier@symfony-project.com>
+ */
+class Twig_Node_From extends Twig_Node_Import
+{
+ public function __construct(Twig_Node_Expression $expr, array $imports, $lineno, $tag = null)
+ {
+ parent::__construct($expr, new Twig_Node_Expression_AssignName('_imported_'.rand(10000, 99999), $lineno), $lineno, $tag);
+
+ $this->setAttribute('imports', $imports);
+ }
+
+ /**
+ * Compiles the node to PHP.
+ *
+ * @param Twig_Compiler A Twig_Compiler instance
+ */
+ public function compile($compiler)
+ {
+ parent::compile($compiler);
+
+ foreach ($this->getAttribute('imports') as $name => $alias) {
+ $compiler
+ ->write('$context[')
+ ->repr($alias)
+ ->raw('] = new Twig_Function(')
+ ->subcompile($this->getNode('var'))
+ ->raw(', ')
+ ->repr($name)
+ ->raw(");\n")
+ ;
+ }
+ }
+}
--- /dev/null
+<?php
+
+/*
+ * This file is part of Twig.
+ *
+ * (c) 2010 Fabien Potencier
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+class Twig_TokenParser_From extends Twig_TokenParser
+{
+ /**
+ * Parses a token and returns a node.
+ *
+ * @param Twig_Token $token A Twig_Token instance
+ *
+ * @return Twig_NodeInterface A Twig_NodeInterface instance
+ */
+ public function parse(Twig_Token $token)
+ {
+ $macro = $this->parser->getExpressionParser()->parseExpression();
+ $stream = $this->parser->getStream();
+ $stream->expect('import');
+
+ $targets = array();
+ do {
+ $name = $stream->expect(Twig_Token::NAME_TYPE)->getValue();
+
+ $alias = $name;
+ if ($stream->test('as')) {
+ $stream->next();
+
+ $alias = $stream->expect(Twig_Token::NAME_TYPE)->getValue();
+ }
+
+ $targets[$name] = $alias;
+
+ if (!$stream->test(Twig_Token::PUNCTUATION_TYPE, ',')) {
+ break;
+ }
+
+ $stream->next();
+ } while (true);
+
+ $stream->expect(Twig_Token::BLOCK_END_TYPE);
+
+ return new Twig_Node_From($macro, $targets, $token->getLine(), $this->getTag());
+ }
+
+ /**
+ * Gets the tag name associated with this token parser.
+ *
+ * @param string The tag name
+ */
+ public function getTag()
+ {
+ return 'from';
+ }
+}
--- /dev/null
+--TEST--
+"macro" tag
+--TEMPLATE--
+{% from 'forms.twig' import foo %}
+{% from 'forms.twig' import foo as foobar, bar %}
+
+{{ foo('foo') }}
+{{ foobar('foo') }}
+{{ bar('foo') }}
+--TEMPLATE(forms.twig)--
+{% macro foo(name) %}foo{{ name }}{% endmacro %}
+{% macro bar(name) %}bar{{ name }}{% endmacro %}
+--DATA--
+return array()
+--EXPECT--
+foofoo
+foofoo
+barfoo