仿制smarty的模板技术,写了一个自己的mySmarty,供大家参考,和玩转
这是一个仿制的smarty模板
可以给简单变量以及数组变量赋值
在模板中可以使用foreach循环以及if条件语句
demo.php
caching = true;
//定义变量
$webname = 'my Smarty test';
$message = array(
'title'=>'this is title',
'content',
'userMessage'=>array(
array('name'=>'this is johnny','gender'=>1),
array('name'=>'this is lei','gender'=>1),
array('name'=>'this is nicolas','gender'=>1),
array('name'=>'this is ada','gender'=>0),
),
'bookMessage'=> array(
array('name'=>'this is johnny','id'=>1),
array('name'=>'this is lei','id'=>2),
array('name'=>'this is nicolas','id'=>3),
array('name'=>'this is ada','id'=>4),
),
array(
'friendMessage'=>array(
array('name','id'),
array('johnny',1),
array('nicolas',2),
array('leiyuqing2222222222222',3),
),
),
);
$message1 = array(
'userMessage'=>array(
array('name'=>'this is johnny','gender'=>1),
array('name'=>'this is lei','gender'=>1),
array('name'=>'this is nicolas','gender'=>1),
array('name'=>'this is ada','gender'=>0),
),
'bookMessage'=> array(
array('name'=>'this is book1','id'=>1),
array('name'=>'this is book2','id'=>2),
array('name'=>'this is book3','id'=>3),
array('name'=>'this is book4','id'=>4),
),
);
$simpleArr = array('cc','dd','ee');
$dyadicArr = array(
array('ff','gg','hh'),
'key'=>array('ii','jj','kk'),
);
$keyArr = array(
'aa'=>'AA',
'bb'=>'BB',
'cc'=>'cc',
);
$userMessage = array(
array('name'=>'this is johnny','gender'=>1),
array('name'=>'this is lei','gender'=>1),
array('name'=>'this is nicolas','gender'=>1),
array('name'=>'this is ada','gender'=>0),
);
$gender = 0;
$minismarty->assign('webname', $webname);
$minismarty->assign('message', $message);
$minismarty->assign('message1', $message1);
$minismarty->assign('userMessage', $userMessage);
$minismarty->assign('simpleArr', $simpleArr);
$minismarty->assign('dyadicArr', $dyadicArr);
$minismarty->assign('keyArr', $keyArr);
$minismarty->assign('gender', $gender);
//启动编译模板文件
$minismarty->display('demo.tpl.html');
MySmarty/MySmarty.class.php
checkDir();
}
//检查目录是否建好
private function checkDir() {
if (!is_dir($this->template_dir)) {
exit('模板文件目录templates不存在!请手动创建');
}
if (!is_dir($this->compile_dir)) {
exit('编译文件目录templates_c不存在!请手工创建!');
}
if (!is_dir($this->cache_dir)) {
exit('缓存文件目录'.$this->cache_dir.'不存在!请手工创建!');
}
}
//模板变量注入方法
public function assign($tpl_var, $var = null) {
if (isset($tpl_var) && !empty($tpl_var)) {
$this->_tpl_var[$tpl_var] = $var;
} else {
exit('模板变量名没有设置好');
}
}
//文件编译
public function display($file) {
//模板文件
$tpl_file = $this->template_dir.'/'.$file;
if (!file_exists($tpl_file)) {
exit('ERROR:模板文件不存在!');
}
//编译文件
$parse_file = $this->compile_dir.'/'.md5($file).$file.'.php';
//只有当编译文件不存在或者是模板文件被修改过了
//才重新编译文件
if (!file_exists($parse_file) || filemtime($parse_file) < filemtime($tpl_file)) {
$compile = new MySmartyCompile($tpl_file);
$compile->parse($parse_file);
}
// 如果缓存没有开启
if (!$this->caching) {
include $parse_file;
return ;
}
//开启了缓存才加载缓存文件,否则直接加载编译文件
//缓存文件
$cache_file = $this->cache_dir.'/'.md5($file).$file;
//只有当缓存文件不存在,或者编译文件已被修改过
//重新生成缓存文件
if (!file_exists($cache_file) || filemtime($cache_file) < filemtime($parse_file)) {
ob_start();
//引入缓存文件
include $parse_file;
//缓存内容
$content = ob_get_contents();
ob_end_clean();
//生成缓存文件
if (!file_put_contents($cache_file, $content)) {
exit('缓存文件生成出错!');
}
}
//载入缓存文件
include $cache_file;
}
}
MySmarty/MySmartyCompile.class.php
content = file_get_contents($tpl_file);
}
//解析普通变量,如把{$name}解析成$this->_tpl_var['name']
public function replaceSimpleVar() {
/**
* 这个正则表达式是指匹配简单变量
* 不匹配$xxx.或者$xxx[或者$xxx]既不匹配对象和数组
* 也不匹配关键字,比如{foreach}等等
*/
$pattern = '/\{\$(\w+(?!\.|\[|\]))\}/';
if (preg_match($pattern, $this->content)) {
$this->content = preg_replace($pattern, '_tpl_var["$1"]?>', $this->content);
}
}
public function replaceVar() {
/**
* $webname.name.cc.dd
* $webname.name.cc[1].dd
* $author[1][12].name[0]
* $title[1]
* $title.dd
* $title.dd[1]
*/
// \$(\w+(\.|(\[\d+\])+)+)+(\w+(\[\d+\])?)?
// \$((\w+(\.|(\[\d+\])+)+)+(\w+(\[\d+\])?)?)
// $pattern = '/\{\$((\w+(\.|(\[\d+\])+)+)+(\w+(\[\d+\])?)?)\}/';
$pattern = '/\{\$([^\{\}]+)\}/';
$matches = array();
preg_match_all($pattern, $this->content,$matches);
for($i = 0; $i < count($matches[1]); $i++) {
$str = $this->parseVar($matches[1][$i]);
$str = "";
$this->content = str_replace($matches[0][$i], $str, $this->content);
}
}
/**
* 传进来的是一个变量的字串,比如$name,$name[1],$message.name[2]之内的
* 把这个字符串转化成我们的$this->_tpl_var开头的变量
* 比如$name => $this->_tpl_var['name']
* $name[1] =>$this->_tpl_var['name'][1]
* $message.name[2] => $this->_tpl_var['message']['name'][2]
* 返回传化后的字符串
* @param type $arrStr
* @return str
*/
public function parseVar($arrStr) {
$str = '$this->_tpl_var';
$explodeFlag = '.';
$subStr = explode($explodeFlag, $arrStr);
for ($i = 0; $i < count($subStr); $i++) {
$pattern = '/(\w+)((\[\d+\])+)/';
if(!preg_match($pattern, $subStr[$i], $matches)) {
$str .= "["."'".$subStr[$i]."'"."]";
continue;
}
$str .= "["."'".$matches[1]."'"."]".$matches[2];
}
return $str;
}
/**
* 这个函数的作用是替换模板文件中的foreach语句块,和if语句块
*/
public function replaceSpecialWords() {
/**
* 这个正则表达式匹配{{XXX}}里面的东西,既if语句块和foreach语句块
* 这个xxx不能包含{{
* 如果这个语句块是if,那个就要以{/if}结尾,如果是foreach,那么就以{/foreach}结尾
*/
$pattern = '/\{(\{(\w+)(?:(?!\{\{).|\n)+\{\/\2\})\}/';
$matches = array();
preg_match_all($pattern, $this->content, $matches);
for($i = 0; $i < count($matches[1]);$i++) {
$str = $this->parseSpecialWords($matches[1][$i]);
$this->content = str_replace($matches[0][$i], $str, $this->content);
}
}
public function parseSpecialWords($str) {
/**
* 匹配出{}或是{{}或是{}}里面的字符串
*/
$pattern = '/\{([^\{\}]+)\}/';
preg_match_all($pattern, $str, $matches);
/**
* 匹配出第一行里面的变量,并且将他替换掉
*/
$reg = '/\((\$([^\s]+))/';
preg_match($reg, $matches[0][0], $var);
$tmp = $this->parseVar($var[2]);
$matches[1][0] = str_replace($var[1], $tmp, $matches[1][0]);
/**
* flag用来记录这个foreach语句还是if语句块
* flag = fo or if
*/
$flag = substr($matches[1][0], 0,2);
/**
* replace 根据每一行情况不同,赋不同的php语句,替换掉伪代码
*/
$replace = '';
$count = count($matches[1]);
for ($i = 0; $i < $count; $i++) {
/**
* 匹配循环里面需要输出的变量
*/
$reg1 = '/^\$\w+$/';
if (preg_match($reg1, $matches[1][$i])) {
/**
* 找到需要输出的变量
*/
$replace = "";
} else if($matches[1][$i] == $matches[1][$count-1] || substr($matches[1][$i],0,1) == '/'){
/**
* 找到结束的地方,用}替换掉
*/
$replace = "";
} else if(substr($matches[1][$i],0,4) == 'else'){
/**
* 如果是else开头,那么应该在else前面加上一个}然后再在语句后面加上一个{
*/
$replace = "";
} else if($flag == 'if' && substr($matches[1][$i],0,2) == 'fo'){
/**
* 这种情况是if语句块里面嵌套foreach语句,因为if的条件变量和foreach的循环变量一般不会相同所以要特殊考虑
* 既如果用一个flag来控制if怎么走,然后foreach里面的循环变量数组如果是message,那么我们得要找到message的值
*/
//示例字符串foreach ($dyadicArr as $value)
$reg = '/\((\$([^\s]+))/';
preg_match($reg, $matches[0][$i], $var);
$tmp = $this->parseVar($var[2]);
$matches[1][$i] = str_replace($var[1], $tmp, $matches[1][$i]);
$flag = 'fo';
$replace = "";
} else {
/**
* 找到循环开始的地方,应该在语句后面加上一个{
*/
$replace = "";
}
$str = str_replace($matches[0][$i], $replace, $str);
}
return $str;
}
//模板编译
public function parse($parse_file) {
ini_set('pcre.backtrack_limit', -1);
// ini_set('pcre.recursion_limit', 99999);
//解析foreach和if语句块
$this->replaceSpecialWords();
//解析变量
$this->replaceVar();
//编译完成后,生成编译文件
if (!file_put_contents($parse_file, $this->content)) {
exit('编译文件生成出错!');
}
}
}
TODO supply a title
**********这个是一个简单变量**************
{$webname}
**********这是一个数组里面的元素************
{$message[1].friendMessage[1][0]}
*************这里是一个If语句块***********
{{if($gender == 1)}
male
{else}
female
{/if}}
***************这里是一个简单的for循环**************
{{foreach ($dyadicArr as $value)}
{foreach ($value as $v)}
{$v}
{/foreach}
{/foreach}}
*************这是foreach循环里面嵌套一个If语句块********
{{foreach ($userMessage[1] as $k => $v)}
{if($v == 1)}
{$k} => male
{else if($v == 0)}
{$k} => female
{else }
{$k} => {$v}
{/if}
{/foreach}}
*******这里是3重foreach语句块嵌套********
{{foreach ($message1 as $key1 => $value1)}
{$key1} =>
{foreach ($value1 as $key2 => $value2)}
{foreach ($value2 as $key3 => $value3)}
{$key3}=>{$value3}
{/foreach}
{/foreach}
{/foreach}}