* removed the sandboxed attribute of the include tag (use the new sandbox tag instead)
* refactored the Node system (if you have custom nodes, you will have to update them to use the new API)
+ * added self as a special variable that refers to the current template (useful for importing macros from the current template)
* added Twig_Template instance support to the include tag
* added support for dynamic and conditional inheritance ({% extends some_var %} and {% extends standalone ? "minimum" : "base" %})
* added a grammar sub-framework to ease the creation of custom tags
### Macros
Macros are comparable with functions in regular programming languages. They
-are useful to put often used HTML idioms into reusable functions to not repeat
+are useful to put often used HTML idioms into reusable elements to not repeat
yourself.
Here a small example of a macro that renders a form element:
But as PHP functions, macros don't have access to the current template
variables.
-Macros can be defined in any template, and always need to be "imported" before
-being used (see the Import section for more information):
+Macros can be defined in any template, and need to be "imported" before being
+used (see the Import section for more information):
[twig]
{% import "forms.html" as forms %}
<p>{{ forms.input('username') }}</p>
<p>{{ forms.input('password', none, 'password') }}</p>
+If the macros are defined and used in the same template, you can use the
+special `self` variable, without importing them:
+
+ [twig]
+ <p>{{ self.input('username') }}</p>
+
+When you want to use a macro in another one from the same file, use the `self`
+variable:
+
+ [twig]
+ {% macro input(name, value, type, size) %}
+ <input type="{{ type|default('text') }}" name="{{ name }}" value="{{ value|e }}" size="{{ size|default(20) }}" />
+ {% endmacro %}
+
+ {% macro wrapped_input(name, value, type, size) %}
+ <div class="field">
+ {{ self.input(name, value, type, size) }}
+ </div>
+ {% endmacro %}
+
+When the macro is defined in another file, you need to import it:
+
+ [twig]
+ {# forms.html #}
+
+ {% macro input(name, value, type, size) %}
+ <input type="{{ type|default('text') }}" name="{{ name }}" value="{{ value|e }}" size="{{ size|default(20) }}" />
+ {% endmacro %}
+
+ {# shortcuts.html #}
+
+ {% macro wrapped_input(name, value, type, size) %}
+ {% import "forms.html" as forms %}
+ <div class="field">
+ {{ forms.input(name, value, type, size) }}
+ </div>
+ {% endmacro %}
+
### Filters
Filter sections allow you to apply regular Twig filters on a block of template
[twig]
{% import 'forms.html' as forms %}
+
<dl>
<dt>Username</dt>
<dd>{{ forms.input('username') }}</dd>
</dl>
<p>{{ forms.textarea('comment') }}</p>
-Even if the macros are defined in the same template as the one where you want
-to use them, they still need to be imported:
+Importing is not needed if the macros and the template are defined in the file;
+use the special `self` variable instead:
+
+ [twig]
+ {# index.html template #}
+
+ {% macro textarea(name, value, rows) %}
+ <textarea name="{{ name }}" rows="{{ rows|default(10) }}" cols="{{ cols|default(40) }}">{{ value|e }}</textarea>
+ {% endmacro %}
+
+ <p>{{ self.textarea('comment') }}</p>
+
+But you can still create an alias by importing from the `self` variable:
[twig]
{# index.html template #}
<textarea name="{{ name }}" rows="{{ rows|default(10) }}" cols="{{ cols|default(40) }}">{{ value|e }}</textarea>
{% endmacro %}
- {% import "index.html" as forms %}
+ {% import self as forms %}
<p>{{ forms.textarea('comment') }}</p>
* Loads a template by name.
*
* @param string $name The template name
- * @param Boolean $macro Whether to return the macro object if any, or the template one
*
* @return Twig_TemplateInterface A template instance representing the given template name
*/
- public function loadTemplate($name, $macro = false)
+ public function loadTemplate($name)
{
- $cls = $this->getTemplateClass($name).($macro ? '_Macro' : '');
+ $cls = $this->getTemplateClass($name);
if (isset($this->loadedTemplates[$cls])) {
return $this->loadedTemplates[$cls];
public function compile($compiler)
{
- $compiler->raw(sprintf('$this->getContext($context, \'%s\')', $this['name'], $this['name']));
+ if ('self' === $this['name']) {
+ $compiler->raw('$this');
+ } else {
+ $compiler->raw(sprintf('$this->getContext($context, \'%s\')', $this['name'], $this['name']));
+ }
}
}
->write('')
->subcompile($this->var)
->raw(' = ')
- ->raw('$this->env->loadTemplate(')
- ->subcompile($this->expr)
- ->raw(", true);\n")
;
+
+ if ($this->expr instanceof Twig_Node_Expression_Name && 'self' === $this->expr['name']) {
+ $compiler->raw("\$this");
+ } else {
+ $compiler
+ ->raw('$this->env->loadTemplate(')
+ ->subcompile($this->expr)
+ ->raw(", true)")
+ ;
+ }
+
+ $compiler->raw(";\n");
}
}
public function compile($compiler)
{
$this->compileTemplate($compiler);
- $this->compileMacros($compiler);
}
protected function compileTemplate($compiler)
$this->compileGetName($compiler);
+ $this->compileMacros($compiler);
+
$this->compileClassFooter($compiler);
}
protected function compileMacros($compiler)
{
- $compiler
- ->write("\n")
- ->write('class '.$compiler->getEnvironment()->getTemplateClass($this['filename']).'_Macro extends Twig_Macro'."\n")
- ->write("{\n")
- ->indent()
- ;
-
- // macros
$compiler->subcompile($this->macros);
-
- $compiler
- ->outdent()
- ->write("}\n")
- ;
}
}
}
}
-
-class __TwigTemplate_be925a7b06dda0dfdbd18a1509f7eb34_Macro extends Twig_Macro
-{
-}
EOF
, $twig);
}
}
-
-class __TwigTemplate_be925a7b06dda0dfdbd18a1509f7eb34_Macro extends Twig_Macro
-{
-}
EOF
, $twig);
}
}
-
-class __TwigTemplate_be925a7b06dda0dfdbd18a1509f7eb34_Macro extends Twig_Macro
-{
-}
EOF
, $twig);
}
}
-
-class __TwigTemplate_be925a7b06dda0dfdbd18a1509f7eb34_Macro extends Twig_Macro
-{
-}
EOF
, $twig);
}
}
-
-class __TwigTemplate_be925a7b06dda0dfdbd18a1509f7eb34_Macro extends Twig_Macro
-{
-}
EOF
, $twig);
--TEST--
"macro" tag
--TEMPLATE--
-{% import 'index.twig' as forms %}
-
-{{ forms.input('username') }}
-{{ forms.input('password', null, 'password', 1) }}
+{{ self.input('username') }}
+{{ self.input('password', null, 'password', 1) }}
{% macro input(name, value, type, size) %}
<input type="{{ type|default("text") }}" name="{{ name }}" value="{{ value|e|default('') }}" size="{{ size|default(20) }}">
--- /dev/null
+--TEST--
+"macro" tag
+--TEMPLATE--
+{% import self as forms %}
+
+{{ forms.input('username') }}
+{{ forms.input('password', null, 'password', 1) }}
+
+{% macro input(name, value, type, size) %}
+ <input type="{{ type|default("text") }}" name="{{ name }}" value="{{ value|e|default('') }}" size="{{ size|default(20) }}">
+{% endmacro %}
+--DATA--
+return array()
+--EXPECT--
+ <input type="text" name="username" value="" size="20">
+
+ <input type="password" name="password" value="" size="1">