|
马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。
您需要 登录 才可以下载或查看,没有帐号?立即注册
x
学会了生成静态网页,现在你应该接触一下XML了,恩,XML也了解了,那么AJAX你也得接触接触吧?AJAX完了....然后...模板 整体来讲,模板引擎是一个"好器材"
作为一个PHP/Perl的法式员,很多模板引擎(fastTemplate, Smarty, Perl的 HTML::Template)的用户,和我本人的(bTemplate [1] 的作者),我讲这句话良多次了。
但是,在同事停止了长工夫的会商以后,我确信了大批的模板引擎(包含我本人写的)基本是毛病的。 我想独一的破例是Smarty [2],固然我以为它太复杂了,而且思索到这篇文章的其他局部相当的没有概念。但是,就你为何选择Smarty(或相似的处理计划)有几个来由,这些将在文章前面探求。
这篇文章会商模板的实际。咱们将看到为何大局部"模板引擎"是过于肥大,而且终究咱们将回过火来看一个轻量级的,玲珑疾速的另类选择。
下载和受权
模板类和一切在本文中利用的例子可以在这里下载:template.zip [3]。你可以依据宣布 [4]在 OSI [5] 的 MIT Open Source License利用这些文件中的代码。
一些关于模板引擎的后台常识
让咱们起首研讨一下模板引擎的后台常识。模板引擎被设计出来用于把贸易逻辑(例如从数据库中获得数据或盘算商业消耗)从数据的体现分别开来。模板引擎处理了两个次要成绩:
若何完成这类分别
若何从HTML平分离"庞杂"的php代码
这从实际上使得没有PHP经历的HTML设计者可以不看任何PHP代码的前提下修正站点的外不雅。
但是,模板体系也引入了一些庞杂性。起首,咱们如今有一个从多个文件得来的"页面"。典范的,你能够有一个主PHP页担任营业逻辑,一个里面的"结构"模板把全部站点的全体结构停止衬着,一个外部的内容特定的模板,一个数据库笼统层,和模板引擎自己(这些多是也能够不是由多个文件构成)。也有能够,一些人仅仅复杂地在每一个PHP页面的首尾处包括"头部"和"尾部"文件。
这发生的单个页面的文件数目是很可不雅的。但是,由于PHP解析器十分快,用到的文件数目能够不是那末主要除非你的站点流量很大。
但是,要记住模板体系引入了别的一个处置的条理。模板文件不单单是必需被包括,他们还必需被解析(取决于模板体系,这个行动有良多种体例来完成 ―― 利用正则表达式,字符串交换,编译,词法剖析,等等)。这就是为何对模板停止测速变得盛行起来:由于模板引擎利用各类办法来解析数据,它们中的一些比别的一些要快(并且,一些模板引擎供应了比其他引擎加倍丰厚的功效)。
模板引擎基本常识
复杂地说,模板引擎使用了用C写的剧本言语(PHP)。在这些嵌入的剧本言语中,你有别的一个伪剧本言语(不管你的模板引擎撑持何种标签)。某些供应了复杂的变量改写和轮回。别的一些呢,则供应了前提和嵌套轮回。而再其他的呢(最少有Smarty)供应了一个PHP的对照大的子集的接口,和一个缓冲层。
为何我以为Smarty最接近于准确的偏向?由于Smarty的方针是"把营业逻辑从体现平分离出来"而不是"PHP代码和HTML代码的分别"。这看上去区分不大,然而它恰是要点地点。任何模板引擎的终究方针不该该是从HTML移除一切的逻辑。它应当是把体现逻辑从营业逻辑平分离出来。
有良多你仅仅需求逻辑来准确显示你的数据的例子。例如,你的营业逻辑是从你的数据库中获得一个用户列表。你的体现逻辑多是把用户列表用3列显示。能够修正用户列表函数使得它前往3个数组是很笨的举措。究竟函数不该该关怀数据接上去要怎样处置如许的工作。但是,在你的模板文件中短少一些逻辑,那些恰是你要做的工作。
在这点上Smarty是准确的(使得你使用PHP的良多器材),然而依然有很多成绩。根基上,它仅仅供应了一个以新语法会见PHP的接口。以那入手下手,它看上去不那末伶俐了。是否是现实上写 {foreach --args} 比 <? foreach --args ?> 加倍复杂?假如你以为如许复杂一些,问问你本人是否是在包括一个伟大的模板库离开成这类分别时可以看到真实的意义要加倍复杂一些。固然,Smarty供应了很多其他很好的特征,然而看上去这些好处可以在不必承当包括Smarty类库的情形下也能取得。
别样的处理计划
我次要要宣传的一个处理计划是一个利用PHP代码作为它的原生剧本言语的"模板引擎"。我晓得这之前有人做过。并且当我第一次看到的时分,我想,"为何要如许做?",但是我在思索过我同事的论据以后,而且完成了一个直接利用PHP代码依然完成了把营业逻辑和体现逻辑分别的终究方针的模板体系时(只用了大约25行代码,不包含正文),我意想到了优点地点。
这个体系给像咱们如许的开辟者供应了对PHP中心函数的会见权力,咱们可以利用他们来格局化输入――像日期格局化如许的义务应当在模板中处置。并且,由于模板是通俗的PHP文件,像Zend Performance Suite [6] 和PHP Accelerator [7] 如许的字节码缓存法式,可以主动缓存模板(因此,它们不需求在每次被会见时都被从头注释履行)。只需你记得把你的模板文件定名为法式可以识别出是PHP文件的名字(凡是,你仅仅需求确保它们有一个.php的后缀),这的确是一个优点。
当我以为这类办法比经典的模板引擎要拙劣很多时,一定还有一些要商议的成绩。最分明的不和定见是,PHP代码太庞杂了,并且设计者不该该强制去进修PHP。现实上,PHP代码和像Smarty如许的初级模板引擎的语法差不多复杂(假如不是更复杂的话)。并且,设计者可以利用像<?=$var;?>如许的简写PHP。这要比{$var}庞杂良多?固然,这要长一些,然而假如你习气了,你可以取得了PHP的威力并且不必接受解析模板文件带来的承当。
第二,并且能够更主要的,在基于PHP的模板中没有固有的平安。Smarty供应了选项在模板文件中完全禁用PHP代码。它使得开辟者可以束缚模板可以会见的函数和变量。假如你没有不怀好意的设计者,这不会是甚么成绩。但是,假如你答应内部的用户上传或修正模板,我在此展现的基于PHP的处理计划相对没有任何平安可言!任何代码都能放入模板中而且失掉运转。是的,乃至是一个print_r($GLOBALS)(这将改有歹意的用户会见剧本中任何变量的权力)。
然而,我团体或任务上写过的项目中,绝大多半不答应终究的用户修正或上传模板。假如是如许,成绩就不存在了。因而如今让咱们来看看代码吧。
例子
这是一个复杂的用户列表页面的例子。
<?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');
?>
个中有两个值得注重的主要的概念。第一个就是外部和内部模板的概念。内部模板包括界说站点次要外不雅的HTML代码。而外部模板包括界说站点内容区域的HTML代码。固然,你可以在恣意数量的层上有恣意数量的模板。由于凡是咱们给每一个区域利用分歧的模板对象,所以没着名字空间的成绩。例如,我能在外部和内部模板中都有变量叫"title",而不必惧怕有甚么抵触。
这是一个用来显示用户列表的模板的复杂例子。注重特别的foreach和endforeach;语法在PHP手册中有申明 [8]。它完整是可选择的。
并且,你能够奇异我为何要用.php的后缀来定名我的模板文件。呵呵,很多PHP字节码缓存处理计划(好比 phpAccelerator)假如要被认成PHP文件,需求文件有一个.php后缀。由于这些模板是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>
缓存
由于处理计划复杂如此,完成模板缓存成了一个十分复杂的义务。为了完成缓存,咱们有一个二级类,它扩大了本来的模板类。CachedTemplate类现实上利用和本来的模板类不异的API。分歧点是咱们必需传递缓存的设置给机关函数,而且挪用fetch_cache()而不是fetch()。
缓存的概念是复杂的。复杂的说,咱们设置一个缓存工夫来调表输入应当被保留的时长(以秒为单元)。在发生一个页面的一切任务展开之前,咱们必需起首测试页面是不是已被缓存了,并且缓存是不是依然没有过时。假如缓存在这那,咱们不需求在去费事数据库和营业逻辑来发生页面――咱们可以复杂地输入本来缓存地内容。
这类办法需求处理独一地标识缓存文件的成绩。假如一个站点是被一个显示基于GET变量的中间剧本所掌握,对每一个PHP文件只要一个缓存不会有甚么匡助。例如,假如index.php?page=about_us和用户挪用index.php?page=contact_us失掉的显示完整分歧。
成绩是经由过程给每一个页面发生一个独一的cache_id来处理的。为了做到这个目标,咱们把现实上被恳求的文件酿成REQUEST_URI(根基上就是全部URL:index.php?foo=bar&bar=foo)。固然,这个转换进程是遭到CachedTemplate类掌握的,然而要记住的主要的工作是你相对要在创立CachedTemplate对象时传递一个独一的cache_id。固然上面有例子来讲明。
利用缓存包含以下步调。
include() 模板源文件
创立一个新的CachedTemplate对象(而且传递途径,独一的cache_id弛缓存过时工夫给模板)
测试内容是不是已被缓存了
假如还促拿了,显示文件而且停止剧本
不然,停止一切的处置而且fetch()模板
对fetch_cache()的挪用将主动发生一个新的缓存文件
这个剧本假定你的缓存文件将放到./cache/中,因而你必需创立谁人目次而且改动它的目次权限(chmod)使得Web办事器可以写入文件。并且还要注重假如你在编写剧本的过程当中发明了毛病,毛病也会被缓存!因此在你开辟的过程当中禁用缓存是一个好主张。最好的举措是给cache的保存周期传递0――如许,缓存老是当即就生效了。
这是一个实践的缓存的例子。
<?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);
?>
设置多个变量
咱们若何可以同时设置多个变量?这又一个利用由Ricardo Garcia奉献的函数的例子。
<?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');
?>
相干的模板是如许的:
<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他们的对这篇文章的奉献。
相干的链接
这儿是一个整体上探求模板引擎的好去向的列表。
Web Application Toolkit Template View [9] - 很多关于模板完成办法的信息
MVC Pattern [10] - 描写3层使用法式的设计
SimpleT [11] - 另外一个利用PEAR::Cache_Lite的基于php的模板引擎
Templates and Template Engines [12] - 更多关于各类模板完成的信息
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) {
$this->vars = $vars;
}
else {
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); // Extract the vars to local namespace
ob_start(); // Start output buffering
include($this->path . $file); // Include the file
$contents = ob_get_contents(); // Get the contents of the buffer
ob_end_clean(); // End buffering and discard
return $contents; // 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()) {
@unlink($this->cache_id);
return false;
}
else {
/**
* Cache the results of this is_cached() call. Why? So
* we don't have to double the overhead for each template.
* If we didn't cache, it would be hitting the file system
* twice as much (file_exists() & filemtime() [twice each]).
*/
$this->cached = true;
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()) {
$fp = @fopen($this->cache_id, 'r');
$contents = fread($fp, filesize($this->cache_id));
fclose($fp);
return $contents;
}
else {
$contents = $this->fetch($file);
// Write the cache
if($fp = @fopen($this->cache_id, 'w')) {
fwrite($fp, $contents);
fclose($fp);
}
else {
die('Unable to write cache.');
}
return $contents;
}
}
}
?>
别的一个值得注重的主要的工作是这里展现的处理举措是咱们传递模板的文件名给fetch()函数。假如你需求重用模板对象而不去re-set()一切的变量,这将对照有效。
而且记住:模板引擎的要点是把你的营业逻辑从你的体现逻辑平分离出来,而不是把你的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/ PHP和HTML混合编程应该不成问题,在这期间,你完全可以让PHP给你算算 一加一等于几,然后在浏览器输出,不要觉得幼稚,这的确是跟阿波罗登月一样,你打的是一小段代码,但是对于你的编程之路,可是迈出了一大步啊!兴奋吧?但是不得不再给你泼点冷水,您还是菜鸟一个。 |
|