zoukankan      html  css  js  c++  java
  • 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}*/?> $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."ifs+(.*?)".$this->template_tag_right."/is", $content, $match); 187 $match = $this->preg_match_all("ifs+(.*?)", $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."elseifs+(.*?)".$this->template_tag_right."/is", $content, $match); 199 $match = $this->preg_match_all("elseifs+(.*?)", $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."includes+(.*?)".$this->template_tag_right."/is", $content, $match); 218 $match = $this->preg_match_all("includes+(.*?)", $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."foreachs+(.*?)".$this->template_tag_right."/is", $content, $match); 262 $match = $this->preg_match_all("foreachs+(.*?)", $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(!@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 }

     

  • 相关阅读:
    什么是程序员的优秀品质?【转】
    我也来评“超级女声”五强选手
    ddd
    在window 2003 server下遇到的asp错误
    几个asp+操作日期的函数
    vb.net常用函数
    WordPress使用小记
    asp.net身份验证方式
    水晶报表如何导出为Excel文档
    ListView选中selectedItem上下移动
  • 原文地址:https://www.cnblogs.com/XACOOL/p/5441162.html
Copyright © 2011-2022 走看看