Creating Web Page Templates with PHP and Twig (part 2)

Feature Power

In the first segment of this article, I introduced you to Twig, a PHP-based template engine created and maintained by Sensio Labs and Fabien Potencier. I walked you through the process of installing Twig, understanding how templates and template variables work, and using Twig’s built-in conditional and loop constructs to automate some aspects of template development. I also gave you a crash course in some of Twig’s built-in filters, which provide a convenient way to quickly manipulate template content.

In this second and concluding segment, I’ll look at some of Twig’s other features, including such goodies as template inheritance, custom filters and caching. If you enjoyed the first part of this article, keep reading to find out more about what goes on under Twig’s hood, and how you can add even more power and flexibility to your templates.

Nest Egg

One of Twig’s most powerful features is its support for template inheritance. This means that you can define a base template and derive child templates from it, overriding key elements of the parent on an as-needed basis within the children. This offers a great deal of flexibility when defining the interface for a Web application, as you can use this feature to create a nested template structure for a Web application and override sections of each page as needed.

The keys to Twig’s template inheritance system are the 'extends' tag, which allows one template to extend another, and the 'block' tag, which allows a designer to decompose a page into separate, independent units. To better understand how this works, assume that you have a base template containing a block for an unordered list:

This template uses the 'block' tag to define a block within the template. This block can now be overridden as needed from within a child template. To illustrate, consider one such child template:

Notice the 'extends' tag in the child template; this indicates that the template extends another one. When extending a parent template in this manner, blocks in the child template will override identically-named blocks in the parent. Consider the following script, which renders the child template and illustrates the result:

Here’s the output:

As you can see from the output, the list in the child template has overridden the list in the parent template.

Block Print

Another useful feature of Twig, with respect to inheritance, relates to usage of parent blocks within child templates. Twig provides a parent() function, which can be used within a child template’s block to render the contents of the parent template. Consider the following template, which carries on from the previous example and illustrates the process:

Here’s what the output looks like:

Twig doesn’t let you have two blocks with the same name within the same template, which can pose a problem if you want to repeat blocks. Fortunately, there’s a block() function, which can be used to import the result of a block into a specific section of a template. Here’s an example of it in use:

In this case, the block() function will display the list from the first block again, as shown below:

Fun With Filters

In the previous article, I showed you how to use Twig’s built-in filters to perform direct manipulation of template content, such as upper-casing text blocks or formatting date and time values. However, you’re not restricted only to using Twig’s built-in filters; you can also define your own.

New filters are defined using the Twig_Filter_Function object, and passed to the Twig_Environment using the addFilter() function. This function accepts two arguments: the name of the filter and the name of the PHP function to use when the filter is invoked. Here’s an example, which illustrates by building a Twig filter to obfuscate email addresses:

Here’s how you’d use this in a Twig template:

And here’s an example of what the output looks like:

A Good Argument

It’s also possible to define custom filters that accept arguments, as shown in this revision of the previous example:

In this case, the 'obfuscate_address' filter will accept an additional argument, indicating whether the email address should be displayed as a hyperlink or not. Here’s an example of a template that uses this revised filter:

And here’s what the output looks like:

Notice also the 'is_safe' array being passed in the previous listing. This specifies that it is safe to display the output as unescaped HTML.

In a similar vein, it’s possible to define Twig functions using the Twig_Function_Function object and the addFunction() method. Here’s a simple example, which defines a truncate() function:

And here’s an example of a template that uses this function:

Here’s what the output looks like:

The Need For Speed

Twig will automatically compile its templates before rendering them. On high-traffic Web sites, it’s possible to cache the compiled templates and reuse them as needed, thereby improving performance by orders of magnitude. This “compilation cache” can be controlled by two configuration variables, which must be passed to the Twig_Environment object:

  • The 'cache' option specifies the location of the cache directory. If this directory does not already exist, Twig will create it automatically.
  • The 'auto_reload' option specifies whether the template should be recompiled when Twig detects a change in the template file. It’s generally a good idea to keep this on during development, or else you might wonder why your rendered output doesn’t reflect recent changes in the template code; you can however turn it off in a production environment where templates are unlikely to change frequently.

Here’s an example of how these two variables can be used in a PHP script:

Here’s the corresponding template:

And now, when you run the previous script, you’ll see that Twig creates a cache directory and stores the compiled template output in that directory. Subsequent requests for the same script will use the cached, compiled version instead of recompiling it each time. When the template code changes, Twig will automatically detect the change and recompile and recache the template.

Cache Cow

It’s important to point out that the compilation cache discussed on the previous page only caches the compiled template code, and not the rendered output of the page. You can, however, integrate Twig with any external caching system, such as Zend_Cache or PEAR Cache, to also cache the rendered output of each page.

When output caching is enabled, pages can be directly rendered from the cache, without needing to re-execute the PHP code inside the template on each run. For pages that contain many lines of PHP code – for example, pages that use database result sets or integrate data from third-party Web services – this feature can result in massive performance improvements.

To see how this works, consider the following revision of the previous script, which combines Twig’s compilation cache with Zend_Cache’s output cache to further improve page performance:

There’s a three-step process to enable output caching with Zend_Cache:

  1. Specify a unique cache identifier and cache duration for the page as arguments to the Zend_Cache frontend, and a directory location for the cache as an argument to the Zend_Cache backend. Note that unlike with the Twig compilation cache, this cache directory must already exist, or else Zend_Cache will throw an error.
  2. Wrap your template generation code in calls to the Zend_Cache load() method, and return the page from the cache if a cached copy exists.
  3. If a cached copy does not exist or is stale, generate the page afresh and save it to the cache so it can be reused for subsequent requests.

In the previous script, these steps have been combined with the Twig compilation cache, such that stale data in the output cache is refreshed using templates from the compilation cache.

Plugging In

If you like to develop your applications using a framework (and why wouldn’t you?), then you’ll be interested to learn that you can also use Twig with the Zend Framework. A number of different techniques are available, but the simplest is the one proposed by Benjamin Zikarsky in this discussion thread, and it also forms the basis for the material on this page.

To understand how it works, assume for a moment that you’re creating a new Zend Framework project, and you wish to replace the Zend_View component of the Zend Framework with Twig. First, ensure that the Twig library files are accessible from within the Zend Framework project, by copying them to the library/ directory or adding them to the PHP include path.

Next, create a Twig adapter for the Zend Framework that implements Zend_View_Interface, by copying Benjamin Zikarsky’s original code and saving it to your project directory tree under library/Twig/Adapter/ZendView.php. While you’re there, find and replace the adapter’s getScriptPaths() method with this revised version:

Next, update the application bootstrapper with a new _initTwig() method, which takes care of initializing the Twig adapter and defining the base path for the application templates. Note that Twig will automatically add the controller and template name to this path, assuming that they follow standard Zend Framework conventions.

You should now be able to use Twig syntax in your controllers and view scripts, as shown below:

In application/controllers/IndexController.php:

In application/views/scripts/index/index.phtml:

Here’s what the output looks like:

For more information on (and limitations of) this approach, in addition to some options, take a look at the original discussion thread in the Twig users mailing list.

And that’s about all we have time for. As these examples illustrate, Twig comes with a number of useful features for developers looking to expand their repertoire and create complex Web sites and applications using templates. Its support for template inheritance speeds up development time and reduces the effort involved in maintaining a Web application, while the filter system allow for easy integration of custom functionality. Built-in compilation caching can help improve performance on high-traffic pages, and easy usability in both standalone and framework mode makes it an attractive and convenient option for PHP developers of all stripes. Take a look at it sometime and see for yourself!

Copyright Melonfire, 2011. All rights reserved.