How to make a Twig extension for Symfony2

I like Twig. I started using it since I found out that it will be built-in part of new Symfony2. In this tutorial I'm going to make two simple Twig extension. First one just remaps classic PHP function var_dump into a Twig filter and the second one highlights specified string in a given text.

UPDATE: Mar 10th - Article reviewed for Symfony2 PR7
UPDATE: Apr 28th - Article reviewed for Symfony2 PR12
UPDATE: Oct 11th - Article reviewed for Symfony 2.0.4

Since Symfony2 is completely based on Dependency Injection design pattern and coded on Sensio Labs Symfony Dependency Injection libraries, things got a little more complicated. That's why this tutorial is more about Symfony2 and how to tie our Twig extension with a Symfony2 bundle.

If you've never seen Symfony2 or Dependency Injection pattern, some things will be probably a little odd for you (maybe check official documentation or my Symfony2 and Dependency Injection article).

So, the first thing we have to do is to write the Twig extension itself. I'm not going to describe how exactly write a Twig extension, for more information see the official Twig documentation. Fortunately, in our case it's really simple:

 1 
 2 
 3 
 4 
 5 
 6 
 7 
 8 
 9 
 10 
 11 
 12 
 13 
 14 
 15 
 16 
 17 
 18 
 19 
 20 
 21 
 22 
 23 
// HelloBundle/Extension/MyTwigExtension.php
namespace Bundle\HelloBundle\Extension;

class MyTwigExtension extends \Twig_Extension {

    public function getFilters() {
        return array(
            'var_dump'   => new \Twig_Filter_Function('var_dump'),
            'highlight'  => new \Twig_Filter_Method($this, 'highlight'),
        );
    }

    public function highlight($sentence, $expr) {
        return preg_replace('/(' . $expr . ')/',
                            '<span style="color:red">\1</span>', $sentence);
    }

    public function getName()
    {
        return 'my_twig_extension';
    }

}

As I've already mentioned before we should follow dependency injection design pattern. This means that we have to write a simple class that specifies what exactly our bundle needs, what services does it provide and eventualy configure our bundle.

 1 
 2 
 3 
 4 
 5 
 6 
 7 
 8 
 9 
 10 
 11 
 12 
 13 
 14 
 15 
 16 
 17 
 18 
 19 
 20 
 21 
 22 
 23 
 24 
 25 
 26 
// HelloBundle/DependencyInjection/HelloExtension.php
namespace Bundle\HelloBundle\DependencyInjection;

use Symfony\Component\HttpKernel\DependencyInjection\Extension;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Definition;

class HelloExtension extends Extension {

    public function load(array $config, ContainerBuilder $container) {
        $definition = new Definition('Bundle\HelloBundle\Extension\MyTwigExtension');
        // this is the most important part. Later in the startup process TwigBundle
        // searches through the container and registres all services taged as twig.extension.
        $definition->addTag('twig.extension');
        $container->setDefinition('my_twig_extension', $definition);
    }

    /**
     * Was necessary in previous Symfony2 PR releases.
     * Symfony2 calls `load` method automatically now.
     *
     * public function getAlias() {
     *     return 'hello'; // that's how we'll call this extension in configuration files
     * }
     */
}

The last thing we have to do is to tell Symfony2 something like: "My bundle needs some special configuration, load it before you start".

To do so, we add a new line to our configuration file that triggers the class above (for more information about bundle configuration see the official Symfony2 documentation).

It's too much work...

There's an alternative way of enabling Twig extensions. As it's described in Service Container official documentation documentation you can do the same by just adding a few lines in your configuration file.

 1 
 2 
 3 
 4 
 5 
 6 
# app/config/config.yml
services:
    foo.twig.extension:
        class: Bundle\HelloBundle\Extension\HelloExtension
        tags:
            -  { name: twig.extension }

This is nice when you're making some single purpose bundle but if you wanted to reuse it among more applications you would have to enable it manually every time.

Twig template

And that's all. Now we can test the output in Twig template.

 1 
 2 
{{ 'dump some variables'|var_dump }}
{{ 'Highlight this sentence'|highlight('i')|raw }}

We have to turn off autoescaping because highlight function returns HTML tags, that's why there's the |raw filter at the end. Visual output is following ... (note: I have xdebug extension enabled so my var_dump might be different to yours):

and HTML ...

 1 
 2 
<pre class='xdebug-var-dump' dir='ltr'>
   <small>string</small> <font color='#cc0000'>'dump some variables'</font> <i>(length=19)</i>

Highlight this sentence

By the way, there's a great Netbeans plugin for Twig syntax highlighting.

blog comments powered by Disqus