Building Template-Driven Web Applications with Dwoo (part 2)

August 6, 2010

Zend Framework

In the previous segment of this article, I introduced you to Dwoo, a powerful, PHP 5.x template engine that allows Web application developers easily separate the user interface of their application from its business logic. I gave you a crash course in Dwoo’s basic features, including features such as variable interpolation, loops, conditional statements and built-in plugins.

In this second, and concluding, segment, I’ll delve a little deeper, exploring some of Dwoo’s lesser-known features. Mastering these features can make your Dwoo experience more fun, by allowing you to reuse templates, create custom plugins and integrate with well-known PHP frameworks. So come on in and see what’s cooking!

Extending Yourself

One of Dwoo’s most powerful features is its support for template inheritance. Very simply, 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. Clever developers can use this feature to build an entire inheritance tree for the visual interface of a Web application, significantly reducing the time spent on updates.

The key to Dwoo’s template inheritance system is the “extends” plugin, which allows one template to extend another, and the “blocks” plugin, which allows designer to break a page up into separate units that can be overridden. To better understand how this works, assume that you have a base template (‘base.tpl’) containing two blocks, one for navigation and one for content:

<html>
  <head></head>
  <body>    
    {block "nav"}
    {/block}
    {block "content"}
    <div id="content">
    This is content for the main page.
    </div>      
    {/block}
  </body>
</html>

Now, let’s say some of your pages need a horizontal navigation bar. You can simply extend the base template, adding a horizontal navigation bar by defining a new “nav” block containing the required HTML in the child template (‘child.tpl’).

{extends "base.tpl"}

  {block "nav"}
    <div id="nav">
      <a href="#">Home</a> | 
      <a href="#">News</a> |
      <a href="#">Weather</a> |
      <a href="#">Hotels</a> |
      <a href="#">Dining</a>
      </tr>
      </table>
    </div>
  {/block}

In this case, the “extends” call at the top of the template tells Dwoo that this template should inherit the content of the named parent template, unless specifically overridden. Since this template defines a new “nav” block, the new “nav” block will override the parent’s “nav” block, producing a result like the one below:

Now for a twist. Let’s say that some pages need additional sub-navigation, in the form of a vertical navigation bar running along the side. In other words, these pages need the “nav” block defined above, plus some new HTML elements. Dwoo allows you to do this via its special $dwoo.parent construct, which serves as a placeholder for the parent block’s content. Here’s what the template (‘grandchild.tpl’) would look like:

{extends "child.tpl"}

  {block "nav"}
    {$dwoo.parent}
    
    <div id="sub-nav">
      <h2>{$subtitle}</h2>
      <ul>
      {loop $items}
      	<li><a href="#">{$item}</a></li>      
      {/loop}
      </ul>
    </div>    
  {/block}
  
  {block "content"}
    <div id="content">
    This is content for the Dining page.
    </div>
  {/block}

In this case, the “nav” block uses the $dwoo.parent construct to first inject the parent’s horizontal navigation bar into the template. It then adds additional custom HTML elements – an unordered list – to serve as vertical navigation. A new “content” block is also used to override the “content” block defined in the parent instance. The rendered result looks something like this:

As this example illustrates, template inheritance makes it possible to define one or more base templates for the entire site or application, then sub-class them to address specific needs. This allows for rapid deployment of interface changes across an application: a designer needs to simply modify a few base templates and then let inheritance do the rest of the work.

Slaves And Subs

Dwoo also supports sub-templates, which are analogous to page widgets that you can reuse in multiple places across your application. Sub-templates are created as blocks enclosed within {template}...{/template} markers, and are identified with a unique name; this name serves as a key that allows you to use the sub-template inside other templates. When sub-templates are located in external files, they can be loaded into a template using the “load_templates” plugin.

Sub-templates can also be passed one or more arguments, which serve as input to the processing code inside the sub-template. This means that, for example, you could have a sub-template for menu navigation and use it in multiple places, passing it an array of different menu items each time.

The easiest way to understand this is with an example. Begin by creating a simple sub-template (‘slave.tpl’) that contains an ordered list, as below:

{template mylist data}
<ul>
  {foreach $data d}
  <li>{$d}</li>
  {/foreach}
</ul>
{/template}

This code creates a sub-template named “mylist”, which is initialized with a data array. Within the sub-template, a {foreach} block takes care of iterating over the array and printing its contents as an unordered list.

You can now use this sub-template wherever you need an unordered list, by loading it with the “load_templates” plugin and invoking it by name, always remembering to pass it the array of list items as argument. Here’s an example:

{load_templates "slave.tpl"}

{mylist $items}

And of course, you can set the template variable {$items) to an array of list items using a PHP script, as shown below:

<?php
// set up auto-loader
include 'dwooAutoload.php';

