亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb

首頁 > 編程 > PHP > 正文

超越模板引擎

2019-09-08 23:11:29
字體:
供稿:網(wǎng)友

總體來說,模板引擎是一個"好東西"

作為一個PHP/Perl的程序員,許多模板引擎(fastTemplate, Smarty, Perl的 HTML::Template)的用戶,以及我自己的(bTemplate [1] 的作者),我講這句話很多次了。

然而,在同事進(jìn)行了長時間的討論之后,我確信了大量的模板引擎(包括我自己寫的)根本是錯誤的。 我想唯一的例外是Smarty [2],雖然我認(rèn)為它太龐大了,并且考慮到這篇文章的其余部分相當(dāng)?shù)臎]有觀點(diǎn)。然而,就你為什么選擇Smarty(或者類似的解決方案)有幾個理由,這些將在文章后面探究。

這篇文章討論模板的理論。我們將看到為什么大部分"模板引擎"是過于肥大,并且最終我們將回過頭來看一個輕量級的,小巧快速的另類選擇。


下載和授權(quán)

模板類和所有在本文中使用的例子能夠在這里下載:template.zip [3]。你可以根據(jù)發(fā)布 [4]在 OSI [5] 的 MIT Open Source License使用這些文件中的代碼。


一些關(guān)于模板引擎的背景知識

讓我們首先研究一下模板引擎的背景知識。模板引擎被設(shè)計(jì)出來用于把商業(yè)邏輯(例如從數(shù)據(jù)庫中獲取數(shù)據(jù)或者計(jì)算貿(mào)易耗費(fèi))從數(shù)據(jù)的表現(xiàn)分離開來。模板引擎解決了兩個主要問題:

  1. 如何實(shí)現(xiàn)這種分離
  2. 如何從HTML中分離"復(fù)雜"的php代碼

這從理論上使得沒有PHP經(jīng)驗(yàn)的HTML設(shè)計(jì)者能夠不看任何PHP代碼的條件下修改站點(diǎn)的外觀。

然而,模板系統(tǒng)也引入了一些復(fù)雜性。首先,我們現(xiàn)在有一個從多個文件得來的"頁面"。典型的,你可能有一個主PHP頁負(fù)責(zé)業(yè)務(wù)邏輯,一個外面的"布局"模板把整個站點(diǎn)的整體布局進(jìn)行渲染,一個內(nèi)部的內(nèi)容特定的模板,一個數(shù)據(jù)庫抽象層,以及模板引擎本身(這些可能是也可能不是由多個文件組成)。也有可能,一些人僅僅簡單地在每個PHP頁面的首尾處包含"頭部"和"尾部"文件。

這產(chǎn)生的單個頁面的文件數(shù)量是很可觀的。然而,因?yàn)镻HP解析器非??欤玫降奈募?shù)量可能不是那么重要除非你的站點(diǎn)流量很大。
然而,要記住模板系統(tǒng)引入了另外一個處理的層次。模板文件不僅僅是必須被包含,他們還必須被解析(取決于模板系統(tǒng),這個行為有很多種方式來完成 ―― 使用正則表達(dá)式,字符串替換,編譯,詞法分析,等等)。這就是為什么對模板進(jìn)行測速變得流行起來:因?yàn)槟0逡媸褂酶鞣N方法來解析數(shù)據(jù),它們中的一些比另外一些要快(而且,一些模板引擎提供了比其他引擎更加豐富的功能)。


模板引擎基礎(chǔ)知識

簡單地說,模板引擎利用了用C寫的腳本語言(PHP)。在這些嵌入的腳本語言中,你有另外一個偽腳本語言(無論你的模板引擎支持何種標(biāo)簽)。某些提供了簡單的變量改寫和循環(huán)。另外一些呢,則提供了條件和嵌套循環(huán)。而再其他的呢(至少有Smarty)提供了一個PHP的比較大的子集的接口,以及一個緩沖層。

為什么我認(rèn)為Smarty最接近于正確的方向?因?yàn)镾marty的目標(biāo)是"把業(yè)務(wù)邏輯從表現(xiàn)中分離出來"而不是"PHP代碼和HTML代碼的分離"。這看上去區(qū)別不大,但是它正是要點(diǎn)所在。任何模板引擎的最終目標(biāo)不應(yīng)該是從HTML移除所有的邏輯。它應(yīng)該是把表現(xiàn)邏輯從業(yè)務(wù)邏輯中分離出來。

