前端學PHP之自定義模板引擎

什麼是網站模板?準確地說,是指網站頁面模板,即每一個頁面僅是一個板式,包括結構、樣式和頁面佈局,是建立網頁內容的樣板,也能夠理解爲已有的網頁框架。能夠將模板中原有的內容替換成從服務器端數據庫中動態內容,目的是能夠保持頁面風格一致javascript

  PHP是一種HTML內嵌式的在服務器端執行的腳本語言,因此大部分PHP開發出來的Web應用,初始的開發模板就是混合層的數據編程。雖然經過MVC設計模式能夠把程序應用邏輯與網頁呈現邏輯強制性分離,但也只是將應用程序的輸入、處理和輸出分開,網頁呈現邏輯(視圖)還會有HTML代碼和PHP程序強耦合在一塊兒。PHP腳本的編寫者必須既是網頁設計者,又是PHP開發者php

  如今已經有不少解決方案,能夠將網站的頁面設計和PHP應用程序幾乎徹底分離。這些解決方案稱爲「模板引擎」,它們正在逐步消除因爲缺少層次分離而帶來的難題。模板引擎的目的,就是要達到上述提到的邏輯分離的功能。它能讓程序開發者專一於資料的控制或是功能的達成。所以,模板引擎很適合公司的Web開發團隊使用,使每一個人都能發揮其專長html

  模板引擎技術的核心比較簡單。只要將前端頁面指定爲模板文件,並將這個模板文件中動態的內容,如數據庫輸出、用戶交互等部分,定義成使用特殊「定界符」包含的「變量」,而後放在模板文件中相應的位置。當用戶瀏覽時,由PHP腳本程序打開該模板文件,並將模板文件中定義的變量進行替換。這樣,模板中的特殊變量被替換爲不一樣的動態內容時,就會輸出須要的頁面前端

  目前,能夠在PHP中應用的而且比較成熟的模板有不少,例如Smarty、PHPLIB、IPB等幾十種。使用這些經過PHP編寫的模板引擎,可讓代碼脈絡更加清晰,結構更加合理化。也可讓網站的維護和更新變得更容易,創造一個更加良好的開發環境,讓開發和設計工做更容易結合在一塊兒。可是,沒有哪個PHP模板是最合適、最完美的。由於PHP模板就是大衆化的東西,並非針對某我的開發的。若是能在對模板的特色、應用有清楚的認識基礎上,充分認識到模板的優點劣勢,就能夠知道是否選擇使用模板引擎或選擇使用哪一個模板引擎java

 

自定義模板引擎類

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

  在下例中,經過前面介紹的模板引擎概念建立了屬於本身的一個簡單模板引擎,能夠用來處理模板的基本功能。例如:變量替換、分支結構、數組循環遍歷,以及模板之間相互嵌套等,以下所示:程序員

複製代碼

<?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);                  
        }        
        /**  
            內部使用的私有方法,使用正則表達式將模板文件'<{ }>'中的語句替換爲對應的值或PHP代碼 
            @param    string    $content    提供從模板文件中讀入的所有內容字符串   
            @return    $repContent            返回替換後的字符串        */
        private function tpl_replace($content) {            /* 將左右定界符號中,有影響正則的特殊符號轉義  例如,<{ }>轉義\<\{ \}\> */
            $left = preg_quote($this->left_delimiter, '/');            $right = preg_quote($this->right_delimiter, '/');            /* 匹配模板中各類標識符的正則表達式的模式數組 */
            $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'                    
            );            
            /* 替換從模板中使用正則表達式匹配到的字符串數組 */
            $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解析後發送給客戶端正則表達式

 

使用模板引擎

  使用自定義的模板引擎比較容易,都是本身定義的語法格式。但要記住,全部流行的模板引繁解決方案都遵循一樣的一組相同的核心實現原則,就是與編程語言同樣,學習了一種語言就能夠更容易地掌握其餘語言。使用模板引擎最主要的緣由就是將前端工程師和後端工程師的工做分開,因此模板引擎不只後端工程師須要使用,前端工程師也須要使用sql

一、後端工程師對模板引擎的使用數據庫

  在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」爲特定目錄下的模板文件

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

  前端工程師須要將編寫的模板文件存放到指定的目錄中,這個目錄是經過在模板對象中使用$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中分配過來的動態數據在模板中引用

一、數據庫的設計

  假設數據庫服務器在「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");

二、模板的設計

  模板的設計不要出現任何的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

        <div style="width:200px;margin: 0 auto;">##### <{$author}> #####</div>        
    </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中動態分配給模板的數據

三、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.phpdata中。接着使用包含進來的當前目錄下的「mytplclss.php」文件,建立並初始化模板引擎類的對象tpl。再經過該對象中的assign()方法向模板分配一些數據,而後使用該對象中的display()方法載入模板文件main.tpl。並將模板中標記的特殊變量替換爲從PHP中分配的動態數據,處理完畢之後輸出模板頁面。頁面的輸出結果以下所示

  限於各類不一樣的條件限制,好比時間、經驗,作一個自定義的PHP模板引擎是很是困難的。其實,須要的並非從新構造一個PHP模板,而是選擇一個最貼近本身的PHP模板加以改造

相關文章
相關標籤/搜索