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

July 20, 2010

Tutorials

Form and Function

When discussing the best practices of PHP application development, there’s one that appears on almost every list: separating the business logic of a Web application from its user interface. This separation lets interface designers work on a site’s appearance without disrupting the code of the application developers implementing business-level features and workflows.

Most PHP frameworks, including well-known ones like Zend Framework, Agavi, CakePHP and CodeIgniter, natively support this separation of interface and business logic. However, if your project is small enough that you don’t need a framework (or if you just don’t like them), you can still achieve the same benefits by using a standalone template engine. In this article, I’ll introduce you to one such engine, which goes by the rather unique name of Dwoo.

Ghostly Knocking

This tutorial makes a couple of assumptions. First, it assumes that you know the basics of PHP and database programming, and you have a reasonably-good understanding of using objects and object methods in PHP. Second, it assumes that you have an Apache/PHP/MySQL development environment already set up, and that you’ve managed to successfully install Dwoo. If you’re using the automated PEAR installer, you can install Dwoo simply by running the following commands at your command prompt:

shell> pear channel-discover pearhub.org
shell> pear install pearhub/Dwoo

Alternatively, you can manually install the Dwoo package, by downloading it from http://dwoo.org/download. Dwoo is currently maintained by Jordi Boggiano and is released under the Modified BSD License. This article uses Dwoo v1.1.1

Assuming you’ve got Dwoo installed, let’s get started with the basics. Dwoo assumes that a single Web page is made up of one or more independent templates. Each template contains both static HTML elements and template variables, which serve as placeholders for content. Dwoo works by injecting these placeholders with “real” data at run-time, thereby creating a final page that is suitable for display.

The values for the variable placeholders are set inside a controlling PHP script; this script may also perform other business-level functions, such as calculations, database queries, or XML processing. The separation between the PHP business logic and the HTML page template means that changes to the visual appearance of the page can be accomplished entirely by an interface designer, without a PHP developer’s input or coordination.

This might sound complicated but it’s actually very simple. To see how this works, create a simple HTML page, as below, and save it as templates/knock.tpl in your working directory:

<html>
  <head></head>
  <body>
    <blockquote>

    Knock knock! <br/>
    Who's there? <br/>
    {$name}      <br/>
    {$name} who? <br/>
    {$punchline}
    </blockquote>

  </body>
</html>

This is an HTML page template that contains two variables, $name and $punchline. Curly braces surround these variables; this tells Dwoo that they are template variables and must be replaced with actual values at run-time. Let’s see how that works:

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

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

// read template file
$tpl = new Dwoo_Template_File('tmpl/knock.tpl');

// assign values to template variables
$data = array();
$data['name'] = 'Boo';
$data['punchline'] = 'Don\'t cry, it\'s only a joke';

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

There’s a fairly standard sequence to rendering a template using Dwoo:

1. Include the Dwoo autoloader script. This registers the Dwoo autoloader with PHP, so that Dwoo object definitions and plugin files are automatically found and loaded as needed.

2. Create an instance of the Dwoo object. This object serves as a central point for all communication with the Dwoo template engine in subsequent operations.

3. Load the page template file. This is done by creating an instance of the Dwoo_Template_File object, passing the file name and path to the Dwoo_Template_File object constructor. Dwoo will then find and load this template into memory.

4. Set values for template variables. With Dwoo, this is done simply by defining a new associative array, whose keys and values correspond to template variable names and content.

5. Render the template. This is accomplished by calling the Dwoo object’s output() method, passing it the template object and the data array. The output() method interpolates the content from the data array into the template object, generating and rendering the composite result.

Here’s what the result looks like:

An alternative here is to use the get() method instead of the output() method. This retrieves the interpolated version of the template and assigns it to a variable instead of sending it directly to the browser. This is handy if you’re working with complex template trees or simply want to perform further processing on the output before displaying it. You’ll see an example of this method further along.

Right and Wrong

Dwoo supports the use of conditional tests within templates, via its “if” plugin. Here’s a simple example of a template that uses this plugin:

<html>
  <head></head>
  <body>

   {if $auth == 0}
   Not logged in
   {else}
   Logged in as: Anonymous User   
   {/if}
  </body>
</html>