有很多你僅僅需要邏輯來正確顯示你的數(shù)據(jù)的例子。例如,你的業(yè)務(wù)邏輯是從你的數(shù)據(jù)庫中獲取一個用戶列表。你的表現(xiàn)邏輯可能是把用戶列表用3列顯示??赡苄薷挠脩袅斜砗瘮?shù)使得它返回3個數(shù)組是很笨的辦法。畢竟函數(shù)不應(yīng)該關(guān)心數(shù)據(jù)接下來要怎么處理這樣的事情。然而,在你的模板文件中缺少一些邏輯,那些正是你要做的事情。

在這點(diǎn)上Smarty是正確的(使得你利用PHP的很多東西),但是仍然有許多問題。基本上,它僅僅提供了一個以新語法訪問PHP的接口。以那開始,它看上去不那么聰明了。是不是事實(shí)上寫 {foreach --args}<? foreach --args ?> 更加簡單?如果你認(rèn)為這樣簡單一些,問問你自己是不是在包含一個巨大的模板庫來到成這種分離時能夠看到真正的意義要更加簡單一些。誠然,Smarty提供了許多其他很好的特性,但是看上去這些益處能夠在不用承擔(dān)包含Smarty類庫的情況下也能獲得。


別樣的解決方案

我主要要鼓吹的一個解決方案是一個使用PHP代碼作為它的原生腳本語言的"模板引擎"。我知道這以前有人做過。而且當(dāng)我第一次看到的時候,我想,"為什么要這樣做?",然而我在考慮過我同事的論據(jù)之后,并且實(shí)現(xiàn)了一個直接使用PHP代碼仍然實(shí)現(xiàn)了把業(yè)務(wù)邏輯和表現(xiàn)邏輯分離的最終目標(biāo)的模板系統(tǒng)時(只用了大約25行代碼,不包括注釋),我意識到了好處所在。

這個系統(tǒng)給像我們這樣的開發(fā)者提供了對PHP核心函數(shù)的訪問權(quán)利,我們能夠使用他們來格式化輸出――像日期格式化這樣的任務(wù)應(yīng)該在模板中處理。而且,因?yàn)槟0迨瞧胀ǖ腜HP文件,像Zend Performance Suite [6] 和PHP Accelerator [7] 這樣的字節(jié)碼緩存程序,能夠自動緩存模板(因而,它們不需要在每次被訪問時都被重新解釋執(zhí)行)。只要你記得把你的模板文件命名為程序能夠辨認(rèn)出是PHP文件的名字(通常,你僅僅需要確保它們有一個.php的后綴),這確實(shí)是一個好處。

當(dāng)我認(rèn)為這種方法比經(jīng)典的模板引擎要高明得多時,肯定還有一些要商榷的問題。最明顯的反面意見是,PHP代碼太復(fù)雜了,而且設(shè)計(jì)者不應(yīng)該強(qiáng)迫去學(xué)習(xí)PHP。事實(shí)上,PHP代碼和像Smarty這樣的高級模板引擎的語法差不多簡單(如果不是更簡單的話)。而且,設(shè)計(jì)者能夠使用像<?=$var;?>這樣的簡寫PHP。這要比{$var}復(fù)雜很多?當(dāng)然,這要長一些,但是如果你習(xí)慣了,你能夠獲得了PHP的威力而且不用承受解析模板文件帶來的負(fù)擔(dān)。

第二,而且可能更重要的,在基于PHP的模板中沒有固有的安全。Smarty提供了選項(xiàng)在模板文件中徹底禁用PHP代碼。它使得開發(fā)者能夠約束模板能夠訪問的函數(shù)和變量。如果你沒有不懷好意的設(shè)計(jì)者,這不會是什么問題。然而,如果你允許外部的用戶上傳或者修改模板,我在此展示的基于PHP的解決方案絕對沒有任何安全可言!任何代碼都能放入模板中并且得到運(yùn)行。是的,甚至是一個print_r($GLOBALS)(這將改有惡意的用戶訪問腳本中任何變量的權(quán)利)。

但是,我個人或者工作上寫過的項(xiàng)目中,絕大多數(shù)不允許最終的用戶修改或者上傳模板。如果是這樣,問題就不存在了。因此現(xiàn)在讓我們來看看代碼吧。


例子

這是一個簡單的用戶列表頁面的例子。

<?php  
require_once('template.php');  

/**  
* This variable holds the file system path to all our template files.  
*/  
$path = './templates/';  

