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');
139 // or a class static method
140 $filter = new Twig_SimpleFilter('rot13', array('SomeClass', 'rot13Filter'));
141 $filter = new Twig_SimpleFilter('rot13', 'SomeClass::rot13Filter');
144 $filter = new Twig_SimpleFilter('rot13', array($this, 'rot13Filter'));
145 // the one below needs a runtime implementation (see below for more information)
146 $filter = new Twig_SimpleFilter('rot13', array('SomeClass', 'rot13Filter'));
148 The first argument passed to the ``Twig_SimpleFilter`` constructor is the name
149 of the filter you will use in templates and the second one is the PHP callable
150 to associate with it.
152 Then, add the filter to your Twig environment::
154 $twig = new Twig_Environment($loader);
155 $twig->addFilter($filter);
157 And here is how to use it in a template:
159 .. code-block:: jinja
163 {# will output Gjvt #}
165 When called by Twig, the PHP callable receives the left side of the filter
166 (before the pipe ``|``) as the first argument and the extra arguments passed
167 to the filter (within parentheses ``()``) as extra arguments.
169 For instance, the following code:
171 .. code-block:: jinja
174 {{ now|date('d/m/Y') }}
176 is compiled to something like the following::
178 <?php echo strtolower('TWIG') ?>
179 <?php echo twig_date_format_filter($now, 'd/m/Y') ?>
181 The ``Twig_SimpleFilter`` class takes an array of options as its last
184 $filter = new Twig_SimpleFilter('rot13', 'str_rot13', $options);
186 Environment-aware Filters
187 ~~~~~~~~~~~~~~~~~~~~~~~~~
189 If you want to access the current environment instance in your filter, set the
190 ``needs_environment`` option to ``true``; Twig will pass the current
191 environment as the first argument to the filter call::
193 $filter = new Twig_SimpleFilter('rot13', function (Twig_Environment $env, $string) {
194 // get the current charset for instance
195 $charset = $env->getCharset();
197 return str_rot13($string);
198 }, array('needs_environment' => true));
200 Context-aware Filters
201 ~~~~~~~~~~~~~~~~~~~~~
203 If you want to access the current context in your filter, set the
204 ``needs_context`` option to ``true``; Twig will pass the current context as
205 the first argument to the filter call (or the second one if
206 ``needs_environment`` is also set to ``true``)::
208 $filter = new Twig_SimpleFilter('rot13', function ($context, $string) {
210 }, array('needs_context' => true));
212 $filter = new Twig_SimpleFilter('rot13', function (Twig_Environment $env, $context, $string) {
214 }, array('needs_context' => true, 'needs_environment' => true));
219 If automatic escaping is enabled, the output of the filter may be escaped
220 before printing. If your filter acts as an escaper (or explicitly outputs HTML
221 or JavaScript code), you will want the raw output to be printed. In such a
222 case, set the ``is_safe`` option::
224 $filter = new Twig_SimpleFilter('nl2br', 'nl2br', array('is_safe' => array('html')));
226 Some filters may need to work on input that is already escaped or safe, for
227 example when adding (safe) HTML tags to originally unsafe output. In such a
228 case, set the ``pre_escape`` option to escape the input data before it is run
229 through your filter::
231 $filter = new Twig_SimpleFilter('somefilter', 'somefilter', array('pre_escape' => 'html', 'is_safe' => array('html')));
236 .. versionadded:: 1.19
237 Support for variadic filters was added in Twig 1.19.
239 When a filter should accept an arbitrary number of arguments, set the
240 ``is_variadic`` option to ``true``; Twig will pass the extra arguments as the
241 last argument to the filter call as an array::
243 $filter = new Twig_SimpleFilter('thumbnail', function ($file, array $options = array()) {
245 }, array('is_variadic' => true));
247 Be warned that named arguments passed to a variadic filter cannot be checked
248 for validity as they will automatically end up in the option array.
253 A filter name containing the special ``*`` character is a dynamic filter as
254 the ``*`` can be any string::
256 $filter = new Twig_SimpleFilter('*_path', function ($name, $arguments) {
260 The following filters will be matched by the above defined dynamic filter:
265 A dynamic filter can define more than one dynamic parts::
267 $filter = new Twig_SimpleFilter('*_path_*', function ($name, $suffix, $arguments) {
271 The filter will receive all dynamic part values before the normal filter
272 arguments, but after the environment and the context. For instance, a call to
273 ``'foo'|a_path_b()`` will result in the following arguments to be passed to
274 the filter: ``('a', 'b', 'foo')``.
279 .. versionadded:: 1.21
280 Support for deprecated filters was added in Twig 1.21.
282 You can mark a filter as being deprecated by setting the ``deprecated`` option
283 to ``true``. You can also give an alternative filter that replaces the
284 deprecated one when that makes sense::
286 $filter = new Twig_SimpleFilter('obsolete', function () {
288 }, array('deprecated' => true, 'alternative' => 'new_one'));
290 When a filter is deprecated, Twig emits a deprecation notice when compiling a
291 template using it. See :ref:`deprecation-notices` for more information.
296 Functions are defined in the exact same way as filters, but you need to create
297 an instance of ``Twig_SimpleFunction``::
299 $twig = new Twig_Environment($loader);
300 $function = new Twig_SimpleFunction('function_name', function () {
303 $twig->addFunction($function);
305 Functions support the same features as filters, except for the ``pre_escape``
306 and ``preserves_safety`` options.
311 Tests are defined in the exact same way as filters and functions, but you need
312 to create an instance of ``Twig_SimpleTest``::
314 $twig = new Twig_Environment($loader);
315 $test = new Twig_SimpleTest('test_name', function () {
318 $twig->addTest($test);
320 Tests allow you to create custom application specific logic for evaluating
321 boolean conditions. As a simple example, let's create a Twig test that checks if
324 $twig = new Twig_Environment($loader);
325 $test = new Twig_SimpleTest('red', function ($value) {
326 if (isset($value->color) && $value->color == 'red') {
329 if (isset($value->paint) && $value->paint == 'red') {
334 $twig->addTest($test);
336 Test functions should always return true/false.
338 When creating tests you can use the ``node_class`` option to provide custom test
339 compilation. This is useful if your test can be compiled into PHP primitives.
340 This is used by many of the tests built into Twig::
342 $twig = new Twig_Environment($loader);
343 $test = new Twig_SimpleTest(
346 array('node_class' => 'Twig_Node_Expression_Test_Odd'));
347 $twig->addTest($test);
349 class Twig_Node_Expression_Test_Odd extends Twig_Node_Expression_Test
351 public function compile(Twig_Compiler $compiler)
355 ->subcompile($this->getNode('node'))
362 The above example shows how you can create tests that use a node class. The
363 node class has access to one sub-node called 'node'. This sub-node contains the
364 value that is being tested. When the ``odd`` filter is used in code such as:
366 .. code-block:: jinja
368 {% if my_value is odd %}
370 The ``node`` sub-node will contain an expression of ``my_value``. Node-based
371 tests also have access to the ``arguments`` node. This node will contain the
372 various other arguments that have been provided to your test.
374 If you want to pass a variable number of positional or named arguments to the
375 test, set the ``is_variadic`` option to ``true``. Tests also support dynamic
376 name feature as filters and functions.
381 One of the most exciting features of a template engine like Twig is the
382 possibility to define new language constructs. This is also the most complex
383 feature as you need to understand how Twig's internals work.
385 Let's create a simple ``set`` tag that allows the definition of simple
386 variables from within a template. The tag can be used like follows:
388 .. code-block:: jinja
390 {% set name = "value" %}
394 {# should output value #}
398 The ``set`` tag is part of the Core extension and as such is always
399 available. The built-in version is slightly more powerful and supports
400 multiple assignments by default (cf. the template designers chapter for
403 Three steps are needed to define a new tag:
405 * Defining a Token Parser class (responsible for parsing the template code);
407 * Defining a Node class (responsible for converting the parsed code to PHP);
409 * Registering the tag.
411 Registering a new tag
412 ~~~~~~~~~~~~~~~~~~~~~
414 Adding a tag is as simple as calling the ``addTokenParser`` method on the
415 ``Twig_Environment`` instance::
417 $twig = new Twig_Environment($loader);
418 $twig->addTokenParser(new Project_Set_TokenParser());
420 Defining a Token Parser
421 ~~~~~~~~~~~~~~~~~~~~~~~
423 Now, let's see the actual code of this class::
425 class Project_Set_TokenParser extends Twig_TokenParser
427 public function parse(Twig_Token $token)
429 $parser = $this->parser;
430 $stream = $parser->getStream();
432 $name = $stream->expect(Twig_Token::NAME_TYPE)->getValue();
433 $stream->expect(Twig_Token::OPERATOR_TYPE, '=');
434 $value = $parser->getExpressionParser()->parseExpression();
435 $stream->expect(Twig_Token::BLOCK_END_TYPE);
437 return new Project_Set_Node($name, $value, $token->getLine(), $this->getTag());
440 public function getTag()
446 The ``getTag()`` method must return the tag we want to parse, here ``set``.
448 The ``parse()`` method is invoked whenever the parser encounters a ``set``
449 tag. It should return a ``Twig_Node`` instance that represents the node (the
450 ``Project_Set_Node`` calls creating is explained in the next section).
452 The parsing process is simplified thanks to a bunch of methods you can call
453 from the token stream (``$this->parser->getStream()``):
455 * ``getCurrent()``: Gets the current token in the stream.
457 * ``next()``: Moves to the next token in the stream, *but returns the old one*.
459 * ``test($type)``, ``test($value)`` or ``test($type, $value)``: Determines whether
460 the current token is of a particular type or value (or both). The value may be an
461 array of several possible values.
463 * ``expect($type[, $value[, $message]])``: If the current token isn't of the given
464 type/value a syntax error is thrown. Otherwise, if the type and value are correct,
465 the token is returned and the stream moves to the next token.
467 * ``look()``: Looks at the next token without consuming it.
469 Parsing expressions is done by calling the ``parseExpression()`` like we did for
474 Reading the existing ``TokenParser`` classes is the best way to learn all
475 the nitty-gritty details of the parsing process.
480 The ``Project_Set_Node`` class itself is rather simple::
482 class Project_Set_Node extends Twig_Node
484 public function __construct($name, Twig_Node_Expression $value, $line, $tag = null)
486 parent::__construct(array('value' => $value), array('name' => $name), $line, $tag);
489 public function compile(Twig_Compiler $compiler)
492 ->addDebugInfo($this)
493 ->write('$context[\''.$this->getAttribute('name').'\'] = ')
494 ->subcompile($this->getNode('value'))
500 The compiler implements a fluid interface and provides methods that helps the
501 developer generate beautiful and readable PHP code:
503 * ``subcompile()``: Compiles a node.
505 * ``raw()``: Writes the given string as is.
507 * ``write()``: Writes the given string by adding indentation at the beginning
510 * ``string()``: Writes a quoted string.
512 * ``repr()``: Writes a PHP representation of a given value (see
513 ``Twig_Node_For`` for a usage example).
515 * ``addDebugInfo()``: Adds the line of the original template file related to
516 the current node as a comment.
518 * ``indent()``: Indents the generated code (see ``Twig_Node_Block`` for a
521 * ``outdent()``: Outdents the generated code (see ``Twig_Node_Block`` for a
524 .. _creating_extensions:
526 Creating an Extension
527 ---------------------
529 The main motivation for writing an extension is to move often used code into a
530 reusable class like adding support for internationalization. An extension can
531 define tags, filters, tests, operators, global variables, functions, and node
534 Most of the time, it is useful to create a single extension for your project,
535 to host all the specific tags and filters you want to add to Twig.
539 When packaging your code into an extension, Twig is smart enough to
540 recompile your templates whenever you make a change to it (when
541 ``auto_reload`` is enabled).
545 Before writing your own extensions, have a look at the Twig official
546 extension repository: http://github.com/twigphp/Twig-extensions.
548 An extension is a class that implements the following interface::
550 interface Twig_ExtensionInterface
553 * Initializes the runtime environment.
555 * This is where you can load some file that contains filter functions for instance.
557 * @deprecated since 1.23 (to be removed in 2.0), implement Twig_Extension_InitRuntimeInterface instead
559 function initRuntime(Twig_Environment $environment);
562 * Returns the token parser instances to add to the existing list.
564 * @return (Twig_TokenParserInterface|Twig_TokenParserBrokerInterface)[]
566 function getTokenParsers();
569 * Returns the node visitor instances to add to the existing list.
571 * @return Twig_NodeVisitorInterface[]
573 function getNodeVisitors();
576 * Returns a list of filters to add to the existing list.
578 * @return Twig_SimpleFilter[]
580 function getFilters();
583 * Returns a list of tests to add to the existing list.
585 * @return Twig_SimpleTest[]
590 * Returns a list of functions to add to the existing list.
592 * @return Twig_SimpleFunction[]
594 function getFunctions();
597 * Returns a list of operators to add to the existing list.
599 * @return array<array> First array of unary operators, second array of binary operators
601 function getOperators();
604 * Returns a list of global variables to add to the existing list.
606 * @return array An array of global variables
608 * @deprecated since 1.23 (to be removed in 2.0), implement Twig_Extension_GlobalsInterface instead
610 function getGlobals();
613 * Returns the name of the extension.
615 * @return string The extension name
617 * @deprecated since 1.26 (to be removed in 2.0), not used anymore internally
622 To keep your extension class clean and lean, inherit from the built-in
623 ``Twig_Extension`` class instead of implementing the interface as it provides
624 empty implementations for all methods:
626 class Project_Twig_Extension extends Twig_Extension
630 Of course, this extension does nothing for now. We will customize it in the
635 Prior to Twig 1.26, you must implement the ``getName()`` method which must
636 return a unique identifier for the extension.
638 Twig does not care where you save your extension on the filesystem, as all
639 extensions must be registered explicitly to be available in your templates.
641 You can register an extension by using the ``addExtension()`` method on your
642 main ``Environment`` object::
644 $twig = new Twig_Environment($loader);
645 $twig->addExtension(new Project_Twig_Extension());
649 The Twig core extensions are great examples of how extensions work.
654 Global variables can be registered in an extension via the ``getGlobals()``
657 class Project_Twig_Extension extends Twig_Extension implements Twig_Extension_GlobalsInterface
659 public function getGlobals()
662 'text' => new Text(),
672 Functions can be registered in an extension via the ``getFunctions()``
675 class Project_Twig_Extension extends Twig_Extension
677 public function getFunctions()
680 new Twig_SimpleFunction('lipsum', 'generate_lipsum'),
690 To add a filter to an extension, you need to override the ``getFilters()``
691 method. This method must return an array of filters to add to the Twig
694 class Project_Twig_Extension extends Twig_Extension
696 public function getFilters()
699 new Twig_SimpleFilter('rot13', 'str_rot13'),
709 Adding a tag in an extension can be done by overriding the
710 ``getTokenParsers()`` method. This method must return an array of tags to add
711 to the Twig environment::
713 class Project_Twig_Extension extends Twig_Extension
715 public function getTokenParsers()
717 return array(new Project_Set_TokenParser());
723 In the above code, we have added a single new tag, defined by the
724 ``Project_Set_TokenParser`` class. The ``Project_Set_TokenParser`` class is
725 responsible for parsing the tag and compiling it to PHP.
730 The ``getOperators()`` methods lets you add new operators. Here is how to add
731 ``!``, ``||``, and ``&&`` operators::
733 class Project_Twig_Extension extends Twig_Extension
735 public function getOperators()
739 '!' => array('precedence' => 50, 'class' => 'Twig_Node_Expression_Unary_Not'),
742 '||' => array('precedence' => 10, 'class' => 'Twig_Node_Expression_Binary_Or', 'associativity' => Twig_ExpressionParser::OPERATOR_LEFT),
743 '&&' => array('precedence' => 15, 'class' => 'Twig_Node_Expression_Binary_And', 'associativity' => Twig_ExpressionParser::OPERATOR_LEFT),
754 The ``getTests()`` method lets you add new test functions::
756 class Project_Twig_Extension extends Twig_Extension
758 public function getTests()
761 new Twig_SimpleTest('even', 'twig_test_even'),
768 Definition vs Runtime
769 ~~~~~~~~~~~~~~~~~~~~~
771 Twig filters, functions, and tests runtime implementations can be defined as
772 any valid PHP callable:
774 * **functions/static methods**: Simple to implement and fast (used by all Twig
775 core extensions); but it is hard for the runtime to depend on external
778 * **closures**: Simple to implement;
780 * **object methods**: More flexible and required if your runtime code depends
783 The simplest way to use methods is to define them on the extension itself::
785 class Project_Twig_Extension extends Twig_Extension
787 private $rot13Provider;
789 public function __construct($rot13Provider)
791 $this->rot13Provider = $rot13Provider;
794 public function getFunctions()
797 new Twig_SimpleFunction('rot13', array($this, 'rot13')),
801 public function rot13($value)
803 return $rot13Provider->rot13($value);
807 This is very convenient but not recommended as it makes template compilation
808 depend on runtime dependencies even if they are not needed (think for instance
809 as a dependency that connects to a database engine).
811 As of Twig 1.26, you can easily decouple the extension definitions from their
812 runtime implementations by registering a ``Twig_RuntimeLoaderInterface``
813 instance on the environment that knows how to instantiate such runtime classes
814 (runtime classes must be autoload-able)::
816 class RuntimeLoader implements Twig_RuntimeLoaderInterface
818 public function load($class)
820 // implement the logic to create an instance of $class
821 // and inject its dependencies
822 // most of the time, it means using your dependency injection container
823 if ('Project_Twig_RuntimeExtension' === $class) {
824 return new $class(new Rot13Provider());
831 $twig->addRuntimeLoader(new RuntimeLoader());
835 As of Twig 1.32, Twig comes with a PSR-11 compatible runtime loader
836 (``Twig_ContainerRuntimeLoader``) that works on PHP 5.3+.
838 It is now possible to move the runtime logic to a new
839 ``Project_Twig_RuntimeExtension`` class and use it directly in the extension::
841 class Project_Twig_RuntimeExtension
843 private $rot13Provider;
845 public function __construct($rot13Provider)
847 $this->rot13Provider = $rot13Provider;
850 public function rot13($value)
852 return $rot13Provider->rot13($value);
856 class Project_Twig_Extension extends Twig_Extension
858 public function getFunctions()
861 new Twig_SimpleFunction('rot13', array('Project_Twig_RuntimeExtension', 'rot13')),
863 new Twig_SimpleFunction('rot13', 'Project_Twig_RuntimeExtension::rot13'),
871 To overload an already defined filter, test, operator, global variable, or
872 function, re-define it in an extension and register it **as late as
873 possible** (order matters)::
875 class MyCoreExtension extends Twig_Extension
877 public function getFilters()
880 new Twig_SimpleFilter('date', array($this, 'dateFilter')),
884 public function dateFilter($timestamp, $format = 'F j, Y H:i')
886 // do something different from the built-in date filter
890 $twig = new Twig_Environment($loader);
891 $twig->addExtension(new MyCoreExtension());
893 Here, we have overloaded the built-in ``date`` filter with a custom one.
895 If you do the same on the ``Twig_Environment`` itself, beware that it takes
896 precedence over any other registered extensions::
898 $twig = new Twig_Environment($loader);
899 $twig->addFilter(new Twig_SimpleFilter('date', function ($timestamp, $format = 'F j, Y H:i') {
900 // do something different from the built-in date filter
902 // the date filter will come from the above registration, not
903 // from the registered extension below
904 $twig->addExtension(new MyCoreExtension());
908 Note that overloading the built-in Twig elements is not recommended as it
917 You can create functional tests for extensions simply by creating the
918 following file structure in your test directory::
932 The ``IntegrationTest.php`` file should look like this::
934 class Project_Tests_IntegrationTest extends Twig_Test_IntegrationTestCase
936 public function getExtensions()
939 new Project_Twig_Extension1(),
940 new Project_Twig_Extension2(),
944 public function getFixturesDir()
946 return dirname(__FILE__).'/Fixtures/';
950 Fixtures examples can be found within the Twig repository
951 `tests/Twig/Fixtures`_ directory.
956 Testing the node visitors can be complex, so extend your test cases from
957 ``Twig_Test_NodeTestCase``. Examples can be found in the Twig repository
958 `tests/Twig/Node`_ directory.
960 .. _`rot13`: http://www.php.net/manual/en/function.str-rot13.php
961 .. _`tests/Twig/Fixtures`: https://github.com/twigphp/Twig/tree/master/test/Twig/Tests/Fixtures
962 .. _`tests/Twig/Node`: https://github.com/twigphp/Twig/tree/master/test/Twig/Tests/Node