Security update for Core, with self-updated composer
[yaffs-website] / vendor / twig / twig / doc / advanced.rst
1 Extending 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
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.
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
28 Before extending Twig, you must understand the differences between all the
29 different possible extension points and when to use them.
30
31 First, 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
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
39 generate).
40
41 You can use a ``lipsum`` *tag*:
42
43 .. code-block:: jinja
44
45     {% lipsum 40 %}
46
47 That works, but using a tag for ``lipsum`` is not a good idea for at least
48 three 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
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.
60
61 Now, let's use a ``lipsum`` *filter*:
62
63 .. code-block:: jinja
64
65     {{ 40|lipsum }}
66
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
70 transform).
71
72 Next, let's use a ``lipsum`` *function*:
73
74 .. code-block:: jinja
75
76     {{ lipsum(40) }}
77
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:
80
81 .. code-block:: jinja
82
83     {{ 'some text' ~ lipsum(40) ~ 'some more text' }}
84
85     {% set lipsum = lipsum(40) %}
86
87 Last but not the least, you can also use a *global* object with a method able
88 to generate lorem ipsum text:
89
90 .. code-block:: jinja
91
92     {{ text.lipsum(40) }}
93
94 As a rule of thumb, use functions for frequently used features and global
95 objects for everything else.
96
97 Keep in mind the following when you want to extend Twig:
98
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 ========== ========================== ========== =========================
110
111 Globals
112 -------
113
114 A global variable is like any other template variable, except that it's
115 available in all templates and macros::
116
117     $twig = new Twig_Environment($loader);
118     $twig->addGlobal('text', new Text());
119
120 You can then use the ``text`` variable anywhere in a template:
121
122 .. code-block:: jinja
123
124     {{ text.lipsum(40) }}
125
126 Filters
127 -------
128
129 Creating a filter is as simple as associating a name with a PHP callable::
130
131     // an anonymous function
132     $filter = new Twig_SimpleFilter('rot13', function ($string) {
133         return str_rot13($string);
134     });
135
136     // or a simple PHP function
137     $filter = new Twig_SimpleFilter('rot13', 'str_rot13');
138
139     // or a class static method
140     $filter = new Twig_SimpleFilter('rot13', array('SomeClass', 'rot13Filter'));
141     $filter = new Twig_SimpleFilter('rot13', 'SomeClass::rot13Filter');
142
143     // or a class method
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'));
147
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.
151
152 Then, add the filter to your Twig environment::
153
154     $twig = new Twig_Environment($loader);
155     $twig->addFilter($filter);
156
157 And here is how to use it in a template:
158
159 .. code-block:: jinja
160
161     {{ 'Twig'|rot13 }}
162
163     {# will output Gjvt #}
164
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.
168
169 For instance, the following code:
170
171 .. code-block:: jinja
172
173     {{ 'TWIG'|lower }}
174     {{ now|date('d/m/Y') }}
175
176 is compiled to something like the following::
177
178     <?php echo strtolower('TWIG') ?>
179     <?php echo twig_date_format_filter($now, 'd/m/Y') ?>
180
181 The ``Twig_SimpleFilter`` class takes an array of options as its last
182 argument::
183
184     $filter = new Twig_SimpleFilter('rot13', 'str_rot13', $options);
185
186 Environment-aware Filters
187 ~~~~~~~~~~~~~~~~~~~~~~~~~
188
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::
192
193     $filter = new Twig_SimpleFilter('rot13', function (Twig_Environment $env, $string) {
194         // get the current charset for instance
195         $charset = $env->getCharset();
196
197         return str_rot13($string);
198     }, array('needs_environment' => true));
199
200 Context-aware Filters
201 ~~~~~~~~~~~~~~~~~~~~~
202
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``)::
207
208     $filter = new Twig_SimpleFilter('rot13', function ($context, $string) {
209         // ...
210     }, array('needs_context' => true));
211
212     $filter = new Twig_SimpleFilter('rot13', function (Twig_Environment $env, $context, $string) {
213         // ...
214     }, array('needs_context' => true, 'needs_environment' => true));
215
216 Automatic Escaping
217 ~~~~~~~~~~~~~~~~~~
218
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::
223
224     $filter = new Twig_SimpleFilter('nl2br', 'nl2br', array('is_safe' => array('html')));
225
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::
230
231     $filter = new Twig_SimpleFilter('somefilter', 'somefilter', array('pre_escape' => 'html', 'is_safe' => array('html')));
232
233 Variadic Filters
234 ~~~~~~~~~~~~~~~~
235
236 .. versionadded:: 1.19
237     Support for variadic filters was added in Twig 1.19.
238
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::
242
243     $filter = new Twig_SimpleFilter('thumbnail', function ($file, array $options = array()) {
244         // ...
245     }, array('is_variadic' => true));
246
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.
249
250 Dynamic Filters
251 ~~~~~~~~~~~~~~~
252
253 A filter name containing the special ``*`` character is a dynamic filter as
254 the ``*`` can be any string::
255
256     $filter = new Twig_SimpleFilter('*_path', function ($name, $arguments) {
257         // ...
258     });
259
260 The following filters will be matched by the above defined dynamic filter:
261
262 * ``product_path``
263 * ``category_path``
264
265 A dynamic filter can define more than one dynamic parts::
266
267     $filter = new Twig_SimpleFilter('*_path_*', function ($name, $suffix, $arguments) {
268         // ...
269     });
270
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')``.
275
276 Deprecated Filters
277 ~~~~~~~~~~~~~~~~~~
278
279 .. versionadded:: 1.21
280     Support for deprecated filters was added in Twig 1.21.
281
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::
285
286     $filter = new Twig_SimpleFilter('obsolete', function () {
287         // ...
288     }, array('deprecated' => true, 'alternative' => 'new_one'));
289
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.
292
293 Functions
294 ---------
295
296 Functions are defined in the exact same way as filters, but you need to create
297 an instance of ``Twig_SimpleFunction``::
298
299     $twig = new Twig_Environment($loader);
300     $function = new Twig_SimpleFunction('function_name', function () {
301         // ...
302     });
303     $twig->addFunction($function);
304
305 Functions support the same features as filters, except for the ``pre_escape``
306 and ``preserves_safety`` options.
307
308 Tests
309 -----
310
311 Tests are defined in the exact same way as filters and functions, but you need
312 to create an instance of ``Twig_SimpleTest``::
313
314     $twig = new Twig_Environment($loader);
315     $test = new Twig_SimpleTest('test_name', function () {
316         // ...
317     });
318     $twig->addTest($test);
319
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
322 objects are 'red'::
323
324     $twig = new Twig_Environment($loader);
325     $test = new Twig_SimpleTest('red', function ($value) {
326         if (isset($value->color) && $value->color == 'red') {
327             return true;
328         }
329         if (isset($value->paint) && $value->paint == 'red') {
330             return true;
331         }
332         return false;
333     });
334     $twig->addTest($test);
335
336 Test functions should always return true/false.
337
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::
341
342     $twig = new Twig_Environment($loader);
343     $test = new Twig_SimpleTest(
344         'odd',
345         null,
346         array('node_class' => 'Twig_Node_Expression_Test_Odd'));
347     $twig->addTest($test);
348
349     class Twig_Node_Expression_Test_Odd extends Twig_Node_Expression_Test
350     {
351         public function compile(Twig_Compiler $compiler)
352         {
353             $compiler
354                 ->raw('(')
355                 ->subcompile($this->getNode('node'))
356                 ->raw(' % 2 == 1')
357                 ->raw(')')
358             ;
359         }
360     }
361
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:
365
366 .. code-block:: jinja
367
368     {% if my_value is odd %}
369
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.
373
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.
377
378 Tags
379 ----
380
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.
384
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:
387
388 .. code-block:: jinja
389
390     {% set name = "value" %}
391
392     {{ name }}
393
394     {# should output value #}
395
396 .. note::
397
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
401     more information).
402
403 Three steps are needed to define a new tag:
404
405 * Defining a Token Parser class (responsible for parsing the template code);
406
407 * Defining a Node class (responsible for converting the parsed code to PHP);
408
409 * Registering the tag.
410
411 Registering a new tag
412 ~~~~~~~~~~~~~~~~~~~~~
413
414 Adding a tag is as simple as calling the ``addTokenParser`` method on the
415 ``Twig_Environment`` instance::
416
417     $twig = new Twig_Environment($loader);
418     $twig->addTokenParser(new Project_Set_TokenParser());
419
420 Defining a Token Parser
421 ~~~~~~~~~~~~~~~~~~~~~~~
422
423 Now, let's see the actual code of this class::
424
425     class Project_Set_TokenParser extends Twig_TokenParser
426     {
427         public function parse(Twig_Token $token)
428         {
429             $parser = $this->parser;
430             $stream = $parser->getStream();
431
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);
436
437             return new Project_Set_Node($name, $value, $token->getLine(), $this->getTag());
438         }
439
440         public function getTag()
441         {
442             return 'set';
443         }
444     }
445
446 The ``getTag()`` method must return the tag we want to parse, here ``set``.
447
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).
451
452 The parsing process is simplified thanks to a bunch of methods you can call
453 from the token stream (``$this->parser->getStream()``):
454
455 * ``getCurrent()``: Gets the current token in the stream.
456
457 * ``next()``: Moves to the next token in the stream, *but returns the old one*.
458
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.
462
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.
466
467 * ``look()``: Looks at the next token without consuming it.
468
469 Parsing expressions is done by calling the ``parseExpression()`` like we did for
470 the ``set`` tag.
471
472 .. tip::
473
474     Reading the existing ``TokenParser`` classes is the best way to learn all
475     the nitty-gritty details of the parsing process.
476
477 Defining a Node
478 ~~~~~~~~~~~~~~~
479
480 The ``Project_Set_Node`` class itself is rather simple::
481
482     class Project_Set_Node extends Twig_Node
483     {
484         public function __construct($name, Twig_Node_Expression $value, $line, $tag = null)
485         {
486             parent::__construct(array('value' => $value), array('name' => $name), $line, $tag);
487         }
488
489         public function compile(Twig_Compiler $compiler)
490         {
491             $compiler
492                 ->addDebugInfo($this)
493                 ->write('$context[\''.$this->getAttribute('name').'\'] = ')
494                 ->subcompile($this->getNode('value'))
495                 ->raw(";\n")
496             ;
497         }
498     }
499
500 The compiler implements a fluid interface and provides methods that helps the
501 developer generate beautiful and readable PHP code:
502
503 * ``subcompile()``: Compiles a node.
504
505 * ``raw()``: Writes the given string as is.
506
507 * ``write()``: Writes the given string by adding indentation at the beginning
508   of each line.
509
510 * ``string()``: Writes a quoted string.
511
512 * ``repr()``: Writes a PHP representation of a given value (see
513   ``Twig_Node_For`` for a usage example).
514
515 * ``addDebugInfo()``: Adds the line of the original template file related to
516   the current node as a comment.
517
518 * ``indent()``: Indents the generated code (see ``Twig_Node_Block`` for a
519   usage example).
520
521 * ``outdent()``: Outdents the generated code (see ``Twig_Node_Block`` for a
522   usage example).
523
524 .. _creating_extensions:
525
526 Creating an Extension
527 ---------------------
528
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
532 visitors.
533
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.
536
537 .. tip::
538
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).
542
543 .. note::
544
545     Before writing your own extensions, have a look at the Twig official
546     extension repository: http://github.com/twigphp/Twig-extensions.
547
548 An extension is a class that implements the following interface::
549
550     interface Twig_ExtensionInterface
551     {
552         /**
553          * Initializes the runtime environment.
554          *
555          * This is where you can load some file that contains filter functions for instance.
556          *
557          * @deprecated since 1.23 (to be removed in 2.0), implement Twig_Extension_InitRuntimeInterface instead
558          */
559         function initRuntime(Twig_Environment $environment);
560
561         /**
562          * Returns the token parser instances to add to the existing list.
563          *
564          * @return (Twig_TokenParserInterface|Twig_TokenParserBrokerInterface)[]
565          */
566         function getTokenParsers();
567
568         /**
569          * Returns the node visitor instances to add to the existing list.
570          *
571          * @return Twig_NodeVisitorInterface[]
572          */
573         function getNodeVisitors();
574
575         /**
576          * Returns a list of filters to add to the existing list.
577          *
578          * @return Twig_SimpleFilter[]
579          */
580         function getFilters();
581
582         /**
583          * Returns a list of tests to add to the existing list.
584          *
585          * @return Twig_SimpleTest[]
586          */
587         function getTests();
588
589         /**
590          * Returns a list of functions to add to the existing list.
591          *
592          * @return Twig_SimpleFunction[]
593          */
594         function getFunctions();
595
596         /**
597          * Returns a list of operators to add to the existing list.
598          *
599          * @return array<array> First array of unary operators, second array of binary operators
600          */
601         function getOperators();
602
603         /**
604          * Returns a list of global variables to add to the existing list.
605          *
606          * @return array An array of global variables
607          *
608          * @deprecated since 1.23 (to be removed in 2.0), implement Twig_Extension_GlobalsInterface instead
609          */
610         function getGlobals();
611
612         /**
613          * Returns the name of the extension.
614          *
615          * @return string The extension name
616          *
617          * @deprecated since 1.26 (to be removed in 2.0), not used anymore internally
618          */
619         function getName();
620     }
621
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:
625
626     class Project_Twig_Extension extends Twig_Extension
627     {
628     }
629
630 Of course, this extension does nothing for now. We will customize it in the
631 next sections.
632
633 .. note::
634
635     Prior to Twig 1.26, you must implement the ``getName()`` method which must
636     return a unique identifier for the extension.
637
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.
640
641 You can register an extension by using the ``addExtension()`` method on your
642 main ``Environment`` object::
643
644     $twig = new Twig_Environment($loader);
645     $twig->addExtension(new Project_Twig_Extension());
646
647 .. tip::
648
649     The Twig core extensions are great examples of how extensions work.
650
651 Globals
652 ~~~~~~~
653
654 Global variables can be registered in an extension via the ``getGlobals()``
655 method::
656
657     class Project_Twig_Extension extends Twig_Extension implements Twig_Extension_GlobalsInterface
658     {
659         public function getGlobals()
660         {
661             return array(
662                 'text' => new Text(),
663             );
664         }
665
666         // ...
667     }
668
669 Functions
670 ~~~~~~~~~
671
672 Functions can be registered in an extension via the ``getFunctions()``
673 method::
674
675     class Project_Twig_Extension extends Twig_Extension
676     {
677         public function getFunctions()
678         {
679             return array(
680                 new Twig_SimpleFunction('lipsum', 'generate_lipsum'),
681             );
682         }
683
684         // ...
685     }
686
687 Filters
688 ~~~~~~~
689
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
692 environment::
693
694     class Project_Twig_Extension extends Twig_Extension
695     {
696         public function getFilters()
697         {
698             return array(
699                 new Twig_SimpleFilter('rot13', 'str_rot13'),
700             );
701         }
702
703         // ...
704     }
705
706 Tags
707 ~~~~
708
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::
712
713     class Project_Twig_Extension extends Twig_Extension
714     {
715         public function getTokenParsers()
716         {
717             return array(new Project_Set_TokenParser());
718         }
719
720         // ...
721     }
722
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.
726
727 Operators
728 ~~~~~~~~~
729
730 The ``getOperators()`` methods lets you add new operators. Here is how to add
731 ``!``, ``||``, and ``&&`` operators::
732
733     class Project_Twig_Extension extends Twig_Extension
734     {
735         public function getOperators()
736         {
737             return array(
738                 array(
739                     '!' => array('precedence' => 50, 'class' => 'Twig_Node_Expression_Unary_Not'),
740                 ),
741                 array(
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),
744                 ),
745             );
746         }
747
748         // ...
749     }
750
751 Tests
752 ~~~~~
753
754 The ``getTests()`` method lets you add new test functions::
755
756     class Project_Twig_Extension extends Twig_Extension
757     {
758         public function getTests()
759         {
760             return array(
761                 new Twig_SimpleTest('even', 'twig_test_even'),
762             );
763         }
764
765         // ...
766     }
767
768 Definition vs Runtime
769 ~~~~~~~~~~~~~~~~~~~~~
770
771 Twig filters, functions, and tests runtime implementations can be defined as
772 any valid PHP callable:
773
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
776   objects;
777
778 * **closures**: Simple to implement;
779
780 * **object methods**: More flexible and required if your runtime code depends
781   on external objects.
782
783 The simplest way to use methods is to define them on the extension itself::
784
785     class Project_Twig_Extension extends Twig_Extension
786     {
787         private $rot13Provider;
788
789         public function __construct($rot13Provider)
790         {
791             $this->rot13Provider = $rot13Provider;
792         }
793
794         public function getFunctions()
795         {
796             return array(
797                 new Twig_SimpleFunction('rot13', array($this, 'rot13')),
798             );
799         }
800
801         public function rot13($value)
802         {
803             return $this->rot13Provider->rot13($value);
804         }
805     }
806
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).
810
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)::
815
816     class RuntimeLoader implements Twig_RuntimeLoaderInterface
817     {
818         public function load($class)
819         {
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());
825             } else {
826                 // ...
827             }
828         }
829     }
830
831     $twig->addRuntimeLoader(new RuntimeLoader());
832
833 .. note::
834
835     As of Twig 1.32, Twig comes with a PSR-11 compatible runtime loader
836     (``Twig_ContainerRuntimeLoader``) that works on PHP 5.3+.
837
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::
840
841     class Project_Twig_RuntimeExtension
842     {
843         private $rot13Provider;
844
845         public function __construct($rot13Provider)
846         {
847             $this->rot13Provider = $rot13Provider;
848         }
849
850         public function rot13($value)
851         {
852             return $this->rot13Provider->rot13($value);
853         }
854     }
855
856     class Project_Twig_Extension extends Twig_Extension
857     {
858         public function getFunctions()
859         {
860             return array(
861                 new Twig_SimpleFunction('rot13', array('Project_Twig_RuntimeExtension', 'rot13')),
862                 // or
863                 new Twig_SimpleFunction('rot13', 'Project_Twig_RuntimeExtension::rot13'),
864             );
865         }
866     }
867
868 Overloading
869 -----------
870
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)::
874
875     class MyCoreExtension extends Twig_Extension
876     {
877         public function getFilters()
878         {
879             return array(
880                 new Twig_SimpleFilter('date', array($this, 'dateFilter')),
881             );
882         }
883
884         public function dateFilter($timestamp, $format = 'F j, Y H:i')
885         {
886             // do something different from the built-in date filter
887         }
888     }
889
890     $twig = new Twig_Environment($loader);
891     $twig->addExtension(new MyCoreExtension());
892
893 Here, we have overloaded the built-in ``date`` filter with a custom one.
894
895 If you do the same on the ``Twig_Environment`` itself, beware that it takes
896 precedence over any other registered extensions::
897
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
901     }));
902     // the date filter will come from the above registration, not
903     // from the registered extension below
904     $twig->addExtension(new MyCoreExtension());
905
906 .. caution::
907
908     Note that overloading the built-in Twig elements is not recommended as it
909     might be confusing.
910
911 Testing an Extension
912 --------------------
913
914 Functional Tests
915 ~~~~~~~~~~~~~~~~
916
917 You can create functional tests for extensions simply by creating the
918 following file structure in your test directory::
919
920     Fixtures/
921         filters/
922             foo.test
923             bar.test
924         functions/
925             foo.test
926             bar.test
927         tags/
928             foo.test
929             bar.test
930     IntegrationTest.php
931
932 The ``IntegrationTest.php`` file should look like this::
933
934     class Project_Tests_IntegrationTest extends Twig_Test_IntegrationTestCase
935     {
936         public function getExtensions()
937         {
938             return array(
939                 new Project_Twig_Extension1(),
940                 new Project_Twig_Extension2(),
941             );
942         }
943
944         public function getFixturesDir()
945         {
946             return dirname(__FILE__).'/Fixtures/';
947         }
948     }
949
950 Fixtures examples can be found within the Twig repository
951 `tests/Twig/Fixtures`_ directory.
952
953 Node Tests
954 ~~~~~~~~~~
955
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.
959
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