/**  
* Create a template object for the outer template and set its variables.  
*/  
$tpl = & new Template($path);  
$tpl->set('title', 'User List');  

/**  
* Create a template object for the inner template and set its variables.  The  
* fetch_user_list() function simply returns an array of users.  
*/  
$body = & new Template($path);  
$body->set('user_list', fetch_user_list());  

/**  
* Set the fetched template of the inner template to the 'body' variable in  
* the outer template.  
*/  
$tpl->set('body', $body->fetch('user_list.tpl.php'));  

/**  
* Echo the results.  
*/  
echo $tpl->fetch('index.tpl.php');  
?>

其中有兩個值得注意的重要的概念。第一個就是內(nèi)部和外部模板的概念。外部模板包含定義站點(diǎn)主要外觀的HTML代碼。而內(nèi)部模板包含定義站點(diǎn)內(nèi)容區(qū)域的HTML代碼。當(dāng)然,你能夠在任意數(shù)目的層上有任意數(shù)目的模板。因?yàn)橥ǔN覀兘o每個區(qū)域使用不同的模板對象,所以沒有名字空間的問題。例如,我能在內(nèi)部和外部模板中都有變量叫"title",而不用害怕有什么沖突。

這是一個用來顯示用戶列表的模板的簡單例子。注意特殊的foreach和endforeach;語法在PHP手冊中有說明 [8]。它完全是可選擇的。

而且,你可能奇怪我為什么要用.php的后綴來命名我的模板文件。呵呵,許多PHP字節(jié)碼緩存解決方案(比如 phpAccelerator)如果要被認(rèn)成PHP文件,需要文件有一個.php后綴。因?yàn)檫@些模板是PHP文件,為什么不去獲得這些好處?

<table>  
  <tr>  
      <th>Id</th>  
      <th>Name</th>  
      <th>Email</th>  
      <th>Banned</th>  
  </tr>  
<? foreach($user_list as $user): ?>  
  <tr>  
      <td align="center"><?=$user['id'];?></td>  
      <td><?=$user['name'];?></td>  
      <td><a href="mailto:<?=$user['email'];?>"><?=$user['email'];?></a></td>  
      <td align="center"><?=($user['banned'] ? 'X' : ' ');?></td>  
  </tr>  
<? endforeach; ?>  
</table>


這個layout.tpl.php是一個簡單的例子(定義了整個頁面看上去是什么樣子的模板文件)

<html>
  <head>
      <title><?=$title;?></title>
  </head>

  <body>

      <h2><?=$title;?></h2>

<?=$body;?>

  </body>
</html>


而這是解析后的輸出。

<html>
<head>
  <title>User List</title>
</head>

<body>

  <h2>User List</h2>

<table>
<tr>
  <th>Id</th>
  <th>Name</th>
  <th>Email</th>
  <th>Banned</th>
</tr>
<tr>
  <td align="center">1</td>
  <td>bob</td>
  <td><a href="mailto:bob@mozilla.org">bob@mozilla.org</a></td>
  <td align="center"> </td>
</tr>
<tr>
  <td align="center">2</td>
  <td>judy</td>
  <td><a href="mailto:judy@php.net">judy@php.net</a></td>
  <td align="center"> </td>
</tr>
<tr>
  <td align="center">3</td>
  <td>joe</td>
  <td><a href="mailto:joe@opera.com">joe@opera.com</a></td>
  <td align="center"> </td>
</tr>
<tr>
  <td align="center">4</td>
  <td>billy</td>
  <td><a href="mailto:billy@wakeside.com">billy@wakeside.com</a></td>
  <td align="center">X</td>
</tr>
<tr>
  <td align="center">5</td>
  <td>eileen</td>
  <td><a href="mailto:eileen@slashdot.org">eileen@slashdot.org</a></td>
  <td align="center"> </td>
</tr>
</table>
</body>
</html>


緩存

因?yàn)榻鉀Q方案簡單如斯,實(shí)現(xiàn)模板緩存成為了一個非常簡單的任務(wù)。為了實(shí)現(xiàn)緩存,我們有一個二級類,它擴(kuò)展了原來的模板類。CachedTemplate類事實(shí)上使用和原來的模板類相同的API。不同點(diǎn)是我們必須傳遞緩存的設(shè)置給構(gòu)造函數(shù),并且調(diào)用fetch_cache()而不是fetch()。