try {
  // create Dwo object
  $dwoo = new Dwoo();
  
  // read template file
  $tpl = new Dwoo_Template_File('tmpl/master.tpl');

  // set template variables
  $data = array();  
  $data['items'] = array('red', 'blue', 'green', 'yellow');;
  
  // interpolate values into template
  // send interpolated result to output device
  $dwoo->output($tpl, $data);
} catch (Exception $e) {
  echo "Error: " . $e->getMessage();      
}
?>

Here’s what the output looks like:

Sub-templates thus offer a convenient way to encapsulate common interface elements, reusing them as needed in multiple places. Needless to say, any change in the sub-template automatically reflects in all its instances, making updates convenient and efficient. The ability to inject data into a sub-template adds a further degree of flexibility, allowing for a variety of different uses.

Plugging In

In addition to providing you with an abundance of built-in plugins, Dwoo also allows you to create your own. These plugins may be implemented as user-defined functions or standalone classes, and can be either manually added at run-time via Dwoo’s addPlugin() method or automatically loaded by the Dwoo autoloader.

To illustrate, consider the following script, which sets up a simple plugin to manipulate email addresses:

<?php
// simple plugin
function fix_address(Dwoo $dwoo, $str) {
    return str_replace(
      array('@', '.', '-'), 
      array(' at ', ' dot ', ' dash '), 
      $str
    );
}

// set up auto-loader
include 'dwooAutoload.php';

try {
  // create Dwoo object
  $dwoo = new Dwoo();
    
  // read template file
  $tpl = new Dwoo_Template_File('tmpl/plugin.tpl');
  
  // add custom plugin
  $dwoo->addPlugin('email_safe', 'fix_address');

  // set sample string  
  $data['string']= 'vikram@example.com';
      
  // interpolate values into template
  // send interpolated result to output device
  $dwoo->output($tpl, $data);
} catch (Exception $e) {
  echo "Error: " . $e->getMessage();      
}
?>

Here, the fix_address() function replaces common email address characters with human-readable words, in order to make it harder for spammers to harvest these addresses from Web pages. The addPlugin() method takes care of adding this function to Dwoo’s plugin list, specifying both the local plugin name and the function callback. Here’s how you’d use it in a template:

   {email_safe($string)}

And here’s what the output would look like:

Plugins can also be implemented as classes extending the Dwoo_Filter abstract class. Here’s an example of one such plugin, which is equivalent to the previous one:

<?php
// class implementation of plugin
class Dwoo_Plugin_email_safe extends Dwoo_Plugin
{	  
    public function process($email)
    {
        return str_replace(
          array('@', '.', '-'), 
          array(' at ', ' dot ', ' dash '), 
          $email
        );
    }
}
?>

If this class definition is stored in the Dwoo plugins directory, the Dwoo autoloader will automatically find and load it into the template engine without needing the addPlugin() method call.

Mail Merge

Dwoo also supports user-defined filters, which can be used to perform operations on template output after it is rendered, but before it is cached. Like plugins, filters can be implemented as user-defined functions, added to the engine manually via the addFilter() method, or as classes extending the Dwoo_Filter abstract class.

To illustrate, consider the following filter, which automatically activates wraps mailto: links around email addresses in rendered output:

<?php
// simple filter
function activate_mailto_links(Dwoo $dwoo, $str) {
  return preg_replace('/([a-zA-Z0-9]+@[a-zA-Z0-9._-]+\.[a-zA-Z]+)/', '<a href="mailto:$1">$1</a>', $str);
}

// set up auto-loader
include 'dwooAutoload.php';

try {
  // create Dwoo object
  $dwoo = new Dwoo();
  
  $dwoo->addFilter('activate_mailto_links');
  
  // read template file
  $tpl = new Dwoo_Template_File('tmpl/filter.tpl');
  
  // set example string
  $data['string']= 'Press enquiries: press@example-domain.com or call 1-800-1234. General inquiries: info@example-domain.com.';
  
  // interpolate values into template
  // send interpolated result to output device
  $dwoo->output($tpl, $data);
} catch (Exception $e) {
  echo "Error: " . $e->getMessage();      
}
?>

Here’s an example of the output:

You could also implement this as a class, saving it to the Dwoo plugins directory to have it automatically loaded. Here’s what this alternative implementation might look like:

<?php
// class implementation of filter
class Dwoo_Filter_activate_mailto_links extends Dwoo_Filter
{	  
  public function process($str)
  {
    return preg_replace('/([a-zA-Z0-9]+@[a-zA-Z0-9._-]+\.[a-zA-Z]+)/', '<a href="mailto:$1">$1</a>', $str);
  }
}
?>

Cache Cow

Dwoo also comes with a built-in output caching system, which can be used to improve performance on high-traffic Web pages. When output caching is enabled, Dwoo can directly render the final pages from disk, 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.

