Experimental Project

This is a sandbox project, which contains experimental code for developer use only.

The drupal 7 implementation of TWIG template language.

Updated 21-07-2014 for version

At https://github.com/renebakx/twig-for-drupal/tree/7.1.6 you will find the last build I made for this project.
It is a copy of the version i've been using in production for the last 6 or 7 months. The package contains the somewhat cleaned up version of TFD it self (with almost flawless working auto-render)

Included in the repository is a small module stub mostly created to ad a new file stream to separate the twig cache from the normal public path, aswel as a Drush command to clean the cache.

Also Solid, the base theme I've created and used for most projects is included. The theme is heavily inspired on MortenDK's mothership and includes stripped down twig'ed versions of most base templates, or at least for modules I use ;)

And last but not least, a simple Drush make file to setup a project using Twig in conjunction with the libraries module and x-autoload module. Using x-autoload instead of the custom autoload i've used in the previous versions also gives a small performance gain, especially when used with a APC cache.

There is not really a up to date manual for this project, but you can check most of the functionality by reading the code in the Environment PHP file. Most of the wrapping and extending of basic Drupal stuff is done in there.

This will most likely be the final version of TFD i create, as the project for me is mature enough to handle my personal needs within the Drupal 7 branche. But feel free to fork it, clone it, abuse it or whatever you need to do to make it fit your own personal needs. And please use the Github version for this, as the D.O version is living proof dinosaurs actually used PHP ;)

Updated 22-11-2012 for version

Be careful, information below may not be fully up to date!, please check https://github.com/renebakx/twig-for-drupal/blob/7.1.6/TFD/TFD/Extension... for more up to date usages

This project adds TWIG as a second theme engine to drupal 7. This project is mostly intended for those who want to use twig in D7 and are not afraid to move files into the correct place in their drupal installation. Don't worry to much, it's only folder and one file.

Beware this version is not 100% downards compatible with the previous version.


  • Compiled templates are now stored in a private folder. Be sure to set one at
  • Removed the old custom function call node as the functionallity is now
    available native in TWIG
  • Rebuild the way templates are discovered and internally stored.
  • Added drupal_static construction to the disk expensive calls.
  • Moved the autoloader into the project, patching of the config is not needed.

General information and installation

For more information about Twig, visit http://twig-project.org