緩存的概念是簡單的。簡單的說,我們設(shè)置一個緩存時間來調(diào)表輸出應(yīng)該被保存的時長(以秒為單位)。在產(chǎn)生一個頁面的所有工作開展之前,我們必須首先測試頁面是否已經(jīng)被緩存了,而且緩存是否仍然沒有過期。如果緩存在這那,我們不需要在去麻煩數(shù)據(jù)庫和業(yè)務(wù)邏輯來產(chǎn)生頁面――我們可以簡單地輸出原先緩存地內(nèi)容。

這種方法需要解決唯一地標(biāo)識緩存文件的問題。如果一個站點(diǎn)是被一個顯示基于GET變量的中心腳本所控制,對每個PHP文件只有一個緩存不會有什么幫助。例如,如果index.php?page=about_us和用戶調(diào)用index.php?page=contact_us得到的顯示完全不同。

問題是通過給每個頁面產(chǎn)生一個唯一的cache_id來解決的。為了做到這個目的,我們把事實(shí)上被請求的文件變成REQUEST_URI(基本上就是整個URL:index.php?foo=bar&bar=foo)。當(dāng)然,這個轉(zhuǎn)換過程是受到CachedTemplate類控制的,但是要記住的重要的事情是你絕對要在創(chuàng)建CachedTemplate對象時傳遞一個唯一的cache_id。當(dāng)然下面有例子來說明。

使用緩存包括以下步驟。

  1. include() 模板源文件

  2. 創(chuàng)建一個新的CachedTemplate對象(并且傳遞路徑,唯一的cache_id和緩存過期時間給模板)

  3. 測試內(nèi)容是否已經(jīng)被緩存了

  4. 如果還促拿了,顯示文件并且結(jié)束腳本

  5. 否則,進(jìn)行所有的處理并且fetch()模板

  6. fetch_cache()的調(diào)用將自動產(chǎn)生一個新的緩存文件

這個腳本假定你的緩存文件將放到./cache/中,因此你必須創(chuàng)建那個目錄并且改變它的目錄權(quán)限(chmod)使得Web服務(wù)器能夠?qū)懭胛募?。而且還要注意如果你在編寫腳本的過程中發(fā)現(xiàn)了錯誤,錯誤也會被緩存!因而在你開發(fā)的過程中禁用緩存是一個好主意。最好的辦法是給cache的生存周期傳遞0――這樣,緩存總是立即就失效了。

這是一個實(shí)際的緩存的例子。

<?php
/**
* Example of cached template usage.  Doesn't provide any speed increase since
* we're not getting information from multiple files or a database, but it
* introduces how the is_cached() method works.
*/

/**
* First, include the template class.
*/
require_once('template.php');

/**
* Here is the path to the templates.
*/
$path = './templates/';

/**
* Define the template file we will be using for this page.
*/
$file = 'list.tpl.php';

/**
* Pass a unique string for the template we want to cache.  The template
* file name + the server REQUEST_URI is a good choice because:
*    1. If you pass just the file name, re-used templates will all
*       get the same cache.  This is not the desired behavior.
*    2. If you just pass the REQUEST_URI, and if you are using multiple
*       templates per page, the templates, even though they are completely
*       different, will share a cache file (the cache file names are based
*       on the passed-in cache_id.
*/
$cache_id = $file . $_SERVER['REQUEST_URI'];
$tpl = & new CachedTemplate($path, $cache_id, 900);

/**
* Test to see if the template has been cached.  If it has, we don't
* need to do any processing.  Thus, if you put a lot of db calls in
* here (or file reads, or anything processor/disk/db intensive), you
* will significantly cut the amount of time it takes for a page to
* process.
*
* This should be read aloud as "If NOT Is_Cached"
*/
if(!($tpl->is_cached())) {
  $tpl->set('title', 'My Title');
  $tpl->set('intro', 'The intro paragraph.');
  $tpl->set('list', array('cat', 'dog', 'mouse'));
}

/**
* Fetch the cached template.  It doesn't matter if is_cached() succeeds
* or fails - fetch_cache() will fetch a cache if it exists, but if not,
* it will parse and return the template as usual (and make a cache for
* next time).
*/
echo $tpl->fetch_cache($file);
?>


設(shè)置多個變量

我們?nèi)绾文軌蛲瑫r設(shè)置多個變量?這又一個使用由Ricardo Garcia貢獻(xiàn)的函數(shù)的例子。

<?php  
require_once('template.php');  

$tpl = & new Template('./templates/');  
$tpl->set('title', 'User Profile');  

