PHP中MVC模式的模板引擎开发经验分享


Posted in PHP onMarch 23, 2011

使Web系统的开发与维护更加方便,从而有效的节省人力物力,受到了越来越多企业的青眯。

模板引擎是MVC模式建立过程的重要方法,开发者可以设计一套赋予含义的标签,通过技术解析处理有效的把数据逻辑处理从界面模板中提取出来,通过解读标签的含义把控制权提交给相应业务逻辑处理程序,从而获取到需要的数据,以模板设计的形式展现出来,使设计人员能把精力更多放在表现形式上。下面是我对模板引擎的认识与设计方法:

说的好听些叫模板引擎,实际就是解读模板数据的过程(个人观点^^)。通过我对建站方面的思考认识,网站在展现形式上无非归纳为单条和多条两种形式,那么我们可以设定两种对应标签(如data、list)来处理这两种情况,关键点在于解决两种标签的多层相互嵌套问题,基本适合实现80%界面形式。

解读模板的方法有多种,常用的包括字符串处理(解决嵌套稍麻烦)、正则表达式。在这里我选用的正则表达式,下面是我的处理方法(本文仅提供思路和参考代码,可能不能直接使用)。

模板文件解析类:

<?php 
/* 
* class: 模板解析类 
* author: 51JS.COM-ZMM 
* date: 2011.3.1 
* email: 304924248@qq.com 
* blog: http://www.cnblogs.com/cnzmm/ 
*/ 
class Template { 
public $html, $vars, $bTag, $eTag; 
public $bFlag='{', $eFlag='}', $pfix='zmm:'; 
private $folder, $file; 
function __construct($vars=array()) { 
!empty($vars) && $this->vars = $vars; 
!empty($GLOBALS['cfg_tag_prefix']) && 
$this->pfix = $GLOBALS['cfg_tag_prefix'].':'; 
$this->bTag = $this->bFlag.$this->pfix; 
$this->eTag = $this->bFlag.'\/'.$this->pfix; 
empty(Tags::$vars) && Tags::$vars = &$this->vars; 
} 
public function LoadTpl($tpl) { 
$this->file = $this->GetTplPath($tpl); 
Tags::$file = &$this->file; 
if (is_file($this->file)) { 
if ($this->GetTplHtml()) { 
$this->SetTplTags(); 
} else { 
exit('模板文件加载失败!'); 
} 
} else { 
exit('模板文件['.$this->file.']不存在!'); 
} 
} 
private function GetTplPath($tpl) { 
$this->folder = WEBSITE_DIRROOT. 
$GLOBALS['cfg_tpl_root']; 
return $this->folder.'/'.$tpl; 
} 
private function GetTplHtml() { 
$html = self::FmtTplHtml(file_get_contents($this->file)); 
if (!empty($html)) { 
$callFunc = Tags::$prefix.'Syntax'; 
$this->html = Tags::$callFunc($html, new Template()); 
} else { 
exit('模板文件内容为空!'); 
} return true; 
} 
static public function FmtTplHtml($html) { 
return preg_replace('/(\r)|(\n)|(\t)|(\s{2,})/is', '', $html); 
} 
public function Register($vars=array()) { 
if (is_array($vars)) { 
$this->vars = $vars; 
Tags::$vars = &$this->vars; 
} 
} 
public function Display($bool=false, $name="", $time=0) { 
if (!empty($this->html)) { 
if ($bool && !empty($name)) { 
if (!is_int($time)) $time = 600; 
$cache = new Cache($time); 
$cache->Set($name, $this->html); 
} 
echo $this->html; flush(); 
} else { 
exit('模板文件内容为空!'); 
} 
} 
public function SetAssign($souc, $info) { 
if (!empty($this->html)) { 
$this->html = str_ireplace($souc, self::FmtTplHtml($info), $this->html); 
} else { 
exit('模板文件内容为空!'); 
} 
} 
private function SetTplTags() { 
$this->SetPanelTags(); $this->SetTrunkTags(); $this->RegHatchVars(); 
} 
private function SetPanelTags() { 
$rule = $this->bTag.'([^'.$this->eFlag.']+)\/'.$this->eFlag; 
preg_match_all('/'.$rule.'/ism', $this->html, $out_matches); 
$this->TransTag($out_matches, 'panel'); unset($out_matches); 
} 
private function SetTrunkTags() { 
$rule = $this->bTag.'(\w+)\s*([^'.$this->eFlag.']*?)'.$this->eFlag. 
'((?:(?!'.$this->bTag.')[\S\s]*?|(?R))*)'.$this->eTag.'\\1\s*'.$this->eFlag; 
preg_match_all('/'.$rule.'/ism', $this->html, $out_matches); 
$this->TransTag($out_matches, 'trunk'); unset($out_matches); 
} 
private function TransTag($result, $type) { 
if (!empty($result[0])) { 
switch ($type) { 
case 'panel' : { 
for ($i = 0; $i < count($result[0]); $i ++) { 
$strTag = explode(' ', $result[1][$i], 2); 
if (strpos($strTag[0], '.')) { 
$itemArg = explode('.', $result[1][$i], 2); 
$callFunc = Tags::$prefix.ucfirst($itemArg[0]); 
if (method_exists('Tags', $callFunc)) { 
$html = Tags::$callFunc(chop($itemArg[1])); 
if ($html !== false) { 
$this->html = str_ireplace($result[0][$i], $html, $this->html); 
} 
} 
} else { 
$rule = '^([^\s]+)\s*([\S\s]+)$'; 
preg_match_all('/'.$rule.'/is', trim($result[1][$i]), $tmp_matches); 
$callFunc = Tags::$prefix.ucfirst($tmp_matches[1][0]); 
if (method_exists('Tags', $callFunc)) { 
$html = Tags::$callFunc($tmp_matches[2][0]); 
if ($html !== false) { 
$this->html = str_ireplace($result[0][$i], $html, $this->html); 
} 
} unset($tmp_matches); 
} 
} break; 
} 
case 'trunk' : { 
for ($i = 0; $i < count($result[0]); $i ++) { 
$callFunc = Tags::$prefix.ucfirst($result[1][$i]); 
if (method_exists('Tags', $callFunc)) { 
$html = Tags::$callFunc($result[2][$i], $result[3][$i]); 
$this->html = str_ireplace($result[0][$i], $html, $this->html); 
} 
} break; 
} 
default: break; 
} 
} else { 
return false; 
} 
} 
private function RegHatchVars() { 
$this->SetPanelTags(); 
} 
function __destruct() {} 
} 
?>

