搜索
首页后端开发php教程PHP模板解析类

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


声明
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn
您什么时候使用特质与PHP中的抽象类或接口?您什么时候使用特质与PHP中的抽象类或接口?Apr 10, 2025 am 09:39 AM

在PHP中,trait适用于需要方法复用但不适合使用继承的情况。1)trait允许在类中复用方法,避免多重继承复杂性。2)使用trait时需注意方法冲突,可通过insteadof和as关键字解决。3)应避免过度使用trait,保持其单一职责,以优化性能和提高代码可维护性。

什么是依赖性注入容器(DIC),为什么在PHP中使用一个?什么是依赖性注入容器(DIC),为什么在PHP中使用一个?Apr 10, 2025 am 09:38 AM

依赖注入容器(DIC)是一种管理和提供对象依赖关系的工具,用于PHP项目中。DIC的主要好处包括:1.解耦,使组件独立,代码易维护和测试;2.灵活性,易替换或修改依赖关系;3.可测试性,方便注入mock对象进行单元测试。

与常规PHP阵列相比,解释SPL SplfixedArray及其性能特征。与常规PHP阵列相比,解释SPL SplfixedArray及其性能特征。Apr 10, 2025 am 09:37 AM

SplFixedArray在PHP中是一种固定大小的数组,适用于需要高性能和低内存使用量的场景。1)它在创建时需指定大小,避免动态调整带来的开销。2)基于C语言数组,直接操作内存,访问速度快。3)适合大规模数据处理和内存敏感环境,但需谨慎使用,因其大小固定。

PHP如何安全地上载文件?PHP如何安全地上载文件?Apr 10, 2025 am 09:37 AM

PHP通过$\_FILES变量处理文件上传,确保安全性的方法包括:1.检查上传错误,2.验证文件类型和大小,3.防止文件覆盖,4.移动文件到永久存储位置。

什么是无效的合并操作员(??)和无效分配运算符(?? =)?什么是无效的合并操作员(??)和无效分配运算符(?? =)?Apr 10, 2025 am 09:33 AM

JavaScript中处理空值可以使用NullCoalescingOperator(??)和NullCoalescingAssignmentOperator(??=)。1.??返回第一个非null或非undefined的操作数。2.??=将变量赋值为右操作数的值,但前提是该变量为null或undefined。这些操作符简化了代码逻辑,提高了可读性和性能。

什么是内容安全策略(CSP)标头,为什么重要?什么是内容安全策略(CSP)标头,为什么重要?Apr 09, 2025 am 12:10 AM

CSP重要因为它能防范XSS攻击和限制资源加载,提升网站安全性。1.CSP是HTTP响应头的一部分,通过严格策略限制恶意行为。2.基本用法是只允许从同源加载资源。3.高级用法可设置更细粒度的策略,如允许特定域名加载脚本和样式。4.使用Content-Security-Policy-Report-Only头部可调试和优化CSP策略。

什么是HTTP请求方法(获取,发布,放置,删除等),何时应该使用?什么是HTTP请求方法(获取,发布,放置,删除等),何时应该使用?Apr 09, 2025 am 12:09 AM

HTTP请求方法包括GET、POST、PUT和DELETE,分别用于获取、提交、更新和删除资源。1.GET方法用于获取资源,适用于读取操作。2.POST方法用于提交数据,常用于创建新资源。3.PUT方法用于更新资源,适用于完整更新。4.DELETE方法用于删除资源,适用于删除操作。

什么是HTTP,为什么对Web应用程序至关重要?什么是HTTP,为什么对Web应用程序至关重要?Apr 09, 2025 am 12:08 AM

HTTPS是一种在HTTP基础上增加安全层的协议,主要通过加密数据保护用户隐私和数据安全。其工作原理包括TLS握手、证书验证和加密通信。实现HTTPS时需注意证书管理、性能影响和混合内容问题。

See all articles

热AI工具

Undresser.AI Undress

Undresser.AI Undress

人工智能驱动的应用程序,用于创建逼真的裸体照片

AI Clothes Remover

AI Clothes Remover

用于从照片中去除衣服的在线人工智能工具。

Undress AI Tool

Undress AI Tool

免费脱衣服图片

Clothoff.io

Clothoff.io

AI脱衣机

AI Hentai Generator

AI Hentai Generator

免费生成ai无尽的。

热门文章

R.E.P.O.能量晶体解释及其做什么(黄色晶体)
3 周前By尊渡假赌尊渡假赌尊渡假赌
R.E.P.O.最佳图形设置
3 周前By尊渡假赌尊渡假赌尊渡假赌
R.E.P.O.如果您听不到任何人,如何修复音频
3 周前By尊渡假赌尊渡假赌尊渡假赌
WWE 2K25:如何解锁Myrise中的所有内容
3 周前By尊渡假赌尊渡假赌尊渡假赌

热工具

SublimeText3 英文版

SublimeText3 英文版

推荐:为Win版本,支持代码提示!

Atom编辑器mac版下载

Atom编辑器mac版下载

最流行的的开源编辑器

WebStorm Mac版

WebStorm Mac版

好用的JavaScript开发工具

VSCode Windows 64位 下载

VSCode Windows 64位 下载

微软推出的免费、功能强大的一款IDE编辑器

MinGW - 适用于 Windows 的极简 GNU

MinGW - 适用于 Windows 的极简 GNU

这个项目正在迁移到osdn.net/projects/mingw的过程中,你可以继续在那里关注我们。MinGW:GNU编译器集合(GCC)的本地Windows移植版本,可自由分发的导入库和用于构建本地Windows应用程序的头文件;包括对MSVC运行时的扩展,以支持C99功能。MinGW的所有软件都可以在64位Windows平台上运行。