天天看點

怎麼自己建立一個類似于smart的模版引擎

先說一下模版引擎的的概念,模版引擎是為了使使用者界面與業務資料(内容)分理而産生的,他可以生成特定格式的文檔,用于網站的模版引擎就會生成一個标準的html文檔。

我今天這裡說一下類似與smarty的模版引擎。

我們先來了解一下smart的特點:smarty的特點是将模版編譯成php腳本。smart裡面有專門的模版引擎。模版的隻要功能就是邏輯與顯示的分離,也就是php和html的分離。

其實模版引擎制作的主要有三步驟:

——擷取模版源檔案

——編譯模版(正則替換)

——輸出給使用者

這裡編譯模版是最重要的,需要用到正則替換的知識。我們需要了解一下,我們先學習一下正規表達式的基礎知識。

正規表達式:是使用單個字元串來描述、比對一系列符合某個句法規則的字元串。

為什麼要使用正規表達式:1)正規表達式能夠很大幅度的簡化代碼,實作起來也更為順手。

2)用正規表達式去處理字元串,代碼更容易了解。

3)通常來說,正規表達式的速度遠比自己寫邏輯要高很多。

正規表達式怎麼使用:

正規表達式取決于你使用什麼程式設計語言,比如這裡的php有幾個常用的正規表達式函數

1)preg_match()與preg_match_all();

2)preg_match(pattern,subject,[array &matches])3)pregmatch(pattern,subject,array &matches)

這些是php中常用的正則函數,他的使用方法可以下去查文檔。

然後我說一下那個pattern怎麼寫,他有什麼組成部分。

正規表達式中的元字元

要寫出正規表達式,一定要知道表達式中可以使用哪些字元,代表哪些意思。下面列出了所有的元字元和對于他的描述:

元字元描述

