How to write a simple PHP template engine
Programming, Tutorials May 13th, 2007 - 6,958 viewsTemplates are a great way to separate logic from presentation in an application. There’s no shortage of template engines available for PHP, so why would you want to write another one? Well, because sometimes you don’t need a full fledged template language like smarty, and writing your own simple engine is as easy as it is useful.
When I wrote the impress wordpress plugin (which calculates some simple blog stats - see the bottom right of this page) I wanted to make it very easy for bloggers to configure. The obvious solution was to create a function for each statistic, and let the user call these functions from their template files. With this approach you end up with code like the following:
<p>So far I've written <?php echo impress_wordcount(); ?> words in <?php echo impress_posts(); ?> posts. <php echo impress_comments(); ?> comments have been posted, with a total of <php echo impress_comment_wordcount(); ?> words.</p>
It works, but it’s not that maintainable. And it’s a pain in the ass to write.
To simplify things I implemented a simple template engine that makes configuration a snap. Instead of strategically placing clumsy function calls within HTML, users call a single function that takes a specially formatted string, expands certain keywords (the template variables), and prints the result. The new code looks like this:
<p><?php impress("So far I've written :post_wordcount words
in :posts posts. :comments comments have been posted, with a total
of :comment_wordcount words."); ?></p>
Template variables begin with a colon, and consist of upper or lowercase letters and the underscore character. You could easily modify the code to allow additional characters in variable names, or to use different delimiters. The best part is that it took only a few lines of code to implement:
function impress( $format=':post_wordcount' ) {
echo preg_replace_callback('/:([a-zA-Z_] )/',
'impress_callback', $format);
}
function impress_callback( $matches ) {
$function = 'impress_'.$matches[1];
if(function_exists($function)) {
return call_user_func($function);
}
return $matches[0];
}
The preg_replace_callback() function is a savior here. It searches for text matching a pattern (the first argument), in the subject string (the third argument). For each match it invokes a callback function (the second argument), passing the matched text as an argument. The callback function first checks that a function exists for the matched template variable. If it exists, the function is called and the result is returned. Otherwise the template variable is returned unchanged.
I’ve used simple templates like this one in a number of projects. They’re simple to implement, and make configuration and maintenance a breeze.
May 14th, 2007 at 5:56 pm
I never had a situation where I’ve had to use preg_replace_callback() but now that I’ve seen an example, it seems like a perfect fit. I’ll keep this in mind whenever I have another project to tinker with.
May 16th, 2007 at 5:47 am
Thanks for that, i have been doing a lot of custom CMS development for clients lately and taking a similar approach although it is a little less tidy. I am definately going to give this approach go the next project i get.
May 17th, 2007 at 8:54 am
Very nice. Calling it a full template engine is a bit much. I like the flexibility of it, but what’s the performance impact of using preg_replace callback?
May 17th, 2007 at 9:02 am
No, it’s not really a full template engine, but it’s a start. And it’s a framework for how to build a complete solution. You can add functionality (arrays, looping constructs, etc.) to what’s there very easily using the same basic concepts.
Performance is good. I don’t have any stats to share with you, but I’d be interested in seeing them (if I have time I may even generate some). I can tell you that empirically, this sort of system is quick enough that it won’t be a bottleneck in any non-trivial application. Plus, you can always cache.
May 17th, 2007 at 7:30 pm
Nice job Mike. I love the simplicity of it.
June 24th, 2007 at 6:10 am
If you are interested; I have written a full template engine using the same approach. It includes the proposed caching and can be found here:
http://sjon.hortensius.net/blog/2006/01/simpletemplate-a-simple-templateparser-in-php
September 13th, 2007 at 2:17 am
hmmm templating is kinda convenient for me.
March 13th, 2008 at 11:22 am
I wanted a simple, fast (compiling) template engine in under 1000 lines of code, so I ended up writing my own! Check it out:
http://outline.mindplay.dk
It borrows from Smarty syntax, but the syntax is generally shorter, and it uses partial PHP syntax.
It has a multilevel cache, UTF-8 support, and all the usual commands, blocks, modifiers, etc. with a simple plugin architecture.
It has a minimal footprint of about 200 lines of code for the engine, under 1000 lines of code for the whole thing.
The engine has been in development for about a year now. If you’re looking for something small, but you want the convenience and legibility of a real template engine, take a look!