<?php
/**
* 模版引擎类
*/
class Tpl
{
//缓存目录
protected $cacheDir = './Cache/';
//模版目录
protected $tplDir = './Tpl/';
//保存变量的成员方法
protected $vars = [];
//缓存有效期
protected $cacheLifeTime = 3600;
//初始化成员属性
public function __construct($tplDir = null ,$cacheDir = null ,$cacheLifeTime = null)
{
//判断缓存目录是否存在
if (isset($tplDir)) {
if ($this->_checkDir($tplDir)) {
$this->tplDir = rtrim($tplDir,'/') . '/';
}
}
//判断模板路径是否存在 如果不存在要创建,权限是否可写 就需要更改权限
if (isset($cacheDir)) {
if ($this->_checkDir($cacheDir)) {
$this->tplDir = rtrim($cacheDir,'/') . '/';
}
}
//初始化缓存时间
if (isset($cacheLifeTime)) {
$thsi->cacheLifeTime = $cacheLifeTime;
}
}
//目录的创建以及权限的处理
private function _checkDir($path)
{
//判断是否存在目录以及是否是目录
if (!file_exists($path) || !is_dir($path)) {
return mkdir($path,0755,true);
}
//判断目录的权限是否可读可写
if (!is_readable($path) || !is_writable($path)) {
return chmod($path,0755);
}
return true;
}
//分配变量 主要的目的是将模板中需要处理的代码在php中替换做准备
public function assign($key,$val)
{
$this->vars[$key] = $val;
}
//display 显示模板文件
public function display($file,$isExecute = true ,$uri = null)
{
//获得模板路径(模板目录路径+模板文件)
$tplFilePath = $this->tplDir.$file;
//判断文件是否存在
if (!file_exists($tplFilePath)) {
exit('模板文件不存在'); }
//获得编译文件路径(组合一个全新的文件名和路径)
$cacheFile = md5($file.$uri) . '.php';
$cacheFilePath = $this->cacheDir . $cacheFile;
//检测是否编译过 是否修改过模板文件 如果变编译过 而且在有效期内则不编译
if (!file_exists($cacheFilePath)) {
$html = $this->compile($tplFilePath);
} else {
//如果修改过模版文件 就需要删除原来的编译文件重新编译
if (filemtime($tplFilePath) > filemtime($cacheFilePath)) {
//删除原来的文件
unlink($cacheFilePath);
//重新编译
$html = $this->compile($tplFilePath);
}
//文件创建时间+缓存有效期 > time() [当前时间], 没有过期 否则过期
$isTimeout = (filemtime($tplFilePath) + $this->cacheLifeTime > time()) ? false : true;
if ($isTimeout) {
$html = $this->compile($tplFilePath);
}
}
//编译
if (isset($html)) {
if (!file_put_contents($cacheFilePath, $html)) {
exit('编译文件写入失败');
}
}
//执行
if ($isExecute) {
extract($this->vars);
include $cacheFilePath;
}
}
//编译的方法 将html的php代码替换成php代码来执行 并且生成一个缓存
protected function compile($tplFilePath)
{
//将整个文件读入一个字符串
$html = file_get_contents($tplFilePath);
//正则替换规则
$keys = [
'{if %%}' => '<?php if(\1): ?>',
'{else}' => '<?php else : ?>',
'{else if %%}' => '<?php elseif(\1) : ?>',
'{elseif %%}' => '<?php elseif(\1) : ?>',
'{/if}' => '<?php endif;?>',
'{$%%}' => '<?=$\1;?>',
'{foreach %%}' => '<?php foreach(\1) :?>',
'{/foreach}' => '<?php endforeach;?>',
'{for %%}' => '<?php for(\1):?>',
'{/for}' => '<?php endfor;?>',
'{while %%}' => '<?php while(\1):?>',
'{/while}' => '<?php endwhile;?>',
'{continue}' => '<?php continue;?>',
'{break}' => '<?php break;?>',
'{$%% = $%%}' => '<?php $\1 = $\2;?>',
'{$%%++}' => '<?php $\1++;?>',
'{$%%--}' => '<?php $\1--;?>',
'{comment}' => '<?php /* ',
'{/comment}' => ' */ ?>',
'{/*}' => '<?php /* ',
'{*/}' => '* ?>',
'{section}' => '<?php ',
'{/section}' => '?>',
'{{%%(%%)}}' => '<?=\1(\2);?>',
'{include %%}' => '<?php include "\1";?>',
];
//通过遍历 然后一一替换
foreach ($keys as $key => $value) {
//正则匹配替换
$patten = '#' . str_replace('%%', '(.+)', preg_quote($key,'#')) . '#imsU';
$replace = $value;
//包含文件处理 include处理方式不一样
if (stripos($patten, 'include')) {
// 执行一个正则表达式搜索并且使用一个回调进行替换
$html = preg_replace_callback($patten, array($this,'parseInclude'), $html);
} else {
$html = preg_replace($patten, $replace, $html);
}
}
return $html;
}
protected function parseInclude($data)
{
$file = str_replace(['\'','"'],'',$data[1]);
$path = $this->parsePath($file);
$this->display($file,false);
return '<?php include "' . $path . '";?>';
}
//处理文件名 将汉字符号等统一设置为32位的加密文件名
protected function parsePath($file)
{
$path = $this->cacheDir . md5($file).'.php';
return $path;
}
//删除缓存文件(递归)
public function clearCache()
{
self::delDir($this->cacheDir);
}
//递归删除缓存的文件
public static function delDir($path)
{
$res = opendir($path);
while($file = readdir($res)){
if ($file == '.' || $file == '..') {
continue;
}
$newPath = $path.'/'.$file;
if (is_dir($newPath)) {
self::delDir($newpath);
} else {
unlink($newPath);
}
}
}
}
测试代码:
$tpl = new Tpl();
$tpl->assign('hello','world');
$tpl->assign('chensen','你好森林么么哒');
$tpl->display('index.html');