溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點(diǎn)擊 登錄注冊 即表示同意《億速云用戶服務(wù)條款》

PHP如何實(shí)現(xiàn)模板解析類

發(fā)布時(shí)間:2021-09-02 14:01:28 來源:億速云 閱讀:140 作者:小新 欄目:開發(fā)技術(shù)

這篇文章主要為大家展示了“PHP如何實(shí)現(xiàn)模板解析類”,內(nèi)容簡而易懂,條理清晰,希望能夠幫助大家解決疑惑,下面讓小編帶領(lǐng)大家一起研究并學(xué)習(xí)一下“PHP如何實(shí)現(xiàn)模板解析類”這篇文章吧。

具體如下:

<?php
class template {
 private $vars = array();
 private $conf = '';
 private $tpl_name = 'index';
 //如果模板不存在 會(huì)查找當(dāng)前 controller默認(rèn)index模板
 private $tpl_suffix = '.html';//如果CONFIG沒配置默認(rèn)后綴 則顯示
 private $tpl_compile_suffix= '.tpl.php';//編譯模板路徑
 private $template_tag_left = '<{';//模板左標(biāo)簽
 private $template_tag_right = '}>';//模板右標(biāo)簽
 private $template_c = '';//編譯目錄
 private $template_path = '';//模板完整路徑 
 private $template_name = '';//模板名稱 index.html
 //定義每個(gè)模板的標(biāo)簽的元素
 private $tag_foreach = array('from', 'item', 'key');
 private $tag_include = array('file');//目前只支持讀取模板默認(rèn)路徑
 public function __construct($conf) {
  $this->conf = &$conf;
  $this->template_c = $this->conf['template_config']['template_c'];//編譯目錄
  $this->_tpl_suffix = $this->tpl_suffix();
 }
 private function str_replace($search, $replace, $content) {
  if(empty($search) || empty($replace) || empty($content)) return false;
  return str_replace($search, $replace, $content);
 }
 /**
  * preg_match_all
  * @param $pattern 正則
  * @param $content 內(nèi)容
  * @return array
  */
 private function preg_match_all($pattern, $content) {
  if(empty($pattern) || empty($content)) core::show_error('查找模板標(biāo)簽失敗!');
  preg_match_all("/".$this->template_tag_left.$pattern.$this->template_tag_right."/is", $content, $match);
  return $match;
 }
 /**
  * 模板文件后綴 
  */
 public function tpl_suffix() {
  $tpl_suffix = empty($this->conf['template_config']['template_suffix']) ? 
       $this->tpl_suffix : 
       $this->conf['template_config']['template_suffix'] ;
  return $tpl_suffix;
 }
 /**
  * 此處不解釋了
  * @return 
  */
 public function assign($key, $value) {
  $this->vars[$key] = $value;
 }
 /**
  * 渲染頁面
  * @param 
  * 使用方法 1
  * $this->view->display('error', 'comm/');
  * 默認(rèn)是指向TPL模版的跟目錄,所以comm/就是 tpl/comm/error.html
  * 使用方法 2
  * $this->view->display('errorfile'); 
  * 默認(rèn)指向控制器固定的文件夾
  * 例如你的域名是 http://heartphp/admin/index, 那么正確路徑就是tpl/admin/index/errorfile.html
  * @return 
  */
 public function display($filename = '', $view_path = '') {
  $tpl_path_arr = $this->get_tpl($filename, $view_path);//獲取TPL完整路徑 并且向指針傳送路徑以及名稱
  if(!$tpl_path_arr) core::show_error($filename.$this->_tpl_suffix.'模板不存在');
  //編譯開始
  $this->view_path_param = $view_path;//用戶傳遞過來的模版跟目錄
  $this->compile();
 }
 /**
  * 編譯控制器
  * @param 
  * @return 
  */
 private function compile() {
  $filepath = $this->template_path.$this->template_name;
  $compile_dirpath = $this->check_temp_compile();
  $vars_template_c_name = str_replace($this->_tpl_suffix, '', $this->template_name);
  $include_file = $this->template_replace($this->read_file($filepath), $compile_dirpath, $vars_template_c_name);//解析
  if($include_file) {
   $this->read_config() && $config = $this->read_config();
   extract($this->vars, EXTR_SKIP);
   [url=home.php?mod=space&uid=48608]@include[/url] $include_file;
  }
 }
 /**
  * 讀取當(dāng)前項(xiàng)目配置文件
  */
 protected function read_config() {
  if(file_exists(SYSTEM_PATH.'conf/config.php')) {
   @include SYSTEM_PATH.'conf/config.php'; 
   return $config;
  }
  return false;
 }
 /**
  * 解析模板語法
  * @param $str 內(nèi)容
  * @param $compile_dirpath 模版編譯目錄
  * @param $vars_template_c_name 模版編譯文件名
  * @return 編譯過的PHP模板文件名
  */
 private function template_replace($str, $compile_dirpath, $vars_template_c_name) {
  if(empty($str)) core::show_error('模板內(nèi)容為空!');
  //處理編譯頭部
  $compile_path = $compile_dirpath.$vars_template_c_name.$this->tpl_compile_suffix;//編譯文件
  if(is_file($compile_path)) {
   //$header_content = $this->get_compile_header($compile_path);
   //$compile_date = $this->get_compile_header_comment($header_content);
   $tpl_filemtime = filemtime($this->template_path.$this->template_name);
   $compile_filemtime = filemtime($compile_path);
   //echo $tpl_filemtime.'=='.date('Y-m-d H:i:s', $tpl_filemtime).'<br/>';
   //echo $compile_filemtime.'=='.date('Y-m-d H:i:s', $compile_filemtime);
   //如果文件過期編譯 當(dāng)模板標(biāo)簽有include并且有修改時(shí) 也重新編譯
   //<{include file="public/left.html"}> 當(dāng)修改include里的文件,非DEBUG模式時(shí) 如果不更改主文件 目前是不重新編譯include里的文件,我在考慮是否也要更改,沒想好,暫時(shí)這樣,所以在開發(fā)階段一定要開啟DEBUG=1模式 要不然修改include文件無效 。 有點(diǎn)羅嗦,不知道表述清楚沒
   if($tpl_filemtime > $compile_filemtime || DEBUG) {
    $ret_file = $this->compile_file($vars_template_c_name, $str, $compile_dirpath);
   } else {
    $ret_file = $compile_path;
   }
  } else {//編譯文件不存在 創(chuàng)建他
   $ret_file = $this->compile_file($vars_template_c_name, $str, $compile_dirpath);
  }
  return $ret_file;
 }
 /**
  * 模板文件主體
  * @param string $str 內(nèi)容
  * @return html
  */
 private function body_content($str) {
  //解析
  $str = $this->parse($str);
  $header_comment = "Create On##".time()."|Compiled from##".$this->template_path.$this->template_name;
  $content = "<? if(!defined('IS_HEARTPHP')) exit('Access Denied');/*{$header_comment}*/?>\r\n$str";
  return $content;
 }
 /**
  * 開始解析相關(guān)模板標(biāo)簽
  * @param $content 模板內(nèi)容
  */
 private function parse($content) {
  //foreach
  $content = $this->parse_foreach($content);
  //include
  $content = $this->parse_include($content);
  //if
  $content = $this->parse_if($content);
  //elseif
  $content = $this->parse_elseif($content);
  //模板標(biāo)簽公用部分
  $content = $this->parse_comm($content); 
  //轉(zhuǎn)為PHP代碼
  $content = $this->parse_php($content);
  return $content;
 }
 /**
  * echo 如果默認(rèn)直接<{$config['domain']}> 轉(zhuǎn)成 <?php echo $config['domain']?>
  */
 private function parse_echo($content) {
 }
 /**
  * 轉(zhuǎn)換為PHP
  * @param $content html 模板內(nèi)容
  * @return html 替換好的HTML
  */
 private function parse_php($content){
  if(empty($content)) return false;
  $content = preg_replace("/".$this->template_tag_left."(.+?)".$this->template_tag_right."/is", "<?php $1 ?>", $content);
  return $content;
 }
 /**
  * if判斷語句
  * <{if empty($zhang)}>
  * zhang
  * <{elseif empty($liang)}>
  * liang
  * <{else}>
  * zhangliang
  * <{/if}>
  */
 private function parse_if($content) {
  if(empty($content)) return false;
  //preg_match_all("/".$this->template_tag_left."if\s+(.*?)".$this->template_tag_right."/is", $content, $match);
  $match = $this->preg_match_all("if\s+(.*?)", $content);
  if(!isset($match[1]) || !is_array($match[1])) return $content;
  foreach($match[1] as $k => $v) {
   //$s = preg_split("/\s+/is", $v);
   //$s = array_filter($s);
   $content = str_replace($match[0][$k], "<?php if({$v}) { ?>", $content);
  }
  return $content;
 }
 private function parse_elseif($content) {
  if(empty($content)) return false;
  //preg_match_all("/".$this->template_tag_left."elseif\s+(.*?)".$this->template_tag_right."/is", $content, $match);
  $match = $this->preg_match_all("elseif\s+(.*?)", $content);
  if(!isset($match[1]) || !is_array($match[1])) return $content;
  foreach($match[1] as $k => $v) {
   //$s = preg_split("/\s+/is", $v);
   //$s = array_filter($s);
   $content = str_replace($match[0][$k], "<?php } elseif ({$v}) { ?>", $content);
  }
  return $content;
 }
 /**
  * 解析 include include標(biāo)簽不是實(shí)時(shí)更新的 當(dāng)主體文件更新的時(shí)候 才更新標(biāo)簽內(nèi)容,所以想include生效 請修改一下主體文件
  * 記錄一下 有時(shí)間開發(fā)一個(gè)當(dāng)DEBUG模式的時(shí)候 每次執(zhí)行刪除模版編譯文件
  * 使用方法 <{include file="www.phpddt.com"}>
  * @param $content 模板內(nèi)容
  * @return html
  */
 private function parse_include($content) {
  if(empty($content)) return false;
  //preg_match_all("/".$this->template_tag_left."include\s+(.*?)".$this->template_tag_right."/is", $content, $match);
  $match = $this->preg_match_all("include\s+(.*?)", $content);
  if(!isset($match[1]) || !is_array($match[1])) return $content;
  foreach($match[1] as $match_key => $match_value) {
   $a = preg_split("/\s+/is", $match_value);
   $new_tag = array();
   //分析元素
   foreach($a as $t) {
    $b = explode('=', $t);
    if(in_array($b[0], $this->tag_include)) {
     if(!empty($b[1])) {
      $new_tag[$b[0]] = str_replace("\"", "", $b[1]);
     } else {
      core::show_error('模板路徑不存在!');
     }
    }
   }
   extract($new_tag);
   //查詢模板文件
   foreach($this->conf['view_path'] as $v){
    $conf_view_tpl = $v.$file;//include 模板文件
    if(is_file($conf_view_tpl)) {
     $c = $this->read_file($conf_view_tpl);
     $inc_file = str_replace($this->_tpl_suffix, '', basename($file));
     $this->view_path_param = dirname($file).'/';
     $compile_dirpath = $this->check_temp_compile();
     $include_file = $this->template_replace($c, $compile_dirpath, $inc_file);//解析
     break;
    } else {
     core::show_error('模板文件不存在,請仔細(xì)檢查 文件:'. $conf_view_tpl);
    }
   } 
   $content = str_replace($match[0][$match_key], '<?php include("'.$include_file.'")?>', $content);
  }
  return $content;
 }
 /**
  * 解析 foreach
  * 使用方法 <{foreach from=$lists item=value key=kk}>
  * @param $content 模板內(nèi)容
  * @return html 解析后的內(nèi)容
  */
 private function parse_foreach($content) {
  if(empty($content)) return false;
  //preg_match_all("/".$this->template_tag_left."foreach\s+(.*?)".$this->template_tag_right."/is", $content, $match);
  $match = $this->preg_match_all("foreach\s+(.*?)", $content);
  if(!isset($match[1]) || !is_array($match[1])) return $content;
  foreach($match[1] as $match_key => $value) {
   $split = preg_split("/\s+/is", $value);
   $split = array_filter($split);
   $new_tag = array();
   foreach($split as $v) {
    $a = explode("=", $v);
    if(in_array($a[0], $this->tag_foreach)) {//此處過濾標(biāo)簽 不存在過濾
     $new_tag[$a[0]] = $a[1];
    }
   }
   $key = '';
   extract($new_tag);
   $key = ($key) ? '$'.$key.' =>' : '' ;
   $s = '<?php foreach('.$from.' as '.$key.' $'.$item.') { ?>';
   $content = $this->str_replace($match[0][$match_key], $s, $content);
  }
  return $content;
 }
 /**
  * 匹配結(jié)束 字符串
  */
 private function parse_comm($content) {
  $search = array(
   "/".$this->template_tag_left."\/foreach".$this->template_tag_right."/is",
   "/".$this->template_tag_left."\/if".$this->template_tag_right."/is",
   "/".$this->template_tag_left."else".$this->template_tag_right."/is",
  );
  $replace = array(
   "<?php } ?>",
   "<?php } ?>",
   "<?php } else { ?>"
  );
  $content = preg_replace($search, $replace, $content);
  return $content;
 }
 /**
  * 檢查編譯目錄 如果沒有創(chuàng)建 則遞歸創(chuàng)建目錄
  * @param string $path 文件完整路徑
  * @return 模板內(nèi)容
  */
 private function check_temp_compile() {
  //$paht = $this->template_c.
  $tpl_path = ($this->view_path_param) ? $this->view_path_param : $this->get_tpl_path() ;
  $all_tpl_apth = $this->template_c.$tpl_path;
  if(!is_dir($all_tpl_apth)) {
   $this->create_dir($tpl_path);
  }
  return $all_tpl_apth;
 }
 /**
  * 讀文件
  * @param string $path 文件完整路徑
  * @return 模板內(nèi)容
  */
 private function read_file($path) {
  //$this->check_file_limits($path, 'r');
  if(($r = @fopen($path, 'r')) === false) {
   core::show_error('模版文件沒有讀取或執(zhí)行權(quán)限,請檢查!');
  }
  $content = fread($r, filesize($path));
  fclose($r);
  return $content;
 }
 /**
  * 寫文件
  * @param string $filename 文件名
  * @param string $content 模板內(nèi)容
  * @return 文件名
  */
 private function compile_file($filename, $content, $dir) {
  if(empty($filename)) core::show_error("{$filename} Creation failed");
  $content = $this->body_content($content);//對(duì)文件內(nèi)容操作
  //echo '開始編譯了=====';
  $f = $dir.$filename.$this->tpl_compile_suffix;
  //$this->check_file_limits($f, 'w');
  if(($fp = @fopen($f, 'wb')) === false) {
   core::show_error($f.'<br/>編譯文件失敗,請檢查文件權(quán)限.');
  }
  //開啟flock
  flock($fp, LOCK_EX + LOCK_NB);
  fwrite($fp, $content, strlen($content));
  flock($fp, LOCK_UN + LOCK_NB);
  fclose($fp);
  return $f;
 }
 /**
  * 這個(gè)檢查文件權(quán)限函數(shù) 暫時(shí)廢棄了
  * @param [$path] [路徑]
  * @param [status] [w=write, r=read]
  */
 public function check_file_limits($path , $status = 'rw') {
  clearstatcache();
  if(!is_writable($path) && $status == 'w') {
   core::show_error("{$path}<br/>沒有寫入權(quán)限,請檢查.");
  } elseif(!is_readable($path) && $status == 'r') {
   core::show_error("{$path}<br/>沒有讀取權(quán)限,請檢查.");
  } elseif($status == 'rw') {//check wirte and read
   if(!is_writable($path) || !is_readable($path)) {
    core::show_error("{$path}<br/>沒有寫入或讀取權(quán)限,請檢查");
   }
  }
 }
 /**
  * 讀取編譯后模板的第一行 并分析成數(shù)組
  * @param string $filepath 文件路徑
  * @param number $line  行數(shù)
  * @return 返回指定行數(shù)的字符串 
  */
 /*
 private function get_compile_header($filepath, $line = 0) {
  if(($file_arr = @file($filepath)) === false) {
   core::show_error($filepath.'<br/>讀取文件失敗,請檢查文件權(quán)限!');
  }
  return $file_arr[0];
 }
 */
 /**
  * 分析頭部注釋的日期
  * @param string $cotnent 編譯文件頭部第一行
  * @return 返回上一次日期 
  */
 /*
 private function get_compile_header_comment($content) {
  preg_match("/\/\*(.*?)\*\//", $content, $match);
  if(!isset($match[1]) || empty($match[1])) core::show_error('編譯錯(cuò)誤!'); 
  $arr = explode('|', $match[1]);
  $arr_date = explode('##', $arr[0]);
  return $arr_date[1];
 }
 */
 /**
  * 獲取模板完整路徑 并返回已存在文件
  * @param string $filename 文件名
  * @param string $view_path 模板路徑 
  * @return 
  */
 private function get_tpl($filename, $view_path) {
  empty($filename) && $filename = $this->tpl_name;
  //遍歷模板路徑
  foreach($this->conf['view_path'] as $path) {
   if($view_path) {//直接從tpl跟目錄找文件
    $tpl_path = $path.$view_path;
    $view_file_path = $tpl_path.$filename.$this->_tpl_suffix;
   } else {//根據(jù)目錄,控制器,方法開始找文件
    $view_file_path = ($tpl_path = $this->get_tpl_path($path)) ? $tpl_path.$filename.$this->_tpl_suffix : exit(0);
   }
   if(is_file($view_file_path)) {
    //向指針傳送模板路徑和模板名稱
    $this->template_path = $tpl_path;//
    $this->template_name = $filename.$this->_tpl_suffix;
    return true;
   } else {
    core::show_error($filename.$this->_tpl_suffix.'模板不存在');
   }
  }
 }
 /**
  * 獲取模板路徑
  * @param string $path 主目錄
  * @return URL D和M的拼接路徑
  */
 private function get_tpl_path($path = '') {
  core::get_directory_name() && $path_arr[0] = core::get_directory_name();
  core::get_controller_name() && $path_arr[1] = core::get_controller_name();
  (is_array($path_arr)) ? $newpath = implode('/', $path_arr) : core::show_error('獲取模板路徑失敗!') ;
  return $path.$newpath.'/';
 }
 /**
  * 創(chuàng)建目錄
  * @param string $path 目錄
  * @return 
  */
 private function create_dir($path, $mode = 0777){
  if(is_dir($path)) return false;
  $dir_arr = explode('/', $path);
  $dir_arr = array_filter($dir_arr);
  $allpath = '';
  $newdir = $this->template_c;
  foreach($dir_arr as $dir) {
   $allpath = $newdir.'/'.$dir;
   if(!is_dir($allpath)) {
    $newdir = $allpath;
    if(!@mkdir($allpath, $mode)) {
     core::show_error( $allpath.'<br/>創(chuàng)建目錄失敗,請檢查是否有可都寫權(quán)限!');
    }
    chmod($allpath, $mode);
   } else {
    $newdir = $allpath;
   }
  }
  return true;
 }
 public function __destruct(){
  $this->vars = null;
  $this->view_path_param = null;
 }
}

以上是“PHP如何實(shí)現(xiàn)模板解析類”這篇文章的所有內(nèi)容,感謝各位的閱讀!相信大家都有了一定的了解,希望分享的內(nèi)容對(duì)大家有所幫助,如果還想學(xué)習(xí)更多知識(shí),歡迎關(guān)注億速云行業(yè)資訊頻道!

向AI問一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

php
AI