Depending on the value set for the decision variable within your PHP controller script, the template will display one of two possible messages. Here’s the controller script, which randomly sets the decision variable to either 1 or 0:

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

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

  // read template file
  $tpl = new Dwoo_Template_File('tmpl/auth.tpl');

  // assign values to template variables
  $data = new Dwoo_Data();
  $data->assign('auth', rand(0,1));

  // 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 will look like:

Notice that this script introduces a new object, the Dwoo_Data object. If you have a large number of template variables, using Dwoo_Data to store template variable values is often easier than using regular PHP associative arrays, as it comes with numerous methods to assign, retrieve, merge and clear template variables.

You can also use the “if” plugin to set up more complex conditional tests, via its support for if-elseif-else syntax. Here’s an example:

<html>
  <head></head>

  <body>
   {if $auth == 1}
   Logged in as: Anonymous User   
   {elseif $auth == 2}
   Logged in as: Administrator 
   {else}
   Not logged in
   {/if}
  </body>
</html>

And here’s the revised PHP script and output:

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

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

  // read template file
  $tpl = new Dwoo_Template_File('tmpl/auth.tpl');

  // assign values to template variables
  $data = new Dwoo_Data();
  $data->assign('auth', rand(0,4));

  // interpolate values into template
  // send interpolated result to output device
  $dwoo->output($tpl, $data);
} catch (Exception $e) {
  echo "Error: " . $e->getMessage();      
}
?>

Looping The Loop

Dwoo also supports loops, allowing you to repeat a particular segment of a template via its {loop} construct. To illustrate, consider the following template, which specifies a loop for a list item element:

<html>
  <head></head>

  <body>
    <ul>
   {loop $items}
     <li>{escape($item)}</li>
   {/loop}
    </ul>
  </body>

</html>

To activate this loop, you must specify an array of values for the loop template variable in the PHP controller script. Dwoo will then automatically iterate over the array at render-time, repeatedly printing the loop segment with different content until the entire array has been processed. Here’s the PHP script:

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

try {
  // create Dwoo object
  $dwoo = new Dwoo();
  
  // read template file
  $tpl = new Dwoo_Template_File('tmpl/list.tpl');
  
  // assign values to template variables
  $data = new Dwoo_Data();
  $items = array();
  $items[] = array('item' => 'red');
  $items[] = array('item' => 'yellow');
  $items[] = array('item' => 'blue');
  $items[] = array('item' => 'green');
  $data->assign('items', $items);
  
  // 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:

Notice also that this template make use of a Dwoo plugin, “escape”, which takes care of automatically escaping output before printing it. It’s a good idea to wrap all output in this method call to reduce your vulnerability to XSS attacks. By default, the “escape” plugin escapes output using HTML entity encoding but it’s quite easy to alter this to use other formats as well.

You can also use Dwoo’s {foreach} construct to iterate through indexed and associative arrays, in a similar manner to that of PHP’s foreach() loop. Consider the following revision of the previous template, which illustrates:


<html>
  <head></head>
  <body>
    <ul>
   {foreach $items item}
     <li>{escape($item)}</li>

   {/foreach}
    </ul>
  </body>
</html>

In this case, $items is an indexed array, which is populated from within the PHP page controller. On each iteration, the current element of the array is assigned to the local loop variable $item. Here’s the PHP script:


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

try {
  // create Dwoo object
  $dwoo = new Dwoo();
  
  // read template file
  $tpl = new Dwoo_Template_File('tmpl/list.tpl');
  
  // assign values to template variables
  $data = new Dwoo_Data();
  $items = array('red', 'blue', 'green', 'yellow');
  $data->assign('items', $items);
  
  // interpolate values into template
  // send interpolated result to output device
  $dwoo->output($tpl, $data);
} catch (Exception $e) {
  echo "Error: " . $e->getMessage();      
}
?>

If you’re using an associative array, the procedure is similar, except that you need to specify local “key” and “value” variables within the {foreach} block inside the template. Here’s how:

<html>
  <head></head>

  <body>
    <ul>
   {foreach $items key value}
     <li>{upper($key)} is for {$value}</li>
   {/foreach}
    </ul>
  </body>

</html>

And here’s the PHP script that activates and populates this template:

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

try {
  // create Dwoo object
  $dwoo = new Dwoo();
  
  // read template file
  $tpl = new Dwoo_Template_File('tmpl/list.tpl');
  
  // assign values to template variables
  $data = new Dwoo_Data();
  $items = array(
    'a' => 'apple',
    'b' => 'ball',
    'c' => 'cat',
    'd' => 'dog'
  );
  $data->assign('items', $items);
  
  // interpolate values into template
  // send interpolated result to output device
  $dwoo->output($tpl, $data);
} catch (Exception $e) {
  echo "Error: " . $e->getMessage();      
}
?>

Here’s the output:

Getting Booked

The ability to use loops inside template comes in particularly handy when you need to integrate the results of a database query into your output template. To illustrate, consider the following template for a two-column table:

<html>
  <head>

    <style type="text/css">
      table {
        border-collapse: collapse;
        width: 320px;
      }        
      tr.heading {      
        font-weight: bolder;
      }        
      td {
        border: 1px solid black;
        padding: 0 0.5em;
      }    
    </style>  
  </head>
  <body>
    <table>
      <tr class="heading">

        <td>Author</td>
        <td>Title</td>
      </tr> 
      {loop $records}
      <tr>
        <td>{$author}</td>

        <td>{$title}</td>
      </tr> 
      {/loop}
    </table>
  </body>
</html>

Here’s the corresponding PHP script, which retrieves a list of countries and languages from a MySQL database, drops them into an array, and uses that array to populate the template above:

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

// attempt a connection
try {
  $dbh = new PDO('mysql:dbname=library;host=localhost', 'user', 'pass');
} catch (PDOException $e) {
  echo "Error: Could not connect. " . $e->getMessage();
}

// set error mode
$dbh->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);

