`
xcgh
  • 浏览: 77198 次
  • 来自: ...
社区版块
存档分类
最新评论

Beyond The Template Engine超越模板引擎

    博客分类:
  • php
阅读更多
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.
而这是解析后的输出。
<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">&nbsp;</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">&nbsp;</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">&nbsp;</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">&nbsp;</td>
 </tr>
</table>
 </body>
</html>
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——这样,缓存总是立即就失效了。
由于排版需要,请点击以下地址查看全文:
分享到:
评论

相关推荐

    超越SAS - Beyond the Basics Book-FINAL(英文版)

    在“Beyond the Basics Book-FINAL(英文版)”这一参考资料中,我们可以了解到SAS技术的许多基础和高级概念。 该书详细介绍了SAS的各个层次结构,如端口层(Port Layer)、链路层(Link Layer)、物理层(Phy ...

    超越感觉 beyond feelings

    《超越感觉:批判性思维指南》是第九版的一本经典著作,主要探讨的是思维的本质、逻辑与批判性思考。这本书的核心在于教导读者如何摆脱情绪的影响,以更为理性、科学的方式去分析问题,提升个人的决策能力和理解力。...

    RUN Beyond the Metaphor-An Efficient Optimization Algorithm Based on Runge K.pdf

    "RUN Beyond the Metaphor-An Efficient Optimization Algorithm Based on Runge Kutta Method" 本文介绍了一种基于 Runge-Kutta 方法的高效优化算法,称为 RUN Beyond the Metaphor。该算法旨在解决复杂优化问题,...

    Beyond the C++ Standard Library : An Introduction to Boost (中文)

    Beyond the C++ Standard Library : An Introduction to Boost (Chinese)

    Beyond the C++ Standard Library: An Introduction to Boost

    Beyond the C++ Standard Library: An Introduction to Boost

    beyondadmin最新后台管理模板

    "BeyondAdmin最新后台管理模板"是一款专为开发者设计的现代化、功能丰富的Web应用程序后台界面模板。这个模板基于流行的前端框架,如Bootstrap,提供了多种布局、组件和预设样式,旨在帮助开发人员快速构建高效、...

    Addison.Wesley.C++.Template.Metaprogramming.LiB.chm

    C++ Template Metaprogramming: Concepts, Tools, and Techniques from Boost and Beyond By David Abrahams, Aleksey Gurtovoy Publisher : Addison Wesley Professional Pub Date : December 10, 2004 ISBN ...

    Cheat Engine 5.6.1

    AutoAssembler: You can't move the cursor beyond the end of a line anymore Pointerscan: Pointerscanner now tells you that pressing stop is a stupid thing to do Pointerscan: doubleclicking an pointer ...

    excel 教程(Excel 2007 Beyond the Manual)

    《Excel 2007 Beyond the Manual》是一个深入学习Excel的专业教程,专为那些希望提升Excel技能、超越基本操作的用户设计。Excel是Microsoft Office套件中的重要组件,广泛应用于数据处理、分析和报告制作。本教程的...

    Mesh Free Methods - Moving Beyond the Finite Element Method

    刘桂荣的英文著作《Mesh Free Methods - Moving Beyond the Finite Element Method》pdf格式

    Beyond The C++ Standard Library - An Introduction To Boost (2005)

    《Beyond The C++ Standard Library - An Introduction To Boost (2005)》是关于C++编程领域的一个重要参考资料,特别是对于那些希望深入了解和利用Boost库的开发者来说。Boost是一个开源的C++库集合,它提供了许多...

    Game Engine Architecture

    The examples are often grounded in specific technologies, but the discussion extends way beyond any particular engine or API. The references and citations make it a great jumping off point for those ...

    BeyondAdmin 后台模板

    BeyondAdmin 是一款基于Bootstrap3框架的高质量后台管理模板,它专为构建响应式的、功能丰富的Web应用程序而设计。Bootstrap3是Twitter开发的一款流行的前端开发框架,以其灵活性、易用性和跨设备兼容性著称,使得...

    Beyond the C++ Standard Library An Introduction to Boost.chm

    Beyond the C++ Standard Library An Introduction to Boost.chm

    PHP.Beyond.the.Web

    Leverage your existing web based ...This book will introduce you to the tools, techniques and background necessary to program just about anything you can think of, using the ...

    Beyond the C++ Standard Library 中文版 超越c++标准库

    1. **模板元编程**:Boost库在模板元编程方面有着显著的贡献,如Boost.MPL(Meta-Programming Library)允许在编译时执行计算,极大地增强了C++的类型系统。 2. **智能指针**:Boost库提供了多种智能指针,如shared...

    BeyondAdmin后台模板

    "BeyondAdmin后台模板"是一款基于Bootstrap框架设计的高级管理界面模板,专为构建现代、响应式的Web应用程序而设计。这款模板以其丰富的功能、美观的界面和高效的性能在IT行业中受到广泛应用。Bootstrap是Twitter...

    Google's PageRank and Beyond: The Science of Search Engine Rankings

    It can be a reference book for people who want to know more about the ideas behind the currently popular search engines, and it provides an introductory text for beginning researchers in the area of ...

Global site tag (gtag.js) - Google Analytics