Skip to Main Content
Official App
Free – Google Play
Get it
FreshBooks is Loved by American Small Business Owners
FreshBooks is Loved by Canadian Small Business Owners
FreshBooks is Loved by Small Business Owners in the UK
Dev Blog

Twig in Production

by Mark on September 28/2011

For the past year we’ve been busy behind the scenes updating much of the existing FreshBooks application to use Twig. If you haven’t heard of Twig before, it’s a great templating library for PHP. Its syntax and features are mostly inspired by Jinja2. Twig provides a number of features that are useful in larger applications:

  • Template inheritance – Useful for creating application layouts and re-using common page layouts.
  • Horizontal reuse of blocks – You can reuse blocks from one template in another giving you an alternative way to re-use template code beyond extension.
  • Macros – Create re-usable ‘functions’ entirely of template code.
  • Automatic HTML escaping – The ‘killer feature’ you should look for in any template language.
  • Sandboxing – Great for limiting what can be accessed in a template. This is really useful when exposing templates to end users.

Automatic escaping is something I think every PHP template language should have. While PHP is ok at being a template language, it is not automatically safe against cross-site scripting attacks. Having a template language offer both syntactic sugar and invisibily securing template code with no additional work is fantastic feature. If you’re using a template language without automatic escaping you should really switch to one that does. Having to explicitly turn off HTML escaping forces you to think about whether or not content is actually safe to output. In practice, we’ve found autoescaping makes developers more confident about not having accidentally created a new XSS problems. Unlike an omitted |escape, which could easily get lost in the crowd, every |raw sticks out like a sore thumb in code review.

We first started using Twig around the 0.9.6 release. It was chosen over the existing solution (Smarty) primarily because Twig offers a better feature set. Inheritance and automatic escaping were two concrete features that would improve our workflow. At the time Smarty 3 was still in beta, and the team wasn’t really happy with how Smarty had treated them in the past. We also considered other options:

  • Dwoo – This was not chosen because the project activity was not overly high, and it seemed like development had stalled.
  • PHPTal – XML only meant there were going to be significant integration hurdles with our non-compliant legacy code. It

    also meant it would be impossible to use with plain text email templates.

Getting twig into production

While updating all of our templates to use Twig we learned a few things along the way that are worth sharing.

Flushing the compiled templates is important. Forgetting to do it will cause you a great deal of pain. Thankfully, Twig has this functionality built in which makes it super easy to integrate as part of your deployment:

 * Clean out compiled templates.
require_once './inc/bootstrap.php';
$environments = array();
$return = 0;
echo "Clearing template cache files...\n";
// Cache files used for the actual application
$environments['app'] = Fresh_Twig::getEnvironment();
// Other configurations like tests are loaded as well,
// but have been omitted.
foreach ($environments as $name => $environment) {
    $cache = $environment->getCache();
    if (!$cache) {
        echo "[Info '$name']: skipping because caching is disabled.\n";
    if (!is_dir($cache)) {
        echo "[Info '$name']: skipping because '$cache' does not exist.\n";
    if (!is_writable($cache)) {
        echo "[Error '$name']: '$cache' is not writable. Try levelling up.\n";
    echo "[Success '$name']: cleared directory '$cache'.\n";

Automate building the cache files. Deploying on a cold cache will work for a while. If you have multiple webservers and many concurrent users you should think about pre-compiling your template cache. Deploying with a hot cache will remove the possibility of two users generating the same template cache file. It also removes the possibility for contention to occur around the creation of cache files, and finally it just reduces the amount of work done overall. Since each webserver isn’t spending time generating cache files individually they can get back to serving users faster. We use make and a simple php script to do this:

 * Simple script that gets a Twig_Enviornment and compiles all the 
 * templates that match the glob pattern.
if (empty($argv[1])) {
    echo "No file path provided, please give a directory to compile templates in.\n";
if (empty($argv[2])) {
    $argv[2] = 'twig';
$verbose = false;
if (in_array('--verbose', $argv)) {
    $verbose = true;
require_once './inc/bootstrap.php';
// A wrapper that provides a Twig_Environment with
// all the extensions we use attached.
$environment = Fresh_Twig::getEnvironment();
$directory = new RecursiveDirectoryIterator($argv[1]);
$recurser = new RecursiveIteratorIterator($directory);
$matcher = new RegexIterator($recurser, '/.*?\.' . $argv[2] . '$/', RegexIterator::GET_MATCH);
echo "Compiling Templates\n";
$i = 0;
foreach ($matcher as $file) {
    try {
        $filename = str_replace($argv[1], '', $file[0]);
        $environment->loadTemplate('/' . $filename);
        if ($verbose) {
            echo " - " . $filename . "\n";
        } else {
            if ($i % 72 == 0) {
                echo "\n";
            echo '.';
    } catch (Exception $e) {
        echo $e->getMessage() . "\n";
echo "\nTemplates compiled\n";

We compile both the slash-less and leading slash versions of the template. This produces more template files, but saves us from having to be super picky about how we use twig files in our application. Before each deploy we run the make script to generate all the template cache files. The compiled templates are shipped with the code changes.

In closing

Since the switch to Twig last year we haven’t really hit any real problems with it. Its been a fantastic and easily extensible library. Thanks to Fabien and all the other Twig contributors for making such a solid product.