用 Smarty 分离 PHP 应用程序中的形式与功能

来源:互联网 发布:php商品计算代码 编辑:程序博客网 时间:2024/05/29 05:07

 

使用智能模板引擎分离 Web 页面标记与底层设计逻辑

 文档选项 将此页作为电子邮件发送

将此页作为电子邮件发送

未显示需要 JavaScript 的文档选项

样例代码



级别: 中级

Martin Streicher (martin.streicher@linux-mag.com), 主编, Linux Magazine

2007 年 9 月 06 日

随意混用 PHP 与其他 Web 页面标记将导致程序逻辑、HTML、层叠样式表(Cascading Style Sheets,CSS)和 JavaScript 处于混乱状态,使维护成为一项艰巨的任务。Smarty 模板引擎可以将形式与功能分离。

PHP Web 应用程序易于上手。PHP 语言的语法整洁且易于掌握。可以将 PHP 与 HTML、JavaScript 和 CSS 直接混用以快速生成可视结果。而且,把 PHP 应用程序部署到您自己的 Web 服务器或托管服务中只是小菜一碟。

但是混用 PHP 与其他页面标记也是一项责任。PHP 代码通常是含有程序逻辑、结构化查询语言(Structured Query Language,SQL)查询、函数、类、开发人员注释、HTML、CSS 样式和脚本的复杂 web(不是开玩笑)。更糟糕的是,把内容从 PHP、echo 发送到输出缓冲区有很多种方法。维护这样混乱的页面十分费力。对代码或标记做出无关紧要的更改会带来严重破坏,并且增强页面可能需要设计人员与程序员的共同努力。使用 PHP,形式(页面的布局)及功能(页面的目的和构造)将被混在一起。

在理想情况下,形式与功能是相互独立的。例如,CSS 和 HTML 一定应该如此。CSS 是形式,而 HTML 是功能。在使用 PHP 的情况下,如果页面标记和代码能够分离将是十分理想的。代码将处理输入,制定决策并生成显示数据,而标记将期待获得数据并提供所需的支架以渲染信息。

例如,主页的标记可能留下一个 “填空” (fill in the blank) 以供用户登录,以及其他占位符以供保存用户的图像和重要信息。此模板 —— 这样命名是因为它将提供页面显示的模式 —— 只面向设计人员,设计人员将控制页面的整体外观并留下名称、图片和其他数据的占位符。代码只是为占位符提供数据。开发人员的任务仍然主要集中在计算上。

当然,形式与功能必须协作。如果模板期望获得以美元为单位的金额,则代码不应当提供 URL。如果模板期望获得对象,则代码不应当提供列表。因此,模板系统必须将表单与函数分离,但还必须在两者之间建立联系。

最流行的 Web 应用程序编程语言(Perl、Python、Ruby、Java™)都有模板引擎,而 PHP 也不例外。在搜索引擎中键入 PHP template engine,然后您可能会找到 25 个以上的选项(有关强调所研究的每个引擎功能的列表 The PHP Template Engine Roundup,请参阅 参考资料)。

一些 PHP 模板引擎进行了速度优化。其他 PHP 模板引擎旨在鼓励分离表单与函数的同时简化使用。在某些包中,占位符是在 PHP 本身中描述的,而其他解决方案都有一种自定义的简短编程语言。如何选择模板引擎在很大程度上取决于要求,因此适宜进行少量研究和试验。

在这里,我向您介绍 Smarty,它是最流行的 PHP 模板引擎之一。Smarty “代码” 有它自己的语法和运算符扩展列表,但是系统并不难学。阅读或浏览 Smarty 文档,以便熟悉它的所有功能。从 Smarty 的小修改开始,根据需求扩展您的技能,然后越来越精通。

获得 Smarty

Smarty Web 站点维护着一张活动邮件列表、一个支持论坛和一个 Internet Relay Chat (IRC) 论坛(请参阅 参考资料)。开发正在进行,而本文基于 V2.6.18 版本,该版本发布于 2007 年 3 月 7 日。

Smarty 有两个方面:PHP 应用程序编程接口 (API) 和显示引擎。应用程序代码将调用 API 把代码变量与模板占位符关联起来,而显示引擎将解释 Smarty 标记、执行循环、引用占位符和显示最终结果。Smarty 功能包括:

