Template engine: transform template syntax to PHP
Posted: Wed Jan 23, 2008 1:29 pm
For my current site I'm developing a template engine. Let me explan the basics: my template syntax consist out of:
Looks a bit like TemplatePower, which I would have used if it did support cache. On the other hand, writing your own is a great experience. I like the caching system of PHPBB, where template syntax is converted to PHP and then saved in cache. Exactly what I want.
But I've ran into the following problem: how to code nested blocks in PHP? I tried:
This works, but it looks all messy and ugly to me. It uses a lot of arrays and I'm quite sure there has to be a more efficient and cleaner way to program this, because now the script has to remember the blocks structure (who the parents are). Hopefully someone has an idea how to code it properly?
My current _parse() of the template class (I excluded all the non-block parsing parts):As you can see, it uses create_function (with global $tpl (we assume $tpl->new Template()) ), which is like eval = evil, so I'm not happy with what I've coded here, although it might work if I work it out completely.
Code: Select all
{variables}
{include: file}
{* comments *}
{block: blockname}
[i]block-content[/i]
{blockname/}
But I've ran into the following problem: how to code nested blocks in PHP? I tried:
Code: Select all
for($blockname = 0; $blockname < $this->_blocks['blockname'][$parent1iteration][$parent2iteration]; $blockname++) ...
My current _parse() of the template class (I excluded all the non-block parsing parts):
Code: Select all
/**
* Parses content, $this->_content is the raw template file
*/
private function _parse() {
/**
* Parse blocks
*/
$this->_content = preg_replace_callback($this->_regex['block'], array('self', '_parseBlock'), $this->_content);
/**
* Put files block structure on top, because we have to remember our blockstructure
*/
$structure = '/*block structure*/ $this->_blocks = array_merge($this->_blocks, array(';
foreach($this->_blocks as $block => $childs) {
$structure .= '\'' . $block . '\'=>\'' . $childs . '\',';
}
$structure .= '));';
$this->_content = $this->_phpOpen . $structure . $this->_phpClose . $this->_content;
}
/**
* Parses blocks
* @param $blockContent
* @param $root
* @return parsed block
*/
public function _parseBlock($blockContent, $root = NULL) {
$block = $blockContent[1];
if(!isset($root)) {
$root = $block;
}
else {
$root .= '.' . $block;
}
$this->_blocks[$block] = $root;
/**
* Find child blocks (continue while match found)
*/
$childContent = $blockContent[2];
if(preg_match($this->_regex['block'], $childContent) == 1) {
$childContent = preg_replace_callback($this->_regex['block'], create_function('$matches','global $tpl; return $tpl->_parseBlock($matches, \'' . $root . '\');'), $childContent);
}
/**
* Now make the real code change, first we get the root needed by for-statements
*/
$blockRoot = explode('.', $this->_blocks[$block], -1);
$blockRoot = (!empty($blockRoot) ? '[$' . implode('][$', $blockRoot) . ']' : '');
$blockRoot = '$this->_blockCount[\'' . $block . '\']' . $blockRoot;
return $this->_phpOpen . 'for($' . $block . ' = 0; $' . $block . ' < ' . $blockRoot . '; $' . $block . '++) {' . $this->_phpClose . $block . $childContent . $this->_phpOpen . '} /*end ' . $block .' loop*/' . $this->_phpClose;
}