$profile = array(  
  'name' => 'Frank',  
  'email' => 'frank@bob.com',  
  'password' => 'ultra_secret'  
);  

$tpl->set_vars($profile);  

echo $tpl->fetch('profile.tpl.php');  
?>

相關(guān)的模板是這樣的:

<table cellpadding="3" border="0" cellspacing="1">  
  <tr>  
      <td>Name</td>  
      <td><?=$name;?></td>  
  </tr>  
  <tr>  
      <td>Email</td>  
      <td><?=$email;?></td>  
  </tr>  
  <tr>  
      <td>Password</td>  
      <td><?=$password;?></td>  
  </tr>  
</table>

而且解析后的輸出是這樣的:

<table cellpadding="3" border="0" cellspacing="1">
<tr>
  <td>Name</td>
  <td>Frank</td>
</tr>
<tr>
  <td>Email</td>
  <td>frank@bob.com</td>
</tr>
<tr>
  <td>Password</td>
  <td>ultra_secret</td>
</tr>
</table>

特別感謝Ricardo Garcia和Harry Fuecks他們的對這篇文章的貢獻(xiàn)。


相關(guān)的鏈接

這兒是一個總體上探究模板引擎的好去處的列表。

  • Web Application Toolkit Template View [9] - 許多關(guān)于模板實(shí)現(xiàn)方法的信息
  • MVC Pattern [10] - 描述3層應(yīng)用程序的設(shè)計(jì)
  • SimpleT [11] - 另一個使用PEAR::Cache_Lite的基于php的模板引擎
  • Templates and Template Engines [12] - 更多關(guān)于各種模板實(shí)現(xiàn)的信息
  • Smarty [13] - 編譯型模板引擎


模板類源代碼

以及最后出場的,模板類。

<?php
/**
* Copyright (c) 2003 Brian E. Lozier (brian@massassi.net)
*
* set_vars() method contributed by Ricardo Garcia (Thanks!)
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to
* deal in the Software without restriction, including without limitation the
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
* sell copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
* IN THE SOFTWARE.
*/

class Template {
  var $vars; /// Holds all the template variables
  var $path; /// Path to the templates

  /**
   * Constructor
   *
   * @param string $path the path to the templates
   *
   * @return void
   */
  function Template($path = null) {
      $this->path = $path;
      $this->vars = array();
  }

  /**
   * Set the path to the template files.
   *
   * @param string $path path to template files
   *
   * @return void
   */
  function set_path($path) {
      $this->path = $path;
  }

  /**
   * Set a template variable.
   *
   * @param string $name name of the variable to set
   * @param mixed $value the value of the variable
   *
   * @return void
   */
  function set($name, $value) {
      $this->vars[$name] = $value;
  }

  /**
   * Set a bunch of variables at once using an associative array.
   *
   * @param array $vars array of vars to set
   * @param bool $clear whether to completely overwrite the existing vars
   *
   * @return void
   */
  function set_vars($vars, $clear = false) {
      if($clear) {
/t   $this->vars = $vars;
      }
      else {
/t   if(is_array($vars)) $this->vars = array_merge($this->vars, $vars);
      }
  }

  /**
   * Open, parse, and return the template file.
   *
   * @param string string the template file name
   *
   * @return string
   */
  function fetch($file) {
      extract($this->vars);/t  // Extract the vars to local namespace
      ob_start();/t/t    // Start output buffering
      include($this->path . $file);  // Include the file
      $contents = ob_get_contents(); // Get the contents of the buffer
      ob_end_clean();/t/t// End buffering and discard
      return $contents;/t      // Return the contents
  }
}

/**
* An extension to Template that provides automatic caching of
* template contents.
*/
class CachedTemplate extends Template {
  var $cache_id;
  var $expire;
  var $cached;

  /**
   * Constructor.
   *
   * @param string $path path to template files
   * @param string $cache_id unique cache identifier
   * @param int $expire number of seconds the cache will live
   *
   * @return void
   */
  function CachedTemplate($path, $cache_id = null, $expire = 900) {
      $this->Template($path);
      $this->cache_id = $cache_id ? 'cache/' . md5($cache_id) : $cache_id;
      $this->expire   = $expire;
  }

