Sunday, August 11, 2013

Pre-compiling Twig Templates

To be able to use option 'auto_reload'=>false in production with Twig, I need to be able to force a recompilation.  The easy way would be to clear the cache directory in the deployer when pushing an update, but if I'm micro-optimizing, why not ship built templates as part of the distribution?

The next obvious question is, "How do I make twig compile templates without rendering them?" and it turns out there's a simple answer.  The long form $twig->loadTemplate("name.twig")->render($vars) is the secret: loadTemplate writes to the cache if caching is enabled when the template is loaded.

Conceptually, then, it's dead simple: configure Twig how you normally would, list out all your template files, and call $twig->loadTemplate() on each of them.  I believe the minimal solution on POSIX platforms would look like this:


twig-build.sh:
#!/bin/sh
mkdir -p cache
find templates -name "*.twig" -type f | cut -d/ -f2- | php _build.php
_build.php:
<?php
$DS = DIRECTORY_SEPARATOR;
require(__DIR__ . $DS . 'vendor' . $DS . 'autoload.php');
$twig = new Twig_Environment(new Twig_Loader_Filesystem('templates'),
  array('cache' => 'cache', 'auto_reload' => true));
while (($fn = fgets(STDIN)))
  $twig->loadTemplate($fn);
Beautiful! We start out by ensuring the cache directory exists, then find files matching *.twig in the templates directory. Then we pass it through cut, making fields delimited by "/" and taking only the second field and onward. Since find produces names like "templates/user/profile.twig", this strips off the leading "templates/" so that only "user/profile.twig" gets passed to _build.php.

The job of _build.php is to set up Twig, of course, and then call loadTemplate on all the filenames that were piped in.  (The autoload line assumes Twig was installed via Composer.)

The only downside here is that the list of template directories is written twice: once in the find command, and once in the Twig_Loader_Filesystem constructor.  I could easily do all the file manipulation in PHP to take care of that, but it would add more lines of code.

2 comments:

Unknown said...

there is a small typo:

new Twig_Environment(Twig_Loader_Filesystem('templates'),

should be

new Twig_Environment(new Twig_Loader_Filesystem('templates'),

sapphirepaw said...

Thanks! I've updated the post.