Off Topic:
Rereading my posts I’m testier than normal. My apologies - some personal issues going on
Eval is expensive in terms of CPU. Further, eval code can’t be cached by APC. Finally eval can cause signifcant security problems. It has it’s place, but it’s not something I’d recommend.
The reason your templates wouldn’t work with inline javascript is, well, javascript uses {} in it’s syntax - constantly. The str_replace above would cause the inline javascript to try to be eval’ed as PHP code - probably resulting in a crash.
Stripped of all extra bells and whistles, this is my template engine
class Template extends ArrayObject {
public $template;
protected $output;
public function __construct( $path, $array = array() ) {
parent::__construct( $array );
$this->template = $path;
}
public function __toString() {
if (is_null($this->output) ) {
$this->output = $this->parseTemplate();
}
return $this->output;
}
protected function parseTemplate() {
extract( $this->getArrayCopy() );
ob_start();
if (!include($template) {
throw new Exception('Unable to read template file '.$template);
}
return ob_get_clean();
}
}
Now, outside this class, say we need to display a table from the database
// create the template
$resultTable = new Template('myTableTemplate.phtml');
// using pdo, fetch some data
$s = $pdo->query("SELECT displayname, username, phone FROM users");
$resultTable['users'] = $s->fetchAll( PDO::FETCH_ASSOC );
Meanwhile, let’s look at myTableTemplate.phtml
<table>
<thead>
<tr>
<th>Display Name</th>
<th>User Name</th>
<th>Phone</th>
</tr>
</thead>
<tbody>
<?php if(count($users) == 0): ?>
<tr>
<td colspan="3" class="empty">No results</td>
</tr>
<?php else: foreach($users as $user ): ?>
<tr>
<td><?= $user['displayname'] ?></td>
<td><?= $user['username'] ?></td>
<td><?= $user['phone'] ?></td>
</tr>
<?php endif ?>
</tbody>
</table>
What extract() does is it maps the contents of the template to the local scope. Hence $resultTable[‘users’] becomes $users inside the template.
Continuing on with our example controller code above, displaying a table by itself isn’t a full html response. This is why the __toString method was used on my templates to return their contents – it allows simply, powerful, and effective template nesting.
$response = new Template('index.phtml', array( 'users' => $resultTable ));
echo $response;
exit;
The index.phtml template is…
<html>
<body><h1>My users are</h1>
<?= $users ?>
</html>
Since the two templates have different scopes the repeat of ‘users’ as a variable isn’t a problem.
Now, all that said, it’s just a starting point. My actual template system has additional methods for assembling common html objects, negotiating the http connection correctly, compressing the output, caching it, finding templates within the template system without requiring the programmer to type in the full path, and finally allowing project specific templates to pre-empt system templates at include time. Hopefully I’ll be able to release it soon as part of it’s larger package.
One testament to this system’s flexibility is that despite how much I hate smarty (and I really hate it), I can use this within smarty on my legacy projects. Again, the __toString method is the key - when smarty template engine asks a template object for it’s string representation it happily returns the html string.
Twig is cow manure to smarty’s horse manure. Neither is pleasant - both suffer from the same brain dead starting assumption that PHP is somehow unable to parse HTML or that designers who can’t grok short tags can somehow grok another markup entirely.