Templating & PHP
There are a lot of php templating solutions out there, but this one's mine.
It is based on an eye-opening article by Brian Lozier found here, with some additional functions (loading extra css/js) and some functions removed (caching). To be honest, all the clever bits are copied pretty much directly from Mr Lozier's solution. So it might be worth your while to check out his site.
Back to the engine. If you're interested in a quick and dirty templating solution, this is it. If you're looking for a fast, sound, infinitely extendible templating engine this is also it.
So what's the big idea?
PHP = A Templating Engine
Why yes indeed it is. Thing is, with all the OOP, jpeg generation and database abstraction layers you tend to forget these things. I did anyway. The advantages of using PHP for your templates are obvious:
- It's fast.
- You don't need to learn a new language (or, if you're programming your own template engine, you don't have to define a new language and then figure out how to parse it).
- It's powerful.
It has a few downsides too, but no more than any other php templating solution. The only serious issue I can think of is that it might be too powerful: your template can still contain all the php you want. Does this mean things get muddy? Well... Only if you let them. With great power etc etc. Whether you're willing to give this power to the template writer is another thing.
So what does the Template class do?
- Let the user set variables.
- Insert the content in the template & parse it.
Both steps are made ridiculously easy by php. The first step is simply storing values in an associative array. The second step is solved by using the extract() command, which takes an associative array and loads it into the local namespace, using the keys for variable names and the values for values. Basically it turns $ar = array("foo" => "bar") into $foo = "bar"
Here's what it looks like in code:
<?php
/**
* A simple templating class using PHP as/instead of a 'native scripting language'
* Based on an article by Brian Lozier (brian@massassi.net)
*/
class Template
{
private $vars = array(); // Holds all the template variables
private $file; // The template file loaded
public function __construct($file = null)
{
$this->file = $file;
}
/*
* Set a template variable.
*/
public function set($name, $value)
{
$this->vars[$name] = $value;
}
/*
* Open, parse, and return the template file.
* @param $file string the template file name
*/
public function fetch($file = null)
{
if(!$file) $file = $this->file;
extract($this->vars); // Extract the vars to local namespace
ob_start(); // Start output buffering
include($file); // Include the file
return ob_get_clean(); // Return the contents of the buffer
}
}
?>
With a grand total of 7 lines needed for actual execution, this is certainly quick, but is it dirty? No. The beauty of it all is that the extract() command acts on the local namespace, in this case the class's fetch(). This means you can use variables like $home, $username, $db etc in your templates without
interfering with any other variables of the same name, defined in some more global namespace.
Another thing some people might be bothered by is the output buffering. Very few people seem to realise what a great tool php output buffering is. It's the fastest way to concatenate a large number of strings, consistent use of output buffering means you can set header & cookie information anywhere in your script and - most importantly - you can make mulitple nested output buffers without (serious) loss of performance.
Here's what php.net says about it:
Output buffers are stackable, that is, you may call ob_start() while another ob_start() is active. Just make sure that you call ob_end_flush() the appropriate number of times. If multiple output callback functions are active, output is being filtered sequentially through each of them in nesting order
This means that the fetch method's use of output buffering is totally legit. It creates a new (possibly nested) output buffer, fills it, and then deletes it again. No worries
Here's how you use it:
In two easy steps
- Create a template: basically this is just a .php file containing lots of html and a few instructions like
<?=$content?>in place of real content. - In the file the user requests, do the following:
- Create a Template object from the template file
- Fetch the content
- Add the content to the template, something like:
$tpl->set('content', $content); - Compile & display the template:
echo $tpl->fetch();
I like to combine these two steps, resulting in a page that looks like this:
<?php
// Load the template class
require_once('class.Template.php');
// Load a template
$tpl = new Template('template.whitebox.php');
// Content starts here:
ob_start();
?>
<h1>The truth about being awesome</h1>
<p>Remember back in the day? Oh boy oh boy oh boy did we have content then. Mm-mmm!
None of this spoot-faced half-assed smegging web two dot oh. Oh no. No sirree.
Back then, we <strong>never</strong> made our own entertainment.</p>
<p>Not us.</p>
<?php
// Content ends here
// Send the contents of the output buffer to the template
$tpl->set('content', ob_get_clean());
// Display!
echo $tpl->fetch();
?>
Is that all?
Yes and no. Some minor adjustments improve things a lot.
Something present in the original version which I removed for simplicity is the capability to add templates to templates. In its simplest form this means replacing the set() method by this one:
<?php
/*
* Set a template variable.
*/
public function set($name, $value)
{
$this->vars[$name] = ($value instanceof Template) ? $value->fetch() : $value;
}
?>
If you're a stickler for that kind of thing you might want to do a more thorough check to see if the object is actually an instance of the Template class. That aside, this is a very useful feature and I left it in in the version I use.
Another thing you might want to do is make a caching function that stores rendered templates, reducing load on the server. I decided against this, on the principle that if server load ever became an issue I could just store the rendered version as a html file manually. Alternatively, the caching of certain pages could be handled by a content management system, and should not (necessarily) be the responsibility of the template class.
And that's just about it. Hope it works for you. Someday soon I'll make a comments script and you'll be able to let me know :)
Jul 4th, 2008
Comments
No comments yet! Feel free to post some using the form below.
If you wish to add code to your comment you can use code tags, like this: <code class="php">yourCodeHere</code>.
Quite a large number of languages are supported, although I can't guarantee it'll be pretty. Inside the code tags you can use any characters except for the string "</code>".