Kier, the lead programmer at vBulletin, learned PHP working on it. Previously he was a visual basic programmer (I think). I owe a LOT to vbulletin having learned from it, and I too wrote a template engine (me write a template engine, imagine that) that mimicked it. So trust me, stop.
vBulletin does templates from the database because it is a plug and play forum solution. It allows huge flexibility, but those templates have to be revisited almost any time there’s a code change, and BeyondCompare becomes your friend quickly when dealing with vb. :\
The main reason for the design choice is, once the system is installed the user doesn’t have to touch the files at all to effect a template change. But the effect is less than idea, and eval has a significant perform cost in that anything passing through it won’t be cached.
Anyway, what I learned after a long while is PHP is itself a template engine. There’s a reason the language allows for this.
<?php $var = "Hello" ?>
<html>
<body>
<?= $var ?>
</body>
</html>
(The <?= tag becomes standard as of PHP 5.4. Currently it is only available if short tags are enabled ).
The need to separate display logic from business logic is clear. That doesn’t mean display has no logic. Iterations over data collections, particularly if they are to be displayed tabular is a logic problem. But templating is not a huge problem. It’s small. Here’s a very basic template engine.
class Template extends ArrayObject {
protected $file = '';
public function __construct( $template ) {
parent::__construct();
$this->file = $template
}
public function __toString() {
extract ($this->getArrayCopy());
ob_start();
require($this->file);
return ob_get_clean();
}
}
How you would use this class. Well, first you’d start it and give it a template file.
$tmp = new Template( 'path/to/template/file.phtml');
I use the phtml extension for templates to distinguish them from non-template files.
Your business code feeds the template data as if it was an array - that’s the reason for extending ArrayObject.
$tmp['hello'] = 'Hello World';
Since the magic toString method was used the templates can nest.
$tmp['subtemplate'] = new Template('path/to/sub/template.phtml');
Extract allows the variables in this array to become local scope vars during the toString method. Here’s what the template file looks like
<html><body>
<?= $hello ?>
<?= $subtemplate ?>
</body></html>
When your code needs to evaluate the template, simply get the string value of the template. Any of these would work.
echo $tmp;
$evaledTemplate = (string) $tmp;
And there you have it. You can extrapolate from here. My own template system does more than this - result caching, resolving the path to templates, allowing users to make override template over system templates and so on, but this usage of extract() and output buffers lies at the heart of it all.