PHP中MVC模式的模板引擎开发经验分享
2015-01-24信息快讯网
MVC是模型(Model)、视图(View)和控制(Controller)的缩写,PHP中采用MVC模式的目的是实现Web系统的职能分工,通俗的说就是把业务逻辑处理从用户界面视图中分离出来。
使Web系统的开发与维护更加方便,从而有效的节省人力物力,受到了越来越多企业的青眯。模板引擎是MVC模式建立过程的重要方法,开发者可以设计一套赋予含义的标签,通过技术解析处理有效的把数据逻辑处理从界面模板中提取出来,通过解读标签的含义把控制权提交给相应业务逻辑处理程序,从而获取到需要的数据,以模板设计的形式展现出来,使设计人员能把精力更多放在表现形式上。下面是我对模板引擎的认识与设计方法:
说的好听些叫模板引擎,实际就是解读模板数据的过程(个人观点^^)。通过我对建站方面的思考认识,网站在展现形式上无非归纳为单条和多条两种形式,那么我们可以设定两种对应标签(如data、list)来处理这两种情况,关键点在于解决两种标签的多层相互嵌套问题,基本适合实现80%界面形式。
解读模板的方法有多种,常用的包括字符串处理(解决嵌套稍麻烦)、正则表达式。在这里我选用的正则表达式,下面是我的处理方法(本文仅提供思路和参考代码,可能不能直接使用)。
模板文件解析类:
<?php /* * class: 模板解析类 * author: 51JS.COM-ZMM * date: 2011.3.1 * email: [email protected] * 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: [email protected] * 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学习之function的用法
php 操作调试的方法
php输出xml格式字符串(用的这个)
php长字符串定义方法
有关PHP中MVC的开发经验分享
PHP原生模板引擎 最简单的模板引擎
TMDPHP 模板引擎使用教程
php打造属于自己的MVC框架
Pain 全世界最小最简单的PHP模板引擎 (普通版)
php中Smarty模板初体验
模板引擎正则表达式调试小技巧
php skymvc 一款轻量、简单的php
关于php mvc开发模式的感想
PHP发明人谈MVC和网站设计架构 貌似他不支持php用mvc
常见的PHP五种设计模式小结
PHP面向接口编程 耦合设计模式 简单范例
PHP中用接口、抽象类、普通基类实现“面向接口编程”与“耦合方法”简述
php中取得URL的根域名的代码
PHP+JS+rsa数据加密传输实现代码
PHP 事件机制(2)
php模板中出现空行解决方法
smarty模板嵌套之include与fetch性能测试
php下正则来匹配dede模板标签的代码
php自动获取目录下的模板的代码
php Smarty模板生成html文档的方法
Blitz templates 最快的PHP模板引擎
PHP MVC模式在网站架构中的实现分析
MayFish PHP的MVC架构的开发框架