很简单的一个php轻量级模板解析类。速度很快官方网站http://www.phpguru.org/
<?php
/**
* This is (yet) another templating class for PHP5. Called Bob. No real reason for the name.
*
* $tpl = new Bob('myTemplate.html'); // Defaults to current directory
* $tpl->debug = 1;
* $tpl->Display();
*
* If you're having trouble, thes first thing to do is turn off caching -
* it will only confuse you. And at the same time turn on debug messages.
* You can do these by using:
*
* $tpl->useCache = false;
* $tpl->debug = true;
*/
/**
* The template class
*/
class Bob
{
/**
* Show or hide debug messages
*/
public $debug = false;
/**
* Whether to use the cache or not
*/
public $useCache = true;
/**
* The path to store cached templates in
*/
public $cachePath = '/tmp';
/**
* The variables
*/
private $variables;
/**
* The constructor
*
* @param string $template The name of the template to use
*/
public function __construct($template)
{
$this->template = $template;
$this->timings['start'] = microtime();
$this->Debug('Template filename: ' . $template);
}
/**
* Sets the path to store cached templates
*/
public function SetCachePath($path)
{
$this->cachePath = $path;
$this->Debug('Using cache path: ' . $path);
}
/**
* The callback handler for dynamic {php ...} blocks
*
* @param array $matches The matches found by the regex
*/
public function phpCallbackHandler($matches)
{
eval('$result = ' . $matches[1] . ';');
return $result;
}
/**
* The callback for handling if conditions
*
* @param array $matches The matches found by the regex
*/
public function ifCallbackHandler($matches)
{//处理模板里的嵌套if结构,只能处理简单的if
if (!empty($this->variables[trim($matches[1])])) {
return $matches[2];
} elseif (!empty($matches[3])) {
return $matches[3];
} else {
return '';
}
}
/**
* The callback for handling inclusions
*
* @param array $matches The matches found by the regex
*/
private function incCallbackHandler($matches)
{//处理模板里的include结构。把里边的include文件用file_get_contents获得,然后替换
$filename = trim($matches[1]);
$output = file_exists($filename) ? file_get_contents($filename) : '<p style="color: red; font-weight: bold">File does not exist: ' . $filename . '</p>';
return $output;
}
/**
* Callback for handling plugin calls. A bit redundant because of the {php ...} tag, but hey ho
*/
private function pluginCallbackHandler($matches)
{//处理简单的标签直接返回替换内容
return $matches[1]();
}
/**
* Displays the template
*/
public function Display()
{//显示替换内容主要函数是一个替换递归函数preg_replace_callback
/**
* Try and get the template from the cache if it's there
*/
$filename = str_replace('//', '/', $this->template);
$cached_filename = $this->getCacheFileName($filename);
$this->Debug('Cached template filename: ' . $cached_filename);
$this->Debug('Request cache control header: <span style="color: blue">Cache-Control: ' . @$_SERVER['HTTP_CACHE_CONTROL'] . '</span>');
/**
* Show the cached template. Depending on the browser and how the page was requested will control the
* Cache-Control header that the the browser sends:
*
* Internet Explorer
* =================
* Normally cached. Hold down the CTRL key and CLICK refresh to get a fresh copy.
*
* Firefox
* =======
* Normally cached when the page is retrieved by following a link. If you hit refresh, you'll get a fresh copy.
* Simple as that.
*
* Opera
* =====
* Seems to always request a fresh copy. Can't figure this browser out. Maybe it's something to do with my settings -
* just don't know.
*
* So the moral is... always use IE :-)
*/
if ( $this->useCache
AND file_exists($cached_filename)
AND filemtime($filename) <= filemtime($cached_filename)
AND $contents = file_get_contents($cached_filename)
AND @$_SERVER['HTTP_CACHE_CONTROL'] != 'no-cache'
AND @$_SERVER['HTTP_CACHE_CONTROL'] != 'max-age=0') {
echo $contents;
$this->timings['end'] = microtime();
/**
* And show the amount of time taken
*/
if ($this->debug) {
$this->Debug('Using cached template file: ' . $cached_filename);
$this->ShowDebugInformation();
}
return;
}
/**
* Debug caching information
*/
$this->Debug('Server side template caching is ' . ($this->useCache ? 'on' : 'off') );
/**
* Generate the template and show it
*/
$output = file_get_contents(str_replace('//', '/', $filename));
/**
* Handle conditionals (ie if/else) If conditions cannot be nested
*/
$output = preg_replace_callback('/{if ([^}]*)}(.*)(?:{else}([^}]*))?{\/if}/Uis', array($this, 'ifCallbackHandler'), $output);
/**
* This does straight replacement of include calls
*/
$output = preg_replace_callback('/{include ([^}]+)}/is', array($this, 'incCallbackHandler'), $output);
/**
* Handle loops
*/
foreach ($this->variables as $k => $var) {
if (is_array($var) OR is_object($var)) {
if (preg_match('/{foreach\s+\$?[a-z0-9_]+\s*}(.*){\/foreach}/Uis', $output, $matches)) {
$table = '';
foreach ($var as $value) {
$replacement = $matches[1];
foreach ($value as $a => $b) {
$replacement = preg_replace('/{' . $a . '}/i', $b, $replacement);
}
$table .= $replacement;
}
$output = str_replace($matches[1], $table, $output);
}
} else {
$output = str_replace('{' . $k . '}', $var, $output);
}
}
/**
* Replace any {php: ...} blocks by executing whatever it is and using the return value as the replacement
*/
$output = preg_replace_callback('/\{php (.*)\}/i', array($this, 'phpCallbackHandler'), $output);
/**
* Send the output to the browser
*/
$output = preg_replace('/[^\\\\]{.*[^\\\\]}/i', '', $output);
$output = str_replace(array('\\{', '\\}'), array('{', '}'), $output);
echo $output;
/**
* Save the generated content to the cache, remembering to set the modified time appropriately
*/
if ($this->useCache) {
$filename = $this->getCacheFilename();
$this->Debug('Saving output to: ' . $filename);
file_put_contents($filename, $output);
// Update the timestamp
touch($filename, time());
}
/**
* And show the amount of time taken
*/
if ($this->debug) {
/**
* Store the end time
*/
$this->timings['end'] = microtime();
$this->ShowDebugInformation();
}
}
/**
* Outputs debug message
*
* @param string $msg The debug message
*/
private function Debug($msg)
{//显示调试信息
$this->debugMessages[] = nl2br(str_replace(' ', ' ', $msg));
}
/**
* Returns a cache filename based on the given filename
*/
private function getCacheFileName()
{
return preg_replace('/\/+/', '/', ($this->cachePath ? $this->cachePath . '/template_' : '') . md5($this->template . serialize($this->variables))) . '.html';
}
/**
* Reads the template file
*/
private function readTemplate()
{
return file_exists($cached_filename) ? file_get_contents($filename) : false;
}
/**
* Sets a variable, be it singular or complex
*
* @param string $name The name of the variable
* @param mixed $value The value of the variable
*/
public function Set($name, $value)
{
$this->Debug("Setting variable: {$name}=" . print_r($value, true));
$this->variables[$name] = $value;
}
/**
* Shows the debug information
*/
public function ShowDebugInformation()
{
$this->Debug(sprintf('Time taken: %02.5fs', array_sum(explode(' ', $this->timings['end'])) - array_sum(explode(' ', $this->timings['start']))));
printf('<div id="template_debug">Debug window<br /><br />%s</div>', implode("<br />\r\n", $this->debugMessages));
}
}
?>
下边是调用的例子
<?php
/**
* Test the template class
*/
require_once('bob.class.php');
/**
* A plugin handler
*/
function myPlugin()
{
return 'This is an example PHP block that returns the title';
}
/**
* This is some example data. It's a 2d array, indexed first numerically, and then by "column name".
* Just like the structure returned by the PEAR::DB getAll method.
*/
$fredriko[0]['first'] = 'fred'; $fredriko[1]['first'] = 'barney'; $fredriko[2]['first'] = 'Riko'; $fredriko[3]['first'] = 'Orvill';
$fredriko[0]['second'] = 'Barney'; $fredriko[1]['second'] = 'Jim'; $fredriko[2]['second'] = 'life'; $fredriko[3]['second'] = 'Juniper';
$fredriko[0]['third'] = 'time lucky'; $fredriko[1]['third'] = 'Joon'; $fredriko[2]['third'] = 'Hop'; $fredriko[3]['third'] = 'Thing';
$fredriko[0]['fourth'] = 'Orvill'; $fredriko[1]['fourth'] = 'Joomla'; $fredriko[2]['fourth'] = 'Goa'; $fredriko[3]['fourth'] = 'King';
$fredriko[0]['fifth'] = 'Loop'; $fredriko[1]['fifth'] = 'Kid'; $fredriko[2]['fifth'] = 'Funky'; $fredriko[3]['fifth'] = 'Flop';
/**
* Create the template object
*/
$tpl = new Bob('example.html');
$tpl->debug =0;//0是不显示调试信息,1是显示
/**
* The cache
*/
$tpl->useCache = false;
$tpl->SetCachePath('/tmp');
/**
* This produces the counter
*/
$counter=1;
/**
* Set some variables
*/
$tpl->Set('ifVar', 1); // Used as the if condition value
$tpl->Set('myVar', 'A simple string');
$tpl->Set('title', 'The title');
$tpl->Set('fredriko', $fredriko);
$tpl->Set('counter', $counter);
/**
* Display the template
*/
$tpl->Display();
?>