项目结构:
- index.php:主文件,要调用的文件。
- templates.inc.php:模板配置文件。
- cache:目录:存放静态缓存文件。
- config:目录:配置文件,模板引擎的系统变量。
- profile.xml:用于模板引擎中的系统变量。
- includes:目录:核心文件
- Parser.class.php:解析类,用于解析模板文件中的变量,将模板中的变量转换为php代码。
- Template.class.php:模板类,提供调用的方法,注入变量,显示模板文件。
- templates:目录:存放模板文件。
- templates_c:目录:存放编译文件,通过解析类解析后的文件,其中的代码,是html与php代码结合,相当于平时的混写PHP文件。
模板变量规定:
--普通边变量 {$name} --注释 {#}其中为注释内容...{#} --if判断 {if ($a)} {/if} --if else 判断 {if ($a)} {else} {/if} --foreach循环 {foreach $array(key, value)} {@key}{@value} {/foreach} --include包含 {#include file="abc.php"}
代码:
templates.inc.php,配置文件代码:
<?php //创建网站根目录常量 define('ROOT_PATH', dirname(__FILE__) . DIRECTORY_SEPARATOR); //模板文件目录 define('TPL_DIR', ROOT_PATH . 'templates' . DIRECTORY_SEPARATOR); //编译文件目录 define('TPL_C_DIR', ROOT_PATH . 'templates_c' . DIRECTORY_SEPARATOR); //缓存目录 define('CACHE', ROOT_PATH . 'cache' . DIRECTORY_SEPARATOR); //是否开启缓存 define('IS_CACHE', true); IS_CACHE ? ob_start() : NULL; //引入模板类 require ROOT_PATH . 'includes' . DIRECTORY_SEPARATOR . 'Template.class.php'; ?>
index.php代码:
<?php //设置输出编码格式 header('Content-Type:text/html;charset="UTF-8"'); //引入模板配置文件 require dirname(__FILE__) . DIRECTORY_SEPARATOR . 'templates.inc.php'; //实例化模版类 $template = new Templates(); //注入变量 $template->assign('title', '自定义模板引擎'); $template->assign('content', '自定义模板引擎文件!'); //调用display方法 $template->display('index.tpl'); ?>
index.tpl模板文件代码:
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> <title>{$title}</title> </head> <body> {$content} <!--{webname}--> </body> </html>
profile.xml配置文件:
<?xml version="1.0" encoding="UTF-8"?> <root> <taglib> <name>webname</name> <value>系统变量</value> </taglib> <taglib> <name>pagesize</name> <value>10</value> </taglib> </root>
Parser.class.php解析文件:
<?php /** * 模板解析类 */ class Parser { //存储模板内容 private $_tplcontent; /** * 构造函数,获取模板内容 * @param unknown $_tplPath */ public function __construct($_tplPath) { if ( !$this->_tplcontent = file_get_contents($_tplPath) ) { exit('ERROR:获取模板文件出错!'); } } //解析普通变量 private function parVar() { //替换变量的正则表达式 $_patten = '/{$([w]+)}/'; //如果匹配成功,则替换变量 if ( preg_match($_patten, $this->_tplcontent) ) { //[ 1 ],正则表达式,得到替换的内容。 $this->_tplcontent = preg_replace($_patten, '<?php echo $this->_vars["1"];?>', $this->_tplcontent); } else { exit($this->_tplcontent); } } /** * 解析if语句 */ private function parIf() { $_varStartIf = '/{ifs+($([w]+))}/'; $_varMidlleElse = '/{else}/'; $_varEndIf = '/{/if}/'; if ( preg_match($_varStartIf, $this->_tplcontent) ) { if (preg_match($_varEndIf, $this->_tplcontent) ) { //替换头if $this->_tplcontent = preg_replace($_varStartIf, '<?php if ($this->_vars["1"]) { ?>', $this->_tplcontent); //替换尾if $this->_tplcontent = preg_replace($_varEndIf, '<?php } ?>', $this->_tplcontent); //替换 else if ( preg_match($_varMidlleElse, $this->_tplcontent) ) { $this->_tplcontent = preg_replace($_varMidlleElse, '<?php } else { ?>', $this->_tplcontent); } } else { exit('ERROR:if语句没有关闭!'); } } } /** * 解析Foreach */ private function parForeach() { $_pattenStartForeach = '/{foreachs+$([w]+)(([w]+),([w]+))}/'; $_pattenMiddleForeach = '/{@([w]+)}/'; $_pattenEndForeach = '/{/foreach}/'; if ( preg_match($_pattenStartForeach, $this->_tplcontent) ) { if ( preg_match($_pattenEndForeach, $this->_tplcontent) ) { //替换开头和结尾 $this->_tplcontent = preg_replace($_pattenStartForeach, '<?php foreach ($this->_vars["1"] as $2=>$3) { ?>', $this->_tplcontent); $this->_tplcontent = preg_replace($_pattenEndForeach, '<?php } ?>', $this->_tplcontent); //替换中间内容 if ( preg_match($_pattenMiddleForeach, $this->_tplcontent) ) { $this->_tplcontent = preg_replace($_pattenMiddleForeach, '<?php echo $1; ?>', $this->_tplcontent); } } else { exit('ERROR:foreach标签没有关闭!'); } } } /** * 解析include */ private function parInclude() { $_pattenInclude = '/{#includes+file="([w.-]+)"}/'; if ( preg_match($_pattenInclude, $this->_tplcontent,$_file) ) { //判断被包含文件是否存在 if ( !file_exists($_file[1]) || empty($_file[1]) ) { exit('ERROR:包含文件出错!'); } //替换为PHP代码 $this->_tplcontent = preg_replace($_pattenInclude, '<?php include "1"; ?>', $this->_tplcontent); } } /** * 解析系统变量 */ private function parConfig() { $_pattenConfig = '/<!--{([w]+)}-->/'; if (preg_match($_pattenConfig, $this->_tplcontent) ) { $this->_tplcontent = preg_replace($_pattenConfig, '<?php echo $this->_config["1"]; ?>', $this->_tplcontent); } } /** * 解析注释 */ private function parCommon() { $_patten = '/{#}(.*){#}/'; if ( preg_match($_patten, $this->_tplcontent) ) { $this->_tplcontent = preg_replace($_patten, '<?php /* 1 */ ?>', $this->_tplcontent); } } /** * 解析文件方法 */ public function compile($_compileName) { //解析普通变量 $this->parVar(); //解析if语句 $this->parIf(); //解析include $this->parInclude(); //解析系统变量 $this->parConfig(); //解析注释 $this->parCommon(); //解析foreach $this->parForeach(); //经过解析变量,最后生成编译文件 if ( !file_put_contents($_compileName, $this->_tplcontent) ) { exit('ERROR:编译文件出错!'); } } } ?>
Template.class.php模板文件:
<?php /** * 模板类 */ class Templates { //存储注入的变量 private $_vars = array(); //存储配置文件 private $_config = array(); /** * 构造函数 */ public function __construct() { //验证各个目录是否存在s if ( !is_dir(TPL_DIR) || !is_dir(TPL_C_DIR) || !is_dir(CACHE) ) { exit('ERROR:目录不存在,请添加!'); } //读取系统变量 $_root = simplexml_load_file('config/profile.xml'); $_tagLib = $_root->xpath('/root/taglib'); foreach ($_tagLib as $tag) { $this->_config["$tag->name"] = $tag->value; } } /** * 注入变量 */ public function assign($_varName, $_varValue) { //判断变量名称是否为空 if ( !isset($_varName) || empty($_varName) ) { exit('ERROR:变量名不能为空!'); } else { //将变量注入到数组中去 $this->_vars[$_varName] = $_varValue; } } /** * 显示模板文件 */ public function display($filename) { //获取模板路径 $_tplPath = TPL_DIR . $filename; //判断模板文件是否存在 if ( !file_exists($_tplPath) ) { exit('ERROR:模板文件不存在!'); } //编译后文件的文件名 $_compileName = TPL_C_DIR . md5($filename) . $filename . '.php'; //缓存文件的文件名 $_cacheFileName = CACHE . md5($filename) . $filename . '.html'; //第二次载入相同文件,直接载入缓存文件 if ( IS_CACHE ) { //判断缓存文件和编译文件是否都存在,如果都存在则直接执行缓存文件 if ( file_exists($_cacheFileName) && file_exists($_compileName) ) { //判断模板文件和编译文件是否修改 if ( filemtime($_compileName) >= filemtime($_tplPath) && filemtime($_cacheFileName) >= filemtime($_compileName) ) { include $_cacheFileName; return; } } } //如果编译文件还不存在,或者模板文件被修改(检查最后修改时间),则重新生成编译文件 if ( !file_exists($_compileName) || filemtime($_compileName) < filemtime($_tplPath) ) { //引入解析类 require ROOT_PATH . 'includes' . DIRECTORY_SEPARATOR . 'Parser.class.php'; //声明类的时候,传入模板文件路径 $parser = new Parser($_tplPath); //调用解析方法的时候,传入编译文件名称 $parser->compile($_compileName); } //载入编译文件,载入后已经输出到浏览器 include $_compileName; //是否开启了缓存 if ( IS_CACHE ) { //接受缓冲文件,并生成缓存文件 file_put_contents($_cacheFileName, ob_get_contents()); //清除缓冲区,意思就是清除了编译文件加载的内容 ob_end_clean(); //载入缓存文件,直接输出到浏览器 include $_cacheFileName; } } } ?>
生成的编译文件:
1d7c7a527b6335cc7a623305ca940e1findex.tpl.php
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> <title><?php echo $this->_vars["title"];?></title> </head> <body> <?php echo $this->_vars["content"];?> <?php echo $this->_config["webname"]; ?> </body> </html>
生成的缓存文件:
1d7c7a527b6335cc7a623305ca940e1findex.tpl.html
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> <title>自定义模板引擎</title> </head> <body> 自定义模板引擎文件! 系统变量 </body> </html>
------------
源码:网盘的Smarty文件夹下,名称【手写模板引擎】。