  /**
   * Test to see whether the currently loaded cache_id has a valid
   * corrosponding cache file.
   *
   * @return bool
   */
  function is_cached() {
      if($this->cached) return true;

      // Passed a cache_id?
      if(!$this->cache_id) return false;

      // Cache file exists?
      if(!file_exists($this->cache_id)) return false;

      // Can get the time of the file?
      if(!($mtime = filemtime($this->cache_id))) return false;

      // Cache expired?
      if(($mtime + $this->expire) < time()) {
/t   @unlink($this->cache_id);
/t   return false;
      }
      else {
/t   /**
/t    * Cache the results of this is_cached() call.  Why?  So
/t    * we don't have to double the overhead for each template.
/t    * If we didn't cache, it would be hitting the file system
/t    * twice as much (file_exists() & filemtime() [twice each]).
/t    */
/t   $this->cached = true;
/t   return true;
      }
  }

  /**
   * This function returns a cached copy of a template (if it exists),
   * otherwise, it parses it as normal and caches the content.
   *
   * @param $file string the template file
   *
   * @return string
   */
  function fetch_cache($file) {
      if($this->is_cached()) {
/t   $fp = @fopen($this->cache_id, 'r');
/t   $contents = fread($fp, filesize($this->cache_id));
/t   fclose($fp);
/t   return $contents;
      }
      else {
/t   $contents = $this->fetch($file);

/t   // Write the cache
/t   if($fp = @fopen($this->cache_id, 'w')) {
/t       fwrite($fp, $contents);
/t       fclose($fp);
/t   }
/t   else {
/t       die('Unable to write cache.');
/t   }

/t   return $contents;
      }
  }
}
?>

另外一個值得注意的重要的事情是這里展示的解決辦法是我們傳遞模板的文件名給fetch()函數(shù)。如果你需要重用模板對象而不去re-set()所有的變量,這將比較有用。

并且記?。耗0逡娴囊c(diǎn)是把你的業(yè)務(wù)邏輯從你的表現(xiàn)邏輯中分離出來,而不是把你的PHP代碼從HTML代碼中分離出來。

本文附件下載:template.zip

[1] http://www.massassi.com/bTemplate/
[2] http://smarty.php.net/
[3] http://www.sitepoint.com/examples/tempeng/template.zip
[4] http://opensource.org/licenses/mit-license.html
[5] http://www.opensource.org/
[6] http://zend.com/store/products/zend-performance-suite.php
[7] http://www.php-accelerator.co.uk/
[8] http://www.php.net/manual/en/control-structures.alternative-syntax.php
[9] http://wact.sourceforge.net/index.php/TemplateView
[10] http://www.phppatterns.com/index.php/article/articleview/11/
[11] http://simplet.sourceforge.net/
[12] http://phppatterns.com/index.php/article/articleview/4/1/1/
[13] http://smarty.php.net/

