<?
/**
* Dextep - simple template engine for PHP.
* Version 1.0 (2010-11-02)
* 2010 (c) Alexey Burkov
* Licensed under the terms of the BSD License.
*/
class Dextep{
var $VARIABLE = '(?:\$|%|\$%)[\w]+(?:\.%?[\w]+)*'; // regexp for variables
var $OPERATOR = '[\s!|><&)(:^?%.\/*+=-]'; // regexp for operators
var $NUMBER = '[0-9.]+?'; // regexp for numbers
var $pool; // stack for checking inclusions
var $vars; // storage for passed variables
/**
* Configuration variables
*/
var $cacheEnabled = true; // true = caching enabled, false = disabled
var $cachePath = 'cache/'; // path to cache folder
var $templatePath = 'templates/'; // path to templates folder
var $templateExt = '.html'; // templates extension
/**
* Object constructor
*/
function template(){
$this->vars = array();
}
/**
* Returns template variable with name $key
*/
function getVar($key){
$value = &$this->vars;
foreach(explode('.', $key) as $v){
if (!isset($value[$v])) return null;
$value = &$value[$v];
}
return $value;
}
/**
* Sets template variable with name $key to $value
*/
function setVar($key, $value){
$vars = &$this->vars;
$parts = explode('.', $key);
for($i=0, $len=count($parts); $i<$len-1; $i++){
if (!isset($vars[$parts[$i]]) || !is_array($vars[$parts[$i]])) $vars[$parts[$i]] = array();
$vars = &$vars[$parts[$i]];
}
$vars[$parts[count($parts)-1]] = $value;
}
/**
* Executes and returns template $name
* If $recache = true then overrides cache (if caching is used)
*/
function getTemplate($name, $recache = false){
$cachefile = $this->cachePath . str_replace('/','-',$name).'.cache';
if (!$this->cacheEnabled || $recache || !file_exists($cachefile)){
if (!$template = $this->preprocessing($name)) return false; // parse comments and {include}
$template = 'ob_start();' . PHP_EOL . 'echo \''.strtr($template, array("\\"=>"\\\\","'"=>"\'")).'\';' . PHP_EOL . '$result = ob_get_clean();' . PHP_EOL;
$template = preg_replace_callback('/{@?(('.$this->OPERATOR.'|'.$this->NUMBER.'|'.$this->VARIABLE.')+)}/U', array($this, 'callbackExpression'), $template); // parse expressions
$template = preg_replace_callback('/{foreach\s+('.$this->VARIABLE.')\s+as\s+(?:%([\w]+)\s*=>\s*)?%([\w]+)\s*}/U', array($this, 'callbackForeach'), $template); // parse {foreach}
$template = preg_replace_callback('/{for\s+var\s*=\s*(%[\w]+)\s+from\s*=((?:'.$this->OPERATOR.'|'.$this->NUMBER.'|'.$this->VARIABLE.')+)\s+to\s*=((?:'.$this->OPERATOR.'|'.$this->NUMBER.'|'.$this->VARIABLE.')+)\s+step\s*=\s*(-?'.$this->NUMBER.')\s*}/U', array($this, 'callbackFor'), $template); // parse {for}
$template = preg_replace_callback('/{(else)?if\s+(('.$this->OPERATOR.'|'.$this->NUMBER.'|'.$this->VARIABLE.')+)}/U', array($this, 'callbackIf'), $template); // parse {if}, {elseif}
$template = preg_replace('/{else\s*}/', '\';' . PHP_EOL . '} else {' . PHP_EOL . 'echo \'', $template); // parse {else}
$template = preg_replace('/{\/(foreach|if|for)\s*}/', '\';' . PHP_EOL . '}' . PHP_EOL . 'echo \'', $template); // parse closing tags
$template = preg_replace('/{\s*lb\s*}/', '{', $template); // parse {lb}
$template = preg_replace('/{\s*rb\s*}/', '}', $template); // parse {rb}
if ($this->cacheEnabled) file_put_contents($cachefile, $template); // save cache
} else $template = file_get_contents($cachefile); // load from cache
eval($template);
return isset($result) ? $result : false;
}
/**
* Processes error. $str - error message.
*/
function error($str){
die($str);
}
/**
* Parses template comments and {include} tags
*/
function preprocessing($name, $included = array()){
$tplfile = $this->templatePath . $name . $this->templateExt;
if (!file_exists($tplfile)) $this->error('Template «<code>'.$tplfile.'</code>» not found.');
$template = file_get_contents($tplfile);
if ($template){
$template = preg_replace('/{\*(.*)\*}/sU', '', $template); // parse comments
$this->pool = count($included) ? $included : array($name); // use pool to check for self-inclusions
$template = preg_replace_callback('/{include\s+([\'"]?)([\w\/]+)\1\s*}/U', array($this, 'callbackIncludes'), $template); // parse {include}
}
return $template;
}
/**
* Callback functions used with regular expressions
*/
function callbackIncludes($param){
$name = $param[2];
$pool = $this->pool;
if (in_array($name, $this->pool)) $this->error('Self-inclusion of «<code>'.$name.'</code>» template.');
$result = $this->preprocessing($name, array_merge($this->pool, array($name)));
$this->pool = $pool;
return $result;
}
function callbackForeach($param){
$result = '\';' . PHP_EOL;
$result .= 'foreach('.$this->getVariableStr($param[1]).' as '.(strlen($param[2]) ? '$_'.$param[2].' => ' : '').'$_'.$param[3].'){' . PHP_EOL;
$result .= 'echo \'';
return $result;
}
function callbackIf($param){
$result = '\';' . PHP_EOL;
$result .= ($param[1]=='else'?'}else':'').'if('.preg_replace_callback('/'.$this->VARIABLE.'/', array($this, 'callbackExpressionVariables'), $param[2]).'){' . PHP_EOL;
$result .= 'echo \'';
return $result;
}
function callbackFor($param){
$var = $this->getVariableStr($param[1]);
$step = $param[4];
$result = '\';' . PHP_EOL;
$result .= 'for('.$var.'=('.preg_replace_callback('/'.$this->VARIABLE.'/', array($this, 'callbackExpressionVariables'), $param[2]).');'.$var.($step>0?'<=':'>=').'('.preg_replace_callback('/'.$this->VARIABLE.'/', array($this, 'callbackExpressionVariables'), $param[3]).');'.$var.'='.$var.'+('.$step.')){' . PHP_EOL;
$result .= 'echo \'';
return $result;
}
function callbackExpression($param){
$result = preg_replace_callback('/'.$this->VARIABLE.'/', array($this, 'callbackExpressionVariables'), $param[1]);
return $param[0]{1} == '@' ? '\';'.$result.';echo \'' : '\'.('. $result .').\'';
}
function callbackExpressionVariables($param){
return $this->getVariableStr($param[0]);
}
function getVariableStr($s){
$global = $s{0} == '$' ? true : false;
if ($global) $s = substr($s, 1);
$result = $global ? '$this->vars' : '';
$p = explode('.', $s);
for($i=0; $i<count($p); $i++) $result .= $p[$i]{0} == '%' ? (!$i && !$global ? '$_'.substr($p[$i], 1) : '[$_'.substr($p[$i], 1).']') : "['".$p[$i]."']";
return $result;
}
}
?>