To use this engine you will need to clone Twig from git
(https://github.com/fabpot/Twig.git) and manually copy the contents of /lib/Twig
into sites/all/libraries in a folder called Twig

The contents of TFD from this module needs to be moved into libraries as well.

So you end up with a folder structure like this.

sites/all/libraries/Twig/Extension/ ... etc
sites/all/libraries/TFD/Loader/ ... etc

Then move the twig.engine file into ./themes/engines/twig/ so that is on the
same level as the phptemplate engine drupal comes with.


To create a theme for twig, simply set engine = twig in your theme.info and
start creating templates

Twig for Drupal 7

The implementation of this Twig engine is purely for Drupal 7. The implementation of Drupal 8 most likely will be different from this!
However most extensions should be compatible with Drupal 8 as they are done as an extension of Twig instead of Drupal.

This is a 100% compatible drop in replacement for the default drupal phptemplate system, with one restriction due to a core bug that is unsolvable at this moment because of the backportability and structural changes between Drupal 7 and 8. You can NOT extend a phptemplate based theme with a twig theme. See http://drupal.org/node/1545964 for more information. However drupal modules that do not provide twig based templates will work as long as the template is within the module folder. If you want to extend or change the modules default template you MUST convert it to a twig template within your theme.

By convention twig for drupal templates are postfixed with .tpl.twig




My first twig theme.

Declaring a theme as a twig theme works exactly the same as with a phptemplate based theme. Start with creating a folder in your themes structure, add a .info file and a tempate.php if needed. The only difference is, instead of engine = phptemplate you use engine = twig

For examplae

name = Twiggy
description = a cool twig based theme   
engine = twig
core = 7.x

There is nothing more needed to use twig as engine.

Twig for Drupal specific extensions

Besides the default twig tags, filters and functions this version comes with a few extra bells and whistles that can make taming the drupal theming layer a bit easier.

Including and extending templates

One of the cool features of twig is the extendability of templates. The behaviour is adapted to drupal using a symfony2 like aproach of resolving templates within the current theme and the base theme if needed. Please note, that loading templates from other themes then the current enabled theme and the base theme for that theme is not supported!

The golden rule to remember is, every '/' in the path from 'templates' is replaced by a ':'
And the base theme is referred by it's machine name seperated by '::'

Asume you have a theme called twiggy that extends mothertwig and all templates are in the /template folder of that theme.

Loading a template from the current theme 'template' folder.

{% include 'node-default.tpl.twig' %}

Loading a template from a subfolder called pages as seen from the current theme 'template' folder

{% include 'pages:node-default.tpl.twig' %}

Loading a template from the base theme 'template' folder

{% include 'mothertwig::node-default.tpl.twig' %}

Loading a template from a subfolder called pages as seen from the base theme.

{% include 'mothertwig::pages:node-default.tpl.twig' %}

The template locations are cached, so if you add a new template, the normal routine of clearing the theme cache in drupal is required!



The 'with' tag allows a scope-shift into a defined array. The format is as follows:

The with construct sets the argument as the current context. If an 'as name' is defined,the variable is defined as that local name.
The two flags sandboxed or merged define an additional behaviour, allowing to sandbox the contents of the construct from the current scope (which results in having only the defined variables in the current scope), or merging them with the current scope respectively.

If none of the flags is defined, the current context is stacked in the context as _parent. After the end of the with construct, thecontext is restored.

Usage :

{% with expr [as localName] [, expr2 [as localName2], [....]]  {sandboxed|merged} %}
{% endwith %}

Assume the following context:

   'foo' => array(                                          
      'name' => 'Foo',                                      
      'id'   => 1                                           
   'bar' => array(                                          
      'name' => 'Bar',                                      
      'id'   => 2                                           

{% with foo %}                                              
   {{ id }}: {{ name }} {# would output: "1: Foo" #}        
{% endwith %}                                               

{% with foo as baz %}                                       
   {{ baz.id }}: {{ baz.name }} {# would output: "1: Foo" #}
{% endwith %}                                               

{% with foo as bar, bar as foo %}                           
   {{ bar.id }}: {{ bar.name }} {# would output: "1: Foo" #}
   {{ foo.id }}: {{ foo.name }} {# would output: "2: Bar" #}
{% endwith %}

This tag originally was contributed by Gerard van Helden


The 'switch' tag allows a switch case construction in your template. By default the case constructs are not fallthrough, which means that there is no need to break from a switch like you normally would in PHP. You can use the fallthrough parameter at a case to overrule the default breaking behaviour. Default is the non obligatory catch all constructor. The case constructor is case sensitive!

{% switch bar %}
    {% case 'beer' %}
        Here is a cold beer.

    {% case 'water' %}
        The tap is in the toilet

    {% case 'howdie' fallthrough %}
        Hello stranger,
    {% case 'hello' %}   
        What do you want to drink?

    {% default %}
        say what?   
{% endswitch %}

'beer','water' and 'hello' produce a single line, yet 'howdie' produces both the outcome of the 'howdie' and 'hello'
And if bar is empty or none of the other case, it will produce 'say what?'

This tag originally was contributed by Gerard van Helden


Unset removes a variable from the current context, it is the opposite of the twig set tag.
Usefull if you assigned a local scope set and want to remove that for later use in the template

{% set foo = 'bar' %}
    do something with foo

{% unset(foo) %}



Returns a merged array of defaults upon the variable the filter is called.

{{ foo|defaults({'bar','baz'}) }}


dumps the variable in a smart way. The dump filter checks if the 'devel' module is loaded, and if so by default does a kpr($variable); else it uses the PHP var_dump($variable)

However you can specify the method to use by a single parameter.

Default PHP:

  • dump('v') maps to var_dump();
  • dump('p') maps to print_r();

Devel module powered:

  • dump('k') maps to kpr();
  • dump('r') maps to dpr();
  • dump('d') maps to dpm();

Please note, by using a parameter the method is forced, yet checked if callable. So using dump('d') when the devel module is not enabled will not fallback and will print nothing.


Maps a variable to the drupal url() and checkurl() methods. When the variable is numeric, it is asumed to be a node and prepended by /node/ before passing it to the checkurl() method.

<a href="{{node.nid|url}}">click me</a>

will produce a fully path_auto extended url to the given node.

<a href="{{'<front>'|url}}">home</a>

will produce a link to the frontpage.

See the drupal documentation for the options you can use as parameters.


A one to one mapping to the drupal t() method.


A one to one mapping to the php ucfirst() method.


Removes al the '#meta' data from the first level of a 'render array of doom'.


A strongly discouraged in favour of a proper field fieldformatter unix timestamp to date filter.

Usage: |date_format('format','method').
Where method is either date or strftime and the format is a apropriate format for the used function.
Defaults to strftime('%d-%m-%Y %H:%M')



A 'smarter' render function

If you want to render just the contents of a single value field, you normally would do
something like this {{ r(field.foobar[0]) }} because of the field wrappers.
But typing the extra [0] can be a bit a over-verbose.
This render method checks if the array you pass has a single [0] key, and if so pass that to the render() method.
in any other case the whole array is passed trough.

For extra RSI preventing convenience a shortcut version {{ r() }} is available to.


A 100% mapped version of the drupal hide() method. Unlike the above mentioned render this function does nothing smart.

For extra RSI preventing convenience a shortcut version {{ h() }} is available to.


The function equivalent of the above documented dump filter. Parameter and usage are the same.

{{ dump(var,'method') }}


Flattens a typical drupal select field array into a more usable format.

input  = array( 0 => array ('value' => 1),                                  
               1 => array ('value' => 4)                                   

output = array(0 => 1 ,1 => 4)


A one on one mapping to the drupal theme_get_setting(); method.


A one on one mapping to the drupal module_exists(); method.


Test are mostly used in {% if constraints %}


Checks if the given property excists in a 'render array of doom'.

     $var = array("#theme" => 'aaaa')

      {% if var is property('theme') %}
        do something
      {% endif %}


checks if the given value is a number.

convenience mapping

For developers convenience, the twig default binary or & and are mapped to || & && however the original style of using them in a if function is strongly encouraged.


All drupal hooks are called upon rebuilding of theme registry and thus on the 'cache clear all' call in Drupal


Add your own tokens (tags) to the tokenparser. These new tokens should extend the Twig_TokenParser.
In most circumstances you will actually never extend the tokenparser as this is a language construct rather then extending display logic.

See twig documentation for more information on creating your own tokenparser.

    $token[] = new My_Twig_Token();


Project Information