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 相关文章推荐
PHP 加密/解密函数 dencrypt(动态密文,带压缩功能,支持中文)
Jan 30 PHP
PHP获取类中常量,属性,及方法列表的方法
Apr 09 PHP
php date与gmdate的获取日期的区别
Feb 08 PHP
php学习笔记 数组的常用函数
Jun 13 PHP
destoon实现会员商铺中指定会员或会员组投放广告的方法
Aug 21 PHP
php无限极分类递归排序实现方法
Nov 11 PHP
PHP aes (ecb)解密后乱码问题
Jun 22 PHP
php利用嵌套数组拼接与解析json的方法
Feb 07 PHP
Django中的cookie与session操作实例代码
Aug 17 PHP
php实现websocket实时消息推送
Mar 30 PHP
PHP实现的DES加密解密类定义与用法示例
Nov 02 PHP
php设计模式之状态模式实例分析【星际争霸游戏案例】
Mar 26 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中去除换行解决办法小结(PHP_EOL)
2011/11/27 PHP
php+ajax做仿百度搜索下拉自动提示框(有实例)
2012/08/21 PHP
PHP图像处理类库及演示分享
2015/05/17 PHP
PHP加密3DES报错 Call to undefined function: mcrypt_module_open() 如何解决
2016/04/17 PHP
PHP编程求最大公约数与最小公倍数的方法示例
2017/05/29 PHP
phpStorm2020 注册码
2020/09/17 PHP
jQuery 性能优化指南(3)
2009/05/21 Javascript
JQuery 常用操作代码
2010/03/14 Javascript
jQuery中even选择器的定义和用法
2014/12/23 Javascript
jQuery 处理页面的事件详解
2015/01/20 Javascript
jQuery选择器源码解读(一):Sizzle方法
2015/03/31 Javascript
jQuery选择器源码解读(二):select方法
2015/03/31 Javascript
javascript模拟命名空间
2015/04/17 Javascript
jquery延迟对象解析
2016/10/26 Javascript
Javascript之图片的延迟加载的实例详解
2017/07/24 Javascript
JavaScript 中的 this 简单规则
2017/09/19 Javascript
在SSM框架下用laypage和ajax实现分页和数据交互的方法
2019/09/27 Javascript
JS使用正则表达式判断输入框失去焦点事件
2019/10/16 Javascript
详解vue中多个有顺序要求的异步操作处理
2019/10/29 Javascript
如何优雅地在Node应用中进行错误异常处理
2019/11/25 Javascript
python合并文本文件示例
2014/02/07 Python
Python中时间datetime的处理与转换用法总结
2019/02/18 Python
Python实现的合并两个有序数组算法示例
2019/03/04 Python
Python实现检测文件的MD5值来查找重复文件案例
2020/03/12 Python
python爬虫实现POST request payload形式的请求
2020/04/30 Python
css3的transition效果和transfor效果示例介绍
2013/10/30 HTML / CSS
基于HTML5 的人脸识别活体认证的实现方法
2016/06/22 HTML / CSS
策划创业计划书
2014/02/06 职场文书
2014最新自愿离婚协议书范本
2014/11/19 职场文书
小兵张嘎电影观后感
2015/06/03 职场文书
对公司的意见和建议
2015/06/04 职场文书
通讯稿范文
2015/07/22 职场文书
《老人与海鸥》教学反思
2016/02/16 职场文书
高中议论文(范文2篇)
2019/08/19 职场文书
MySQL中的隐藏列的具体查看
2021/09/04 MySQL
css中:last-child不生效的解决方法
2022/08/05 HTML / CSS