There are two simple steps to enable output caching for a Dwoo template:

1. Specify a unique cache identifier and cache duration for the template, as arguments to the Dwoo_Template_File constructor

2. Wrap your template generation code in calls to the isCached() method, and return the page from the cache if a cached copy exists.

To illustrate, let’s build a template to display the results of a Twitter search:

<html>
  <head>
    <style type="text/css">
      div.outer {
      	border-bottom: dashed orange 1px;
      	padding: 4px;
      	clear: both;
      	height: 50px;
      }        
      div.img {
        float:left;
        padding-right: 2px;
      }
      span.attrib {
        font-style: italic;
      }
    </style>  
  </head>
  <body>
    <h2>{$title}</h2>
    {loop $records}
    <div class="outer">
      <div class="img"><img width=48" height="48" src="{$image}" /></div>
      <div>{$tweet} <br/> <span class="attrib">By <a href="{$uri}">{$owner}</a> on {$time}</span></div>
    </div> 
    {/loop}
  </body>
</html>

The following PHP script demonstrates how to retrieve and cache search results with Dwoo’s output caching and the above template:

<?php
// set up auto-loader
include 'dwooAutoload.php';

// create Dwoo object
$dwoo = new Dwoo();

// read template file
$tpl = new Dwoo_Template_File('tmpl/tweets.tpl', 120, 'id_g75430i472j');

// check if output is cached
// if yes, display from cache with message
// if no, regenerate and add to cache
if ($dwoo->isCached($tpl)) {
  $dwoo->output($tpl, array());
  echo '(cached output)';
} else {

  // search Twitter for 'pasta'  
  $result = simplexml_load_file('http://search.twitter.com/search.atom?q=pasta&lang=en');

  // process Atom feed of search results
  $records = array();
  foreach ($result->entry as $entry) {
    $item['image'] = (string)$entry->link[1]['href']; 
    $item['owner'] = (string)$entry->author->name;
    $item['uri'] = (string)$entry->author->uri;
    $item['tweet'] = (string)$entry->content;
    $item['time'] = date('d M Y, h:i', strtotime($entry->published)); 
    $records[] = $item;
  }
    
  // set up data storage
  $data = new Dwoo_Data();

  // assign search results 
  $data->assign('records', $records);
  $data->assign('title', $result->title);

  // interpolate values into template
  // send interpolated result to output device
  $dwoo->output($tpl, $data);    
}
?>

The first time you access this script, it will connect to the Twitter Search API, retrieve an Atom-formatted list of search results matching ‘pasta’, process the Atom feed and render the final output. However, because output caching is enabled, the rendered output will also be saved to the disk cache as an HTML output file. As a result, every subsequent request for the page will be served from the cache, leading to a noticeable reduction in load time (try it for yourself and see).

Note that the cache identifier passed to the Dwoo_Template_File constructor as its third argument must be unique across the application, and should reflect application-level attributes like language, user content settings and so on. You can alter the location of Dwoo’s cache directory with the setCacheDir() method.

It’s important to point out that Dwoo’s output caching system is distinct from its compiled caching system. Output caching caches the rendered HTML output of the page, while compiled caching caches the compiled PHP code of the page. You can disable the compiled caching system and force Dwoo to recompile the template on each run by adding a call to the Dwoo_Template_File object’s forceCompilation() method in your PHP script. Note that this method should not be used on production sites, as it can reduce performance significantly; it is intended only for debugging.

In The Frame

If you like to develop your applications using a framework (and why wouldn’t you?), then you’ll be interested to hear that Dwoo comes with adapters for Agavi, CakePHP, CodeIgniter and the Zend Framework.

To illustrate how this 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 Dwoo. First, copy the Dwoo library files to the project’s library/ directory, following the structure shown below:

Next, update the application bootstrapper with a new _initDwoo() method, which takes care of initializing the Dwoo autoloader, setting up the Zend Framework view adapter, and configuring the plugin proxy:

<?php
class Bootstrap extends Zend_Application_Bootstrap_Bootstrap
{
  public function _initDwoo()
  {
    // set up Dwoo autoloader
    require_once 'dwoo/dwooAutoload.php';
            
    // configure cache and compiled directories
    $options = array();
    $options['engine']['compileDir'] = APPLICATION_PATH . '/temp/compiled';
    $options['engine']['cacheDir'] = APPLICATION_PATH . '/data/cache';

    // set up the view adapter and renderer
    $viewInterface = new Dwoo_Adapters_ZendFramework_View($options);
    $viewRenderer = new Zend_Controller_Action_Helper_ViewRenderer($viewInterface);
 
    // set up the plugin proxy
    $viewInterface->setPluginProxy(new Dwoo_Adapters_ZendFramework_PluginProxy(new Zend_View()));

    Zend_Controller_Action_HelperBroker::addHelper($viewRenderer);    
  }
}

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

