PHP自訂模板引擎的發法

來源:互聯網
上載者:User
本文將詳細介紹PHP中的模板引擎。具有很好的參考價值。感興趣的朋友參考下,希望對大家有所協助。

前面的話

在大多數的項目組中,開發一個Web程式都會出現這樣的流程:計劃文檔提交之後,前端工程師製作了網站的外觀模型,然後把它交給後端工程師,它們使用後端代碼實現程式邏輯,同時使用外觀模型做成基本架構,然後工程被返回到前端工程師繼續完善。就這樣工程可能在後端工程師和前端工程師之間來來回回好幾次。由於後端工程師不干預任何相關HTML標籤,同時也不需要前端代碼和後端代碼混合在一起。前端工程師只需要設定檔,動態區塊和其他的介面部分,不必要去接觸那些錯綜複雜的後端代碼。因此,這時候有一個很好的模板支援就顯得很重要了。本文將詳細介紹PHP中的模板引擎

概述

什麼是網站模板?準確地說,是指網站頁面模板,即每個頁面僅是一個板式,包括結構、樣式和頁面配置,是建立網頁內容的樣板,也可以理解為已有的網頁架構。可以將模板中原有的內容替換成從伺服器端資料庫中動態內容,目的是可以保持頁面風格一致

PHP是一種HTML內嵌式的在伺服器端執行的指令碼語言,所以大部分PHP開發出來的Web應用,初始的開發模板就是混合層的資料編程。雖然通過MVC設計模式可以把程式應用邏輯與網頁呈現邏輯強制性分離,但也只是將應用程式的輸入、處理和輸出分開,網頁呈現邏輯(視圖)還會有HTML代碼和PHP程式強耦合在一起。PHP指令碼的編寫者必須既是網頁設計者,又是PHP開發人員

現在已經有很多解決方案,可以將網站的頁面設計和PHP應用程式幾乎完全分離。這些解決方案稱為“模板引擎”,它們正在逐步消除由於缺乏層次分離而帶來的難題。模板引擎的目的,就是要達到上述提到的邏輯分離的功能。它能讓程式開發人員專註於資料的控制或是功能的達成。因此,模板引擎很適合公司的WebTeam Dev使用,使每個人都能發揮其專長

模板引擎技術的核心比較簡單。只要將前端頁面指定為模板檔案,並將這個模板檔案中動態內容,如資料庫輸出、使用者互動等部分,定義成使用特殊“定界符”包含的“變數”,然後放在模板檔案中相應的位置。當使用者瀏覽時,由PHP指令碼程式開啟該模板檔案,並將模板檔案中定義的變數進行替換。這樣,模板中的特殊變數被替換為不同的動態內容時,就會輸出需要的頁面

目前,可以在PHP中應用的並且比較成熟的模板有很多,例如Smarty、PHPLIB、IPB等幾十種。使用這些通過PHP編寫的模板引擎,可以讓代碼脈絡更加清晰,結構更加合理化。也可以讓網站的維護和更新變得更容易,創造一個更加良好的開發環境,讓開發和設計工作更容易結合在一起。但是,沒有哪一個PHP模板是最合適、最完美的。因為PHP模板就是福士化的東西,並不是針對某個人開發的。如果能在對模板的特點、應用有清楚的認識基礎上,充分認識到模板的優勢劣勢,就可以知道是否選擇使用模板引擎或選擇使用哪個模板引擎

自訂模板引擎類

自訂模板引擎,能夠更好的掌握模板引擎的工作機制,為學習Smarty做好準備。更重要的是,屬於自己的PHP模板引擎永遠不是固定不變的,可以根據項目的需要為其量身定製

在下例中,通過前面介紹的模板引擎概念建立了屬於自己的一個簡單模板引擎,可以用來處理模板的準系統。例如:變數替換、分支結構、數組迴圈遍曆,以及模板之間相互嵌套等,如下所示:

<?php /**  file: mytpl.class.php 類名為MyTpl是自訂的模板引擎  通過該類對象載入模板檔案並解析,將解析後的結果輸出  */ class Mytpl {  public $template_dir = 'templates';  //定義模板檔案存放的目錄   public $compile_dir = 'templates_c';  //定義通過模板引擎組合後檔案存放目錄  public $left_delimiter = '<{';   //在模板中嵌入動態資料變數的左定界符號  public $right_delimiter = '}>';   //在模板中嵌入動態資料變數的右定界符號  private $tpl_vars = array();    //內部使用的臨時變數  /**    將PHP中分配的值會儲存到成員屬性$tpl_vars中,用於將模板中對應的變數進行替換    @param string $tpl_var 需要一個字串參數作為關聯陣列下標,要和模板中的變數名對應    @param mixed $value  需要一個標量類型的值,用來分配給模板中變數的值    */  function assign($tpl_var, $value = null) {    if ($tpl_var != '')         $this->tpl_vars[$tpl_var] = $value;  }  /**    載入指定目錄下的模板檔案,並將替換後的內容產生組合檔案存放到另一個指定目錄下   @param string $fileName 提供模板檔案的檔案名稱              */   function display($fileName) {    /* 到指定的目錄中尋找模板檔案 */   $tplFile = $this->template_dir.'/'.$fileName;    /* 如果需要處理的模板檔案不存在,則退出並報告錯誤 */   if(!file_exists($tplFile)) {         die("模板檔案{$tplFile}不存在!");   }   /* 擷取組合的模板檔案,該檔案中的內容都是被替換過的 */   $comFileName = $this->compile_dir."/com_".$fileName.'.php';    /* 判斷替換後的檔案是否存在或是存在但有改動,都需要重新建立 */   if(!file_exists($comFileName) || filemtime($comFileName) < filemtime($tplFile)) {    /* 調用內部替換模板方法 */    $repContent = $this->tpl_replace(file_get_contents($tplFile));     /* 儲存由系統組合後的指令檔 */    file_put_contents($comFileName, $repContent);   }   /* 包含處理後的模板檔案輸出給用戶端 */   include($comFileName);       }  /**    內部使用的私人方法,使用Regex將模板檔案'<{ }>'中的語句替換為對應的值或PHP代碼    @param string $content 提供從模板檔案中讀入的全部內容字串    @return $repContent   返回替換後的字串  */  private function tpl_replace($content) {   /* 將左右定界符號中,有影響正則的特殊符號轉義 例如,<{ }>轉義\<\{ \}\> */   $left = preg_quote($this->left_delimiter, '/');   $right = preg_quote($this->right_delimiter, '/');   /* 匹配模板中各種標識符的Regex的模式數組 */   $pattern = array(      /* 匹配模板中變數 ,例如,"<{ $var }>" */    '/'.$left.'\s*\$([a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*)\s*'.$right.'/i',      /* 匹配模板中if標識符,例如 "<{ if $col == "sex" }> <{ /if }>" */    '/'.$left.'\s*if\s*(.+?)\s*'.$right.'(.+?)'.$left.'\s*\/if\s*'.$right.'/ies',    /* 匹配elseif標識符, 例如 "<{ elseif $col == "sex" }>" */    '/'.$left.'\s*else\s*if\s*(.+?)\s*'.$right.'/ies',     /* 匹配else標識符, 例如 "<{ else }>" */    '/'.$left.'\s*else\s*'.$right.'/is',     /* 用來匹配模板中的loop標識符,用來遍曆數組中的值, 例如 "<{ loop $arrs $value }> <{ /loop}>" */    '/'.$left.'\s*loop\s+\$(\S+)\s+\$([a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*)\s*'.$right.'(.+?)'.$left.'\s*\/loop\s*'.$right.'/is',    /* 用來遍曆數組中的鍵和值,例如 "<{ loop $arrs $key => $value }> <{ /loop}>" */    '/'.$left.'\s*loop\s+\$(\S+)\s+\$([a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*)\s*=>\s*\$(\S+)\s*'.$right.'(.+?)'.$left.'\s*\/loop \s*'.$right.'/is',     /* 匹配include標識符, 例如,'<{ include "header.html" }>' */    '/'.$left.'\s*include\s+[\"\']?(.+?)[\"\']?\s*'.$right.'/ie'        );   /* 替換從模板中使用Regex匹配到的字串數組 */   $replacement = array(     /* 替換模板中的變數 <?php echo $this->tpl_vars["var"]; */    '<?php echo $this->tpl_vars["${1}"]; ?>',      /* 替換模板中的if字串 <?php if($col == "sex") { ?> <?php } ?> */    '$this->stripvtags(\'<?php if(${1}) { ?>\',\'${2}<?php } ?>\')',      /* 替換elseif的字串 <?php } elseif($col == "sex") { ?> */    '$this->stripvtags(\'<?php } elseif(${1}) { ?>\',"")',     /* 替換else的字串 <?php } else { ?> */    '<?php } else { ?>',     /* 以下兩條用來替換模板中的loop標識符為foreach格式 */    '<?php foreach($this->tpl_vars["${1}"] as $this->tpl_vars["${2}"]) { ?>${3}<?php } ?>',     '<?php foreach($this->tpl_vars["${1}"] as $this->tpl_vars["${2}"] => $this->tpl_vars["${3}"]) { ?>${4}<?php } ?>',     /*替換include的字串*/    'file_get_contents($this->template_dir."/${1}")'       );   /* 使用正則替換函數處理 */   $repContent = preg_replace($pattern, $replacement, $content);     /* 如果還有要替換的標識,遞迴調用自己再次替換 */   if(preg_match('/'.$left.'([^('.$right.')]{1,})'.$right.'/', $repContent)) {      $repContent = $this->tpl_replace($repContent);       }    /* 返回替換後的字串 */   return $repContent;            }   /**   內部使用的私人方法,用來將條件陳述式中使用的變數替換為對應的值   @param string $expr  提供模板中條件陳述式的開始標記      @param string $statement 提供模板中條件陳述式的結束標記    @return strin    將處理後的條件陳述式相連後返回   */  private function stripvtags($expr, $statement='') {   /* 匹配變數的正則 */   $var_pattern = '/\s*\$([a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*)\s*/is';    /* 將變數替換為值 */   $expr = preg_replace($var_pattern, '$this->tpl_vars["${1}"]', $expr);    /* 將開始標記中的引號轉義替換 */   $expr = str_replace("\\\"", "\"", $expr);   /* 替換語句體和結束標記中的引號 */   $statement = str_replace("\\\"", "\"", $statement);    /* 將處理後的條件陳述式相連後返回 */   return $expr.$statement;          } }?>

在Mytpl類中聲明的多個方法中,除被封裝過的方法之外,只有兩個公有方法assign()和display()在建立對象以後可以被凋用。其中assign()方法用來將PHP指令碼中的資料分配給模板中對應的變數,display()方法則用來將特定的templates目錄下的模板檔案載入到PHP指令碼中。同時將模板檔案中使用“<{”和“>>”標記聲明的自訂模板語句,匹配出來並替換成相對應的PHP文法格式,然後將替換後的內容儲存在特定的templates_c目錄下。在運行時還要編譯成一個非模板技術的PHP檔案,並將其以模板檔案名稱加上“com_”首碼和“.php”的副檔名形式儲存。再通過include()函數將處理後的模板檔案包含,並使用PHP解析後發送給用戶端

使用模板引擎

使用自訂的模板引擎比較容易,都是自己定義的文法格式。但要記住,所有流行的模板引繁解決方案都遵循同樣的一組相同的核心實現原則,就是與程式設計語言一樣,學習了一種語言就可以更容易地掌握其他語言。使用模板引擎最主要的原因就是將前端工程師和後端工程師的工作分開,所以模板引擎不僅後端工程師需要使用,前端工程師也需要使用

1、後端工程師對模板引擎的使用

在PHP指令碼中包含模板引擎類所在的檔案。如下所示:

require("mytpl.class.php"); //包含模板引擎類,相當於模板引擎安裝

建立模板引擎類的對象並對一些成員屬性進行初始化賦值。如下所示:

$tpl=new MyTpl(); //建立模板引擎類的對象,也可以根據參數對成員初始化

將動態資料(包括標量和數群組類型的資料,例如從資料庫的表中獲得的資料數組)使用模板引擎對象中的assign()方法分配給模板檔案,這個方法可以使用多次,將任意多個變數分配給模板。如下所示:

$tpl->assign("var","this is a value"); //可以分配標量類型資料,可以使用多次$tpl->assign("arr",array(array(1,2),array("a","b"))); //也可以分配數組包括多維陣列

在PHP指令碼中通過調用模板對象中的display()方法,並將模板檔案名稱作為參數傳入,就會載入指定目錄中對應的模板檔案到PHP指令碼中。再通過模板引擎中的替換方法對模板中自訂的文法進行解析,然後輸出處理後的模板。如下所示:

$tpl->display("test.tpl"); //參數“test.tpl”為特定目錄下的模板檔案

2、前端工程師對模板引擎的使用

前端工程師需要將編寫的模板檔案存放到指定的目錄中,這個目錄是通過在模板對象中使用$template_dir屬性指定的,預設的設定是目前的目錄下的“templates”目錄。另外,模板檔案的命名以及尾碼名的設定可以隨意,例如index.tpl、test.htm、header.tp;等

模板檔案是通過使用HTML、CSS以及javascript等Web前台語言以編寫的純靜態負而。但可以在模板檔案中使用“<{”和“}>”兩個分隔字元中間定義一個變數(類似PHP中的變數格式),該變數可以接受並輸出由PHP指令碼中分配過來的動態資料。在模板中使用的“<{”和“}>”兩個分隔字元對,也可以根據個人愛好在模板引擎類中修改。如下所示:

姓名:<{$name}>,年齡:<{$age}>,性別:<{$sex}> //模板中使用預留位置

如果在PHP指令碼中是將數組分配給模板,也可以在模板中進行遍曆,還可以通過嵌套的方式遍曆多維陣列。使用的是在模板引擎中定義的“<{loop}>”標記對,使用的方式和PHP中foreach結構的文法格式相似。如下所示:

<{loop $arr $value }>     //遍曆數組$arr中的元素值 數組中的元素值<{$value}>   //每次遍曆輸出元素中的值<{/loop}>        //在模板中遍曆數組的結束標記<{loop $arr $key=>$value }>   //遍曆數組$arr中的元素下標和元素值 數組中的元素鍵<{$key}>    //每次遍曆輸出元素中的下標 數組中的元素值<{$value}>   //每次遍曆輸出元素中的值<{/loop}>        //在模板中遍曆數組的結束標記<{loop $arr $value }>     //遍曆數組$arr中的元素值 <{loop $arr $data }>    //使用嵌套標記遍曆二維數組  數組中的元素值<{$value}>  //每次遍曆輸出元素中的值 <{/loop}>       //在模板中遍曆數組的內層結束標記<{/loop}>        //在模板中遍曆數組的外層結束標記

模板引擎還可以解析在模板檔案中使用特殊標記編寫的分支結構,文法風格也是和PHP的分支結構類似。是通過在模板檔案中使用“<{if}>”標記對實現選擇結構,也可以實現多路分支和嵌套分支的選擇結構。如下所示: 

<{if($var=="red")}> <p style="color:red">這是“紅色”的字</p><{elseif($var=="green")}>  <p style="color:green">這是“綠色”的字</p><{else}> <{if($size=20)}>  <p style="font-size:20">這是“20px”大小的字</p> <{/if}><{/if}>

在自訂的模板引擎中,也添加了在模板檔案中包含其他模板檔案的功能。可以使用“<{include‘子模板檔案名稱'}>”標記將子模板包含到當前模板中,還支援在子模板中再次包括另外的子模板。如下所示:

<{include 'other.tpl' }>

使用樣本分析

通過在程式中載入模板引擎可以將前端語言與後端語言的代碼分開。首先在PHP程式中擷取資料庫中儲存的資料,再通過載入模板引擎將資料分配出去,然後將模板檔案再通過模板引擎載入並處理後輸出。所以PHP程式只是建立動態資料,載入模板引擎並將動態資料分配給模板,完成了PHP程式的工作。而模板的設汁也只需要前端工程師獨立完成,使用HTML、CSS及javascript等前台頁面設計語言編寫。另外,在模板檔案中還需要使用模板引擎可以解析的標記,將PHP中分配過來的動態資料在模板中引用

1、資料庫的設計

假設資料庫伺服器在“localhost”主機上,串連的使用者名稱和密碼分別為“admin”和“123456”,在該伺服器上建立一個名為“mydb”的資料庫,並在該資料庫中建立一個名為“User”的使用者表。建立該表的SQL査詢語句如下所示:

CREATE TABLE User( id SMALLINT(3) NOT NULL AUTO_INCREMENT, name VARCHAR(10) NOT NULL DEFAULT '', sex VARCHAR(4) NOT NULL DEFAULT '', age SMALLINT(2) NOT NULL DEFAULT '0', email VARCHAR(20) NOT NULL DEFAULT '', PRIMARY KEY (id));

使用者表User建立完成以後,接著可以向該表中插入一些資料作為樣本示範使用,SQL查詢語句如下所示:

INSERT INTO User (name,sex,age,email) VALUES ("a","男",27,"a@a.com"),("b","女",22,"b@b.com"),("c","女",30,"c@c.com"),("d","女",24,d@d.com);

2、模板的設計

模板的設計不要出現任何的PHP代碼,可以由前端人員來完成。在自訂的模板引擎中,規定了要到指定的目錄中去尋找模板檔案,這個特定的目錄可以在建立模板引擎對象時指定,也可以使用預設的目錄設定,預設可以將模板檔案存放在目前的目錄中的“templates”目錄下。本例共需要三個模板檔案main.tpl、header.tpl和footer.tpl,都存放在這個預設的目錄設定中。這三個模板檔案的代碼如下所示:

模板的頭部檔案header.tpl

<!DOCTYPE html><html lang="en"><head> <meta charset="UTF-8"> <title> <{$title}> </title></head><body>

模板的尾部檔案footer.tpl

  <p style="width:200px;margin: 0 auto;">##### <{$author}> #####</p>   </body></html>

主模板檔案main.tpl

<{include 'header.tpl'}> <table border="1" align="center" width="500">  <{ loop $users $user }>   <tr>     <{loop $user $u }>      <{if $u == "男" }>       <td style="color:green">      <{elseif $u == "女"}>       <td style="color:red">      <{else}>       <td>      <{/if}>       <{$u}></td>        <{/loop}>   </tr>  <{/loop}> </table><{include 'footer.tpl'}>

檔案main.tpl是主模板檔案,在該檔案中使用<{include"header.tpl"}>和<{include"footer.tpl"}>兩個標記分別在該檔案的頂部和底部,將獨立的頭部和尾部模板檔案包含到這個主模板檔案中。並在該檔案中使用<{tableName}>標記擷取從PHP中動態分配過來的表名,以及使用雙層<{loop}>標記嵌套,遍曆從PHP中動態分配過來的在資料庫中擷取到的二維數組$Users,還在<{loop}>標記中使用條件選擇標記<{if}>組合,將資料中性別為“男”的表格背景設定為紅色和一些其他判斷。被包含進來的頭部模板檔案header.tpl和尾部模板檔案footer.tpl也同樣可以擷取從PHP中動態分配給模板的資料

3、PHP程式設計

通過模板引擎的使用,PHP程式員在編寫代碼時,只需要PHP一種語言就可以了,不用再去使用HTML、CSS以及javascript等頁面設計語言完成前端的工作了。下面是一個檔案名稱為index.php的PHP指令檔,和模板引擎類所在的檔案mytpl_class.php在同一個目錄下。代碼如下所示:

<?php //包含模板引擎類 include "mytpl.class.php"; //建立模板引擎對象 $tpl = new Mytpl; //串連資料庫 $pdo = new PDO("mysql:host=localhost;dbname=mydb", "admin", "123456"); //執行SQL語句 $stmt = $pdo -> prepare("select id, name, sex,age,email from User order by id"); $stmt ->execute(); $data = $stmt -> fetchAll(PDO::FETCH_ASSOC); //這是從資料庫擷取的動態資料,需要在模板中顯示 $tpl->assign('title',"自訂模板引擎");$tpl->assign('auto',"小火柴"); $tpl->assign('users',$data); $tpl -> display("main.tpl");?>

在上面的PHP指令檔中,通過PDO對象串連MySQL伺服器,並擷取使用者表User中的全部記錄,並以PHP的二維陣列變數形式儲存在變數data中。接著使用包含進來的目前的目錄下的“mytplclss.php”檔案,建立並初始化模板引擎類的對象data中。接著使用包含進來的目前的目錄下的“mytplclss.php”檔案,建立並初始化模板引擎類的對象tpl。再通過該對象中的assign()方法向模板分配一些資料,然後使用該對象中的display()方法載入模板檔案main.tpl。並將模板中標記的特殊變數替換為從PHP中分配的動態資料,處理完畢以後輸出模板頁面。頁面的輸出結果如下所示

限於各種不同的條件限制,比如時間、經驗,做一個自訂的PHP模板引擎是非常困難的。其實,需要的並不是重新構造一個PHP模板,而是選擇一個最貼近自己的PHP模板加以改造

以上就是本文的全部內容,希望對大家的學習有所協助。


聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.