用于显示 PHP 的所有基本数据结构的运算符
显示简单变量,迭代整个数组或关联数组,以及显示类的成员。
占位符的默认值
如果 PHP 代码没有将变量与占位符关联,则显示默认值。
控制运算符,例如 ifthenelse,可以根据输入数据选择动态显示哪些内容
例如,设计人员可以选择用加粗的红色文本显示负账户余额,而用黑色文本显示正余额。您可以在模板中隔离此类显示逻辑(使您可以更轻松地进行开发)。
循环控制,它将提供用于简化构建列表和表的特殊变量
例如,可以测试循环的第一次迭代并创建表头。还可以像循环迭代一样循环执行值轮循 (round-robin) 列表,循环迭代非常适于改变表行的颜色。
渲染时用于改变数据的修饰符
例如,可以用 Smarty 标记 <strong>{$name|upper}</strong> 大写加粗显示占位符 —— 如 $name

<strong> 是普通 HTML。大括号 ({}) 用于划定 Smarty 标记,$name 是占位符,而 |upper 是修饰符。还可以编写自己的修饰符以扩展 Smarty 的功能。
如果必须 包括脚本和原始 PHP 代码,可以用 literalphp 运算符来完成
literal 运算符内的所有内容都将被逐字传递给最终页面。php 运算符中放置的代码将像嵌入到 <?php ... ?> 转义符内一样执行。

还可以通过 {include ...} 运算符重用 Smarty 模板。要提高性能,则需缓存每个模板,以避免每次使用的转换负载。Smarty Web 站点提供了丰富文档和示例。Packt Publishing 还提供一本名为 Smarty: PHP Template Programming and Applications(请参阅 参考资料)的书,该书适于学习和参考(警告:一些最新运算符并未介绍,而且其他运算符的说明也不正确,因为该书介绍的是 Smarty V2.x 的早期版本)。





回页首

用 Smarty 进行开发

无法通过一篇文章列举和演示 Smarty 的所有功能。但是,即使是一个如下所示的小示例,也能证明模板的力量。

把 Smarty 添加到应用程序中十分轻松:

  1. 下载 Smarty.zip 演示代码(请参阅 下载)。
  2. 解压缩并把 Smarty 安装到路径中。
  3. 编写要求使用 Smarty 类的应用程序。
  4. 创建两个目录一起放置应用程序:
    • templates 将包含模板
    • templates_c 将包含缓存的模板

例如,示例应用程序的文件夹内容包含 Example.class.php index.php templates/ templates_c/

模板目录中的文件将由 Smarty 引擎读取。确保 Web 服务器对那些文件拥有适当的访问权。另外,templates_c 的内容必须可读可写,因为缓存的模板副本放置在该文件夹中。至少要使 Web 服务器可以将数据写入 templates_c。或者,如果不需要考虑安全问题,可以将目录更改为模式 777

清单 1 显示了 Example.class.php,这是一个有代表性的 PHP V5 类。清单 2 包含 index.php,即应用程序。图 1 中显示了用浏览器访问示例应用程序的结果。


清单 1. 简单 PHP V5 类,用于存储任意类型的已命名属性的随机列表
                <?php//// Example is a simple class that stores an arbitrary // number of named properties. //class Example {    private $catalog = array();        public function SetProperties( $arrayVariables ) {        foreach ( $arrayVariables as $name => $value ) {            $this->SetProperty( $name, $value );        }    }        public function SetProperty( $name, $value ) {        $this->$name = $value;        $this->catalog[] = $name;    }        public function GetProperties( ) {        $result = array();        foreach ( $catalog as $name ) {            $result[$name] = $this->GetProperty( $name );        }                return( $result );    }        public function GetProperty( $name ) {        return ( $this->$name );    }}?>

Example 是一个简单类,用于持久保存任意数目的已命名属性。该类最令人感兴趣的部分是第 18 行 $this->$name = $value;。这行代码将动态创建类实例成员。例如,如果调用 $example->SetProperty( 'name', 'Groucho' ),则可以用(传统的)$example->name 检索名称。


清单 2. PHP 应用程序,完全没有任何 Web 页面标记
                <?phprequire( 'Smarty.class.php' );require( 'Example.class.php' );$groucho = new Example();$groucho->SetProperty( 'name', 'Groucho' );$groucho->SetProperty( 'quote',     'Time flies like an arrow. Fruit flies like a banana.' );$chico = new Example();$chico->SetProperties( array(        'name' => 'Chico',        'quote' => "There's no such thing as a sanity clause!"    ));$movies = new Example();$movies->SetProperties( array(    'movies' => array(        'A Night at the Opera',        'Horse Feathers',        'Coconuts',        'The Big Store'    )));$looks = new Example();$looks->SetProperty( 'novelty', array(        array( name =>'Groucho', 'signature' => 'moustache' ),        array( name =>'Chico', 'signature' => 'hat' ),        array( name => 'Harpo', 'signature' => 'raincoat' ),        array( name => 'Zeppo', 'signature' => 'hair')    ));$smarty = new Smarty();$smarty->assign( 'person', $chico );$smarty->assign( 'people', $looks->novelty );$smarty->assign( 'quote', $groucho->quote );$smarty->assign( 'movies', $movies->movies );$smarty->display( 'template.tpl' );?>

