项目结构:
- 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 = '/\{if\s+\(\$([\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 = '/\{foreach\s+\$([\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 = '/\{#include\s+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文件夹下,名称【手写模板引擎】。