Templates can bring order and structure to a web site. In this article I
describe how templates can be used to control the page layout for a site
that is currently composed of many pages of static HTML, using a popular
template class and a new feature of PHP4.
But first, let's set the scene. There are two distinct motivations for
introducing templates:
- to separate functionality (PHP) from layout (HTML)
- to avoid duplication of repeated elements
Separating Functionality from Layout
The first motivation is the most-often discussed. Here the situation is that a
group of programmers produce PHP scripts to generate the content of a page,
whilst a group of graphic designers produce HTML and images to control the
appearance of the finished page. The idea is to allow both groups to work on
independent sets of files. The programmers can work on files that contain only
PHP, without concerning themselves with producing nicely presented HTML, whilst
the graphic designers can work on the layout of the page using their favourite
visual editor, without worrying about breaking any embedded code.
If you have read any tutorial on templates you will have seen the way this
works. Consider a simple page layout with a header, left-hand navigation bar
and a central area for the page content. A site like this might have the
following template files:
<!-- main.htm -->
<html>
<head><title>Example</title></head>
<body>
<table><tr><td>{HEADER}</td></tr>
<tr><td>{LEFTNAV}</td><td>{CONTENT}</td></tr>
</table>
</body></html>
<!-- header.htm -->
<img src="sitelogo.jpg">
<!-- leftnav.htm -->
<br><a href="foo">Foo</a>
<br><a href="bar">Bar</a>
You can see how the page is going to be built from these elements. The main
template controls the layout of the page, and the header and leftnav templates
control the common elements. The identifiers in braces {} act as placeholders
and get substituted by the template code. The key benefit is that the graphic
designers can edit these files as they see fit, adding fonts, colours and
graphics, or perhaps completely altering the layout of the page. They can edit
these files using normal HTML editing tools, even visual ones, because the files
are straight HTML (or HTML fragments) with no PHP code.
The PHP code goes in a separate file. This is the file that is actually called
up by the page URL. The web server uses the PHP engine to parse this file and
returns the result to the browser. Typically the PHP code produces the content
for the page dynamically, perhaps by querying a database or performing a
calculation. The file might be as follows:
<?php
// example.php
require('class.FastTemplate.php');
$tpl = new FastTemplate('.');
$tpl->define( array( 'main' => 'main.htm',
'header' => 'header.htm',
'leftnav' => 'leftnav.htm' ) );
// PHP code here to set $content to the required page content
$tpl->assign('CONTENT', $content);
$tpl->parse('HEADER', 'header');
$tpl->parse('LEFTNAV', 'leftnav');
$tpl->parse('MAIN', 'main');
$tpl->FastPrint('MAIN');
?>
We are using the popular FastTemplate class here, but the idea is the same for
many of the available template classes. You instantiate the class, tell it
where to find the template files, and tell it which template file corresponds
to which page element. You generate the content for the page, perhaps by
querying a database, and assign the result to an identifier. Then you parse
the template files one by one and the template class performs the required
substitutions for you. Finally you output the resulting string to the browser.
The key benefit is that this file is entirely PHP. There is no HTML in it. The
PHP programmer can now concentrate on writing the code to generate the page
content without having to worry about generating HTML to format it nicely for
the final page.
You can build a whole site using this technique and just these files. If the
code that generates the page content does so on the basis of a query string in
the URL, you could build a magazine, for example, using a URL like
http://www.foo.com/example.php?article=099
On the face of it, it looks like we have a second benefit too. In the example,
the left-hand navigation bar is in its own file. We can change how it looks on
every page on our site by editing this one template file.
Avoiding Duplication of Repeated Elements
"That's great", you might think. "I have a site full of largely static pages.
Now I can remove all the common elements from all my pages, which were a real
pain to keep updated, and using templates I can generate an easily maintainable
common layout". Not so fast. That phrase "largely static" is a problem.
Consider the example above. There is in fact only one page, example.php. The
reason it can generate a whole site is that it uses information in the URL to
construct the page dynamically, from information in a database.
Many of us do not run completely database-backed sites. We have sites composed
of many pages of static content, and we use PHP here and there to add some
functionality -- a search engine perhaps, or a feedback form. How to apply
templates to such a site?
The simple solution is to duplicate the PHP file above for each page, and to
set the content variable to the right page content in each one. For example,
if I had three pages; home, about and products, I might generate them with
three files that each looked like this:
<?php
// home.php
require('class.FastTemplate.php');
$tpl = new FastTemplate('.');
$tpl->define( array( 'main' => 'main.htm',
'header' => 'header.htm',
'leftnav' => 'leftnav.htm' ) );
$content = "<p>Welcome to my web site.</p>
<img src=\"demo.jpg\">
<p>I hope you like it.</p>";
$tpl->assign('CONTENT', $content);
$tpl->parse('HEADER', 'header');
$tpl->parse('LEFTNAV', 'leftnav');
$tpl->parse('MAIN', 'main');
$tpl->FastPrint('MAIN');
?>
There are three obvious problems with this: we have to duplicate this complex
template-related PHP code on every page, which is just as un-maintainable as
duplicating the common HTML elements; the file now has HTML mixed in with the
PHP again; and generating the content variable is going to be tedious because
we are going to have to escape special characters.
The key to solving the problem is to address the mixing of PHP and HTML, not by
removing the HTML from this file, but by removing most of the PHP.
A Template Framework for Static Sites
First, we write template files like the ones above for all the common elements
of our pages, and the overall page layout. We remove all the common elements
from our page files, leaving just the page content. Then we add three lines
of PHP to each page, like this:
<?php
<!-- home.php -->
<?php require('prepend.php'); ?>
<?php pageStart('Home'); ?>
<h1>Hello World</h1>
<p>Welcome to my web site.</p>
<img src="demo.jpg" alt="demo image">
<p>I hope you like it.</p>
<?php pageFinish(); ?>
?>
This has largely addressed the problems. There are just three lines of PHP in
the file, none of which contain any template-related code and are thus never
likely to need changing. And the HTML is outside the PHP tags and thus does
not need special characters escaping. We can easily add these three lines of
PHP to all our static HTML pages.
The require function includes a PHP file which contains all the necessary
template-related PHP code. The pageStart function sets up the template object
(and sets a page title), and the pageFinish function parses the template and
generates the output for the browser.
How is this done? Why does the HTML in this file not get sent to the browser
before the pageFinish function gets called? The answer is a new feature
introduced in PHP4 that allows output destined for the browser to be captured
in a buffer. A look at the prepend.php file shows how this works:
<?php
require('class.FastTemplate.php');
function pageStart($title = '') {
GLOBAL $tpl;
$tpl = new FastTemplate('.');
$tpl->define( array( 'main' => 'main.htm',
'header' => 'header.htm',
'leftnav' => 'leftnav.htm' ) );
$tpl->assign('TITLE', $title);
ob_start();
}
function pageFinish() {
GLOBAL $tpl;
$content = ob_get_contents();
ob_end_clean();
$tpl->assign('CONTENT', $content);
$tpl->parse('HEADER', 'header');
$tpl->parse('LEFTNAV', 'leftnav');
$tpl->parse('MAIN', 'main');
$tpl->FastPrint('MAIN');
}
?>
The pageStart function instantiates a template and sets it up, then turns on
output buffering. Now, all HTML from the page itself is captured in the buffer.
The pageFinish function extracts the buffered content and uses it to define the
template object's content before parsing the templates and outputting the
finished page.
That's it. Write template files containing HTML fragments to define your site's
common elements. Delete all the common page layout stuff from all your pages
and replace with three lines of PHP you will never need to change. Put the
FastTemplate class file and the prepend PHP file in your include path. Now you
have a web site where the page layout is centrally controlled, enhancing
reliability and maintainability and making site-wide changes easy.
Resources
Zipped archive of the files in this article,
including a working example site
and with more comments in the code than shown here.
The FastTemplate class can be found at
http://www.thewebmasters.net/. The
latest version is 1.1.0 and there is a small patch to apply to ensure correct
operation with PHP4. Alternatively, the zipped archive above includes the class
already patched.
Acknowledgments
The functions in prepend.php are based on ones (without output buffering)
described in "Professional PHP Programming" by Harish Rawat, et al.
-- Matthew