首页 > 开发 > PHP > 正文

PHP模板解析类实例

2024-05-04 23:37:36
字体:
来源:转载
供稿:网友

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

希望本文所述对大家的php程序设计有所帮助。

发表评论 共有条评论
用户名: 密码:
验证码: 匿名发表