标签解析类:(目前暂时提供data、list两种标签的解析,说明思路)
<?php 
/* 
* class: 标签解析类 
* author: 51JS.COM-ZMM 
* date: 2011.3.2 
* email: 304924248@qq.com 
* blog: http://www.cnblogs.com/cnzmm/ 
*/ 
class Tags { 
static private $attrs=null; 
static public $file, $vars, $rule, $prefix='TAG_'; 
static public function TAG_Syntax($html, $that) { 
$rule = $that->bTag.'if\s+([^'.$that->eFlag.']+)\s*'.$that->eFlag; 
$html = preg_replace('/'.$rule.'/ism', '<?php if (\\1) { ?>', $html); 
$rule = $that->bTag.'elseif\s+([^'.$that->eFlag.']+)\s*'.$that->eFlag; 
$html = preg_replace('/'.$rule.'/ism', '<?php } elseif (\\1) { ?>', $html); 
$rule = $that->bTag.'else\s*'.$that->eFlag; 
$html = preg_replace('/'.$rule.'/ism', '<?php } else { ?>', $html); 
$rule = $that->bTag.'loop\s+(\S+)\s+(\S+)\s*'.$that->eFlag; 
$html = preg_replace('/'.$rule.'/ism', '<?php foreach (\\1 as \\2) { ?>', $html); 
$rule = $that->bTag.'loop\s+(\S+)\s+(\S+)\s+(\S+)\s*'.$that->eFlag; 
$html = preg_replace('/'.$rule.'/ism', '<?php foreach (\\1 as \\2 => \\3) { ?>', $html); 
$rule = $that->eTag.'(if|loop)\s*'.$that->eFlag; 
$html = preg_replace('/'.$rule.'/ism', '<?php } ?>', $html); 
$rule = $that->bTag.'php\s*'.$that->eFlag.'((?:(?!'. 
$that->bTag.')[\S\s]*?|(?R))*)'.$that->eTag.'php\s*'.$that->eFlag; 
$html = preg_replace('/'.$rule.'/ism', '<?php \\1 ?>', $html); 
return self::TAG_Execute($html); 
} 
static public function TAG_List($attr, $html) { 
if (!empty($html)) { 
if (self::TAG_HaveTag($html)) { 
return self::TAG_DealTag($attr, $html, true); 
} else { 
return self::TAG_GetData($attr, $html, true); 
} 
} else { 
exit('标签{list}的内容为空!'); 
} 
} 
static public function TAG_Data($attr, $html) { 
if (!empty($html)) { 
if (self::TAG_HaveTag($html)) { 
return self::TAG_DealTag($attr, $html, false); 
} else { 
return self::TAG_GetData($attr, $html, false); 
} 
} else { 
exit('标签{data}的内容为空!'); 
} 
} 
static public function TAG_Execute($html) { 
ob_clean(); ob_start(); 
if (!empty(self::$vars)) { 
is_array(self::$vars) && 
extract(self::$vars, EXTR_OVERWRITE); 
} 
$file_inc = WEBSITE_DIRINC.'/buffer/'. 
md5(uniqid(rand(), true)).'.php'; 
if ($fp = fopen($file_inc, 'xb')) { 
fwrite($fp, $html); 
if (fclose($fp)) { 
include($file_inc); 
$html = ob_get_contents(); 
} unset($fp); 
} else { 
exit('模板解析文件生成失败!'); 
} ob_end_clean(); @unlink($file_inc); 
return $html; 
} 
static private function TAG_HaveTag($html) { 
$bool_has = false; 
$tpl_ins = new Template(); 
self::$rule = $tpl_ins->bTag.'([^'.$tpl_ins->eFlag.']+)\/'.$tpl_ins->eFlag; 
$bool_has = $bool_has || preg_match('/'.self::$rule.'/ism', $html); 
self::$rule = $tpl_ins->bTag.'(\w+)\s*([^'.$tpl_ins->eFlag.']*?)'.$tpl_ins->eFlag. 
'((?:(?!'.$tpl_ins->bTag.')[\S\s]*?|(?R))*)'.$tpl_ins->eTag.'\\1\s*'.$tpl_ins->eFlag; 
$bool_has = $bool_has || preg_match('/'.self::$rule.'/ism', $html); 
unset($tpl_ins); 
return $bool_has; 
} 
static private function TAG_DealTag($attr, $html, $list) { 
preg_match_all('/'.self::$rule.'/ism', $html, $out_matches); 
if (!empty($out_matches[0])) { 
$child_node = array(); 
for ($i = 0; $i < count($out_matches[0]); $i ++) { 
$child_node[] = $out_matches[3][$i]; 
$html = str_ireplace($out_matches[3][$i], '{-->>child_node_'.$i.'<<--}', $html); 
} 
$html = self::TAG_GetData($attr, $html, $list); 
for ($i = 0; $i < count($out_matches[0]); $i ++) { 
$html = str_ireplace('{-->>child_node_'.$i.'<<--}', $child_node[$i], $html); 
} 
preg_match_all('/'.self::$rule.'/ism', $html, $tmp_matches); 
if (!empty($tmp_matches[0])) { 
for ($i = 0; $i < count($tmp_matches[0]); $i ++) { 
$callFunc = self::$prefix.ucfirst($tmp_matches[1][$i]); 
if (method_exists('Tags', $callFunc)) { 
$temp = self::$callFunc($tmp_matches[2][$i], $tmp_matches[3][$i]); 
$html = str_ireplace($tmp_matches[0][$i], $temp, $html); 
} 
} 
} 
unset($tmp_matches); 
} 
unset($out_matches); return $html; 
} 
static private function TAG_GetData($attr, $html, $list=false) { 
if (!empty($attr)) { 
$attr_ins = new Attbt($attr); 
$attr_arr = $attr_ins->attrs; 
if (is_array($attr_arr)) { 
extract($attr_arr, EXTR_OVERWRITE); 
$source = table_name($source, $column); 
$rule = '\[field:\s*(\w+)\s*([^\]]*?)\s*\/?]'; 
preg_match_all('/'.$rule.'/is', $html, $out_matches); 
$data_str = ''; 
$data_ins = new DataSql(); 
$attr_where = $attr_order = ''; 
if (!empty($where)) { 
$where = str_replace(',', ' and ', $where); 
$attr_where = ' where '. $where; 
} 
if (!empty($order)) { 
$attr_order = ' order by '.$order; 
} else { 
$fed_name = ''; 
$fed_ins = $data_ins->GetFedNeedle($source); 
$fed_cnt = $data_ins->GetFedCount($fed_ins); 
for ($i = 0; $i < $fed_cnt; $i ++) { 
$fed_flag = $data_ins->GetFedFlag($fed_ins, $i); 
if (preg_match('/auto_increment/ism', $fed_flag)) { 
$fed_name = $data_ins->GetFedName($fed_ins, $i); 
break; 
} 
} 
if (!empty($fed_name)) 
$attr_order = ' order by '.$fed_name.' desc'; 
} 
if ($list == true) { 
if (empty($source) && empty($sql)) { 
exit('标签{list}必须指定source属性!'); 
} 
$attr_rows = $attr_page = ''; 
if ($rows > 0) { 
$attr_rows = ' limit 0,'.$rows; 
} 
if (!empty($sql)) { 
$data_sql = $sql; 
} else { 
$data_sql = 'select * from `'.$source.'`'. 
$attr_where.$attr_order.$attr_rows; 
} 
if ($pages=='true' && !empty($size)) { 
$data_num = $data_ins->GetRecNum($data_sql); 
$page_cnt = ceil($data_num / $size); 
global $page; 
if (!isset($page) || $page < 1) $page = 1; 
if ($page > $page_cnt) $page = $page_cnt; 
$data_sql = 'select * from `'.$source.'`'.$attr_where. 
$attr_order.' limit '.($page-1) * $size.','.$size; 
$GLOBALS['cfg_page_curr'] = $page; 
$GLOBALS['cfg_page_prev'] = $page - 1; 
$GLOBALS['cfg_page_next'] = $page + 1; 
$GLOBALS['cfg_page_nums'] = $page_cnt; 
if (function_exists('list_pagelink')) { 
$GLOBALS['cfg_page_list'] = list_pagelink($page, $page_cnt, 2); 
} 
} 
$data_idx = 0; 
$data_ret = $data_ins->SqlCmdExec($data_sql); 
while ($row = $data_ins->GetRecArr($data_ret)) { 
if ($skip > 0 && !empty($flag)) { 
$data_idx != 0 && 
$data_idx % $skip == 0 && 
$data_str .= $flag; 
} 
$data_tmp = $html; 
$data_tmp = str_ireplace('@idx', $data_idx, $data_tmp); 
for ($i = 0; $i < count($out_matches[0]); $i ++) { 
$data_tmp = str_ireplace($out_matches[0][$i], 
$row[$out_matches[1][$i]], $data_tmp); 
} 
$data_str .= $data_tmp; $data_idx ++; 
} 
} else { 
if (empty($source)) { 
exit('标签{data}必须指定source属性!'); 
} 
$data_sql = 'select * from `'.$source. 
'`'.$attr_where.$attr_order; 
$row = $data_ins->GetOneRec($data_sql); 
if (is_array($row)) { 
$data_tmp = $html; 
for ($i = 0; $i < count($out_matches[0]); $i ++) { 
$data_val = $row[$out_matches[1][$i]]; 
if (empty($out_matches[2][$i])) { 
$data_tmp = str_ireplace($out_matches[0][$i], $data_val, $data_tmp); 
} else { 
$attr_str = $out_matches[2][$i]; 
$attr_ins = new Attbt($attr_str); 
$func_txt = $attr_ins->attrs['function']; 
if (!empty($func_txt)) { 
$func_tmp = explode('(', $func_txt); 
if (function_exists($func_tmp[0])) { 
eval('$func_ret ='.str_ireplace('@me', 
'\''.$data_val.'\'', $func_txt)); 
$data_tmp = str_ireplace($out_matches[0][$i], $func_ret, $data_tmp); 
} else { 
exit('调用了不存在的函数!'); 
} 
} else { 
exit('标签设置属性无效!'); 
} 
} 
} 
$data_str .= $data_tmp; 
} 
} 
unset($data_ins); 
return $data_str; 
} else { 
exit('标签设置属性无效!'); 
} 
} else { 
exit('没有设置标签属性!'); 
} 
} 
static public function __callStatic($name, $args) { 
exit('标签{'.$name.'}不存在!'); 
} 
} 
?>
PHP 相关文章推荐
snoopy PHP版的网络客户端提供本地下载
Apr 15 PHP
php Xdebug 调试扩展的安装与使用.
Mar 13 PHP
浅析php中jsonp的跨域实例
Jun 21 PHP
ThinkPHP模板中判断volist循环的最后一条记录的验证方法
Jul 01 PHP
新浪SAE搭建PHP项目教程
Jan 28 PHP
php利用反射实现插件机制的方法
Mar 14 PHP
CentOS 7.2 下编译安装PHP7.0.10+MySQL5.7.14+Nginx1.10.1的方法详解(mini版本)
Sep 01 PHP
PHP常用排序算法实例小结【基本排序,冒泡排序,快速排序,插入排序】
Feb 07 PHP
PHP数据分析引擎计算余弦相似度算法示例
Aug 08 PHP
Laravel模型间关系设置分表的方法示例
Apr 21 PHP
为Plesk PHP7启用Oracle OCI8扩展方法总结
Mar 29 PHP
Linux下安装Memcached服务器和客户端与PHP使用示例
Apr 15 PHP
PHP面向接口编程 耦合设计模式 简单范例
Mar 23 #PHP
PHP中用接口、抽象类、普通基类实现“面向接口编程”与“耦合方法”简述
Mar 23 #PHP
php中取得URL的根域名的代码
Mar 23 #PHP
PHP+JS+rsa数据加密传输实现代码
Mar 23 #PHP
PHP 事件机制(2)
Mar 23 #PHP
php函数之子字符串替换&amp;#65279; str_replace
Mar 23 #PHP
php expects parameter 1 to be resource, array given 错误
Mar 23 #PHP
You might like
两个强悍的php 图像处理类1
2009/06/15 PHP
分享PHP header函数使用教程
2013/09/05 PHP
PHP微信红包生成代码分享
2016/10/06 PHP
PHP无限极分类函数的实现方法详解
2017/04/15 PHP
jQuery 各种浏览器下获得日期区别
2008/12/22 Javascript
event对象的方法 兼容多浏览器
2009/06/27 Javascript
jquery load()在firefox(火狐)下显示不正常的解决方法
2011/04/05 Javascript
JS实现两个大数(整数)相乘
2014/04/28 Javascript
JS实现鼠标箭头变成一个燃烧烛光效果的方法
2015/02/28 Javascript
jQuery实现简单下拉导航效果
2015/09/07 Javascript
Bootstrap轮播插件使用代码
2016/10/11 Javascript
JavaScript数组复制详解
2017/02/02 Javascript
基于Bootstrap漂亮简洁的CSS3价格表(附源码下载)
2017/02/28 Javascript
jQuery图片瀑布流的简单实现代码
2017/03/15 Javascript
nodejs入门教程五:连接数据库的方法分析
2017/04/24 NodeJs
详解如何使用Node.js编写命令工具——以vue-cli为例
2017/06/29 Javascript
微信小程序实现展示评分结果功能
2019/02/15 Javascript
vue2 中二级路由高亮问题及配置方法
2019/06/10 Javascript
浅谈Vue3.0新版API之composition-api入坑指南
2020/04/30 Javascript
python中关于for循环的碎碎念
2017/06/30 Python
Python通用循环的构造方法实例分析
2018/12/19 Python
在VS2017中用C#调用python脚本的实现
2019/07/31 Python
python3+opencv生成不规则黑白mask实例
2020/02/19 Python
宝信软件JAVA工程师面试经历
2012/08/19 面试题
Linux开机引导的步骤是什么
2014/02/26 面试题
竞争上岗演讲稿
2014/01/05 职场文书
大学生自助营养快餐店创业计划书
2014/01/13 职场文书
业绩考核岗位职责
2014/02/01 职场文书
红色故事演讲稿
2014/05/22 职场文书
关于读书的演讲稿400字
2014/08/27 职场文书
授权委托书公证
2014/09/14 职场文书
群众路线个人剖析材料
2014/10/07 职场文书
2015迎新晚会开场白
2015/05/29 职场文书
楚门的世界观后感
2015/06/03 职场文书
教你使用Python pypinyin库实现汉字转拼音
2021/05/27 Python
《极主夫道》真人电影正式预告 定档6月3日上映
2022/04/05 日漫