本文英文原版地址:http://www.sitepoint.com/article/1218/
發(fā)表評論 共有條評論
用戶名: 密碼:
驗(yàn)證碼: 匿名發(fā)表
区一区二视频| 国产日韩欧美电影在线观看| 国产精品你懂的在线欣赏| 国产精品麻豆成人av电影艾秋| 99精品视频在线免费播放| 美女高潮网站| 一本一道久久久a久久久精品91| 欧美激情视频在线播放| 日韩免费在线看| 日本欧美一区二区在线观看| 国产一区二区三区不卡免费观看| 一区二区三区国产好的精华液| 99久久亚洲一区二区三区青草| julia中文字幕久久亚洲蜜臀| 欧美伊人久久久久久久久影院| 国产一卡2卡3卡四卡网站| 日本一区二区动态图| 日韩中文字幕不卡视频| 精品免费视频| 国产成人午夜视频| 日本熟伦人妇xxxx| 三级在线观看一区二区| 永久免费精品视频网站| ●精品国产综合乱码久久久久| 亚洲人在线播放| 黄色成人在线视频| 中文国产亚洲喷潮| 中文字幕亚洲天堂| 欧美一区二区精品| a级毛片免费| 国产精品高清网站| 丝袜诱惑制服诱惑色一区在线观看| 俺去俺来也在线www色官网| 国产精品玖玖玖在线资源| 精品捆绑美女sm三区| 日韩精品第一页| 欧美日产在线观看| 美女诱惑一区| 91po在线观看91精品国产性色| 国产调教精品| 懂色aⅴ精品一区二区三区| 成人午夜视屏| 国产精品福利电影| 蜜乳av另类精品一区二区| 国产一区二区三区黄片| 粉嫩高清一区二区三区精品视频| 亚洲va天堂va国产va久| 亚洲欧美一区二区三区孕妇| 欧美精品尤物在线观看| 性欧美18+| 91精品办公室少妇高潮对白| 亚洲第一会所001| 欧美精品久久久| 成人免费观看男女羞羞视频| 污视频在线观看免费| 国产精品一区二区三区乱码| 久久五月婷婷丁香社区| 国产精品成人一区二区艾草| 欧美在线播放高清精品| 99视频在线| 羞羞答答一区二区| 美女久久久久久久| 欧美禁忌电影| 久久人人爽人人爽人人av| 色久优优欧美色久优优| 欧美深夜福利视频| 久久久久久一区二区三区四区别墅| 天天摸天天做天天爽| 国产激情视频网址| 成人免费高清| 国产露脸91国语对白| 日韩精品在线视频美女| 久久亚洲国产精品日日av夜夜| 国产成人福利夜色影视| 三级精品视频久久久久| 亚洲精品aaa| 欧美猛男gaygay网站| 五月天激情丁香| 色噜噜狠狠狠综合欧洲色8| 亚洲青青久久| 日韩精品免费一区二区在线观看| 试看120秒一区二区三区| 欧美精品福利| 精品免费av| 91.·福利| 久久人人爽人人爽人人av| 欧美日韩综合久久| 国产不卡av一区二区| 热99久久精品| 超碰福利在线观看| 国产日韩精品视频一区二区三区| 欧美成年人视频在线观看| 蜜桃久久影院| 中文字幕9999| 精品欧美午夜寂寞影院| 色偷偷88欧美精品久久久| 91在线观看下载| 五月天婷婷社区| 久久男人资源视频| 在线色视频观看| 国产成人免费在线观看视频| www红色一片_亚洲成a人片在线观看_| 影音先锋中文字幕在线播放| 久久婷婷国产精品| 亚洲1区在线| 丰满少妇一区二区三区专区| 影音先锋久久| 午夜电影网亚洲视频| 超碰成人福利网| 亚洲第一精品在线| 国产欧美1区2区3区| 国产午夜精品一区二区三区四区| 亚洲国产日韩欧美一区二区三区| 黑人精品欧美一区二区蜜桃| 97影院理论片在线播放| 中文字幕国产在线观看| 在线观看视频亚洲| 欧亚精品中文字幕| 最新中文字幕久久| 中文字幕亚洲影院| 日本精品免费观看高清观看| 久久国产精品高清| 色橹橹高清视频在线播放| 后进极品白嫩翘臀在线播放| 亚洲免费在线电影| av在线观看地址| 亚洲激情中文1区| 波多野结衣手机在线视频| 亚洲精选在线视频| 国产拍精品一二三| 日韩专区在线播放| 一区二区三区免费观看| 久久超碰亚洲| 91在线看网站| 婷婷久久综合九色综合99蜜桃| 黄色网战入口| 日韩视频免费中文字幕| 北条麻妃一区二区三区中文字幕| 色哟哟国产精品免费观看| 不用播放器的免费av| 久久影院电视剧免费观看| 中文字幕电影av| 欧洲一区二区在线| 毛片激情在线观看| 欧美成年黄网站色视频| 欧美视频一区二| 久久久久久久久久久久久夜| 成人午夜免费视频| 波多野结衣a v在线| 91国内产香蕉| 国产成人免费av电影| av观看成片免费网站| 中文字幕在线观看第三页| 日本道在线观看| 成人黄色在线网站| 欧美h在线观看| 国产精品女同一区二区| 亚洲成人www| 奇米影视777在线欧美电影观看| a在线观看免费| 亚洲精品av在线播放| 一二三四在线观看视频韩国| 一个人看的日本www的免费视频| 午夜视频福利在线| 国产日产欧美一区二区三区| 在线视频亚洲自拍| bestiality新另类大全| 日韩av在线免费| 黄色三级电影网站| 久久久美女艺术照精彩视频福利播放| 国产精品青草久久| 欧美色图影院| 中文乱码字幕高清在线观看| 欧美xxxxbbbb在线播放| 欧美日本一区二区在线观看| 91精品国产调教在线观看| 亚洲第一网站| 久久激情视频| 精品视频在线观看网站| 欧美黄色性视频| sm捆绑调教视频| 国内一区二区在线视频观看| 精品一区二区在线看| 麻豆91在线播放| www.看毛片| 免费日韩中文字幕| 996这里只有精品| 色综合天天做天天爱| 精品伦精品一区二区三区视频密桃| wwwww在线观看免费视频| 狠狠色噜噜狠狠狠狠888奇米| 国产.精品.日韩.另类.中文.在线.播放| 国产又大又黄又粗的视频| 亚洲日产av中文字幕| 日本视频在线观看一区二区三区| 欧美国产视频在线观看| 国产精品爱啪在线线免费观看| 欧美电影免费观看高清完整| 男女污污的视频| 宅男宅女性影片资源在线1| 欧美色欧美亚洲另类| 欧美伦理视频网站| 久久久国产精彩视频美女艺术照福利| 成人免费网站视频www| 黄色网页网址在线免费| 国产调教在线观看| 欧美狂欢多p性派对| av午夜在线| 婷婷久久综合九色综合99蜜桃| 成人午夜私人影院| 国产视频你懂的| 国产成人在线观看免费网站| 国产精品三区在线观看| 91麻豆精品91久久久久久清纯| 色开心亚洲综合| 伊人久久大香线蕉午夜av| 中文亚洲欧美| 亚州黄色一级| 一个人免费播放在线视频看片| 色狠狠桃花综合| 亚洲欧洲在线播放| 五月天婷婷亚洲| 黄色片av在线| 成人午夜影院| 免费观看h电影在线观看| 国产日韩久久久| chien国产乱露脸对白| 成人黄18免费网站| 亚洲欧洲另类国产综合| 三区四区电影在线观看| 国产河南妇女毛片精品久久久| 中文字幕第21页| 久久精品视频中文字幕| 中文字幕亚洲视频| 成人午夜影院| 亚洲天堂久久久久| 色噜噜狠狠一区二区三区| 久久综合免费视频影院| 亚洲综合五月天婷婷丁香| 日本在线视频中文字幕| 影院欧美亚洲| 亚洲精品欧美综合四区| 欧美一级专区| 91精品国产一区二区在线观看| 五月天婷婷综合| 色av中文字幕一区| 黄页网站视频在线观看| 在线观看操人| xxxxx91麻豆| 精品国产一区二区三区忘忧草| 一级片免费在线观看| 综合图区欧美| 欧美日韩卡一| 狠狠干一区二区| 日韩porn| 内射中出日韩无国产剧情| 麻豆精品视频在线原创| 久久天堂国产精品| 国产精品免费看久久久香蕉| 国产在线精品一区免费香蕉| 最近的2019中文字幕免费一页| 中文字幕日韩电影| a在线视频v视频| 四虎免费av| 日韩一区二区三区观看| 亚洲国产福利视频| 男女超爽视频免费播放| 亚洲精品一区中文字幕电影| 欧洲第一无人区观看| 干出白浆视频| 国内视频自拍在线视频| 99自拍视频在线观看| 91日韩在线播放| 国产在线精品一区二区不卡| 欧美爱爱视频网站| 亚洲第一香蕉网| 欧美成人一品| 国产福利一区二区三区| 亚洲欧美影音先锋| 成人免费在线| 国产精品久久久久久久泡妞| 成人在线视频国产| av成人在线观看| 日韩和的一区二在线| 狠狠色狠狠色综合日日小说| 国产成人一区二区三区影院在线| 天堂日韩电影| 日本成人在线视频网站| 一级黄色a视频| 国产精品美女久久久久av超清| 国产日本在线观看| 国产一区啦啦啦在线观看| 亚洲精品一区中文字幕电影| 亚洲网站三级| 最新欧美电影| 国产精品扒开腿做爽爽爽视频软件| 日韩精品极品毛片系列视频| 国产精品调教视频| 精品亚洲一区二区三区在线观看| 中文字幕+乱码+中文乱码www| 日韩精品视频一区二区三区| 老牛影视av一区二区在线观看| 国产免费色视频| 精品久久美女| 国产高清不卡二三区| 亚洲美女综合网| 欧美性猛交xxxx乱| 亚洲精品久久久久| 免费在线观看黄视频| 欧美人与性禽动交精品| 欧美午夜黄色| 91在线免费播放| 亚洲激情偷拍| 亚洲在线免费观看视频| 亚洲一区二区免费视频软件合集| 日韩一二区视频| 午夜久久久久久久| 中文字幕55页| 亚洲欧洲中文日韩久久av乱码| 真实国产乱子伦对白视频| 久久狠狠久久| 国产精品7777| 国产精品综合视频| 成人综合在线观看| 日韩欧美三级电影| 三上悠亚在线一区| 九一久久久久久| 麻豆国产一区二区|