In application/controllers/IndexController.php:

<?php
class IndexController extends Zend_Controller_Action
{
    public function indexAction()
    {
        $this->view->slug = 'Welcome to the Zend Framework';
    }
}
In application/views/scripts/index/index.phtml:

<h1> {$slug} </h1>

While it isn’t possible to examine the minute details of Dwoo’s integration with each framework in this article, you’ll find detailed instructions for other adapters on the Dwoo wiki.

Locking The Doors

As a general rule, it’s possible to execute PHP code from inside a Dwoo template, simply by enclosing the code in standard <?php…?> tags. However, there may be situations where you do not want this behaviour and so, Dwoo allows you to turn this feature on or off via its security policy system.

Dwoo security policies are implemented via the Dwoo_Security_Policy object, which is normally attached to the main template engine object via its setSecurityPolicy() method. The Dwoo_Security_Policy object exposes a setPhpHandling() method, which accepts a numeric argument between 1 and 3. This method controls how PHP code within a template is handled: escaped and encoded (1), disabled completely (2), or allowed (3).

Here’s an example, which illustrates these settings in action:

<?php
// set up auto-loader
include 'dwooAutoload.php';

try {
  // create Dwo object
  $dwoo = new Dwoo();
  
  // read template file
  $tpl = new Dwoo_Template_File('tmpl/function.tpl', 1);
  $tpl->forceCompilation();

  // set security policy
  // escape PHP functions
  $s = new Dwoo_Security_Policy();
  $s->setPhpHandling(1); 
  $dwoo->setSecurityPolicy($s);
  
  // interpolate values into template
  // send interpolated result to output device
  $dwoo->output($tpl, array());
} catch (Exception $e) {
  echo "Error: " . $e->getMessage();      
}
?>

Here’s what the base template looks like:

I said: <?php echo 'Hello'; ?>

Here’s what the output would be, for the three possible settings of the setPhpHandling() method:

While on the topic of security, turning on auto-escaping is probably a good idea to prevent XSS attacks. As discussed in the previous article, this can be easily done via the “auto_escape” plugin. You can also use Dwoo’s “safe” plugin within an auto-escape block to mark some variables as safe and have them printed without escaping; however, you should only do this with variables that you’re sure don’t contain anything dangerous. Here’s an example:

<html>
  <head></head>
  <body>
  {auto_escape on}
  {$html}
  {safe $str}
  {/auto_escape}
</body>
</html>

In this example, everything within the auto-escape block will be escaped, with the exception of the template variable $str.

And that’s about all we have time for. As these examples illustrate, Dwoo comes with a number of useful features, making it a good choice for use in small small-to-medium Web applications. Its support for template inheritance speeds up development time and reduces the effort involved in maintaining a Web application, while the plugin system allow for easy integration of custom functionality. Built-in output caching can help improve performance on high-traffic pages, and sub-templates offer a convenient solution for “widgetizing” your user interface. Take a look at it sometime and see for yourself!

Copyright Melonfire, 2010. All rights reserved.

2 Responses to “Building Template-Driven Web Applications with Dwoo (part 2)”

  1. mmmmlyz Says:

    Shop to provide quality professional value of the trust-level digital camera battery charger
    Online sales cheap digital camera batteries and charger company’s global delivery of quality value of the letter lazy ~!
    <a href="http://www.panasonic-battery-charger.com/PANASONIC-DMW-BLC12-DMW-GH2-Battery.html
    ">Panasonic DMW-BLC12, DMW-BLC12E, Lumix DMW-GH2 Battery</a><br />

  2. seldaek Says:

    Heya,

    Just a few corrections I’d like to bring after going over the article quickly:

    For output caching, the unique id (‘id_g75430i472j’ in the example) must not be unique per application but per template and content that you render. For example in the case of the twitter search you’d typically want to have the search term in there to be sure every search has its own cache and people don’t get results for pasta when they search for pizza. So you’d pass something like: ‘twitter-search-$search’, and add to that the language of the current user if your site is multi-language and the template contains internationalized content, otherwise they could get some content with mixed languages.

    For the security policy, please use the defined constants, namely Dwoo_Security_Policy::PHP_ENCODE, Dwoo_Security_Policy::PHP_REMOVE and Dwoo_Security_Policy::PHP_ALLOW instead of the numbers directly. I know it’s verbose but it means you’re safe with upgrades if the value of those should change internally, and also if one option disappears you’ll get a php warning when using a non-existing constant, which would allow you to notice the problem early. Now I’m not saying this will likely change in Dwoo, but it’s more of a general best practice comment.

    Other than that thanks again for spending the time writing all this!