advanced.rst 13KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422
  1. Extending Twig
  2. ==============
  3. Twig can be extended in many ways; you can add extra tags, filters, tests,
  4. operators, global variables, and functions. You can even extend the parser
  5. itself with node visitors.
  6. .. note::
  7. This chapter describes how to extend Twig easily. If you want to reuse
  8. your changes in different projects or if you want to share them with
  9. others, you should then create an extension as described in the next
  10. chapter.
  11. Before extending Twig, you must understand the differences between all the
  12. different possible extension points and when to use them.
  13. First, remember that Twig has two main language constructs:
  14. * ``{{ }}``: used to print the result of an expression evaluation;
  15. * ``{% %}``: used to execute statements.
  16. To understand why Twig exposes so many extension points, let's see how to
  17. implement a *Lorem ipsum* generator (it needs to know the number of words to
  18. generate).
  19. You can use a ``lipsum`` *tag*:
  20. .. code-block:: jinja
  21. {% lipsum 40 %}
  22. That works, but using a tag for ``lipsum`` is not a good idea for at least
  23. three main reasons:
  24. * ``lipsum`` is not a language construct;
  25. * The tag outputs something;
  26. * The tag is not flexible as you cannot use it in an expression:
  27. .. code-block:: jinja
  28. {{ 'some text' ~ {% lipsum 40 %} ~ 'some more text' }}
  29. In fact, you rarely need to create tags; and that's good news because tags are
  30. the most complex extension point of Twig.
  31. Now, let's use a ``lipsum`` *filter*:
  32. .. code-block:: jinja
  33. {{ 40|lipsum }}
  34. Again, it works, but it looks weird. A filter transforms the passed value to
  35. something else but here we use the value to indicate the number of words to
  36. generate.
  37. Next, let's use a ``lipsum`` *function*:
  38. .. code-block:: jinja
  39. {{ lipsum(40) }}
  40. Here we go. For this specific example, the creation of a function is the
  41. extension point to use. And you can use it anywhere an expression is accepted:
  42. .. code-block:: jinja
  43. {{ 'some text' ~ ipsum(40) ~ 'some more text' }}
  44. {% set ipsum = ipsum(40) %}
  45. Last but not the least, you can also use a *global* object with a method able
  46. to generate lorem ipsum text:
  47. .. code-block:: jinja
  48. {{ text.lipsum(40) }}
  49. As a rule of thumb, use functions for frequently used features and global
  50. objects for everything else.
  51. Keep in mind the following when you want to extend Twig:
  52. ========== ========================== ========== =========================
  53. What? Implementation difficulty? How often? When?
  54. ========== ========================== ========== =========================
  55. *macro* trivial frequent Content generation
  56. *global* trivial frequent Helper object
  57. *function* trivial frequent Content generation
  58. *filter* trivial frequent Value transformation
  59. *tag* complex rare DSL language construct
  60. *test* trivial rare Boolean decision
  61. *operator* trivial rare Values transformation
  62. ========== ========================== ========== =========================
  63. Globals
  64. -------
  65. A global variable is like any other template variable, except that it's
  66. available in all templates and macros::
  67. $twig = new Twig_Environment($loader);
  68. $twig->addGlobal('text', new Text());
  69. You can then use the ``text`` variable anywhere in a template:
  70. .. code-block:: jinja
  71. {{ text.lipsum(40) }}
  72. Filters
  73. -------
  74. A filter is a regular PHP function or an object method that takes the left
  75. side of the filter (before the pipe ``|``) as first argument and the extra
  76. arguments passed to the filter (within parentheses ``()``) as extra arguments.
  77. Defining a filter is as easy as associating the filter name with a PHP
  78. callable. For instance, let's say you have the following code in a template:
  79. .. code-block:: jinja
  80. {{ 'TWIG'|lower }}
  81. When compiling this template to PHP, Twig looks for the PHP callable
  82. associated with the ``lower`` filter. The ``lower`` filter is a built-in Twig
  83. filter, and it is simply mapped to the PHP ``strtolower()`` function. After
  84. compilation, the generated PHP code is roughly equivalent to:
  85. .. code-block:: html+php
  86. <?php echo strtolower('TWIG') ?>
  87. As you can see, the ``'TWIG'`` string is passed as a first argument to the PHP
  88. function.
  89. A filter can also take extra arguments like in the following example:
  90. .. code-block:: jinja
  91. {{ now|date('d/m/Y') }}
  92. In this case, the extra arguments are passed to the function after the main
  93. argument, and the compiled code is equivalent to:
  94. .. code-block:: html+php
  95. <?php echo twig_date_format_filter($now, 'd/m/Y') ?>
  96. Let's see how to create a new filter.
  97. In this section, we will create a ``rot13`` filter, which should return the
  98. `rot13`_ transformation of a string. Here is an example of its usage and the
  99. expected output:
  100. .. code-block:: jinja
  101. {{ "Twig"|rot13 }}
  102. {# should displays Gjvt #}
  103. Adding a filter is as simple as calling the ``addFilter()`` method on the
  104. ``Twig_Environment`` instance::
  105. $twig = new Twig_Environment($loader);
  106. $twig->addFilter('rot13', new Twig_Filter_Function('rot13'));
  107. The second argument of ``addFilter()`` is an instance of ``Twig_Filter``.
  108. Here, we use ``Twig_Filter_Function`` as the filter is a PHP function. The
  109. first argument passed to the ``Twig_Filter_Function`` constructor is the name
  110. of the PHP function to call, here ``str_rot13``, a native PHP function.
  111. Let's say I now want to be able to add a prefix before the converted string:
  112. .. code-block:: jinja
  113. {{ "Twig"|rot13('prefix_') }}
  114. {# should displays prefix_Gjvt #}
  115. As the PHP ``str_rot13()`` function does not support this requirement, let's
  116. create a new PHP function::
  117. function project_compute_rot13($string, $prefix = '')
  118. {
  119. return $prefix.str_rot13($string);
  120. }
  121. As you can see, the ``prefix`` argument of the filter is passed as an extra
  122. argument to the ``project_compute_rot13()`` function.
  123. Adding this filter is as easy as before::
  124. $twig->addFilter('rot13', new Twig_Filter_Function('project_compute_rot13'));
  125. For better encapsulation, a filter can also be defined as a static method of a
  126. class. The ``Twig_Filter_Function`` class can also be used to register such
  127. static methods as filters::
  128. $twig->addFilter('rot13', new Twig_Filter_Function('SomeClass::rot13Filter'));
  129. .. tip::
  130. In an extension, you can also define a filter as a static method of the
  131. extension class.
  132. Environment aware Filters
  133. ~~~~~~~~~~~~~~~~~~~~~~~~~
  134. The ``Twig_Filter`` classes take options as their last argument. For instance,
  135. if you want access to the current environment instance in your filter, set the
  136. ``needs_environment`` option to ``true``::
  137. $filter = new Twig_Filter_Function('str_rot13', array('needs_environment' => true));
  138. Twig will then pass the current environment as the first argument to the
  139. filter call::
  140. function twig_compute_rot13(Twig_Environment $env, $string)
  141. {
  142. // get the current charset for instance
  143. $charset = $env->getCharset();
  144. return str_rot13($string);
  145. }
  146. Automatic Escaping
  147. ~~~~~~~~~~~~~~~~~~
  148. If automatic escaping is enabled, the output of the filter may be escaped
  149. before printing. If your filter acts as an escaper (or explicitly outputs html
  150. or javascript code), you will want the raw output to be printed. In such a
  151. case, set the ``is_safe`` option::
  152. $filter = new Twig_Filter_Function('nl2br', array('is_safe' => array('html')));
  153. Some filters may have to work on already escaped or safe values. In such a
  154. case, set the ``pre_escape`` option::
  155. $filter = new Twig_Filter_Function('somefilter', array('pre_escape' => 'html', 'is_safe' => array('html')));
  156. Functions
  157. ---------
  158. A function is a regular PHP function or an object method that can be called from
  159. templates.
  160. .. code-block:: jinja
  161. {{ constant("DATE_W3C") }}
  162. When compiling this template to PHP, Twig looks for the PHP callable
  163. associated with the ``constant`` function. The ``constant`` function is a built-in Twig
  164. function, and it is simply mapped to the PHP ``constant()`` function. After
  165. compilation, the generated PHP code is roughly equivalent to:
  166. .. code-block:: html+php
  167. <?php echo constant('DATE_W3C') ?>
  168. Adding a function is similar to adding a filter. This can be done by calling the
  169. ``addFunction()`` method on the ``Twig_Environment`` instance::
  170. $twig = new Twig_Environment($loader);
  171. $twig->addFunction('functionName', new Twig_Function_Function('someFunction'));
  172. You can also expose extension methods as functions in your templates::
  173. // $this is an object that implements Twig_ExtensionInterface.
  174. $twig = new Twig_Environment($loader);
  175. $twig->addFunction('otherFunction', new Twig_Function_Method($this, 'someMethod'));
  176. Functions also support ``needs_environment`` and ``is_safe`` parameters.
  177. Tags
  178. ----
  179. One of the most exciting feature of a template engine like Twig is the
  180. possibility to define new language constructs. This is also the most complex
  181. feature as you need to understand how Twig's internals work.
  182. Let's create a simple ``set`` tag that allows the definition of simple
  183. variables from within a template. The tag can be used like follows:
  184. .. code-block:: jinja
  185. {% set name = "value" %}
  186. {{ name }}
  187. {# should output value #}
  188. .. note::
  189. The ``set`` tag is part of the Core extension and as such is always
  190. available. The built-in version is slightly more powerful and supports
  191. multiple assignments by default (cf. the template designers chapter for
  192. more information).
  193. Three steps are needed to define a new tag:
  194. * Defining a Token Parser class (responsible for parsing the template code);
  195. * Defining a Node class (responsible for converting the parsed code to PHP);
  196. * Registering the tag.
  197. Registering a new tag
  198. ~~~~~~~~~~~~~~~~~~~~~
  199. Adding a tag is as simple as calling the ``addTokenParser`` method on the
  200. ``Twig_Environment`` instance::
  201. $twig = new Twig_Environment($loader);
  202. $twig->addTokenParser(new Project_Set_TokenParser());
  203. Defining a Token Parser
  204. ~~~~~~~~~~~~~~~~~~~~~~~
  205. Now, let's see the actual code of this class::
  206. class Project_Set_TokenParser extends Twig_TokenParser
  207. {
  208. public function parse(Twig_Token $token)
  209. {
  210. $lineno = $token->getLine();
  211. $name = $this->parser->getStream()->expect(Twig_Token::NAME_TYPE)->getValue();
  212. $this->parser->getStream()->expect(Twig_Token::OPERATOR_TYPE, '=');
  213. $value = $this->parser->getExpressionParser()->parseExpression();
  214. $this->parser->getStream()->expect(Twig_Token::BLOCK_END_TYPE);
  215. return new Project_Set_Node($name, $value, $lineno, $this->getTag());
  216. }
  217. public function getTag()
  218. {
  219. return 'set';
  220. }
  221. }
  222. The ``getTag()`` method must return the tag we want to parse, here ``set``.
  223. The ``parse()`` method is invoked whenever the parser encounters a ``set``
  224. tag. It should return a ``Twig_Node`` instance that represents the node (the
  225. ``Project_Set_Node`` calls creating is explained in the next section).
  226. The parsing process is simplified thanks to a bunch of methods you can call
  227. from the token stream (``$this->parser->getStream()``):
  228. * ``getCurrent()``: Gets the current token in the stream.
  229. * ``next()``: Moves to the next token in the stream, *but returns the old one*.
  230. * ``test($type)``, ``test($value)`` or ``test($type, $value)``: Determines whether
  231. the current token is of a particular type or value (or both). The value may be an
  232. array of several possible values.
  233. * ``expect($type[, $value[, $message]])``: If the current token isn't of the given
  234. type/value a syntax error is thrown. Otherwise, if the type and value are correct,
  235. the token is returned and the stream moves to the next token.
  236. * ``look()``: Looks a the next token without consuming it.
  237. Parsing expressions is done by calling the ``parseExpression()`` like we did for
  238. the ``set`` tag.
  239. .. tip::
  240. Reading the existing ``TokenParser`` classes is the best way to learn all
  241. the nitty-gritty details of the parsing process.
  242. Defining a Node
  243. ~~~~~~~~~~~~~~~
  244. The ``Project_Set_Node`` class itself is rather simple::
  245. class Project_Set_Node extends Twig_Node
  246. {
  247. public function __construct($name, Twig_Node_Expression $value, $lineno)
  248. {
  249. parent::__construct(array('value' => $value), array('name' => $name), $lineno);
  250. }
  251. public function compile(Twig_Compiler $compiler)
  252. {
  253. $compiler
  254. ->addDebugInfo($this)
  255. ->write('$context[\''.$this->getAttribute('name').'\'] = ')
  256. ->subcompile($this->getNode('value'))
  257. ->raw(";\n")
  258. ;
  259. }
  260. }
  261. The compiler implements a fluid interface and provides methods that helps the
  262. developer generate beautiful and readable PHP code:
  263. * ``subcompile()``: Compiles a node.
  264. * ``raw()``: Writes the given string as is.
  265. * ``write()``: Writes the given string by adding indentation at the beginning
  266. of each line.
  267. * ``string()``: Writes a quoted string.
  268. * ``repr()``: Writes a PHP representation of a given value (see
  269. ``Twig_Node_For`` for a usage example).
  270. * ``addDebugInfo()``: Adds the line of the original template file related to
  271. the current node as a comment.
  272. * ``indent()``: Indents the generated code (see ``Twig_Node_Block`` for a
  273. usage example).
  274. * ``outdent()``: Outdents the generated code (see ``Twig_Node_Block`` for a
  275. usage example).
  276. .. _`rot13`: http://www.php.net/manual/en/function.str-rot13.php