6 This section describes how to extend Twig as of **Twig 1.12**. If you are
7 using an older version, read the :doc:`legacy<advanced_legacy>` chapter
10 Twig can be extended in many ways; you can add extra tags, filters, tests,
11 operators, global variables, and functions. You can even extend the parser
12 itself with node visitors.
16 The first section of this chapter describes how to extend Twig easily. If
17 you want to reuse your changes in different projects or if you want to
18 share them with others, you should then create an extension as described
19 in the following section.
23 When extending Twig without creating an extension, Twig won't be able to
24 recompile your templates when the PHP code is updated. To see your changes
25 in real-time, either disable template caching or package your code into an
26 extension (see the next section of this chapter).
28 Before extending Twig, you must understand the differences between all the
29 different possible extension points and when to use them.
31 First, remember that Twig has two main language constructs:
33 * ``{{ }}``: used to print the result of an expression evaluation;
35 * ``{% %}``: used to execute statements.
37 To understand why Twig exposes so many extension points, let's see how to
38 implement a *Lorem ipsum* generator (it needs to know the number of words to
41 You can use a ``lipsum`` *tag*:
47 That works, but using a tag for ``lipsum`` is not a good idea for at least
50 * ``lipsum`` is not a language construct;
51 * The tag outputs something;
52 * The tag is not flexible as you cannot use it in an expression:
56 {{ 'some text' ~ {% lipsum 40 %} ~ 'some more text' }}
58 In fact, you rarely need to create tags; and that's good news because tags are
59 the most complex extension point of Twig.
61 Now, let's use a ``lipsum`` *filter*:
67 Again, it works, but it looks weird. A filter transforms the passed value to
68 something else but here we use the value to indicate the number of words to
69 generate (so, ``40`` is an argument of the filter, not the value we want to
72 Next, let's use a ``lipsum`` *function*:
78 Here we go. For this specific example, the creation of a function is the
79 extension point to use. And you can use it anywhere an expression is accepted:
83 {{ 'some text' ~ lipsum(40) ~ 'some more text' }}
85 {% set lipsum = lipsum(40) %}
87 Last but not the least, you can also use a *global* object with a method able
88 to generate lorem ipsum text:
94 As a rule of thumb, use functions for frequently used features and global
95 objects for everything else.
97 Keep in mind the following when you want to extend Twig:
99 ========== ========================== ========== =========================
100 What? Implementation difficulty? How often? When?
101 ========== ========================== ========== =========================
102 *macro* trivial frequent Content generation
103 *global* trivial frequent Helper object
104 *function* trivial frequent Content generation
105 *filter* trivial frequent Value transformation
106 *tag* complex rare DSL language construct
107 *test* trivial rare Boolean decision
108 *operator* trivial rare Values transformation
109 ========== ========================== ========== =========================
114 A global variable is like any other template variable, except that it's
115 available in all templates and macros::
117 $twig = new Twig_Environment($loader);
118 $twig->addGlobal('text', new Text());
120 You can then use the ``text`` variable anywhere in a template:
122 .. code-block:: jinja
124 {{ text.lipsum(40) }}
129 Creating a filter is as simple as associating a name with a PHP callable::
131 // an anonymous function
132 $filter = new Twig_SimpleFilter('rot13', function ($string) {
133 return str_rot13($string);
136 // or a simple PHP function
137 $filter = new Twig_SimpleFilter('rot13', 'str_rot13');
140 $filter = new Twig_SimpleFilter('rot13', array('SomeClass', 'rot13Filter'));
142 The first argument passed to the ``Twig_SimpleFilter`` constructor is the name
143 of the filter you will use in templates and the second one is the PHP callable
144 to associate with it.
146 Then, add the filter to your Twig environment::
148 $twig = new Twig_Environment($loader);
149 $twig->addFilter($filter);
151 And here is how to use it in a template:
153 .. code-block:: jinja
157 {# will output Gjvt #}
159 When called by Twig, the PHP callable receives the left side of the filter
160 (before the pipe ``|``) as the first argument and the extra arguments passed
161 to the filter (within parentheses ``()``) as extra arguments.
163 For instance, the following code:
165 .. code-block:: jinja
168 {{ now|date('d/m/Y') }}
170 is compiled to something like the following::
172 <?php echo strtolower('TWIG') ?>
173 <?php echo twig_date_format_filter($now, 'd/m/Y') ?>
175 The ``Twig_SimpleFilter`` class takes an array of options as its last
178 $filter = new Twig_SimpleFilter('rot13', 'str_rot13', $options);
180 Environment aware Filters
181 ~~~~~~~~~~~~~~~~~~~~~~~~~
183 If you want to access the current environment instance in your filter, set the
184 ``needs_environment`` option to ``true``; Twig will pass the current
185 environment as the first argument to the filter call::
187 $filter = new Twig_SimpleFilter('rot13', function (Twig_Environment $env, $string) {
188 // get the current charset for instance
189 $charset = $env->getCharset();
191 return str_rot13($string);
192 }, array('needs_environment' => true));
194 Context aware Filters
195 ~~~~~~~~~~~~~~~~~~~~~
197 If you want to access the current context in your filter, set the
198 ``needs_context`` option to ``true``; Twig will pass the current context as
199 the first argument to the filter call (or the second one if
200 ``needs_environment`` is also set to ``true``)::
202 $filter = new Twig_SimpleFilter('rot13', function ($context, $string) {
204 }, array('needs_context' => true));
206 $filter = new Twig_SimpleFilter('rot13', function (Twig_Environment $env, $context, $string) {
208 }, array('needs_context' => true, 'needs_environment' => true));
213 If automatic escaping is enabled, the output of the filter may be escaped
214 before printing. If your filter acts as an escaper (or explicitly outputs html
215 or JavaScript code), you will want the raw output to be printed. In such a
216 case, set the ``is_safe`` option::
218 $filter = new Twig_SimpleFilter('nl2br', 'nl2br', array('is_safe' => array('html')));
220 Some filters may need to work on input that is already escaped or safe, for
221 example when adding (safe) html tags to originally unsafe output. In such a
222 case, set the ``pre_escape`` option to escape the input data before it is run
223 through your filter::
225 $filter = new Twig_SimpleFilter('somefilter', 'somefilter', array('pre_escape' => 'html', 'is_safe' => array('html')));
230 A filter name containing the special ``*`` character is a dynamic filter as
231 the ``*`` can be any string::
233 $filter = new Twig_SimpleFilter('*_path', function ($name, $arguments) {
237 The following filters will be matched by the above defined dynamic filter:
242 A dynamic filter can define more than one dynamic parts::
244 $filter = new Twig_SimpleFilter('*_path_*', function ($name, $suffix, $arguments) {
248 The filter will receive all dynamic part values before the normal filter
249 arguments, but after the environment and the context. For instance, a call to
250 ``'foo'|a_path_b()`` will result in the following arguments to be passed to
251 the filter: ``('a', 'b', 'foo')``.
256 Functions are defined in the exact same way as filters, but you need to create
257 an instance of ``Twig_SimpleFunction``::
259 $twig = new Twig_Environment($loader);
260 $function = new Twig_SimpleFunction('function_name', function () {
263 $twig->addFunction($function);
265 Functions support the same features as filters, except for the ``pre_escape``
266 and ``preserves_safety`` options.
271 Tests are defined in the exact same way as filters and functions, but you need
272 to create an instance of ``Twig_SimpleTest``::
274 $twig = new Twig_Environment($loader);
275 $test = new Twig_SimpleTest('test_name', function () {
278 $twig->addTest($test);
280 Tests allow you to create custom application specific logic for evaluating
281 boolean conditions. As a simple, example let's create a Twig test that checks if
284 $twig = new Twig_Environment($loader)
285 $test = new Twig_SimpleTest('red', function ($value) {
286 if (isset($value->color) && $value->color == 'red') {
289 if (isset($value->paint) && $value->paint == 'red') {
294 $twig->addTest($test);
296 Test functions should always return true/false.
298 When creating tests you can use the ``node_class`` option to provide custom test
299 compilation. This is useful if your test can be compiled into PHP primitives.
300 This is used by many of the tests built into Twig::
302 $twig = new Twig_Environment($loader)
303 $test = new Twig_SimpleTest(
306 array('node_class' => 'Twig_Node_Expression_Test_Odd'));
307 $twig->addTest($test);
309 class Twig_Node_Expression_Test_Odd extends Twig_Node_Expression_Test
311 public function compile(Twig_Compiler $compiler)
315 ->subcompile($this->getNode('node'))
322 The above example, shows how you can create tests that use a node class. The
323 node class has access to one sub-node called 'node'. This sub-node contains the
324 value that is being tested. When the ``odd`` filter is used in code like:
326 .. code-block:: jinja
328 {% if my_value is odd %}
330 The ``node`` sub-node will contain an expression of ``my_value``. Node based
331 tests also have access to the ``arguments`` node. This node will contain the
332 various other arguments that have been provided to your test.
337 One of the most exciting feature of a template engine like Twig is the
338 possibility to define new language constructs. This is also the most complex
339 feature as you need to understand how Twig's internals work.
341 Let's create a simple ``set`` tag that allows the definition of simple
342 variables from within a template. The tag can be used like follows:
344 .. code-block:: jinja
346 {% set name = "value" %}
350 {# should output value #}
354 The ``set`` tag is part of the Core extension and as such is always
355 available. The built-in version is slightly more powerful and supports
356 multiple assignments by default (cf. the template designers chapter for
359 Three steps are needed to define a new tag:
361 * Defining a Token Parser class (responsible for parsing the template code);
363 * Defining a Node class (responsible for converting the parsed code to PHP);
365 * Registering the tag.
367 Registering a new tag
368 ~~~~~~~~~~~~~~~~~~~~~
370 Adding a tag is as simple as calling the ``addTokenParser`` method on the
371 ``Twig_Environment`` instance::
373 $twig = new Twig_Environment($loader);
374 $twig->addTokenParser(new Project_Set_TokenParser());
376 Defining a Token Parser
377 ~~~~~~~~~~~~~~~~~~~~~~~
379 Now, let's see the actual code of this class::
381 class Project_Set_TokenParser extends Twig_TokenParser
383 public function parse(Twig_Token $token)
385 $parser = $this->parser;
386 $stream = $parser->getStream();
388 $name = $stream->expect(Twig_Token::NAME_TYPE)->getValue();
389 $stream->expect(Twig_Token::OPERATOR_TYPE, '=');
390 $value = $parser->getExpressionParser()->parseExpression();
391 $stream->expect(Twig_Token::BLOCK_END_TYPE);
393 return new Project_Set_Node($name, $value, $token->getLine(), $this->getTag());
396 public function getTag()
402 The ``getTag()`` method must return the tag we want to parse, here ``set``.
404 The ``parse()`` method is invoked whenever the parser encounters a ``set``
405 tag. It should return a ``Twig_Node`` instance that represents the node (the
406 ``Project_Set_Node`` calls creating is explained in the next section).
408 The parsing process is simplified thanks to a bunch of methods you can call
409 from the token stream (``$this->parser->getStream()``):
411 * ``getCurrent()``: Gets the current token in the stream.
413 * ``next()``: Moves to the next token in the stream, *but returns the old one*.
415 * ``test($type)``, ``test($value)`` or ``test($type, $value)``: Determines whether
416 the current token is of a particular type or value (or both). The value may be an
417 array of several possible values.
419 * ``expect($type[, $value[, $message]])``: If the current token isn't of the given
420 type/value a syntax error is thrown. Otherwise, if the type and value are correct,
421 the token is returned and the stream moves to the next token.
423 * ``look()``: Looks a the next token without consuming it.
425 Parsing expressions is done by calling the ``parseExpression()`` like we did for
430 Reading the existing ``TokenParser`` classes is the best way to learn all
431 the nitty-gritty details of the parsing process.
436 The ``Project_Set_Node`` class itself is rather simple::
438 class Project_Set_Node extends Twig_Node
440 public function __construct($name, Twig_Node_Expression $value, $line, $tag = null)
442 parent::__construct(array('value' => $value), array('name' => $name), $line, $tag);
445 public function compile(Twig_Compiler $compiler)
448 ->addDebugInfo($this)
449 ->write('$context[\''.$this->getAttribute('name').'\'] = ')
450 ->subcompile($this->getNode('value'))
456 The compiler implements a fluid interface and provides methods that helps the
457 developer generate beautiful and readable PHP code:
459 * ``subcompile()``: Compiles a node.
461 * ``raw()``: Writes the given string as is.
463 * ``write()``: Writes the given string by adding indentation at the beginning
466 * ``string()``: Writes a quoted string.
468 * ``repr()``: Writes a PHP representation of a given value (see
469 ``Twig_Node_For`` for a usage example).
471 * ``addDebugInfo()``: Adds the line of the original template file related to
472 the current node as a comment.
474 * ``indent()``: Indents the generated code (see ``Twig_Node_Block`` for a
477 * ``outdent()``: Outdents the generated code (see ``Twig_Node_Block`` for a
480 .. _creating_extensions:
482 Creating an Extension
483 ---------------------
485 The main motivation for writing an extension is to move often used code into a
486 reusable class like adding support for internationalization. An extension can
487 define tags, filters, tests, operators, global variables, functions, and node
490 Creating an extension also makes for a better separation of code that is
491 executed at compilation time and code needed at runtime. As such, it makes
494 Most of the time, it is useful to create a single extension for your project,
495 to host all the specific tags and filters you want to add to Twig.
499 When packaging your code into an extension, Twig is smart enough to
500 recompile your templates whenever you make a change to it (when
501 ``auto_reload`` is enabled).
505 Before writing your own extensions, have a look at the Twig official
506 extension repository: http://github.com/fabpot/Twig-extensions.
508 An extension is a class that implements the following interface::
510 interface Twig_ExtensionInterface
513 * Initializes the runtime environment.
515 * This is where you can load some file that contains filter functions for instance.
517 * @param Twig_Environment $environment The current Twig_Environment instance
519 function initRuntime(Twig_Environment $environment);
522 * Returns the token parser instances to add to the existing list.
524 * @return array An array of Twig_TokenParserInterface or Twig_TokenParserBrokerInterface instances
526 function getTokenParsers();
529 * Returns the node visitor instances to add to the existing list.
531 * @return array An array of Twig_NodeVisitorInterface instances
533 function getNodeVisitors();
536 * Returns a list of filters to add to the existing list.
538 * @return array An array of filters
540 function getFilters();
543 * Returns a list of tests to add to the existing list.
545 * @return array An array of tests
550 * Returns a list of functions to add to the existing list.
552 * @return array An array of functions
554 function getFunctions();
557 * Returns a list of operators to add to the existing list.
559 * @return array An array of operators
561 function getOperators();
564 * Returns a list of global variables to add to the existing list.
566 * @return array An array of global variables
568 function getGlobals();
571 * Returns the name of the extension.
573 * @return string The extension name
578 To keep your extension class clean and lean, it can inherit from the built-in
579 ``Twig_Extension`` class instead of implementing the whole interface. That
580 way, you just need to implement the ``getName()`` method as the
581 ``Twig_Extension`` provides empty implementations for all other methods.
583 The ``getName()`` method must return a unique identifier for your extension.
585 Now, with this information in mind, let's create the most basic extension
588 class Project_Twig_Extension extends Twig_Extension
590 public function getName()
598 Of course, this extension does nothing for now. We will customize it in
601 Twig does not care where you save your extension on the filesystem, as all
602 extensions must be registered explicitly to be available in your templates.
604 You can register an extension by using the ``addExtension()`` method on your
605 main ``Environment`` object::
607 $twig = new Twig_Environment($loader);
608 $twig->addExtension(new Project_Twig_Extension());
610 Of course, you need to first load the extension file by either using
611 ``require_once()`` or by using an autoloader (see `spl_autoload_register()`_).
615 The bundled extensions are great examples of how extensions work.
620 Global variables can be registered in an extension via the ``getGlobals()``
623 class Project_Twig_Extension extends Twig_Extension
625 public function getGlobals()
628 'text' => new Text(),
638 Functions can be registered in an extension via the ``getFunctions()``
641 class Project_Twig_Extension extends Twig_Extension
643 public function getFunctions()
646 new Twig_SimpleFunction('lipsum', 'generate_lipsum'),
656 To add a filter to an extension, you need to override the ``getFilters()``
657 method. This method must return an array of filters to add to the Twig
660 class Project_Twig_Extension extends Twig_Extension
662 public function getFilters()
665 new Twig_SimpleFilter('rot13', 'str_rot13'),
675 Adding a tag in an extension can be done by overriding the
676 ``getTokenParsers()`` method. This method must return an array of tags to add
677 to the Twig environment::
679 class Project_Twig_Extension extends Twig_Extension
681 public function getTokenParsers()
683 return array(new Project_Set_TokenParser());
689 In the above code, we have added a single new tag, defined by the
690 ``Project_Set_TokenParser`` class. The ``Project_Set_TokenParser`` class is
691 responsible for parsing the tag and compiling it to PHP.
696 The ``getOperators()`` methods allows to add new operators. Here is how to add
697 ``!``, ``||``, and ``&&`` operators::
699 class Project_Twig_Extension extends Twig_Extension
701 public function getOperators()
705 '!' => array('precedence' => 50, 'class' => 'Twig_Node_Expression_Unary_Not'),
708 '||' => array('precedence' => 10, 'class' => 'Twig_Node_Expression_Binary_Or', 'associativity' => Twig_ExpressionParser::OPERATOR_LEFT),
709 '&&' => array('precedence' => 15, 'class' => 'Twig_Node_Expression_Binary_And', 'associativity' => Twig_ExpressionParser::OPERATOR_LEFT),
720 The ``getTests()`` methods allows to add new test functions::
722 class Project_Twig_Extension extends Twig_Extension
724 public function getTests()
727 new Twig_SimpleTest('even', 'twig_test_even'),
737 To overload an already defined filter, test, operator, global variable, or
738 function, define it again **as late as possible**::
740 $twig = new Twig_Environment($loader);
741 $twig->addFilter(new Twig_SimpleFilter('date', function ($timestamp, $format = 'F j, Y H:i') {
742 // do something different from the built-in date filter
745 Here, we have overloaded the built-in ``date`` filter with a custom one.
747 That also works with an extension::
749 class MyCoreExtension extends Twig_Extension
751 public function getFilters()
754 new Twig_SimpleFilter('date', array($this, 'dateFilter')),
758 public function dateFilter($timestamp, $format = 'F j, Y H:i')
760 // do something different from the built-in date filter
763 public function getName()
769 $twig = new Twig_Environment($loader);
770 $twig->addExtension(new MyCoreExtension());
774 Note that overloading the built-in Twig elements is not recommended as it
783 You can create functional tests for extensions simply by creating the
784 following file structure in your test directory::
798 The ``IntegrationTest.php`` file should look like this::
800 class Project_Tests_IntegrationTest extends Twig_Test_IntegrationTestCase
802 public function getExtensions()
805 new Project_Twig_Extension1(),
806 new Project_Twig_Extension2(),
810 public function getFixturesDir()
812 return dirname(__FILE__).'/Fixtures/';
816 Fixtures examples can be found within the Twig repository
817 `tests/Twig/Fixtures`_ directory.
822 Testing the node visitors can be complex, so extend your test cases from
823 ``Twig_Test_NodeTestCase``. Examples can be found in the Twig repository
824 `tests/Twig/Node`_ directory.
826 .. _`spl_autoload_register()`: http://www.php.net/spl_autoload_register
827 .. _`rot13`: http://www.php.net/manual/en/function.str-rot13.php
828 .. _`tests/Twig/Fixtures`: https://github.com/fabpot/Twig/tree/master/test/Twig/Tests/Fixtures
829 .. _`tests/Twig/Node`: https://github.com/fabpot/Twig/tree/master/test/Twig/Tests/Node