\ 将下一個字元标記為一個特殊字元、或一個原義字元、或一個向後引用、或一個八進制轉義符。例如,“\n”比對一個換行符。“\n”比對字元”n”。序列“\”比對“\”而“(”則比對“(”。

^ 比對輸入字元串的開始位置。如果設定了RegExp對象的Multiline屬性,^也比對“\n”或“\r”之後的位置。

比對輸入字元串的結束位置。如果設定了RegExp對象的Multiline屬性,也比對“\n”或“\r”之前的位置。

  • 比對前面的子表達式零次或多次。例如,zo*能比對“z”以及“zoo”。*等價于{0,}。
  • 比對前面的子表達式一次或多次。例如,“zo+”能比對“zo”以及“zoo”,但不能比對“z”。+等價于{1,}。

? 比對前面的子表達式零次或一次。例如,“do(es)?”可以比對“does”或“does”中的“do”。?等價于{0,1}。

{n} n是一個非負整數。比對确定的n次。例如,“o{2}”不能比對“Bob”中的“o”,但是能比對“food”中的兩個o。

{n,} n是一個非負整數。至少比對n次。例如,“o{2,}”不能比對“Bob”中的“o”,但能比對“foooood”中的所有o。“o{1,}”等價于“o+”。“o{0,}”則等價于“o*”。

{n,m} m和n均為非負整數,其中n<=m。最少比對n次且最多比對m次。例如,“o{1,3}”将比對“fooooood”中的前三個o。“o{0,1}”等價于“o?”。請注意在逗号和兩個數之間不能有空格。

? 當該字元緊跟在任何一個其他限制符(*,+,?,{n},{n,},{n,m})後面時,比對模式是非貪婪的。非貪婪模式盡可能少的比對所搜尋的字元串,而預設的貪婪模式則盡可能多的比對所搜尋的字元串。例如,對于字元串“oooo”,“o?”将比對單個“o”,而“o+”将比對所有“o”。

.點 比對除“\n”之外的任何單個字元。要比對包括“\n”在内的任何字元,請使用像“[\s\S]”的模式。

(pattern) 比對pattern并擷取這一比對。所擷取的比對可以從産生的Matches集合得到,在VBScript中使用SubMatches集合,在JScript中則使用0…9屬性。要比對圓括号字元,請使用“”或“”。

(?:pattern) 比對pattern但不擷取比對結果,也就是說這是一個非擷取比對,不進行存儲供以後使用。這在使用或字元“(|)”來組合一個模式的各個部分是很有用。例如“industr(?:y|ies)”就是一個比“industry|industries”更簡略的表達式。

(?=pattern) 正向肯定預查,在任何比對pattern的字元串開始處比對查找字元串。這是一個非擷取比對,也就是說,該比對不需要擷取供以後使用。例如,“Windows(?=95|98|NT|2000)”能比對“Windows2000”中的“Windows”,但不能比對“Windows3.1”中的“Windows”。預查不消耗字元,也就是說,在一個比對發生後,在最後一次比對之後立即開始下一次比對的搜尋,而不是從包含預查的字元之後開始。

(?!pattern) 正向否定預查,在任何不比對pattern的字元串開始處比對查找字元串。這是一個非擷取比對,也就是說,該比對不需要擷取供以後使用。例如“Windows(?!95|98|NT|2000)”能比對“Windows3.1”中的“Windows”,但不能比對“Windows2000”中的“Windows”。

(?<=pattern) 反向肯定預查,與正向肯定預查類似,隻是方向相反。例如,“(?<=95|98|NT|2000)Windows”能比對“2000Windows”中的“Windows”,但不能比對“3.1Windows”中的“Windows”。

<?php

require_once 'regexTool.class.php';

$regex = new regexTool();
if(!$regex->noEmpty($_POST['username'])) exit('使用者名是必須填寫的!');
if(!$regex->isEmail($_POST['email'])) exit('email格式錯誤!');
if(!$regex->isMobile($_POST['mobile'])) exit('手機号格式錯誤!');

//資料庫寫入

/*
 * @name : show
 * @description : output debug
 * @param $var : input data
 * @return
function show($var = null, $isdump
    $func = $isdump ? 'var_dump' : 'print_r';
    if(empty($var)) {
        echo 'null';
    } elseif(is_array($var) || is_object($var)) {
        //array,object
        echo '<pre>';
        $func($var);
        echo '</pre>';
    } else {
        //string,int,float...
        $func($var);
    }
}      
<html>
    <head>
        <title>使用者注冊</title>
    </head>
    <body>
        <form action="regCheck.php" method="post">
            使用者名 <input type="text" name="username" id="username" value=""<br<br
            email <input type="text" name="email" id="email" value=""<br<br
            手機号 <input type="text" name="mobile" id="mobile" value=""<br<br
            <input type="submit" value="注冊"/>
        </form>
    </body>
</html>      
<?php

require_once 'template.class.php';

$baseDir = str_replace('\\', '/', dirname(__FILE__));
$temp = new template($baseDir.'/source/', $baseDir.'/compiled/');

$temp->assign('test', 'imooc女神');
$temp->assign('pagetitle', '山寨版smarty');

$temp->getSourceTemplate('index');
$temp->compileTemplate();
$temp->display();

/* 
 *功能擴充
 *  檢測編譯目錄下是否存在已編譯的模闆?
 *      否->編譯此模闆
 *      是->編譯後的模闆最後修改時間是否比模闆源檔案更早?
 *          是->重新編譯此模闆
 *          否->加載此模闆
 *
 *為模闆引擎增加比如foreach循環文法,if判斷語句的文法等等
 *
 */      
<?php

class template

    private $templateDir;
    private $compileDir;
    private $leftTag = '{#';
    private $rightTag = '#}';
    private $currentTemp = '';
    private $outputHtml;
    private $varPool = array();

    public function __construct($templateDir, $compileDir, $leftTag = null, $rightTag
        $this->templateDir = $templateDir;
        $this->compileDir = $compileDir;
        if(!empty($leftTag)) $this->leftTag = $leftTag;
        if(!empty($rightTag)) $this->rightTag = $rightTag;
    }

    public function assign($tag, $var)
        $this->varPool[$tag] = $var;
    }

    public function getVar($tag)
        return $this->varPool[$tag];
    }

    public function getSourceTemplate($templateName, $ext = '.html')
        $this->currentTemp = $templateName;
        $sourceFilename = $this->templateDir.$this->currentTemp.$ext;
        $this->outputHtml = file_get_contents($sourceFilename);
    }

    public function compileTemplate($templateName = null, $ext = '.html')
        $templateName = empty($templateName) ? $this->currentTemp : $templateName;

        $pattern = '/'.preg_quote($this->leftTag);
        $pattern .= ' *\$([a-zA-Z_]\w*) *';
        $pattern .= preg_quote($this->rightTag).'/';
        $this->outputHtml = preg_replace($pattern, '<?php echo $this->getVar(\'$1\');?>', $this->outputHtml);

        $compiledFilename = $this->compileDir.md5($templateName).$ext;
        file_put_contents($compiledFilename, $this->outputHtml);
    }

    public function display($templateName = null, $ext = '.html')
        $templateName = empty($templateName) ? $this->currentTemp : $templateName;
        include_once $this->compileDir.md5($templateName).$ext;
    }

}