1Extending Twig
2==============
3
4.. caution::
5
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
8    instead.
9
10Twig can be extended in many ways; you can add extra tags, filters, tests,
11operators, global variables, and functions. You can even extend the parser
12itself with node visitors.
13
14.. note::
15
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.
20
21.. caution::
22
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).
27
28Before extending Twig, you must understand the differences between all the
29different possible extension points and when to use them.
30
31First, remember that Twig has two main language constructs:
32
33* ``{{ }}``: used to print the result of an expression evaluation;
34
35* ``{% %}``: used to execute statements.
36
37To understand why Twig exposes so many extension points, let's see how to
38implement a *Lorem ipsum* generator (it needs to know the number of words to
39generate).
40
41You can use a ``lipsum`` *tag*:
42
43.. code-block:: jinja
44
45    {% lipsum 40 %}
46
47That works, but using a tag for ``lipsum`` is not a good idea for at least
48three main reasons:
49
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:
53
54  .. code-block:: jinja
55
56      {{ 'some text' ~ {% lipsum 40 %} ~ 'some more text' }}
57
58In fact, you rarely need to create tags; and that's good news because tags are
59the most complex extension point of Twig.
60
61Now, let's use a ``lipsum`` *filter*:
62
63.. code-block:: jinja
64
65    {{ 40|lipsum }}
66
67Again, it works, but it looks weird. A filter transforms the passed value to
68something else but here we use the value to indicate the number of words to
69generate (so, ``40`` is an argument of the filter, not the value we want to
70transform).
71
72Next, let's use a ``lipsum`` *function*:
73
74.. code-block:: jinja
75
76    {{ lipsum(40) }}
77
78Here we go. For this specific example, the creation of a function is the
79extension point to use. And you can use it anywhere an expression is accepted:
80
81.. code-block:: jinja
82
83    {{ 'some text' ~ lipsum(40) ~ 'some more text' }}
84
85    {% set lipsum = lipsum(40) %}
86
87Last but not the least, you can also use a *global* object with a method able
88to generate lorem ipsum text:
89
90.. code-block:: jinja
91
92    {{ text.lipsum(40) }}
93
94As a rule of thumb, use functions for frequently used features and global
95objects for everything else.
96
97Keep in mind the following when you want to extend Twig:
98
99========== ========================== ========== =========================
100What?      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========== ========================== ========== =========================
110
111Globals
112-------
113
114A global variable is like any other template variable, except that it's
115available in all templates and macros::
116
117    $twig = new \Twig\Environment($loader);
118    $twig->addGlobal('text', new Text());
119
120You can then use the ``text`` variable anywhere in a template:
121
122.. code-block:: jinja
123
124    {{ text.lipsum(40) }}
125
126Filters
127-------
128
129Creating a filter is as simple as associating a name with a PHP callable::
130
131    // an anonymous function
132    $filter = new \Twig\TwigFilter('rot13', function ($string) {
133        return str_rot13($string);
134    });
135
136    // or a simple PHP function
137    $filter = new \Twig\TwigFilter('rot13', 'str_rot13');
138
139    // or a class static method
140    $filter = new \Twig\TwigFilter('rot13', ['SomeClass', 'rot13Filter']);
141    $filter = new \Twig\TwigFilter('rot13', 'SomeClass::rot13Filter');
142
143    // or a class method
144    $filter = new \Twig\TwigFilter('rot13', [$this, 'rot13Filter']);
145    // the one below needs a runtime implementation (see below for more information)
146    $filter = new \Twig\TwigFilter('rot13', ['SomeClass', 'rot13Filter']);
147
148The first argument passed to the ``\Twig\TwigFilter`` constructor is the name
149of the filter you will use in templates and the second one is the PHP callable
150to associate with it.
151
152Then, add the filter to your Twig environment::
153
154    $twig = new \Twig\Environment($loader);
155    $twig->addFilter($filter);
156
157And here is how to use it in a template:
158
159.. code-block:: jinja
160
161    {{ 'Twig'|rot13 }}
162
163    {# will output Gjvt #}
164
165When 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
167to the filter (within parentheses ``()``) as extra arguments.
168
169For instance, the following code:
170
171.. code-block:: jinja
172
173    {{ 'TWIG'|lower }}
174    {{ now|date('d/m/Y') }}
175
176is compiled to something like the following::
177
178    <?php echo strtolower('TWIG') ?>
179    <?php echo twig_date_format_filter($now, 'd/m/Y') ?>
180
181The ``\Twig\TwigFilter`` class takes an array of options as its last
182argument::
183
184    $filter = new \Twig\TwigFilter('rot13', 'str_rot13', $options);
185
186Environment-aware Filters
187~~~~~~~~~~~~~~~~~~~~~~~~~
188
189If you want to access the current environment instance in your filter, set the
190``needs_environment`` option to ``true``; Twig will pass the current
191environment as the first argument to the filter call::
192
193    $filter = new \Twig\TwigFilter('rot13', function (Twig_Environment $env, $string) {
194        // get the current charset for instance
195        $charset = $env->getCharset();
196
197        return str_rot13($string);
198    }, ['needs_environment' => true]);
199
200Context-aware Filters
201~~~~~~~~~~~~~~~~~~~~~
202
203If 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
205the first argument to the filter call (or the second one if
206``needs_environment`` is also set to ``true``)::
207
208    $filter = new \Twig\TwigFilter('rot13', function ($context, $string) {
209        // ...
210    }, ['needs_context' => true]);
211
212    $filter = new \Twig\TwigFilter('rot13', function (Twig_Environment $env, $context, $string) {
213        // ...
214    }, ['needs_context' => true, 'needs_environment' => true]);
215
216Automatic Escaping
217~~~~~~~~~~~~~~~~~~
218
219If automatic escaping is enabled, the output of the filter may be escaped
220before printing. If your filter acts as an escaper (or explicitly outputs HTML
221or JavaScript code), you will want the raw output to be printed. In such a
222case, set the ``is_safe`` option::
223
224    $filter = new \Twig\TwigFilter('nl2br', 'nl2br', ['is_safe' => ['html']]);
225
226Some filters may need to work on input that is already escaped or safe, for
227example when adding (safe) HTML tags to originally unsafe output. In such a
228case, set the ``pre_escape`` option to escape the input data before it is run
229through your filter::
230
231    $filter = new \Twig\TwigFilter('somefilter', 'somefilter', ['pre_escape' => 'html', 'is_safe' => ['html']]);
232
233Variadic Filters
234~~~~~~~~~~~~~~~~
235
236.. versionadded:: 1.19
237    Support for variadic filters was added in Twig 1.19.
238
239When 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
241last argument to the filter call as an array::
242
243    $filter = new \Twig\TwigFilter('thumbnail', function ($file, array $options = []) {
244        // ...
245    }, ['is_variadic' => true]);
246
247Be warned that :ref:`named arguments <named-arguments>` passed to a variadic
248filter cannot be checked for validity as they will automatically end up in the
249option array.
250
251Dynamic Filters
252~~~~~~~~~~~~~~~
253
254A filter name containing the special ``*`` character is a dynamic filter as
255the ``*`` can be any string::
256
257    $filter = new \Twig\TwigFilter('*_path', function ($name, $arguments) {
258        // ...
259    });
260
261The following filters will be matched by the above defined dynamic filter:
262
263* ``product_path``
264* ``category_path``
265
266A dynamic filter can define more than one dynamic parts::
267
268    $filter = new \Twig\TwigFilter('*_path_*', function ($name, $suffix, $arguments) {
269        // ...
270    });
271
272The filter will receive all dynamic part values before the normal filter
273arguments, but after the environment and the context. For instance, a call to
274``'foo'|a_path_b()`` will result in the following arguments to be passed to
275the filter: ``('a', 'b', 'foo')``.
276
277Deprecated Filters
278~~~~~~~~~~~~~~~~~~
279
280.. versionadded:: 1.21
281    Support for deprecated filters was added in Twig 1.21.
282
283You can mark a filter as being deprecated by setting the ``deprecated`` option
284to ``true``. You can also give an alternative filter that replaces the
285deprecated one when that makes sense::
286
287    $filter = new \Twig\TwigFilter('obsolete', function () {
288        // ...
289    }, ['deprecated' => true, 'alternative' => 'new_one']);
290
291When a filter is deprecated, Twig emits a deprecation notice when compiling a
292template using it. See :ref:`deprecation-notices` for more information.
293
294Functions
295---------
296
297Functions are defined in the exact same way as filters, but you need to create
298an instance of ``\Twig\TwigFunction``::
299
300    $twig = new \Twig\Environment($loader);
301    $function = new \Twig\TwigFunction('function_name', function () {
302        // ...
303    });
304    $twig->addFunction($function);
305
306Functions support the same features as filters, except for the ``pre_escape``
307and ``preserves_safety`` options.
308
309Tests
310-----
311
312Tests are defined in the exact same way as filters and functions, but you need
313to create an instance of ``\Twig\TwigTest``::
314
315    $twig = new \Twig\Environment($loader);
316    $test = new \Twig\TwigTest('test_name', function () {
317        // ...
318    });
319    $twig->addTest($test);
320
321Tests allow you to create custom application specific logic for evaluating
322boolean conditions. As a simple example, let's create a Twig test that checks if
323objects are 'red'::
324
325    $twig = new \Twig\Environment($loader);
326    $test = new \Twig\TwigTest('red', function ($value) {
327        if (isset($value->color) && $value->color == 'red') {
328            return true;
329        }
330        if (isset($value->paint) && $value->paint == 'red') {
331            return true;
332        }
333        return false;
334    });
335    $twig->addTest($test);
336
337Test functions should always return true/false.
338
339When creating tests you can use the ``node_class`` option to provide custom test
340compilation. This is useful if your test can be compiled into PHP primitives.
341This is used by many of the tests built into Twig::
342
343    $twig = new \Twig\Environment($loader);
344    $test = new \Twig\TwigTest(
345        'odd',
346        null,
347        ['node_class' => '\Twig\Node\Expression\Test\OddTest']);
348    $twig->addTest($test);
349
350    class Twig_Node_Expression_Test_Odd extends \Twig\Node\Expression\TestExpression
351    {
352        public function compile(\Twig\Compiler $compiler)
353        {
354            $compiler
355                ->raw('(')
356                ->subcompile($this->getNode('node'))
357                ->raw(' % 2 == 1')
358                ->raw(')')
359            ;
360        }
361    }
362
363The above example shows how you can create tests that use a node class. The
364node class has access to one sub-node called 'node'. This sub-node contains the
365value that is being tested. When the ``odd`` filter is used in code such as:
366
367.. code-block:: jinja
368
369    {% if my_value is odd %}
370
371The ``node`` sub-node will contain an expression of ``my_value``. Node-based
372tests also have access to the ``arguments`` node. This node will contain the
373various other arguments that have been provided to your test.
374
375.. versionadded:: 1.36
376    Dynamic tests support was added in Twig 1.36.
377
378If you want to pass a variable number of positional or named arguments to the
379test, set the ``is_variadic`` option to ``true``. Tests support dynamic
380names (see dynamic filters and functions for the syntax).
381
382Tags
383----
384
385One of the most exciting features of a template engine like Twig is the
386possibility to define new **language constructs**. This is also the most complex
387feature as you need to understand how Twig's internals work.
388
389Most of the time though, a tag is not needed:
390
391* If your tag generates some output, use a **function** instead.
392
393* If your tag modifies some content and returns it, use a **filter** instead.
394
395  For instance, if you want to create a tag that converts a Markdown formatted
396  text to HTML, create a ``markdown`` filter instead:
397
398  .. code-block:: jinja
399
400      {{ '**markdown** text'|markdown }}
401
402  If you want use this filter on large amounts of text, wrap it with the
403  :doc:`filter <tags/filter>` tag:
404
405  .. code-block:: jinja
406
407      {% filter markdown %}
408      Title
409      =====
410
411      Much better than creating a tag as you can **compose** filters.
412      {% endfilter %}
413
414* If your tag does not output anything, but only exists because of a side
415  effect, create a **function** that returns nothing and call it via the
416  :doc:`filter <tags/do>` tag.
417
418  For instance, if you want to create a tag that logs text, create a ``log``
419  function instead and call it via the :doc:`do <tags/do>` tag:
420
421  .. code-block:: jinja
422
423      {% do log('Log some things') %}
424
425If you still want to create a tag for a new language construct, great!
426
427Let's create a simple ``set`` tag that allows the definition of simple
428variables from within a template. The tag can be used like follows:
429
430.. code-block:: jinja
431
432    {% set name = "value" %}
433
434    {{ name }}
435
436    {# should output value #}
437
438.. note::
439
440    The ``set`` tag is part of the Core extension and as such is always
441    available. The built-in version is slightly more powerful and supports
442    multiple assignments by default (cf. the template designers chapter for
443    more information).
444
445Three steps are needed to define a new tag:
446
447* Defining a Token Parser class (responsible for parsing the template code);
448
449* Defining a Node class (responsible for converting the parsed code to PHP);
450
451* Registering the tag.
452
453Registering a new tag
454~~~~~~~~~~~~~~~~~~~~~
455
456Adding a tag is as simple as calling the ``addTokenParser`` method on the
457``\Twig\Environment`` instance::
458
459    $twig = new \Twig\Environment($loader);
460    $twig->addTokenParser(new Project_Set_TokenParser());
461
462Defining a Token Parser
463~~~~~~~~~~~~~~~~~~~~~~~
464
465Now, let's see the actual code of this class::
466
467    class Project_Set_TokenParser extends \Twig\TokenParser\AbstractTokenParser
468    {
469        public function parse(\Twig\Token $token)
470        {
471            $parser = $this->parser;
472            $stream = $parser->getStream();
473
474            $name = $stream->expect(\Twig\Token::NAME_TYPE)->getValue();
475            $stream->expect(\Twig\Token::OPERATOR_TYPE, '=');
476            $value = $parser->getExpressionParser()->parseExpression();
477            $stream->expect(\Twig\Token::BLOCK_END_TYPE);
478
479            return new Project_Set_Node($name, $value, $token->getLine(), $this->getTag());
480        }
481
482        public function getTag()
483        {
484            return 'set';
485        }
486    }
487
488The ``getTag()`` method must return the tag we want to parse, here ``set``.
489
490The ``parse()`` method is invoked whenever the parser encounters a ``set``
491tag. It should return a ``\Twig\Node\Node`` instance that represents the node (the
492``Project_Set_Node`` calls creating is explained in the next section).
493
494The parsing process is simplified thanks to a bunch of methods you can call
495from the token stream (``$this->parser->getStream()``):
496
497* ``getCurrent()``: Gets the current token in the stream.
498
499* ``next()``: Moves to the next token in the stream, *but returns the old one*.
500
501* ``test($type)``, ``test($value)`` or ``test($type, $value)``: Determines whether
502  the current token is of a particular type or value (or both). The value may be an
503  array of several possible values.
504
505* ``expect($type[, $value[, $message]])``: If the current token isn't of the given
506  type/value a syntax error is thrown. Otherwise, if the type and value are correct,
507  the token is returned and the stream moves to the next token.
508
509* ``look()``: Looks at the next token without consuming it.
510
511Parsing expressions is done by calling the ``parseExpression()`` like we did for
512the ``set`` tag.
513
514.. tip::
515
516    Reading the existing ``TokenParser`` classes is the best way to learn all
517    the nitty-gritty details of the parsing process.
518
519Defining a Node
520~~~~~~~~~~~~~~~
521
522The ``Project_Set_Node`` class itself is rather simple::
523
524    class Project_Set_Node extends \Twig\Node\Node
525    {
526        public function __construct($name, \Twig\Node\Expression\AbstractExpression $value, $line, $tag = null)
527        {
528            parent::__construct(['value' => $value], ['name' => $name], $line, $tag);
529        }
530
531        public function compile(\Twig\Compiler $compiler)
532        {
533            $compiler
534                ->addDebugInfo($this)
535                ->write('$context[\''.$this->getAttribute('name').'\'] = ')
536                ->subcompile($this->getNode('value'))
537                ->raw(";\n")
538            ;
539        }
540    }
541
542The compiler implements a fluid interface and provides methods that helps the
543developer generate beautiful and readable PHP code:
544
545* ``subcompile()``: Compiles a node.
546
547* ``raw()``: Writes the given string as is.
548
549* ``write()``: Writes the given string by adding indentation at the beginning
550  of each line.
551
552* ``string()``: Writes a quoted string.
553
554* ``repr()``: Writes a PHP representation of a given value (see
555  ``\Twig\Node\ForNode`` for a usage example).
556
557* ``addDebugInfo()``: Adds the line of the original template file related to
558  the current node as a comment.
559
560* ``indent()``: Indents the generated code (see ``\Twig\Node\BlockNode`` for a
561  usage example).
562
563* ``outdent()``: Outdents the generated code (see ``\Twig\Node\BlockNode`` for a
564  usage example).
565
566.. _creating_extensions:
567
568Creating an Extension
569---------------------
570
571The main motivation for writing an extension is to move often used code into a
572reusable class like adding support for internationalization. An extension can
573define tags, filters, tests, operators, global variables, functions, and node
574visitors.
575
576Most of the time, it is useful to create a single extension for your project,
577to host all the specific tags and filters you want to add to Twig.
578
579.. tip::
580
581    When packaging your code into an extension, Twig is smart enough to
582    recompile your templates whenever you make a change to it (when
583    ``auto_reload`` is enabled).
584
585.. note::
586
587    Before writing your own extensions, have a look at the Twig official
588    extension repository: https://github.com/twigphp/Twig-extensions.
589
590An extension is a class that implements the following interface::
591
592    interface Twig_ExtensionInterface
593    {
594        /**
595         * Initializes the runtime environment.
596         *
597         * This is where you can load some file that contains filter functions for instance.
598         *
599         * @deprecated since 1.23 (to be removed in 2.0), implement \Twig\Extension\InitRuntimeInterface instead
600         */
601        function initRuntime(\Twig\Environment $environment);
602
603        /**
604         * Returns the token parser instances to add to the existing list.
605         *
606         * @return (Twig_TokenParserInterface|Twig_TokenParserBrokerInterface)[]
607         */
608        function getTokenParsers();
609
610        /**
611         * Returns the node visitor instances to add to the existing list.
612         *
613         * @return \Twig\NodeVisitor\NodeVisitorInterface[]
614         */
615        function getNodeVisitors();
616
617        /**
618         * Returns a list of filters to add to the existing list.
619         *
620         * @return \Twig\TwigFilter[]
621         */
622        function getFilters();
623
624        /**
625         * Returns a list of tests to add to the existing list.
626         *
627         * @return \Twig\TwigTest[]
628         */
629        function getTests();
630
631        /**
632         * Returns a list of functions to add to the existing list.
633         *
634         * @return \Twig\TwigFunction[]
635         */
636        function getFunctions();
637
638        /**
639         * Returns a list of operators to add to the existing list.
640         *
641         * @return array<array> First array of unary operators, second array of binary operators
642         */
643        function getOperators();
644
645        /**
646         * Returns a list of global variables to add to the existing list.
647         *
648         * @return array An array of global variables
649         *
650         * @deprecated since 1.23 (to be removed in 2.0), implement \Twig\Extension\GlobalsInterface instead
651         */
652        function getGlobals();
653
654        /**
655         * Returns the name of the extension.
656         *
657         * @return string The extension name
658         *
659         * @deprecated since 1.26 (to be removed in 2.0), not used anymore internally
660         */
661        function getName();
662    }
663
664To keep your extension class clean and lean, inherit from the built-in
665``\Twig\Extension\AbstractExtension`` class instead of implementing the interface as it provides
666empty implementations for all methods:
667
668    class Project_Twig_Extension extends \Twig\Extension\AbstractExtension
669    {
670    }
671
672Of course, this extension does nothing for now. We will customize it in the
673next sections.
674
675.. note::
676
677    Prior to Twig 1.26, you must implement the ``getName()`` method which must
678    return a unique identifier for the extension.
679
680Twig does not care where you save your extension on the filesystem, as all
681extensions must be registered explicitly to be available in your templates.
682
683You can register an extension by using the ``addExtension()`` method on your
684main ``Environment`` object::
685
686    $twig = new \Twig\Environment($loader);
687    $twig->addExtension(new Project_Twig_Extension());
688
689.. tip::
690
691    The Twig core extensions are great examples of how extensions work.
692
693Globals
694~~~~~~~
695
696Global variables can be registered in an extension via the ``getGlobals()``
697method::
698
699    class Project_Twig_Extension extends \Twig\Extension\AbstractExtension implements \Twig\Extension\GlobalsInterface
700    {
701        public function getGlobals()
702        {
703            return [
704                'text' => new Text(),
705            ];
706        }
707
708        // ...
709    }
710
711Functions
712~~~~~~~~~
713
714Functions can be registered in an extension via the ``getFunctions()``
715method::
716
717    class Project_Twig_Extension extends \Twig\Extension\AbstractExtension
718    {
719        public function getFunctions()
720        {
721            return [
722                new \Twig\TwigFunction('lipsum', 'generate_lipsum'),
723            ];
724        }
725
726        // ...
727    }
728
729Filters
730~~~~~~~
731
732To add a filter to an extension, you need to override the ``getFilters()``
733method. This method must return an array of filters to add to the Twig
734environment::
735
736    class Project_Twig_Extension extends \Twig\Extension\AbstractExtension
737    {
738        public function getFilters()
739        {
740            return [
741                new \Twig\TwigFilter('rot13', 'str_rot13'),
742            ];
743        }
744
745        // ...
746    }
747
748Tags
749~~~~
750
751Adding a tag in an extension can be done by overriding the
752``getTokenParsers()`` method. This method must return an array of tags to add
753to the Twig environment::
754
755    class Project_Twig_Extension extends \Twig\Extension\AbstractExtension
756    {
757        public function getTokenParsers()
758        {
759            return [new Project_Set_TokenParser()];
760        }
761
762        // ...
763    }
764
765In the above code, we have added a single new tag, defined by the
766``Project_Set_TokenParser`` class. The ``Project_Set_TokenParser`` class is
767responsible for parsing the tag and compiling it to PHP.
768
769Operators
770~~~~~~~~~
771
772The ``getOperators()`` methods lets you add new operators. Here is how to add
773``!``, ``||``, and ``&&`` operators::
774
775    class Project_Twig_Extension extends \Twig\Extension\AbstractExtension
776    {
777        public function getOperators()
778        {
779            return [
780                [
781                    '!' => ['precedence' => 50, 'class' => '\Twig\Node\Expression\Unary\NotUnary'],
782                ],
783                [
784                    '||' => ['precedence' => 10, 'class' => '\Twig\Node\Expression\Binary\OrBinary', 'associativity' => \Twig\ExpressionParser::OPERATOR_LEFT],
785                    '&&' => ['precedence' => 15, 'class' => '\Twig\Node\Expression\Binary\AndBinary', 'associativity' => \Twig\ExpressionParser::OPERATOR_LEFT],
786                ],
787            ];
788        }
789
790        // ...
791    }
792
793Tests
794~~~~~
795
796The ``getTests()`` method lets you add new test functions::
797
798    class Project_Twig_Extension extends \Twig\Extension\AbstractExtension
799    {
800        public function getTests()
801        {
802            return [
803                new \Twig\TwigTest('even', 'twig_test_even'),
804            ];
805        }
806
807        // ...
808    }
809
810Definition vs Runtime
811~~~~~~~~~~~~~~~~~~~~~
812
813Twig filters, functions, and tests runtime implementations can be defined as
814any valid PHP callable:
815
816* **functions/static methods**: Simple to implement and fast (used by all Twig
817  core extensions); but it is hard for the runtime to depend on external
818  objects;
819
820* **closures**: Simple to implement;
821
822* **object methods**: More flexible and required if your runtime code depends
823  on external objects.
824
825The simplest way to use methods is to define them on the extension itself::
826
827    class Project_Twig_Extension extends \Twig\Extension\AbstractExtension
828    {
829        private $rot13Provider;
830
831        public function __construct($rot13Provider)
832        {
833            $this->rot13Provider = $rot13Provider;
834        }
835
836        public function getFunctions()
837        {
838            return [
839                new \Twig\TwigFunction('rot13', [$this, 'rot13']),
840            ];
841        }
842
843        public function rot13($value)
844        {
845            return $this->rot13Provider->rot13($value);
846        }
847    }
848
849This is very convenient but not recommended as it makes template compilation
850depend on runtime dependencies even if they are not needed (think for instance
851as a dependency that connects to a database engine).
852
853As of Twig 1.26, you can easily decouple the extension definitions from their
854runtime implementations by registering a ``\Twig\RuntimeLoader\RuntimeLoaderInterface``
855instance on the environment that knows how to instantiate such runtime classes
856(runtime classes must be autoload-able)::
857
858    class RuntimeLoader implements \Twig\RuntimeLoader\RuntimeLoaderInterface
859    {
860        public function load($class)
861        {
862            // implement the logic to create an instance of $class
863            // and inject its dependencies
864            // most of the time, it means using your dependency injection container
865            if ('Project_Twig_RuntimeExtension' === $class) {
866                return new $class(new Rot13Provider());
867            } else {
868                // ...
869            }
870        }
871    }
872
873    $twig->addRuntimeLoader(new RuntimeLoader());
874
875.. note::
876
877    As of Twig 1.32, Twig comes with a PSR-11 compatible runtime loader
878    (``\Twig\RuntimeLoader\ContainerRuntimeLoader``) that works on PHP 5.3+.
879
880It is now possible to move the runtime logic to a new
881``Project_Twig_RuntimeExtension`` class and use it directly in the extension::
882
883    class Project_Twig_RuntimeExtension
884    {
885        private $rot13Provider;
886
887        public function __construct($rot13Provider)
888        {
889            $this->rot13Provider = $rot13Provider;
890        }
891
892        public function rot13($value)
893        {
894            return $this->rot13Provider->rot13($value);
895        }
896    }
897
898    class Project_Twig_Extension extends \Twig\Extension\AbstractExtension
899    {
900        public function getFunctions()
901        {
902            return [
903                new \Twig\TwigFunction('rot13', ['Project_Twig_RuntimeExtension', 'rot13']),
904                // or
905                new \Twig\TwigFunction('rot13', 'Project_Twig_RuntimeExtension::rot13'),
906            ];
907        }
908    }
909
910Overloading
911-----------
912
913To overload an already defined filter, test, operator, global variable, or
914function, re-define it in an extension and register it **as late as
915possible** (order matters)::
916
917    class MyCoreExtension extends \Twig\Extension\AbstractExtension
918    {
919        public function getFilters()
920        {
921            return [
922                new \Twig\TwigFilter('date', [$this, 'dateFilter']),
923            ];
924        }
925
926        public function dateFilter($timestamp, $format = 'F j, Y H:i')
927        {
928            // do something different from the built-in date filter
929        }
930    }
931
932    $twig = new \Twig\Environment($loader);
933    $twig->addExtension(new MyCoreExtension());
934
935Here, we have overloaded the built-in ``date`` filter with a custom one.
936
937If you do the same on the ``\Twig\Environment`` itself, beware that it takes
938precedence over any other registered extensions::
939
940    $twig = new \Twig\Environment($loader);
941    $twig->addFilter(new \Twig\TwigFilter('date', function ($timestamp, $format = 'F j, Y H:i') {
942        // do something different from the built-in date filter
943    }));
944    // the date filter will come from the above registration, not
945    // from the registered extension below
946    $twig->addExtension(new MyCoreExtension());
947
948.. caution::
949
950    Note that overloading the built-in Twig elements is not recommended as it
951    might be confusing.
952
953Testing an Extension
954--------------------
955
956Functional Tests
957~~~~~~~~~~~~~~~~
958
959You can create functional tests for extensions simply by creating the
960following file structure in your test directory::
961
962    Fixtures/
963        filters/
964            foo.test
965            bar.test
966        functions/
967            foo.test
968            bar.test
969        tags/
970            foo.test
971            bar.test
972    IntegrationTest.php
973
974The ``IntegrationTest.php`` file should look like this::
975
976    class Project_Tests_IntegrationTest extends \Twig\Test\IntegrationTestCase
977    {
978        public function getExtensions()
979        {
980            return [
981                new Project_Twig_Extension1(),
982                new Project_Twig_Extension2(),
983            ];
984        }
985
986        public function getFixturesDir()
987        {
988            return dirname(__FILE__).'/Fixtures/';
989        }
990    }
991
992Fixtures examples can be found within the Twig repository
993`tests/Twig/Fixtures`_ directory.
994
995Node Tests
996~~~~~~~~~~
997
998Testing the node visitors can be complex, so extend your test cases from
999``\Twig\Test\NodeTestCase``. Examples can be found in the Twig repository
1000`tests/Twig/Node`_ directory.
1001
1002.. _`rot13`:                   https://secure.php.net/manual/en/function.str-rot13.php
1003.. _`tests/Twig/Fixtures`:     https://github.com/twigphp/Twig/tree/master/test/Twig/Tests/Fixtures
1004.. _`tests/Twig/Node`:         https://github.com/twigphp/Twig/tree/master/test/Twig/Tests/Node
1005