11.3. Template Systems
Template systems are PHP components that let you separate application logic from display logic, and offer a simpler template format than PHP itself.
It is ironic that PHP, which essentially started out as a template language, is used to implement template systems. But, there are good reasons for doing this besides the code/presentation separation, such as giving web designers a simpler markup format they can use in their page authoring tools, and developers greater control over page generation. For example, a template system can automatically translate text snippets to another language, or fill in a form with default values.
A vast number of template systems are available for PHP. This is caused by the fact that along with database abstraction layers, template systems are one of the PHP components that arouse the strongest feelings and little will for compromise in developers. As a result, many people have written their own template system, resulting in a wonderful diversity and lack of standardization.
11.3.1. Template Terminology
Before you dive into the various template systems, you may want to familiarize yourself with the template lingo (see Table 11.1).
Table 11.1. Template GlossaryWord | Meaning |
---|
Template | The output blueprint; contains placeholders and blocks. | Compile | Transforming a template to PHP code. | Placeholder | Delimited string that is replaced during execution. | Block or Section | Part of a template that may be repeated with different data. |
11.3.2. HTML_Template_IT
The first PEAR template system you will familiarize yourself with is HTML_Template_IT, or just IT. This is the most popular PEAR template package, but it is also the slowest because it parses templates on every request and does not compile them into PHP code.
Tip
The HTML_Template_Sigma package provides an API that is compatible with HTML_Template_IT, but compiles templates into PHP code.
11.3.2.1 Placeholder Syntax
IT uses curly braces as placeholder delimiters, like this:
4
<head><title>{PageTitle}</title></head>
This is the most common placeholder syntax, so chances are a template using only placeholders will actually work with different template packages.
11.3.2.2 Example: Basic IT Template
This example is "Hello World" with HTML_Template_IT:
<?php
require_once "HTML/Template/IT.php";
$tpl = new HTML_Template_IT('./templates');
$tpl->loadTemplateFile('hello.tpl');
$tpl->setVariable('title', 'Hello, World!');
$tpl->setVariable('body', 'This is a test of HTML_Template_IT!');
$tpl->show();
First, you create an HTML_Template_IT object, passing the template directory as a parameter. Next, the template file is loaded and some variables are set. The variable names correspond to placeholders in the template file, so the {title} template placeholder is replaced with the value of the "title" variable. Finally, the show() method does all the substitutions and displays the template output.
This template file is used in this example:
<html>
<head>
<title>{title}</title>
</head>
<body>
<h1>{title}</h1>
<p>{body}</p>
</body>
</html>
Figure 11.1 shows the result.
11.3.2.3 Block Syntax
For blocks, IT uses HTML begin/end comments like this:
<!-- BEGIN blockname -->
<li>{listitem}
<!-- END blockname -->
Blocks may be nested, but it is important that you start processing at the innermost block and work your way out.
11.3.2.4 Example: IT With Blocks
First, install HTML_Template_IT:
$ pear install HTML_Template_IT
downloading HTML_Template_IT-1.1.tgz ...
Starting to download HTML_Template_IT-1.1.tgz (18,563 bytes)
......done: 18,563 bytes
install ok: HTML_Template_IT 1.1
This example uses blocks to implement a simple foreach-like loop in the template:
<?php
require_once "HTML/Template/IT.php";
$list_items = array(
'Computer Science',
'Nuclear Physics',
'Rocket Science',
);
$tpl = new HTML_Template_IT('./templates');
$tpl->loadTemplateFile('it_list.tpl');
$tpl->setVariable('title', 'IT List Example');
foreach ($list_items as $item) {
$tpl->setCurrentBlock("listentry");
$tpl->setVariable("entry_text", $item);
$tpl->parseCurrentBlock("cell");
}
$tpl->show();
This example sets up the IT object like the previous one, but calls setCurrentBlock() that specifies to which block the following setVariable() call applies. When parseCurrentBlock() is called, the block is parsed, placeholders are substituted, and the result is buffered until the template is displayed.
This is how the block template appears
<html>
<head>
<title>{title}</title>
</head>
<body>
<h1>{title}</h1>
<ul>
<!-- BEGIN listentry -->
<li>{entry_text}</li>
<!-- END listentry -->
</ul>
(End of list)
</body>
</html>
Figure 11.2 shows the results.
Finally, IT lets you include other template files anywhere in your template, like this:
<!-- INCLUDE otherfile.tpl -->
In this block example, you could substitute the block contents with just an include tag, and HTML_Template_IT would include that file for every iteration of the block.
By using includes carefully, you can structure your templates so you obtain reusable sub-templates.
11.3.3. HTML_Template_Flexy
The next template package is HTML_Template_Flexy, or just Flexy. Even though pure placeholder templates written for IT will work out-of-the-box with Flexy, these two template packages are very different.
First, Flexy operates on objects and object member variables instead of variables that are in turn stored in associative arrays as with IT. This is not a big difference in itself, but Flexy has the advantage that you can give it any object, of any class, and your template can access its public member variables.
11.3.3.1 Example: Basic Flexy Template
Here is a "Hello, World!" example with Flexy:
<?php
require_once 'HTML/Template/Flexy.php';
$tpldir = 'templates';
$tpl = new HTML_Template_Flexy(array(
'templateDir' => 'templates',
'compileDir' => 'compiled',
));
$tpl->compile('hello.tpl');
$view = new StdClass;
$view->title = 'Hello, World!';
$view->body = 'This is a test of HTML_Template_Flexy';
$tpl->outputObject($view);
A little more code is required to set up Flexy because you need to specify both the template directory and compile directory. The compile directory is where the compiled template files are stored. This directory must be writable by the web server. By default, the compile directory is relative to the template directory.
Next, the hello.tpl template is compiled. You should notice that this is the same template as in the first IT example; this works because the template contains only two simple placeholders.
Compilation is time-consuming, but is done only once or whenever the template file changes. As a result, you will notice that the first time you load this page, it takes a long time. Subsequent page loads are much faster.
When a template is compiled, the compiled version is placed in compileDir. In the previous example, this is the "compiled" directory relative to the current directory. This directory must be writable by the web server, because templates will be compiled on demand by PHP when a user hits the page.
Finally, an object holding view data is created and passed to the outputObject() method, which executes the template and prints the output.
11.3.3.2 Example: Flexy with Blocks
This example corresponds to the "IT with Blocks" example:
<?php
require_once 'HTML/Template/Flexy.php';
$tpldir = 'templates';
$tpl = new HTML_Template_Flexy(array(
'templateDir' => 'templates',
'compileDir' => 'compiled',
));
$tpl->compile('flexy_list.tpl');
$view = new StdClass;
$view->title = 'Flexy Foreach Example';
$view->list_entries = array(
'Computer Science',
'Nuclear Physics',
'Rocket Science',
);
$tpl->outputObject($view);
This time, the template file is different because it is using more than just placeholders and is no longer compatible with IT:
<html>
<head>
<title>{title}</title>
</head>
<body>
<h1>{title}</h1>
<ul>
{foreach:list_entries,entry_text}
<li>{entry_text}
{end:}
</ul>
(End of list)
</body>
</html>
If you compare the PHP code in this example with the corresponding IT example, you see that all the hassle of parsing blocks is gone. This is because the template is compiled; instead of dealing with flow-control on its own, Flexy leaves this to PHP's executor. Look at the PHP file generated by the Flexy compiler:
<html>
<head>
<title><?php echo htmlspecialchars($t->title);?></title>
</head>
<body>
<h1><?php echo htmlspecialchars($t->title);?></h1>
<ul>
<?php if (is_array($t->list_entries) || is_object($t >list_entries)) foreach
($t->list_entries as $entry_text) {?>
<li><?php echo htmlspecialchars($entry_text);?>
<?php }?>
</ul>
(End of list)
</body>
</html>
11.3.3.3 Flexy Markup Format
So far, you have seen examples of placeholders and the {foreach:} construct in Flexy. Table 11.2 gives a full list of the constructs that Flexy supports.
Table 11.2. Flexy Markup TagsTag | Description |
---|
{variable} {variable:h} {variable:u} | This is the regular placeholder. By default, placeholders are encoded by htmlspecialchars(). The :h modifier disables this to pass the raw value through, while the :u modifier encodes with urlencode() instead. | {method()} {method():h} {method():u} | This tag calls a method in the view object and uses the return value. As with variables, htmlspecialchars() is used by default, and you can use the :h and :u modifiers. | {if:variable} {if:method()} {if:!variable} {if:!method()} | If statements are available, but only with Boolean tests no arbitrarily complex logic. ifs are limited to variables, method calls, and negation. | {else:} | The else tag must be used with {If:}. | {end:} | The {end:} tag is used to finish both {foreach:} and {If:}. | {foreach:arr,val} {foreach:arr,ind,val} | Corresponds to PHP's foreach. The first variation iterates over arr and assigns each element in turn to val. The second variation assigns the array index to ind as well. |
11.3.3.4 Flexy HTML Attribute Handling
One of the interesting things about Flexy is how it handles HTML/XML elements and attributes in the template. To give you an example, here is the last example again with the template changed to use a Flexy HTML/XML attribute for controlling a block:
<html>
<head>
<title>{title}</title>
</head>
<body>
<h1>{title}</h1>
<ul>
<li flexy:foreach="list_entries,text">{text}</li>
</ul>
(End of list)
</body>
</html>
The {foreach:} construct is gone; it is replaced by an attribute to the element that is being repeated: <li>. This looks a bit like XML namespaces, but it is not; the Flexy compiler removes the flexy:foreach attribute during compilation, and generates the same PHP code as the {foreach:} variant. The compiled version of this template looks like this:
<html>
<head>
<title><?php echo htmlspecialchars($t->title);?></title>
</head>
<body>
<h1><?php echo htmlspecialchars($t->title);?></h1>
<ul>
<?php if (is_array($t->list_entries) || is_object($t- >list_entries)) foreach
($t->list_entries as $entry_text) {?><li><?php echo htmlspecialchars($entry_text);?><
/li><?php }?>
</ul>
(End of list)
</body>
</html>
The XML/HTML attributes supported by Flexy are outlined in Table 11.3.
Table 11.3. Flexy HTML/XML AttributesAttribute | Description |
---|
Flexy:if="variable" flexy:if="method()" flexy:if="!variable" flexy:if="!method()" | This is a simplified {if:}. The condition applies to the XML/HTML element and its subelements, and there is no {else:}. If the test is false, the current element and all its child elements are ignored. | flexy:start="here" | The flexy:start attribute can be used to ignore everything outside the current element. This is useful if you have sub-templates but still want to be able to view or edit them as complete HTML files. | flexy:startchildren="here" | Similar to flexy:start, but ignores everything to and including the current element. | flexy:ignore="yes" | Ignores the current element and all child elements. It's useful to put mock-up data in templates that are edited with some visual web-design tool. | flexy:ignoreonly="yes" | Ignores all child elements, but not the current element. |
11.3.3.5 Flexy HTML Element Handling
Finally, Flexy can parse HTML form elements and fill them in with correct data. This makes it easy to create a form template in some web-design tool without having to dissect the template before using it on your site.
Flexy handles the following four HTML elements (see Table 11.4).
Table 11.4. HTML Elements<form name="xxx"> | <input name="xxx"> | <select name="xxx"> | <textarea name="xxx"> |
When Flexy finds any of these HTML elements in the template, the element is replaced by PHP code that outputs the element with the right attributes:
<html>
<head><title>{title}</title></head>
<body bgcolor=white>
<form name="myform">
{user_label} <input type="text" name="user">
<br>
{pw_label} <input type="password" name="pw">
</form>
</body>
</html>
In this template, the <form> and <input> elements will be replaced by Flexy, with parameters filled in.
|