// attempt some queries
try {
  // execute SELECT query
  $sql = "SELECT a.AuthorName AS author, t.TitleName AS title FROM author AS a, title AS t, author_title AS at WHERE a.AuthorID = at.AuthorID AND t.TitleID = at.TitleID ORDER BY author LIMIT 0,20";
  $sth = $dbh->query($sql);
  while ($row = $sth->fetchObject()) {
    $records[] = array('author' => $row->author, 'title' => $row->title);
  }
  
  // close connection, clean up
  unset($dbh); 

  // create Dwoo object
  $dwoo = new Dwoo();
  
  // read template file
  $tpl = new Dwoo_Template_File('tmpl/books.tpl');
  
  // set up data storage
  $data = new Dwoo_Data();
  
  // assign query results 
  $data->assign('records', $records);
  
  // interpolate values into template
  // send interpolated result to output device
  $dwoo->output($tpl, $data);    
} catch (PDOException $e) {
  echo "Error: Could not execute query \"$sql\". " . $e->getMessage();    
  unset($dbh);
} catch (Exception $e) {
  echo "Error: " . $e->getMessage();    
}  
?>

Here’s a sample of the output:

Happy Holidays

One of the nicer things about Dwoo is that it allows you to merge templates scattered across different files. If you have a large project to manage, this makes it possible to split commonly-used pieces of the user interface into separate files and (re)use them wherever needed.

To better understand this, consider the following cutaway of a Web page:

Now, assume for a moment that the header and footer are stored as two separate templates, as shown below:

<!-- BEGIN header.tpl -->
<html>
  <head></head>
  <body>
    <table width="100%" border="1">

    <tr>
    	<td align="center"><a href="#">Home</a></td>
    	<td align="center"><a href="#">News</a></td>

    	<td align="center"><a href="#">Weather</a></td>
    	<td align="center"><a href="#">Hotels</a></td>
    	<td align="center"><a href="#">Dining</a></td>

    </tr>
    </table>
    <p />
    <h2>{$title}</h2>
    <p />

<!-- END header.tpl -->
<!-- BEGIN footer -->
    <table width="100%" align="center">
    <tr>
    	<td align="center"><font size="-2">&copy; {$year}. All rights reserved.</font></td>

    </tr>
    </table>
  </body>
</html>
<!-- END footer -->

Dwoo comes with an “include” plugin that can automatically include one or more templates files in another main template. So, for example, the template for the content area could import the header and footer templates via this plugin. Here’s how:

{include(file='header.tpl')}

    <!-- BEGIN main.tpl -->
    <table border="1">
    <tr>
    	<td valign="top">
    	<strong>{$headline}</strong>

    	<p />
    	{$content}
    	</td>
    	<td valign="top" align="center" width="25%">
    	<strong>Special Feature</strong><br />
    	{$feature}
    	</td>

    </tr>
    </table>
    <!-- END main.tpl -->

 
{include(file='footer.tpl')}

This arrangement makes it easy to modify each template independently of the others, making it possible to alter, say, just the top bar or the content layout.

You can now use a PHP script to assign variables to all of the template variables and render the composite output:

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

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

  // read template file
  $tpl = new Dwoo_Template_File('tmpl/main.tpl');

  // assign values to template variables
  $data = new Dwoo_Data();
  $data->assign('title', 'Welcome to London!');
  $data->assign('headline', 'Playing in the Park');
  $data->assign('content', 'It\'s a warm summer day, and Simon finds the lake in St. James Park too inviting for words...');
  $data->assign('feature', 'Tower Bridge - Snapshots from the Top');
  $data->assign('year', date('Y', mktime()));


  // interpolate values into template
  // send interpolated result to output device
  $dwoo->output($tpl, $data);
} catch (Exception $e) {
  echo "Error: " . $e->getMessage();      
}
?>

Here’s the output:

Another approach to accomplish the same result is to create template variables representing the header and footer, and assign rendered HTML content to these variables at run-time. To do this, you’d need to adjust the template for the content area to use template variables, instead of the “include” plugin, for the header and footer, as shown below:

{$header}

    <!-- BEGIN main.tpl -->
    <table border="1">
    <tr>

    	<td valign="top">
    	<strong>{$headline}</strong>
    	<p />
    	{$content}
    	</td>
    	<td valign="top" align="center" width="25%">

    	<strong>Special Feature</strong><br />
    	{$feature}
    	</td>
    </tr>
    </table>
    <!-- END main.tpl -->

{$footer}

To render the complete page, the PHP controller script must first render the header and footer templates, capture the result, interpolate this result into the main template and then render the complete page. This two-step process is accomplished with Dwoo’s get() function, which makes it possible to capture the interpolated result of the header and footer templates in PHP variables, and then assign them back to the main template. Here’s the code:

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

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

  // assign values to template variables
  $data = new Dwoo_Data();
  $data->assign('title', 'Welcome to London!');
  $data->assign('headline', 'Playing in the Park');
  $data->assign('content', 'It\'s a warm summer day, and Simon finds the lake in St. James Park too inviting for words...');
  $data->assign('feature', 'Tower Bridge - Snapshots from the Top');
  $data->assign('year', date('Y', mktime()));

  // interpolate values into sub-templates
  // read and assign rendered output to variables in main template 
  $data->assign('header', $dwoo->get(new Dwoo_Template_File('tmpl/header.tpl'), $data));
  $data->assign('footer', $dwoo->get(new Dwoo_Template_File('tmpl/footer.tpl'), $data));
  $dwoo->output(new Dwoo_Template_File('tmpl/main.tpl'), $data);
} catch (Exception $e) {
  echo "Error: " . $e->getMessage();      
}
?>

String Theory

Dwoo includes a full-featured plugin system that allows developers to add their own functionality to the template engine. A number of plugins are included as part of the source distribution; these provide ready made functionality that can be effectively used by designers and developers.

You’ve already seen the “escape” plugin in action in a previous example. It allows you to sanitize potentially-harmful input, by replacing special characters with HTML entities. However, it can often be tedious to apply this plugin to individual template variables and so, Dwoo offers an “auto_escape” plugin, which can be used to automatically escape an entire block of content. Here’s an example of it in use:

<html>
  <head></head>

  <body>
  {auto_escape on}
  {$html}
  {/auto_escape}
  </body>
</html>

Here’s a PHP script that demonstrates it in use:

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

try {
  // create Dwoo object
  $dwoo = new Dwoo();
  
  // read template file
  $tpl = new Dwoo_Template_File('tmpl/out.tpl');
  
  // string with special characters and HTML code
  $data = array();
  $data['html']= '<span id="ack">Welcome to Jack & Jill\'s humble abode.</span>';
 
  // interpolate values into template
  // send interpolated result to output device
  $dwoo->output($tpl, $data);
} catch (Exception $e) {
  echo "Error: " . $e->getMessage();      
}
?>

And here’s what the output looks like:

In the same vein, Dwoo also comes with a “strip_tags” plugin, which works much like PHP’s strip_tags() function, automatically stripping out all the code from a string. Here’s an example of how it can be used in a template:

<html>
  <head></head>

  <body>
  {strip_tags($html)}
  </body>
</html>

Here’s a script that attempts to interpolate HTML code into the template:

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

try {
  // create Dwoo object
  $dwoo = new Dwoo();
  
  // read template file
  $tpl = new Dwoo_Template_File('tmpl/out.tpl');
  
  // set date
  $data = array();
  $data['html'] = '<a href="http://www.google.com">Search</a>';
  
  // 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:

Dwoo offers a number of plugins for string wrapping, indentation and truncation. These plugins, unsurprisingly called “wordwrap”, “indent” and “truncate”, can come in very handy when dealing with large blocks of content. Here’s a template that demonstrates them in use:

<html>
  <head></head>

  <body>
  Word wrap:
  {indent(wordwrap($extended 8) 6)}        
    
  Truncation:
  {truncate($extended 20)}        
  </body>
</html>

Here’s a PHP script that sets the template variable to a long string of text, to demonstrate how these plugins work:

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

try {
  // create Dwoo object
  $dwoo = new Dwoo();
  
  // read template file
  $tpl = new Dwoo_Template_File('tmpl/out.tpl');
  
  // long string
  $data = array();
  $data['extended']= 'Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.';
      
  // interpolate values into template
  // send interpolated result to output device
  $dwoo->output($tpl, $data);
} catch (Exception $e) {
  echo "Error: " . $e->getMessage();      
}
?>

And here’s the output:

When it comes to formatting dates and times, Dwoo offers the “date_format” plugin, which makes it easy to format dates and times as per local conventions. This plugin takes two arguments: the date to be formatted, either as a human-readable date string or a UNIX timestamp; and a format string that specifies how the output should appear. In case you’re wondering, Dwoo makes use of strftime() format specifiers.

Here’s an example:


<html>
  <head></head>
  <body>
  {date_format $date "%d.%m.%Y"}
  <br/>
  {date_format $date "%B %d, %Y %I:%M %p"}
  </body>

</html>

Here’s the corresponding PHP script:

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

try {
  // create Dwoo object
  $dwoo = new Dwoo();
  
  // read template file
  $tpl = new Dwoo_Template_File('tmpl/out.tpl');
  
  // set date
  $data = array();
  $data['date'] = '14 July 2010 21:35';
  
  // interpolate values into template
  // send interpolated result to output device
  $dwoo->output($tpl, $data);
} catch (Exception $e) {
  echo "Error: " . $e->getMessage();      
}
?>

And here’s what the output looks like:

You’ll find a complete list of Dwoo plugins here.

And that’s about all we have time for at the moment. But this is just the tip of the iceberg, and in the second part of this article, I’ll look at some of the more advanced features available in Dwoo, including such goodies as template inheritance, user-defined plugins and filters, and framework integration. Come back soon for that…and until then, happy coding!

Copyright Melonfire, 2010. All rights reserved.

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

  1. panosru Says:

    Hello although this isn’t Zend Framework related article I integrated Dwoo into my ZF project and I like it so far but I’m bit confused on how I could access ZendX_jQuery view helper as I used to do in my layouts by typing echo $this->jQuery();

    As I understand so far I should create my own Dwoo plugin in order to do it right?

    Thanks in advance

  2. vvaswani Says:

    Nozavroni: Dwoo supports template inheritance and I’ll be discussing it in Part 2. So hang on for a little longer, and you should have an answer to your question.

  3. Nozavroni Says:

    Don’t get me wrong. This looks very well done, but what makes it special? Why would I use this rather than Smarty or one of the other ten million PHP template engines out there? Is it faster? Does it do anything special?

    Also, one thing I have noticed is missing from almost all PHP template engines is template inheritance. This is a feature that is immensely useful, but is missing almost always from PHP template engines. I want to be able to define a base template with "blocks" that I can replace in child templates. For example, take a look at Django’s template engine. Template inheritance is down-right sexy.

    Anyway…

  4. kthari85 Says:

    Have looked Dwoo some days before.
    Its similar to Smarty and fully following OOP concepts. Smarty 3 onwards will be fully compatible.
    Thanks for the wonderful article .