您的位置: 首页 > 技术文档 > 网络编程 > [PHP]超越模板引擎
[php]一次编写,随处运行 回到列表 PHP4之真OO
 [PHP]超越模板引擎

作者:陶文 时间: 2004-02-06 文档类型:翻译 来自:CSDN

作者:Brian Lozier

译者:taowen

In general, template engines are a "good thing."
总体来说,模板引擎是一个"好东西"
  作为一个PHP/Perl的程序员,许多模板引擎(fastTemplate, Smarty, Perl的 HTML::Template)的用户,以及我自己的bTemplate [1]的作者,我讲这句话很多次了。
  然而,在同事进行了长时间的讨论之后,我确信了大量的模板引擎(包括我自己写的)根本是错误的。 我想唯一的例外是Smarty [2],虽然我认为它太庞大了,并且考虑到这篇文章的其余部分相当的没有观点。然而,就你为什么选择Smarty(或者类似的解决方案)有几个理由,这些将在文章后面探究。
  这篇文章讨论模板的理论。我们将看到为什么大部分"模板引擎"是过于肥大,并且最终我们将回过头来看一个轻量级的,小巧快速的另类选择。
下载和授权
  模板类和所有在本文中使用的例子能够在这里下载:template.zip [3]。你可以根据发布 [4]在OSI [5]的MIT Open Source License使用这些文件中的代码。
一些关于模板引擎的背景知识
  让我们首先研究一下模板引擎的背景知识。模板引擎被设计出来用于把商业逻辑(例如从数据库中获取数据或者计算贸易耗费)从数据的表现分离开来。模板引擎解决了两个主要问题:

  1. 如何实现这种分离
  2. 如何从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' : '&nbsp;');?></td>  
   </tr>  
<? endforeach; ?>  
</table>


  这个layout.tpl.php是一个简单的例子(定义了整个页面看上去是什么样子的模板文件)
<html>
   <head>
       <title><?=$title;?></title>
   </head> 
   <body> 
       <h2><?=$title;?></h2>
<?=$body;?> 
   </body>
</html>

And here's the parsed output.
而这是解析后的输出。
代码拷贝框

[Ctrl+A 全部选择 然后拷贝]
Caching
缓存
  因为解决方案简单如斯,实现模板缓存成为了一个非常简单的任务。为了实现缓存,我们有一个二级类,它扩展了原来的模板类。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。当然下面有例子来说明。
使用缓存包括以下步骤。

  1. include() 模板源文件
  2. 创建一个新的CachedTemplate对象(并且传递路径,唯一的cache_id和缓存过期时间给模板)
  3. 测试内容是否已经被缓存了
  4. 如果还促拿了,显示文件并且结束脚本
  5. 否则,进行所有的处理并且fetch()模板
  6. fetch_cache()的调用将自动产生一个新的缓存文件
  这个脚本假定你的缓存文件将放到./cache/中,因此你必须创建那个目录并且改变它的目录权限(chmod)使得Web服务器能够写入文件。而且还要注意如果你在编写脚本的过程中发现了错误,错误也会被缓存!因而在你开发的过程中禁用缓存是一个好主意。最好的办法是给cache的生存周期传递0——这样,缓存总是立即就失效了。
由于排版需要,请点击以下地址查看全文:

出处:CSDN
责任编辑:cjj

◎进入论坛网络编程版块参加讨论

相关文章 更多相关链接
ExpressionEngine模板设计大赛
PHP5 安装方法
学习使用PHP数组
[php]一次编写,随处运行
PHP4之真OO
作者文章
[php]一次编写,随处运行
[PHP]超越模板引擎
关键字搜索 常规搜索 推荐文档
热门搜索:CSS Fireworks 设计比赛 网页制作 web标准 用户体验 UE photoshop Dreamweaver Studio8 Flash 手绘 CG
站点最新 站点最新列表
周大福“敬•自然”设计大赛开启
国际体验设计大会7月将在京举行
中国国防科技信息中心标志征集
云计算如何让安全问题可控
云计算是多数企业唯一拥抱互联网的机会
阿里行云
云手机年终巨献,送礼标配299起
阿里巴巴CTO王坚的"云和互联网观"
1499元买真八核 云OS双蛋大促
首届COCO桌面手机主题设计大赛
栏目最新 栏目最新列表
浅谈JavaScript编程语言的编码规范
如何在illustrator中绘制台历
Ps简单绘制一个可爱的铅笔图标
数据同步算法研究
用ps作简单的作品展示页面
CSS定位机制之一:普通流
25个最佳最闪亮的Eclipse开发项目
Illustrator中制作针线缝制文字效果
Photoshop制作印刷凹凸字体
VS2010中创建自定义SQL Rule

蓝色理想版权申明:除部分特别声明不要转载,或者授权我站独家播发的文章外,大家可以自由转载我站点的原创文章,但原作者和来自我站的链接必须保留(非我站原创的,按照原来自一节,自行链接)。文章版权归我站和作者共有。

转载要求:转载之图片、文件,链接请不要盗链到本站,且不准打上各自站点的水印,亦不能抹去我站点水印。

特别注意:本站所提供的摄影照片,插画,设计作品,如需使用,请与原作者联系,版权归原作者所有,文章若有侵犯作者版权,请与我们联系,我们将立即删除修改。

您的评论
用户名:  口令:
说明:输入正确的用户名和密码才能参与评论。如果您不是本站会员,你可以注册 为本站会员。
注意:文章中的链接、内容等需要修改的错误,请用报告错误,以利文档及时修改。
不评分 1 2 3 4 5
注意:请不要在评论中含与内容无关的广告链接,违者封ID
请您注意:
·不良评论请用报告管理员,以利管理员及时删除。
·尊重网上道德,遵守中华人民共和国的各项有关法律法规
·承担一切因您的行为而直接或间接导致的民事或刑事法律责任
·本站评论管理人员有权保留或删除其管辖评论中的任意内容
·您在本站发表的作品,本站有权在网站内转载或引用
·参与本评论即表明您已经阅读并接受上述条款
推荐文档 | 打印文档 | 评论文档 | 报告错误  
专业书推荐 更多内容
网站可用性测试及优化指南
《写给大家看的色彩书1》
《跟我去香港》
众妙之门—网站UI 设计之道
《Flex 4.0 RIA开发宝典》
《赢在设计》
犀利开发—jQuery内核详解与实践
作品集 更多内容

杂⑦杂⑧ Gold NORMANA V2