PHP模板解析类实例

作者:mckee

这篇文章主要介绍了PHP模板解析类,涉及php针对模板文件的解析与字符串处理的相关技巧,具有一定参考借鉴价值,需要的朋友可以参考下

  1 <?php
  2 class template {
  3  private $vars = array();
  4  private $conf = ‘‘;
  5  private $tpl_name = ‘index‘;
  6  //如果模板不存在 会查找当前 controller默认index模板
  7  private $tpl_suffix = ‘.html‘;//如果CONFIG没配置默认后缀 则显示
  8  private $tpl_compile_suffix= ‘.tpl.php‘;//编译模板路径
  9  private $template_tag_left = ‘<{‘;//模板左标签
 10  private $template_tag_right = ‘}>‘;//模板右标签
 11  private $template_c = ‘‘;//编译目录
 12  private $template_path = ‘‘;//模板完整路径
 13  private $template_name = ‘‘;//模板名称 index.html
 14  //定义每个模板的标签的元素
 15  private $tag_foreach = array(‘from‘, ‘item‘, ‘key‘);
 16  private $tag_include = array(‘file‘);//目前只支持读取模板默认路径
 17  public function __construct($conf) {
 18   $this->conf = &$conf;
 19   $this->template_c = $this->conf[‘template_config‘][‘template_c‘];//编译目录
 20   $this->_tpl_suffix = $this->tpl_suffix();
 21  }
 22  private function str_replace($search, $replace, $content) {
 23   if(empty($search) || empty($replace) || empty($content)) return false;
 24   return str_replace($search, $replace, $content);
 25  }
 26  /**
 27   * preg_match_all
 28   * @param $pattern 正则
 29   * @param $content 内容
 30   * @return array
 31   */
 32  private function preg_match_all($pattern, $content) {
 33   if(empty($pattern) || empty($content)) core::show_error(‘查找模板标签失败!‘);
 34   preg_match_all("/".$this->template_tag_left.$pattern.$this->template_tag_right."/is", $content, $match);
 35   return $match;
 36  }
 37  /**
 38   * 模板文件后缀
 39   */
 40  public function tpl_suffix() {
 41   $tpl_suffix = empty($this->conf[‘template_config‘][‘template_suffix‘]) ?
 42        $this->tpl_suffix :
 43        $this->conf[‘template_config‘][‘template_suffix‘] ;
 44   return $tpl_suffix;
 45  }
 46  /**
 47   * 此处不解释了
 48   * @return
 49   */
 50  public function assign($key, $value) {
 51   $this->vars[$key] = $value;
 52  }
 53  /**
 54   * 渲染页面
 55   * @param
 56   * 使用方法 1
 57   * $this->view->display(‘error‘, ‘comm/‘);
 58   * 默认是指向TPL模版的跟目录,所以comm/就是 tpl/comm/error.html
 59   * 使用方法 2
 60   * $this->view->display(‘errorfile‘);
 61   * 默认指向控制器固定的文件夹
 62   * 例如你的域名是 http://heartphp/admin/index, 那么正确路径就是tpl/admin/index/errorfile.html
 63   * @return
 64   */
 65  public function display($filename = ‘‘, $view_path = ‘‘) {
 66   $tpl_path_arr = $this->get_tpl($filename, $view_path);//获取TPL完整路径 并且向指针传送路径以及名称
 67   if(!$tpl_path_arr) core::show_error($filename.$this->_tpl_suffix.‘模板不存在‘);
 68   //编译开始
 69   $this->view_path_param = $view_path;//用户传递过来的模版跟目录
 70   $this->compile();
 71  }
 72  /**
 73   * 编译控制器
 74   * @param
 75   * @return
 76   */
 77  private function compile() {
 78   $filepath = $this->template_path.$this->template_name;
 79   $compile_dirpath = $this->check_temp_compile();
 80   $vars_template_c_name = str_replace($this->_tpl_suffix, ‘‘, $this->template_name);
 81   $include_file = $this->template_replace($this->read_file($filepath), $compile_dirpath, $vars_template_c_name);//解析
 82   if($include_file) {
 83    $this->read_config() && $config = $this->read_config();
 84    extract($this->vars, EXTR_SKIP);
 85    [url=home.php?mod=space&uid=48608]@include[/url] $include_file;
 86   }
 87  }
 88  /**
 89   * 读取当前项目配置文件
 90   */
 91  protected function read_config() {
 92   if(file_exists(SYSTEM_PATH.‘conf/config.php‘)) {
 93    @include SYSTEM_PATH.‘conf/config.php‘;
 94    return $config;
 95   }
 96   return false;
 97  }
 98  /**
 99   * 解析模板语法
100   * @param $str 内容
101   * @param $compile_dirpath 模版编译目录
102   * @param $vars_template_c_name 模版编译文件名
103   * @return 编译过的PHP模板文件名
104   */
105  private function template_replace($str, $compile_dirpath, $vars_template_c_name) {
106   if(empty($str)) core::show_error(‘模板内容为空!‘);
107   //处理编译头部
108   $compile_path = $compile_dirpath.$vars_template_c_name.$this->tpl_compile_suffix;//编译文件
109   if(is_file($compile_path)) {
110    //$header_content = $this->get_compile_header($compile_path);
111    //$compile_date = $this->get_compile_header_comment($header_content);
112    $tpl_filemtime = filemtime($this->template_path.$this->template_name);
113    $compile_filemtime = filemtime($compile_path);
114    //echo $tpl_filemtime.‘==‘.date(‘Y-m-d H:i:s‘, $tpl_filemtime).‘<br/>‘;
115    //echo $compile_filemtime.‘==‘.date(‘Y-m-d H:i:s‘, $compile_filemtime);
116    //如果文件过期编译 当模板标签有include并且有修改时 也重新编译
117    //<{include file="public/left.html"}> 当修改include里的文件,非DEBUG模式时 如果不更改主文件 目前是不重新编译include里的文件,我在考虑是否也要更改,没想好,暂时这样,所以在开发阶段一定要开启DEBUG=1模式 要不然修改include文件无效 。 有点罗嗦,不知道表述清楚没
118    if($tpl_filemtime > $compile_filemtime || DEBUG) {
119     $ret_file = $this->compile_file($vars_template_c_name, $str, $compile_dirpath);
120    } else {
121     $ret_file = $compile_path;
122    }
123   } else {//编译文件不存在 创建他
124    $ret_file = $this->compile_file($vars_template_c_name, $str, $compile_dirpath);
125   }
126   return $ret_file;
127  }
128  /**
129   * 模板文件主体
130   * @param string $str 内容
131   * @return html
132   */
133  private function body_content($str) {
134   //解析
135   $str = $this->parse($str);
136   $header_comment = "Create On##".time()."|Compiled from##".$this->template_path.$this->template_name;
137   $content = "<? if(!defined(‘IS_HEARTPHP‘)) exit(‘Access Denied‘);/*{$header_comment}*/?>\r\n$str";
138   return $content;
139  }
140  /**
141   * 开始解析相关模板标签
142   * @param $content 模板内容
143   */
144  private function parse($content) {
145   //foreach
146   $content = $this->parse_foreach($content);
147   //include
148   $content = $this->parse_include($content);
149   //if
150   $content = $this->parse_if($content);
151   //elseif
152   $content = $this->parse_elseif($content);
153   //模板标签公用部分
154   $content = $this->parse_comm($content);
155   //转为PHP代码
156   $content = $this->parse_php($content);
157   return $content;
158  }
159  /**
160   * echo 如果默认直接<{$config[‘domain‘]}> 转成 <?php echo $config[‘domain‘]?>
161   */
162  private function parse_echo($content) {
163  }
164  /**
165   * 转换为PHP
166   * @param $content html 模板内容
167   * @return html 替换好的HTML
168   */
169  private function parse_php($content){
170   if(empty($content)) return false;
171   $content = preg_replace("/".$this->template_tag_left."(.+?)".$this->template_tag_right."/is", "<?php $1 ?>", $content);
172   return $content;
173  }
174  /**
175   * if判断语句
176   * <{if empty($zhang)}>
177   * zhang
178   * <{elseif empty($liang)}>
179   * liang
180   * <{else}>
181   * zhangliang
182   * <{/if}>
183   */
184  private function parse_if($content) {
185   if(empty($content)) return false;
186   //preg_match_all("/".$this->template_tag_left."if\s+(.*?)".$this->template_tag_right."/is", $content, $match);
187   $match = $this->preg_match_all("if\s+(.*?)", $content);
188   if(!isset($match[1]) || !is_array($match[1])) return $content;
189   foreach($match[1] as $k => $v) {
190    //$s = preg_split("/\s+/is", $v);
191    //$s = array_filter($s);
192    $content = str_replace($match[0][$k], "<?php if({$v}) { ?>", $content);
193   }
194   return $content;
195  }
196  private function parse_elseif($content) {
197   if(empty($content)) return false;
198   //preg_match_all("/".$this->template_tag_left."elseif\s+(.*?)".$this->template_tag_right."/is", $content, $match);
199   $match = $this->preg_match_all("elseif\s+(.*?)", $content);
200   if(!isset($match[1]) || !is_array($match[1])) return $content;
201   foreach($match[1] as $k => $v) {
202    //$s = preg_split("/\s+/is", $v);
203    //$s = array_filter($s);
204    $content = str_replace($match[0][$k], "<?php } elseif ({$v}) { ?>", $content);
205   }
206   return $content;
207  }
208  /**
209   * 解析 include include标签不是实时更新的 当主体文件更新的时候 才更新标签内容,所以想include生效 请修改一下主体文件
210   * 记录一下 有时间开发一个当DEBUG模式的时候 每次执行删除模版编译文件
211   * 使用方法 <{include file="www.phpddt.com"}>
212   * @param $content 模板内容
213   * @return html
214   */
215  private function parse_include($content) {
216   if(empty($content)) return false;
217   //preg_match_all("/".$this->template_tag_left."include\s+(.*?)".$this->template_tag_right."/is", $content, $match);
218   $match = $this->preg_match_all("include\s+(.*?)", $content);
219   if(!isset($match[1]) || !is_array($match[1])) return $content;
220   foreach($match[1] as $match_key => $match_value) {
221    $a = preg_split("/\s+/is", $match_value);
222    $new_tag = array();
223    //分析元素
224    foreach($a as $t) {
225     $b = explode(‘=‘, $t);
226     if(in_array($b[0], $this->tag_include)) {
227      if(!empty($b[1])) {
228       $new_tag[$b[0]] = str_replace("\"", "", $b[1]);
229      } else {
230       core::show_error(‘模板路径不存在!‘);
231      }
232     }
233    }
234    extract($new_tag);
235    //查询模板文件
236    foreach($this->conf[‘view_path‘] as $v){
237     $conf_view_tpl = $v.$file;//include 模板文件
238     if(is_file($conf_view_tpl)) {
239      $c = $this->read_file($conf_view_tpl);
240      $inc_file = str_replace($this->_tpl_suffix, ‘‘, basename($file));
241      $this->view_path_param = dirname($file).‘/‘;
242      $compile_dirpath = $this->check_temp_compile();
243      $include_file = $this->template_replace($c, $compile_dirpath, $inc_file);//解析
244      break;
245     } else {
246      core::show_error(‘模板文件不存在,请仔细检查 文件:‘. $conf_view_tpl);
247     }
248    }
249    $content = str_replace($match[0][$match_key], ‘<?php include("‘.$include_file.‘")?>‘, $content);
250   }
251   return $content;
252  }
253  /**
254   * 解析 foreach
255   * 使用方法 <{foreach from=$lists item=value key=kk}>
256   * @param $content 模板内容
257   * @return html 解析后的内容
258   */
259  private function parse_foreach($content) {
260   if(empty($content)) return false;
261   //preg_match_all("/".$this->template_tag_left."foreach\s+(.*?)".$this->template_tag_right."/is", $content, $match);
262   $match = $this->preg_match_all("foreach\s+(.*?)", $content);
263   if(!isset($match[1]) || !is_array($match[1])) return $content;
264   foreach($match[1] as $match_key => $value) {
265    $split = preg_split("/\s+/is", $value);
266    $split = array_filter($split);
267    $new_tag = array();
268    foreach($split as $v) {
269     $a = explode("=", $v);
270     if(in_array($a[0], $this->tag_foreach)) {//此处过滤标签 不存在过滤
271      $new_tag[$a[0]] = $a[1];
272     }
273    }
274    $key = ‘‘;
275    extract($new_tag);
276    $key = ($key) ? ‘$‘.$key.‘ =>‘ : ‘‘ ;
277    $s = ‘<?php foreach(‘.$from.‘ as ‘.$key.‘ $‘.$item.‘) { ?>‘;
278    $content = $this->str_replace($match[0][$match_key], $s, $content);
279   }
280   return $content;
281  }
282  /**
283   * 匹配结束 字符串
284   */
285  private function parse_comm($content) {
286   $search = array(
287    "/".$this->template_tag_left."\/foreach".$this->template_tag_right."/is",
288    "/".$this->template_tag_left."\/if".$this->template_tag_right."/is",
289    "/".$this->template_tag_left."else".$this->template_tag_right."/is",
290   );
291   $replace = array(
292    "<?php } ?>",
293    "<?php } ?>",
294    "<?php } else { ?>"
295   );
296   $content = preg_replace($search, $replace, $content);
297   return $content;
298  }
299  /**
300   * 检查编译目录 如果没有创建 则递归创建目录
301   * @param string $path 文件完整路径
302   * @return 模板内容
303   */
304  private function check_temp_compile() {
305   //$paht = $this->template_c.
306   $tpl_path = ($this->view_path_param) ? $this->view_path_param : $this->get_tpl_path() ;
307   $all_tpl_apth = $this->template_c.$tpl_path;
308   if(!is_dir($all_tpl_apth)) {
309    $this->create_dir($tpl_path);
310   }
311   return $all_tpl_apth;
312  }
313  /**
314   * 读文件
315   * @param string $path 文件完整路径
316   * @return 模板内容
317   */
318  private function read_file($path) {
319   //$this->check_file_limits($path, ‘r‘);
320   if(($r = @fopen($path, ‘r‘)) === false) {
321    core::show_error(‘模版文件没有读取或执行权限,请检查!‘);
322   }
323   $content = fread($r, filesize($path));
324   fclose($r);
325   return $content;
326  }
327  /**
328   * 写文件
329   * @param string $filename 文件名
330   * @param string $content 模板内容
331   * @return 文件名
332   */
333  private function compile_file($filename, $content, $dir) {
334   if(empty($filename)) core::show_error("{$filename} Creation failed");
335   $content = $this->body_content($content);//对文件内容操作
336   //echo ‘开始编译了=====‘;
337   $f = $dir.$filename.$this->tpl_compile_suffix;
338   //$this->check_file_limits($f, ‘w‘);
339   if(($fp = @fopen($f, ‘wb‘)) === false) {
340    core::show_error($f.‘<br/>编译文件失败,请检查文件权限.‘);
341   }
342   //开启flock
343   flock($fp, LOCK_EX + LOCK_NB);
344   fwrite($fp, $content, strlen($content));
345   flock($fp, LOCK_UN + LOCK_NB);
346   fclose($fp);
347   return $f;
348  }
349  /**
350   * 这个检查文件权限函数 暂时废弃了
351   * @param [$path] [路径]
352   * @param [status] [w=write, r=read]
353   */
354  public function check_file_limits($path , $status = ‘rw‘) {
355   clearstatcache();
356   if(!is_writable($path) && $status == ‘w‘) {
357    core::show_error("{$path}<br/>没有写入权限,请检查.");
358   } elseif(!is_readable($path) && $status == ‘r‘) {
359    core::show_error("{$path}<br/>没有读取权限,请检查.");
360   } elseif($status == ‘rw‘) {//check wirte and read
361    if(!is_writable($path) || !is_readable($path)) {
362     core::show_error("{$path}<br/>没有写入或读取权限,请检查");
363    }
364   }
365  }
366  /**
367   * 读取编译后模板的第一行 并分析成数组
368   * @param string $filepath 文件路径
369   * @param number $line  行数
370   * @return 返回指定行数的字符串
371   */
372  /*
373  private function get_compile_header($filepath, $line = 0) {
374   if(($file_arr = @file($filepath)) === false) {
375    core::show_error($filepath.‘<br/>读取文件失败,请检查文件权限!‘);
376   }
377   return $file_arr[0];
378  }
379  */
380  /**
381   * 分析头部注释的日期
382   * @param string $cotnent 编译文件头部第一行
383   * @return 返回上一次日期
384   */
385  /*
386  private function get_compile_header_comment($content) {
387   preg_match("/\/\*(.*?)\*\//", $content, $match);
388   if(!isset($match[1]) || empty($match[1])) core::show_error(‘编译错误!‘);
389   $arr = explode(‘|‘, $match[1]);
390   $arr_date = explode(‘##‘, $arr[0]);
391   return $arr_date[1];
392  }
393  */
394  /**
395   * 获取模板完整路径 并返回已存在文件
396   * @param string $filename 文件名
397   * @param string $view_path 模板路径
398   * @return
399   */
400  private function get_tpl($filename, $view_path) {
401   empty($filename) && $filename = $this->tpl_name;
402   //遍历模板路径
403   foreach($this->conf[‘view_path‘] as $path) {
404    if($view_path) {//直接从tpl跟目录找文件
405     $tpl_path = $path.$view_path;
406     $view_file_path = $tpl_path.$filename.$this->_tpl_suffix;
407    } else {//根据目录,控制器,方法开始找文件
408     $view_file_path = ($tpl_path = $this->get_tpl_path($path)) ? $tpl_path.$filename.$this->_tpl_suffix : exit(0);
409    }
410    if(is_file($view_file_path)) {
411     //向指针传送模板路径和模板名称
412     $this->template_path = $tpl_path;//
413     $this->template_name = $filename.$this->_tpl_suffix;
414     return true;
415    } else {
416     core::show_error($filename.$this->_tpl_suffix.‘模板不存在‘);
417    }
418   }
419  }
420  /**
421   * 获取模板路径
422   * @param string $path 主目录
423   * @return URL D和M的拼接路径
424   */
425  private function get_tpl_path($path = ‘‘) {
426   core::get_directory_name() && $path_arr[0] = core::get_directory_name();
427   core::get_controller_name() && $path_arr[1] = core::get_controller_name();
428   (is_array($path_arr)) ? $newpath = implode(‘/‘, $path_arr) : core::show_error(‘获取模板路径失败!‘) ;
429   return $path.$newpath.‘/‘;
430  }
431  /**
432   * 创建目录
433   * @param string $path 目录
434   * @return
435   */
436  private function create_dir($path, $mode = 0777){
437   if(is_dir($path)) return false;
438   $dir_arr = explode(‘/‘, $path);
439   $dir_arr = array_filter($dir_arr);
440   $allpath = ‘‘;
441   $newdir = $this->template_c;
442   foreach($dir_arr as $dir) {
443    $allpath = $newdir.‘/‘.$dir;
444    if(!is_dir($allpath)) {
445     $newdir = $allpath;
446     if([email protected]mkdir($allpath, $mode)) {
447      core::show_error( $allpath.‘<br/>创建目录失败,请检查是否有可都写权限!‘);
448     }
449     chmod($allpath, $mode);
450    } else {
451     $newdir = $allpath;
452    }
453   }
454   return true;
455  }
456  public function __destruct(){
457   $this->vars = null;
458   $this->view_path_param = null;
459  }
460 }
时间: 2024-10-10 01:56:44

PHP模板解析类实例的相关文章

C#命令行参数解析类以及使用实例

http://blog.csdn.net/jackxinxu2100/article/details/6642694 编写命令行程序时如何进行命令行参数解析至关重要,下面将引用codeproject里面的一个命令行参数解析类并阐述如何使用来说明C#命令行参数解析的过程. 先看参数解析类,分为CommandLine类以及CommandArgs类,前者负责解析,后者负责结果封装,解析的结果分为三类:即 a=b 对应的key/value类型,-a b 对应的option与option value(可省

C#之Socket操作类实例解析

本文展示了一个C#的Socket操作类的完整实例,并附带了用法说明,分享给大家供大家参考之用.具体方法如下: 主要功能代码如下: using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Net.Sockets; using System.Collections; using System.Net; using System.Runtime.Serializ

深入解析类对象与类实例的创建过程

class MyType(type): def __init__(self,*args,**kwargs): print("init") super(MyType, self).__init__(*args,**kwargs) def __new__(cls, *args, **kwargs): print("new") print("mro",cls.__mro__) # 调用父类的__new__ 方法来实例出一个Base类对象 return

C++解析(26):函数模板与类模板

0.目录 1.函数模板 1.1 函数模板与泛型编程 1.2 多参数函数模板 1.3 函数重载遇上函数模板 2.类模板 2.1 类模板 2.2 多参数类模板与特化 2.3 特化的深度分析 3.小结 1.函数模板 1.1 函数模板与泛型编程 C++中有几种交换变量的方法? 交换变量的方法--定义宏代码块 vs 定义函数: 定义宏代码块 优点:代码复用,适合所有的类型 缺点:编译器不知道宏的存在,缺少类型检查 定义函数 优点:真正的函数调用,编译器对类型进行检查 缺点:根据类型重复定义函数,无法代码复

函数模板与类模板

函数模板,顾名思义,是在生成函数时依照的模板. 有时,我们需要对不同的数据类型做同样的函数操作. 比如:分别对一个int类型数 和 一个double类型数求平方. 这时,虽然都是同样的求平方操作(函数体内代码一样),但是我们必须要编写两个不同的函数,因为处理int类型的函数的参数和返回值类型都应该是int,而处理double类型的函数的参数和返回值都应该是double. 如下:函数体内操作代码一样,设计为重载函数,用相同的函数名,但是参数类型和返回值类型却都不一样,函数需要定义两次. int S

[转载]ECMall模板解析语法与机制

ECMall模板解析语法与机制 2011-05-22 在ECMall模板中,用"{"开头,以"}"结尾就构成一个标签单元,"{"紧接着的单词就是标签名.在标签单元中单词前含"$"(美元符)的为变量名. 资源引用 res标签 功能:返回当前模板当前风格目录的url路径 实例:{res file=css/ecmall.css}这个标签在模板编译后将变成http://商城域名/themes/default/styles/defaul

C++笔记(7):泛型编程和模板(函数模板和类模板)

泛型编程和模板 0.泛型编程 1.函数模板 2.类模板 ----------------------------------------------------------------------------------------------------------- 0.泛型编程 所谓泛型就是以独立于任何特定类型的方式编写代码.前面介绍的标准库的容器.迭代器和算法都是泛型编程的具体应用. 模板是泛型编程的基础.使用模板的时候不需要知道模板是如何定义的,但今天我们来介绍如何定义自己的模板类和模

ECMALL模板解析机制.MVC架构分析及文件目录说明.二次开发指南手册(转)

ECMALL模板解析语法与机制 http://www.nowamagic.net/architecture/archt_TemplateSyntaxAndAnalysis.php ECMALL模块开发指南 http://wenku.baidu.com/view/785b8a1ea76e58fafab003a6.html ECMall 结构图 http://wenku.baidu.com/view/3e9d9921bcd126fff7050b10.html ECMall 数据库表结构 全面讲解 h

IOS中解决ARC类实例间循环引用(Swfit)

原创Blog,转载请注明出处 http://blog.csdn.net/column/details/swfitexperience.html 备注:本文代码和图片主要来自于官方文档 不熟悉ARC的同学可以看看前一篇关于ARC的简述,这个是我的Swfit教程专栏 http://blog.csdn.net/column/details/swift-hwc.html 一.几个用到的关键概念 弱引用(weak):不会增加自动引用计数,必须为可选类型变量,因为弱引用在引用计数为0的时候,会自动赋为nil