Security update to Drupal 8.4.6
[yaffs-website] / vendor / twig / twig / doc / advanced_legacy.rst
1 Extending Twig
2 ==============
3
4 .. caution::
5
6     This section describes how to extends Twig for versions **older than
7     1.12**. If you are using a newer version, read the :doc:`newer<advanced>`
8     chapter 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 by calling methods on the Twig environment instance,
24     Twig won't be able to recompile your templates when the PHP code is
25     updated. To see your changes in real-time, either disable template caching
26     or package your code into an extension (see the next section of this
27     chapter).
28
29 Before extending Twig, you must understand the differences between all the
30 different possible extension points and when to use them.
31
32 First, remember that Twig has two main language constructs:
33
34 * ``{{ }}``: used to print the result of an expression evaluation;
35
36 * ``{% %}``: used to execute statements.
37
38 To understand why Twig exposes so many extension points, let's see how to
39 implement a *Lorem ipsum* generator (it needs to know the number of words to
40 generate).
41
42 You can use a ``lipsum`` *tag*:
43
44 .. code-block:: jinja
45
46     {% lipsum 40 %}
47
48 That works, but using a tag for ``lipsum`` is not a good idea for at least
49 three main reasons:
50
51 * ``lipsum`` is not a language construct;
52 * The tag outputs something;
53 * The tag is not flexible as you cannot use it in an expression:
54
55   .. code-block:: jinja
56
57       {{ 'some text' ~ {% lipsum 40 %} ~ 'some more text' }}
58
59 In fact, you rarely need to create tags; and that's good news because tags are
60 the most complex extension point of Twig.
61
62 Now, let's use a ``lipsum`` *filter*:
63
64 .. code-block:: jinja
65
66     {{ 40|lipsum }}
67
68 Again, it works, but it looks weird. A filter transforms the passed value to
69 something else but here we use the value to indicate the number of words to
70 generate (so, ``40`` is an argument of the filter, not the value we want to
71 transform).
72
73 Next, let's use a ``lipsum`` *function*:
74
75 .. code-block:: jinja
76
77     {{ lipsum(40) }}
78
79 Here we go. For this specific example, the creation of a function is the
80 extension point to use. And you can use it anywhere an expression is accepted:
81
82 .. code-block:: jinja
83
84     {{ 'some text' ~ ipsum(40) ~ 'some more text' }}
85
86     {% set ipsum = ipsum(40) %}
87
88 Last but not the least, you can also use a *global* object with a method able
89 to generate lorem ipsum text:
90
91 .. code-block:: jinja
92
93     {{ text.lipsum(40) }}
94
95 As a rule of thumb, use functions for frequently used features and global
96 objects for everything else.
97
98 Keep in mind the following when you want to extend Twig:
99
100 ========== ========================== ========== =========================
101 What?      Implementation difficulty? How often? When?
102 ========== ========================== ========== =========================
103 *macro*    trivial                    frequent   Content generation
104 *global*   trivial                    frequent   Helper object
105 *function* trivial                    frequent   Content generation
106 *filter*   trivial                    frequent   Value transformation
107 *tag*      complex                    rare       DSL language construct
108 *test*     trivial                    rare       Boolean decision
109 *operator* trivial                    rare       Values transformation
110 ========== ========================== ========== =========================
111
112 Globals
113 -------
114
115 A global variable is like any other template variable, except that it's
116 available in all templates and macros::
117
118     $twig = new Twig_Environment($loader);
119     $twig->addGlobal('text', new Text());
120
121 You can then use the ``text`` variable anywhere in a template:
122
123 .. code-block:: jinja
124
125     {{ text.lipsum(40) }}
126
127 Filters
128 -------
129
130 A filter is a regular PHP function or an object method that takes the left
131 side of the filter (before the pipe ``|``) as first argument and the extra
132 arguments passed to the filter (within parentheses ``()``) as extra arguments.
133
134 Defining a filter is as easy as associating the filter name with a PHP
135 callable. For instance, let's say you have the following code in a template:
136
137 .. code-block:: jinja
138
139     {{ 'TWIG'|lower }}
140
141 When compiling this template to PHP, Twig looks for the PHP callable
142 associated with the ``lower`` filter. The ``lower`` filter is a built-in Twig
143 filter, and it is simply mapped to the PHP ``strtolower()`` function. After
144 compilation, the generated PHP code is roughly equivalent to:
145
146 .. code-block:: html+php
147
148     <?php echo strtolower('TWIG') ?>
149
150 As you can see, the ``'TWIG'`` string is passed as a first argument to the PHP
151 function.
152
153 A filter can also take extra arguments like in the following example:
154
155 .. code-block:: jinja
156
157     {{ now|date('d/m/Y') }}
158
159 In this case, the extra arguments are passed to the function after the main
160 argument, and the compiled code is equivalent to:
161
162 .. code-block:: html+php
163
164     <?php echo twig_date_format_filter($now, 'd/m/Y') ?>
165
166 Let's see how to create a new filter.
167
168 In this section, we will create a ``rot13`` filter, which should return the
169 `rot13`_ transformation of a string. Here is an example of its usage and the
170 expected output:
171
172 .. code-block:: jinja
173
174     {{ "Twig"|rot13 }}
175
176     {# should displays Gjvt #}
177
178 Adding a filter is as simple as calling the ``addFilter()`` method on the
179 ``Twig_Environment`` instance::
180
181     $twig = new Twig_Environment($loader);
182     $twig->addFilter('rot13', new Twig_Filter_Function('str_rot13'));
183
184 The second argument of ``addFilter()`` is an instance of ``Twig_Filter``.
185 Here, we use ``Twig_Filter_Function`` as the filter is a PHP function. The
186 first argument passed to the ``Twig_Filter_Function`` constructor is the name
187 of the PHP function to call, here ``str_rot13``, a native PHP function.
188
189 Let's say I now want to be able to add a prefix before the converted string:
190
191 .. code-block:: jinja
192
193     {{ "Twig"|rot13('prefix_') }}
194
195     {# should displays prefix_Gjvt #}
196
197 As the PHP ``str_rot13()`` function does not support this requirement, let's
198 create a new PHP function::
199
200     function project_compute_rot13($string, $prefix = '')
201     {
202         return $prefix.str_rot13($string);
203     }
204
205 As you can see, the ``prefix`` argument of the filter is passed as an extra
206 argument to the ``project_compute_rot13()`` function.
207
208 Adding this filter is as easy as before::
209
210     $twig->addFilter('rot13', new Twig_Filter_Function('project_compute_rot13'));
211
212 For better encapsulation, a filter can also be defined as a static method of a
213 class. The ``Twig_Filter_Function`` class can also be used to register such
214 static methods as filters::
215
216     $twig->addFilter('rot13', new Twig_Filter_Function('SomeClass::rot13Filter'));
217
218 .. tip::
219
220     In an extension, you can also define a filter as a static method of the
221     extension class.
222
223 Environment aware Filters
224 ~~~~~~~~~~~~~~~~~~~~~~~~~
225
226 The ``Twig_Filter`` classes take options as their last argument. For instance,
227 if you want access to the current environment instance in your filter, set the
228 ``needs_environment`` option to ``true``::
229
230     $filter = new Twig_Filter_Function('str_rot13', array('needs_environment' => true));
231
232 Twig will then pass the current environment as the first argument to the
233 filter call::
234
235     function twig_compute_rot13(Twig_Environment $env, $string)
236     {
237         // get the current charset for instance
238         $charset = $env->getCharset();
239
240         return str_rot13($string);
241     }
242
243 Automatic Escaping
244 ~~~~~~~~~~~~~~~~~~
245
246 If automatic escaping is enabled, the output of the filter may be escaped
247 before printing. If your filter acts as an escaper (or explicitly outputs HTML
248 or JavaScript code), you will want the raw output to be printed. In such a
249 case, set the ``is_safe`` option::
250
251     $filter = new Twig_Filter_Function('nl2br', array('is_safe' => array('html')));
252
253 Some filters may need to work on input that is already escaped or safe, for
254 example when adding (safe) HTML tags to originally unsafe output. In such a
255 case, set the ``pre_escape`` option to escape the input data before it is run
256 through your filter::
257
258     $filter = new Twig_Filter_Function('somefilter', array('pre_escape' => 'html', 'is_safe' => array('html')));
259
260 Dynamic Filters
261 ~~~~~~~~~~~~~~~
262
263 .. versionadded:: 1.5
264     Dynamic filters support was added in Twig 1.5.
265
266 A filter name containing the special ``*`` character is a dynamic filter as
267 the ``*`` can be any string::
268
269     $twig->addFilter('*_path_*', new Twig_Filter_Function('twig_path'));
270
271     function twig_path($name, $arguments)
272     {
273         // ...
274     }
275
276 The following filters will be matched by the above defined dynamic filter:
277
278 * ``product_path``
279 * ``category_path``
280
281 A dynamic filter can define more than one dynamic parts::
282
283     $twig->addFilter('*_path_*', new Twig_Filter_Function('twig_path'));
284
285     function twig_path($name, $suffix, $arguments)
286     {
287         // ...
288     }
289
290 The filter will receive all dynamic part values before the normal filters
291 arguments. For instance, a call to ``'foo'|a_path_b()`` will result in the
292 following PHP call: ``twig_path('a', 'b', 'foo')``.
293
294 Functions
295 ---------
296
297 A function is a regular PHP function or an object method that can be called from
298 templates.
299
300 .. code-block:: jinja
301
302     {{ constant("DATE_W3C") }}
303
304 When compiling this template to PHP, Twig looks for the PHP callable
305 associated with the ``constant`` function. The ``constant`` function is a built-in Twig
306 function, and it is simply mapped to the PHP ``constant()`` function. After
307 compilation, the generated PHP code is roughly equivalent to:
308
309 .. code-block:: html+php
310
311     <?php echo constant('DATE_W3C') ?>
312
313 Adding a function is similar to adding a filter. This can be done by calling the
314 ``addFunction()`` method on the ``Twig_Environment`` instance::
315
316     $twig = new Twig_Environment($loader);
317     $twig->addFunction('functionName', new Twig_Function_Function('someFunction'));
318
319 You can also expose extension methods as functions in your templates::
320
321     // $this is an object that implements Twig_ExtensionInterface.
322     $twig = new Twig_Environment($loader);
323     $twig->addFunction('otherFunction', new Twig_Function_Method($this, 'someMethod'));
324
325 Functions also support ``needs_environment`` and ``is_safe`` parameters.
326
327 Dynamic Functions
328 ~~~~~~~~~~~~~~~~~
329
330 .. versionadded:: 1.5
331     Dynamic functions support was added in Twig 1.5.
332
333 A function name containing the special ``*`` character is a dynamic function
334 as the ``*`` can be any string::
335
336     $twig->addFunction('*_path', new Twig_Function_Function('twig_path'));
337
338     function twig_path($name, $arguments)
339     {
340         // ...
341     }
342
343 The following functions will be matched by the above defined dynamic function:
344
345 * ``product_path``
346 * ``category_path``
347
348 A dynamic function can define more than one dynamic parts::
349
350     $twig->addFilter('*_path_*', new Twig_Filter_Function('twig_path'));
351
352     function twig_path($name, $suffix, $arguments)
353     {
354         // ...
355     }
356
357 The function will receive all dynamic part values before the normal functions
358 arguments. For instance, a call to ``a_path_b('foo')`` will result in the
359 following PHP call: ``twig_path('a', 'b', 'foo')``.
360
361 Tags
362 ----
363
364 One of the most exciting feature of a template engine like Twig is the
365 possibility to define new language constructs. This is also the most complex
366 feature as you need to understand how Twig's internals work.
367
368 Let's create a simple ``set`` tag that allows the definition of simple
369 variables from within a template. The tag can be used like follows:
370
371 .. code-block:: jinja
372
373     {% set name = "value" %}
374
375     {{ name }}
376
377     {# should output value #}
378
379 .. note::
380
381     The ``set`` tag is part of the Core extension and as such is always
382     available. The built-in version is slightly more powerful and supports
383     multiple assignments by default (cf. the template designers chapter for
384     more information).
385
386 Three steps are needed to define a new tag:
387
388 * Defining a Token Parser class (responsible for parsing the template code);
389
390 * Defining a Node class (responsible for converting the parsed code to PHP);
391
392 * Registering the tag.
393
394 Registering a new tag
395 ~~~~~~~~~~~~~~~~~~~~~
396
397 Adding a tag is as simple as calling the ``addTokenParser`` method on the
398 ``Twig_Environment`` instance::
399
400     $twig = new Twig_Environment($loader);
401     $twig->addTokenParser(new Project_Set_TokenParser());
402
403 Defining a Token Parser
404 ~~~~~~~~~~~~~~~~~~~~~~~
405
406 Now, let's see the actual code of this class::
407
408     class Project_Set_TokenParser extends Twig_TokenParser
409     {
410         public function parse(Twig_Token $token)
411         {
412             $lineno = $token->getLine();
413             $name = $this->parser->getStream()->expect(Twig_Token::NAME_TYPE)->getValue();
414             $this->parser->getStream()->expect(Twig_Token::OPERATOR_TYPE, '=');
415             $value = $this->parser->getExpressionParser()->parseExpression();
416
417             $this->parser->getStream()->expect(Twig_Token::BLOCK_END_TYPE);
418
419             return new Project_Set_Node($name, $value, $lineno, $this->getTag());
420         }
421
422         public function getTag()
423         {
424             return 'set';
425         }
426     }
427
428 The ``getTag()`` method must return the tag we want to parse, here ``set``.
429
430 The ``parse()`` method is invoked whenever the parser encounters a ``set``
431 tag. It should return a ``Twig_Node`` instance that represents the node (the
432 ``Project_Set_Node`` calls creating is explained in the next section).
433
434 The parsing process is simplified thanks to a bunch of methods you can call
435 from the token stream (``$this->parser->getStream()``):
436
437 * ``getCurrent()``: Gets the current token in the stream.
438
439 * ``next()``: Moves to the next token in the stream, *but returns the old one*.
440
441 * ``test($type)``, ``test($value)`` or ``test($type, $value)``: Determines whether
442   the current token is of a particular type or value (or both). The value may be an
443   array of several possible values.
444
445 * ``expect($type[, $value[, $message]])``: If the current token isn't of the given
446   type/value a syntax error is thrown. Otherwise, if the type and value are correct,
447   the token is returned and the stream moves to the next token.
448
449 * ``look()``: Looks a the next token without consuming it.
450
451 Parsing expressions is done by calling the ``parseExpression()`` like we did for
452 the ``set`` tag.
453
454 .. tip::
455
456     Reading the existing ``TokenParser`` classes is the best way to learn all
457     the nitty-gritty details of the parsing process.
458
459 Defining a Node
460 ~~~~~~~~~~~~~~~
461
462 The ``Project_Set_Node`` class itself is rather simple::
463
464     class Project_Set_Node extends Twig_Node
465     {
466         public function __construct($name, Twig_Node_Expression $value, $lineno, $tag = null)
467         {
468             parent::__construct(array('value' => $value), array('name' => $name), $lineno, $tag);
469         }
470
471         public function compile(Twig_Compiler $compiler)
472         {
473             $compiler
474                 ->addDebugInfo($this)
475                 ->write('$context[\''.$this->getAttribute('name').'\'] = ')
476                 ->subcompile($this->getNode('value'))
477                 ->raw(";\n")
478             ;
479         }
480     }
481
482 The compiler implements a fluid interface and provides methods that helps the
483 developer generate beautiful and readable PHP code:
484
485 * ``subcompile()``: Compiles a node.
486
487 * ``raw()``: Writes the given string as is.
488
489 * ``write()``: Writes the given string by adding indentation at the beginning
490   of each line.
491
492 * ``string()``: Writes a quoted string.
493
494 * ``repr()``: Writes a PHP representation of a given value (see
495   ``Twig_Node_For`` for a usage example).
496
497 * ``addDebugInfo()``: Adds the line of the original template file related to
498   the current node as a comment.
499
500 * ``indent()``: Indents the generated code (see ``Twig_Node_Block`` for a
501   usage example).
502
503 * ``outdent()``: Outdents the generated code (see ``Twig_Node_Block`` for a
504   usage example).
505
506 .. _creating_extensions:
507
508 Creating an Extension
509 ---------------------
510
511 The main motivation for writing an extension is to move often used code into a
512 reusable class like adding support for internationalization. An extension can
513 define tags, filters, tests, operators, global variables, functions, and node
514 visitors.
515
516 Creating an extension also makes for a better separation of code that is
517 executed at compilation time and code needed at runtime. As such, it makes
518 your code faster.
519
520 Most of the time, it is useful to create a single extension for your project,
521 to host all the specific tags and filters you want to add to Twig.
522
523 .. tip::
524
525     When packaging your code into an extension, Twig is smart enough to
526     recompile your templates whenever you make a change to it (when the
527     ``auto_reload`` is enabled).
528
529 .. note::
530
531     Before writing your own extensions, have a look at the Twig official
532     extension repository: http://github.com/twigphp/Twig-extensions.
533
534 An extension is a class that implements the following interface::
535
536     interface Twig_ExtensionInterface
537     {
538         /**
539          * Initializes the runtime environment.
540          *
541          * This is where you can load some file that contains filter functions for instance.
542          */
543         function initRuntime(Twig_Environment $environment);
544
545         /**
546          * Returns the token parser instances to add to the existing list.
547          *
548          * @return (Twig_TokenParserInterface|Twig_TokenParserBrokerInterface)[]
549          */
550         function getTokenParsers();
551
552         /**
553          * Returns the node visitor instances to add to the existing list.
554          *
555          * @return Twig_NodeVisitorInterface[]
556          */
557         function getNodeVisitors();
558
559         /**
560          * Returns a list of filters to add to the existing list.
561          *
562          * @return Twig_SimpleFilter[]
563          */
564         function getFilters();
565
566         /**
567          * Returns a list of tests to add to the existing list.
568          *
569          * @return Twig_SimpleTest[]
570          */
571         function getTests();
572
573         /**
574          * Returns a list of functions to add to the existing list.
575          *
576          * @return Twig_SimpleFunction[]
577          */
578         function getFunctions();
579
580         /**
581          * Returns a list of operators to add to the existing list.
582          *
583          * @return array<array> First array of unary operators, second array of binary operators
584          */
585         function getOperators();
586
587         /**
588          * Returns a list of global variables to add to the existing list.
589          *
590          * @return array An array of global variables
591          */
592         function getGlobals();
593
594         /**
595          * Returns the name of the extension.
596          *
597          * @return string The extension name
598          */
599         function getName();
600     }
601
602 To keep your extension class clean and lean, it can inherit from the built-in
603 ``Twig_Extension`` class instead of implementing the whole interface. That
604 way, you just need to implement the ``getName()`` method as the
605 ``Twig_Extension`` provides empty implementations for all other methods.
606
607 The ``getName()`` method must return a unique identifier for your extension.
608
609 Now, with this information in mind, let's create the most basic extension
610 possible::
611
612     class Project_Twig_Extension extends Twig_Extension
613     {
614         public function getName()
615         {
616             return 'project';
617         }
618     }
619
620 .. note::
621
622     Of course, this extension does nothing for now. We will customize it in
623     the next sections.
624
625 Twig does not care where you save your extension on the filesystem, as all
626 extensions must be registered explicitly to be available in your templates.
627
628 You can register an extension by using the ``addExtension()`` method on your
629 main ``Environment`` object::
630
631     $twig = new Twig_Environment($loader);
632     $twig->addExtension(new Project_Twig_Extension());
633
634 Of course, you need to first load the extension file by either using
635 ``require_once()`` or by using an autoloader (see `spl_autoload_register()`_).
636
637 .. tip::
638
639     The bundled extensions are great examples of how extensions work.
640
641 Globals
642 ~~~~~~~
643
644 Global variables can be registered in an extension via the ``getGlobals()``
645 method::
646
647     class Project_Twig_Extension extends Twig_Extension
648     {
649         public function getGlobals()
650         {
651             return array(
652                 'text' => new Text(),
653             );
654         }
655
656         // ...
657     }
658
659 Functions
660 ~~~~~~~~~
661
662 Functions can be registered in an extension via the ``getFunctions()``
663 method::
664
665     class Project_Twig_Extension extends Twig_Extension
666     {
667         public function getFunctions()
668         {
669             return array(
670                 'lipsum' => new Twig_Function_Function('generate_lipsum'),
671             );
672         }
673
674         // ...
675     }
676
677 Filters
678 ~~~~~~~
679
680 To add a filter to an extension, you need to override the ``getFilters()``
681 method. This method must return an array of filters to add to the Twig
682 environment::
683
684     class Project_Twig_Extension extends Twig_Extension
685     {
686         public function getFilters()
687         {
688             return array(
689                 'rot13' => new Twig_Filter_Function('str_rot13'),
690             );
691         }
692
693         // ...
694     }
695
696 As you can see in the above code, the ``getFilters()`` method returns an array
697 where keys are the name of the filters (``rot13``) and the values the
698 definition of the filter (``new Twig_Filter_Function('str_rot13')``).
699
700 As seen in the previous chapter, you can also define filters as static methods
701 on the extension class::
702
703 $twig->addFilter('rot13', new Twig_Filter_Function('Project_Twig_Extension::rot13Filter'));
704
705 You can also use ``Twig_Filter_Method`` instead of ``Twig_Filter_Function``
706 when defining a filter to use a method::
707
708     class Project_Twig_Extension extends Twig_Extension
709     {
710         public function getFilters()
711         {
712             return array(
713                 'rot13' => new Twig_Filter_Method($this, 'rot13Filter'),
714             );
715         }
716
717         public function rot13Filter($string)
718         {
719             return str_rot13($string);
720         }
721
722         // ...
723     }
724
725 The first argument of the ``Twig_Filter_Method`` constructor is always
726 ``$this``, the current extension object. The second one is the name of the
727 method to call.
728
729 Using methods for filters is a great way to package your filter without
730 polluting the global namespace. This also gives the developer more flexibility
731 at the cost of a small overhead.
732
733 Overriding default Filters
734 ..........................
735
736 If some default core filters do not suit your needs, you can easily override
737 them by creating your own extension. Just use the same names as the one you
738 want to override::
739
740     class MyCoreExtension extends Twig_Extension
741     {
742         public function getFilters()
743         {
744             return array(
745                 'date' => new Twig_Filter_Method($this, 'dateFilter'),
746                 // ...
747             );
748         }
749
750         public function dateFilter($timestamp, $format = 'F j, Y H:i')
751         {
752             return '...'.twig_date_format_filter($timestamp, $format);
753         }
754
755         public function getName()
756         {
757             return 'project';
758         }
759     }
760
761 Here, we override the ``date`` filter with a custom one. Using this extension
762 is as simple as registering the ``MyCoreExtension`` extension by calling the
763 ``addExtension()`` method on the environment instance::
764
765     $twig = new Twig_Environment($loader);
766     $twig->addExtension(new MyCoreExtension());
767
768 Tags
769 ~~~~
770
771 Adding a tag in an extension can be done by overriding the
772 ``getTokenParsers()`` method. This method must return an array of tags to add
773 to the Twig environment::
774
775     class Project_Twig_Extension extends Twig_Extension
776     {
777         public function getTokenParsers()
778         {
779             return array(new Project_Set_TokenParser());
780         }
781
782         // ...
783     }
784
785 In the above code, we have added a single new tag, defined by the
786 ``Project_Set_TokenParser`` class. The ``Project_Set_TokenParser`` class is
787 responsible for parsing the tag and compiling it to PHP.
788
789 Operators
790 ~~~~~~~~~
791
792 The ``getOperators()`` methods allows to add new operators. Here is how to add
793 ``!``, ``||``, and ``&&`` operators::
794
795     class Project_Twig_Extension extends Twig_Extension
796     {
797         public function getOperators()
798         {
799             return array(
800                 array(
801                     '!' => array('precedence' => 50, 'class' => 'Twig_Node_Expression_Unary_Not'),
802                 ),
803                 array(
804                     '||' => array('precedence' => 10, 'class' => 'Twig_Node_Expression_Binary_Or', 'associativity' => Twig_ExpressionParser::OPERATOR_LEFT),
805                     '&&' => array('precedence' => 15, 'class' => 'Twig_Node_Expression_Binary_And', 'associativity' => Twig_ExpressionParser::OPERATOR_LEFT),
806                 ),
807             );
808         }
809
810         // ...
811     }
812
813 Tests
814 ~~~~~
815
816 The ``getTests()`` methods allows to add new test functions::
817
818     class Project_Twig_Extension extends Twig_Extension
819     {
820         public function getTests()
821         {
822             return array(
823                 'even' => new Twig_Test_Function('twig_test_even'),
824             );
825         }
826
827         // ...
828     }
829
830 Testing an Extension
831 --------------------
832
833 .. versionadded:: 1.10
834     Support for functional tests was added in Twig 1.10.
835
836 Functional Tests
837 ~~~~~~~~~~~~~~~~
838
839 You can create functional tests for extensions simply by creating the
840 following file structure in your test directory::
841
842     Fixtures/
843         filters/
844             foo.test
845             bar.test
846         functions/
847             foo.test
848             bar.test
849         tags/
850             foo.test
851             bar.test
852     IntegrationTest.php
853
854 The ``IntegrationTest.php`` file should look like this::
855
856     class Project_Tests_IntegrationTest extends Twig_Test_IntegrationTestCase
857     {
858         public function getExtensions()
859         {
860             return array(
861                 new Project_Twig_Extension1(),
862                 new Project_Twig_Extension2(),
863             );
864         }
865
866         public function getFixturesDir()
867         {
868             return dirname(__FILE__).'/Fixtures/';
869         }
870     }
871
872 Fixtures examples can be found within the Twig repository
873 `tests/Twig/Fixtures`_ directory.
874
875 Node Tests
876 ~~~~~~~~~~
877
878 Testing the node visitors can be complex, so extend your test cases from
879 ``Twig_Test_NodeTestCase``. Examples can be found in the Twig repository
880 `tests/Twig/Node`_ directory.
881
882 .. _`spl_autoload_register()`: http://www.php.net/spl_autoload_register
883 .. _`rot13`:                   http://www.php.net/manual/en/function.str-rot13.php
884 .. _`tests/Twig/Fixtures`:     https://github.com/twigphp/Twig/tree/master/test/Twig/Tests/Fixtures
885 .. _`tests/Twig/Node`:         https://github.com/twigphp/Twig/tree/master/test/Twig/Tests/Node