清单 2 反映了把 PHP 变量与 Smarty 占位符关联起来的一般策略。代码行 $smarty->assign( 'name', $x ) 将把 PHP 变量(或数组、对象)$x 与占位符名称关联起来。模板中显示 name 的所有位置都将显示 $x 的值。


图 1. 渲染最终页面,结合表单与函数
渲染最终页面,结合表单与函数

Smarty 模板是什么样的?Smarty 代码都是轻量级的,如清单 3、清单 4 和清单 5 所示。Smarty 将把大括号 ({}) 中的所有内容都视为 Smarty 代码。因此,如果任何其他页面标记(例如嵌入式 CSS 或 JavaScript)使用大括号,则必须用 {literal}...{/literal} 把那个标记括起来,如清单 3 中所示:


清单 3. 主模板中包括的简单 header 模板
                <html><head><title>{$title|default:'The Marx Brothers'}</title><style type="text/css">{literal}body {    font-family: Verdana, Arial, sans-serif;    margin: 5%;}{/literal}</style></head><body>

如前所述,清单 3 将应用 {literal} 运算符来逐字渲染标记:定界符之间的所有文本都将被传递而无需进一步解释。第 3 行将显示名为 <title> 的占位符的值,该占位符与 PHP 应用程序中的标量变量相关联。如果不与 <title> 关联,则 |default: 修饰符将提供默认值。注意 Smarty 运算符中的空白。通常,必须忽略它。幸运的是,Smarty 编译器将提供有帮助的错误消息。

清单 4 页脚显示了使用 {if condition} 在渲染时做出决定。在这里,如果占位符 quote 的值已设定,则将显示 {if}{/if} 之间插入的标记。代码行 {$quote|upper} 将用全大写的形式发送 quote 的值。|upper 是改变字符串输出的众多修饰符之一 —— 同时,它对于分离字符串内容与显示形式十分有用。


清单 4. 页脚模板
                <div style="clear: both;">    <p align="center">        {if $quote}            <{$style|default:'strong'}>                {$quote|upper}                    </{$style|default:'strong'}>        {/if}    </p></div></body></html>

清单 5 渲染了最终页面。


清单 5. 应用程序的主要 Smarty 模板
                {include file='header.tpl'}<p>    {$person->name} said, "{$person->quote}"</p><ul>    {section name=index loop=$movies}    <li>        {$movies[index]}    </li>    {/section}</ul> <table>{foreach item=person from=$people name=people}    {if $smarty.foreach.people.first}        <tr>            <th>#</th>            <th>Name</th>            <th>Signature</th>        </tr>    {/if}    <tr bgcolor="{cycle values="#eeeeee,#d0d0d0}">        <td>{$smarty.foreach.people.iteration}</td>        <td>{$person.name}</td>        <td>{$person.signature}</td>    </tr>{foreachelse}    <tr><td>Print this only if there's no data.</td></tr>{/foreach}</table>{include file='footer.tpl'}

应用程序的这个主要 Smarty 模板采用了若干个 Smarty 运算符:

{include file='filename'}
像是 PHP 自己的 include() 方法一样运行,在适当的位置立即插入和解释 filename 的内容。虽然并未显示,但是可以将变量从一个模板传递给另一个模板,这样做鼓励重用。
{$person->GetProperty('name')}
假定 person 与名为 GetProperty() 的方法相关。您可以调用对象的方法和引用对象成员,像 {$person->quote} 所做的那样。
{section name=index loop=$placeholder}
在数组内迭代。loop 属性将给占位符命名,而 name 属性将指定一个名称以供数组索引使用。在循环内,将把数组元素作为 {$placeholder[index]} 来引用。
foreach
section 一样迭代,但是提供了一个非常优秀的功能来处理一组关联数组,例如数据库查询的行列表。每个关联数组都被 “转换” 到名为 item 的索引中。例如,在清单 5 中,person 被命名为 item。每执行一次循环,person 就会被指定来自数组 people 的关联数组。在那之后,在整个循环过程中,可以通过关键字引用关联数组中的值,如 {$person.signature}
foreach 中的 name 属性
类似于 HTML 标记的 id 属性,它将惟一地识别循环。使用此 ID 来引用反映循环状态的特殊变量集。例如,一个特殊变量是 first,它只在循环的第一次迭代时才被设定。因此,值 $smarty.foreach.people.first 将引用与名为 people (people) 的 foreach 循环 (foreach) 关联的特殊 Smarty 变量 (smarty)。正如您可能会想到的那样,还有 last 值和 iteration 值,它们从 1 开始,并随每次迭代增加(如果需要从零开始的计数器,请使用 index 而不要使用 iteration)。
cycle
用于构建表的优秀运算符。如果提供 values 列表,Smarty 将像循环迭代一样在所有值中循环。将循环添加到 bgcolor 中将改变每个表行的颜色可以使表更清晰。
{foreachelse}
如果要迭代的数组为空,则转而显示 {foreachelse}...{/foreachelse} 的内容。

既然您已经预览了模板,那么 清单 2 读起来可能很简单。跟平常一样,清单 2 将执行计算并把渲染页面的工作传递给 Smarty。代码行 $smarty->display('template.tpl') 将渲染模板。要捕捉 Smarty 的输出,请使用 $smarty->fetch('template.tpl')





回页首

使用 Smarty 更聪明一点,而不是更辛苦一点

虽然本例是经过设计的,但是它展示了 Smarty 的强大之处和灵活性以及使用它分离标记与代码是多么简单。Smarty 还有更多技巧。Smarty 可能实现您所需要的几乎所有功能。您可以将模板输出捕捉到 Smarty 占位符中。您可以过滤模板,无论是在编译前,还是在编译后,还可以在渲染输出被显示或获取之前先进行处理。而且 Smarty 允许您缓存模板。

向 PHP 代码中添加 $smarty->caching = 1; 即可获得上述特性。如果缓存被启用,则调用 display('template.tpl') 将像往常一样渲染模板并将在缓存中保存一份模板副本。下一次调用 display('template.tpl') 将利用缓存的副本,而不再渲染模板。

分享这篇文章……

digg 将这篇文章提交到 Digg del.icio.us 发布到 del.icio.us Slashdot 提交到 Slashdot!

您可以控制缓存的有效期、手动清空缓存,并且如果在保存缓存版本后模板文件发生更改,可以使缓存可以自动刷新。如果希望阅读类似 清单 2 的代码,请把 Smarty 应用到 Web 项目中。






回页首

下载

描述名字大小下载方法Smarty 示例os-php-smarty.zip3KBHTTP关于下载方法的信息

参考资料

学习
  • 您可以参阅本文在 developerWorks 全球站点上的 英文原文。
  • Smarty Web 站点 是了解关于 Smarty 的更多信息的起点。
  • 阅读 Hasin Hader、Joao Prado Maia 和 Lucian Gheorghe 合著的 Smarty: PHP Template Programming and Applications 一书(Packt Publishing,2006 年)。
  • The PHP Template Engine Roundup 是配有非常有帮助的注释的模板引擎完整列表。
  • PHP.net 是 PHP 开发者的资源。
  • 查阅 “PHP 推荐读物列表”。
  • 浏览 developerWorks 上的全部 PHP 文章和PHP 教程。
  • 查看 IBM developerWorks 的 PHP 项目资源 扩展 PHP 技巧。
  • 收听针对软件开发人员的有趣访谈和讨论,一定要访问 developerWorks podcast。
  • 随时关注 developerWorks 的 技术事件和网络广播。
  • 要将数据库与 PHP 结合使用?查看 Zend Core for IBM,它是支持 IBM DB2 9 的无缝、开箱即用且易于安装的 PHP 开发和生产环境。
  • 查阅最近将在全球举办的面向 IBM 开放源码开发人员的研讨会、展览、网络广播和其他 活动。
  • 访问 developerWorks 开放源码专区,获得丰富的 how-to 信息、工具和项目更新,帮助您用开放源码技术进行开发,并与 IBM 产品结合使用。
  • 查看免费的 developerWorks On demand demo 观看并了解 IBM 及开源技术和产品功能。

获得产品和技术
  • 下载 Smarty 模板引擎。
  • 使用 IBM 试用软件 改进您的下一个开发项目,这些软件可以通过下载或从 DVD 中获得。
  • 下载 IBM 产品评估版,并开始使用 DB2®、Lotus®、Rational®、Tivoli® 和 WebSphere® 的应用程序开发工具和中间件产品。

讨论
  • PHP Insider 托管着 Smarty 支持论坛。
  • 从 Smarty IRC channel 的参与者中获得帮助(单击此链接将打开通向 Smarty IRC channel 的默认 IRC 客户机)。
  • 通过参与 developerWorks blog 加入 developerWorks 社区。
  • 加入 developerWorks PHP Forum: Developing PHP applications with IBM Information Management products (DB2, IDS)。


关于作者

martin streicher,linux 杂志主编

Martin Streicher 是 Linux Magazine 的主编兼 Hesketh.com(Raleigh, N.C. 中的用户体验代理机构)的 Web 开发人员,以及 developerWorks 的定期投稿者。他毕业于普渡大学

原创粉丝点击