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

时间:2008-5-31    作者:Deri    分类: 分享


    <p>  <a href="http://file.ddvip.com/2007_09/1189228972_ddvip_2172.zip">本文示例源代码或素材下载</a></p>

   <p>  PHP Web 应用程序易于上手。PHP 语言的语法整洁且易于掌握。可以将 PHP 与 HTML、JavaScript 和 CSS 直接混用以快速生成可视结果。而且,把 PHP 应用程序部署到您自己的 Web 服务器或托管服务中只是小菜一碟。</p><p>  但是混用 PHP 与其他页面标记也是一项责任。PHP 代码通常是含有程序逻辑、结构化查询语言(Structured Query Language,SQL)查询、函数、类、开发人员注释、HTML、CSS 样式和脚本的复杂 web(不是开玩笑)。更糟糕的是,把内容从 PHP、echo 发送到输出缓冲区有很多种方法。维护这样混乱的页面十分费力。对代码或标记做出无关紧要的更改会带来严重破坏,并且增强页面可能需要设计人员与程序员的共同努力。使用 PHP,形式(页面的布局)及功能(页面的目的和构造)将被混在一起。</p><p>  在理想情况下,形式与功能是相互独立的。例如,CSS 和 HTML 一定应该如此。CSS 是形式,而 HTML 是功能。在使用 PHP 的情况下,如果页面标记和代码能够分离将是十分理想的。代码将处理输入,制定决策并生成显示数据,而标记将期待获得数据并提供所需的支架以渲染信息。</p><p>  例如,主页的标记可能留下一个 “填空” (fill in the blank) 以供用户登录,以及其他占位符以供保存用户的图像和重要信息。此模板 ―― 这样命名是因为它将提供页面显示的模式 ―― 只面向设计人员,设计人员将控制页面的整体外观并留下名称、图片和其他数据的占位符。代码只是为占位符提供数据。开发人员的任务仍然主要集中在计算上。</p><p>  当然,形式与功能必须协作。如果模板期望获得以美元为单位的金额,则代码不应当提供 URL。如果模板期望获得对象,则代码不应当提供列表。因此,模板系统必须将表单与函数分离,但还必须在两者之间建立联系。</p>
<p> </p>

   <p>  最流行的 Web 应用程序编程语言(Perl、Python、Ruby、Java&#8482;)都有模板引擎,而 PHP 也不例外。在搜索引擎中键入 PHP template engine,然后您可能会找到 25 个以上的选项。</p><p>  一些 PHP 模板引擎进行了速度优化。其他 PHP 模板引擎旨在鼓励分离表单与函数的同时简化使用。在某些包中,占位符是在 PHP 本身中描述的,而其他解决方案都有一种自定义的简短编程语言。如何选择模板引擎在很大程度上取决于要求,因此适宜进行少量研究和试验。</p><p>  在这里,我向您介绍 Smarty,它是最流行的 PHP 模板引擎之一。Smarty “代码” 有它自己的语法和运算符扩展列表,但是系统并不难学。阅读或浏览 Smarty 文档,以便熟悉它的所有功能。从 Smarty 的小修改开始,根据需求扩展您的技能,然后越来越精通。</p><p>  获得 Smarty</p><p>  Smarty Web 站点维护着一张活动邮件列表、一个支持论坛和一个 Internet Relay Chat (IRC) 论坛。开发正在进行,而本文基于 V2.6.18 版本,该版本发布于 2007 年 3 月 7 日。</p><p>  Smarty 有两个方面:PHP 应用程序编程接口 (API) 和显示引擎。应用程序代码将调用 API 把代码变量与模板占位符关联起来,而显示引擎将解释 Smarty 标记、执行循环、引用占位符和显示最终结果。Smarty 功能包括:</p><p>  用于显示 PHP 的所有基本数据结构的运算符</p><p>  显示简单变量,迭代整个数组或关联数组,以及显示类的成员。</p><p>  占位符的默认值</p><p>  如果 PHP 代码没有将变量与占位符关联,则显示默认值。</p><p>  控制运算符,例如 if、then、else,可以根据输入数据选择动态显示哪些内容</p><p>  例如,设计人员可以选择用加粗的红色文本显示负账户余额,而用黑色文本显示正余额。您可以在模板中隔离此类显示逻辑(使您可以更轻松地进行开发)。</p>
 <p> </p>

   <p>  循环控制,它将提供用于简化构建列表和表的特殊变量</p><p>  例如,可以测试循环的第一次迭代并创建表头。还可以像循环迭代一样循环执行值轮循 (round-robin) 列表,循环迭代非常适于改变表行的颜色。</p><p>  渲染时用于改变数据的修饰符</p><p>  例如,可以用 Smarty 标记 <strong>{$name|upper}</strong> 大写加粗显示占位符 ―― 如 $name。</p><p>  <strong> 是普通 HTML。大括号 ({}) 用于划定 Smarty 标记,$name 是占位符,而 |upper 是修饰符。还可以编写自己的修饰符以扩展 Smarty 的功能。</p><p>  如果必须 包括脚本和原始 PHP 代码,可以用 literal 和 php 运算符来完成</p><p>  literal 运算符内的所有内容都将被逐字传递给最终页面。php 运算符中放置的代码将像嵌入到 <?php ... ?> 转义符内一样执行。</p><p>  还可以通过 {include ...} 运算符重用 Smarty 模板。要提高性能,则需缓存每个模板,以避免每次使用的转换负载。Smarty Web 站点提供了丰富文档和示例。</p><p>  用 Smarty 进行开发</p><p>  无法通过一篇文章列举和演示 Smarty 的所有功能。但是,即使是一个如下所示的小示例,也能证明模板的力量。</p><p>  把 Smarty 添加到应用程序中十分轻松:</p><p>  下载 Smarty.zip 演示代码。 </p><p>  解压缩并把 Smarty 安装到路径中。 </p><p>  编写要求使用 Smarty 类的应用程序。 </p><p>  创建两个目录一起放置应用程序: </p><p>  templates 将包含模板 </p><p>  templates_c 将包含缓存的模板 </p><p>  例如,示例应用程序的文件夹内容包含 Example.class.php index.php templates/ templates_c/。</p><p>  模板目录中的文件将由 Smarty 引擎读取。确保 Web 服务器对那些文件拥有适当的访问权。另外,templates_c 的内容必须可读可写,因为缓存的模板副本放置在该文件夹中。至少要使 Web 服务器可以将数据写入 templates_c。或者,如果不需要考虑安全问题,可以将目录更改为模式 777。</p>
 <p> </p>

   <p>  清单 1 显示了 Example.class.php,这是一个有代表性的 PHP V5 类。清单 2 包含 index.php,即应用程序。图 1 中显示了用浏览器访问示例应用程序的结果。</p><p>  清单 1. 简单 PHP V5 类,用于存储任意类型的已命名属性的随机列表</p><p><code><?php<br />//<br />// Example is a simple class that stores an arbitrary<br />// number of named properties.<br />//<br />class Example {<br />  private $catalog = array();<br />  <br />  public function SetProperties( $arrayVariables ) {<br />    foreach ( $arrayVariables as $name => $value ) {<br />      $this->SetProperty( $name, $value );<br />    }<br />  }<br />  <br />  public function SetProperty( $name, $value ) {<br />    $this->$name = $value;<br />    $this->catalog[] = $name;<br />  }<br />  <br />  public function GetProperties( ) {<br />    $result = array();<br />    foreach ( $catalog as $name ) {<br />      $result[$name] = $this->GetProperty( $name );<br />    }<br />    <br />    return( $result );<br />  }<br />  <br />  public function GetProperty( $name ) {<br />    return ( $this->$name );<br />  }<br />}<br />?></code></p><p>  Example 是一个简单类,用于持久保存任意数目的已命名属性。该类最令人感兴趣的部分是第 18 行 $this->$name = $value;。这行代码将动态创建类实例成员。例如,如果调用 $example->SetProperty( 'name', 'Groucho' ),则可以用(传统的)$example->name 检索名称。</p>
 <p> </p>

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

   <p>  清单 2 反映了把 PHP 变量与 Smarty 占位符关联起来的一般策略。代码行 $smarty->assign( 'name', $x ) 将把 PHP 变量(或数组、对象)$x 与占位符名称关联起来。模板中显示 name 的所有位置都将显示 $x 的值。</p><p>  图 1. 渲染最终页面,结合表单与函数</p><p>  <img src="/content/uploadfile/200805/2008053117214692.jpg" onclick="get_larger(this)" alt="用Smarty分离PHP应用程序中的形式与功能" /></p><p>  Smarty 模板是什么样的?Smarty 代码都是轻量级的,如清单 3、清单 4 和清单 5 所示。Smarty 将把大括号 ({}) 中的所有内容都视为 Smarty 代码。因此,如果任何其他页面标记(例如嵌入式 CSS 或 JavaScript)使用大括号,则必须用 {literal}...{/literal} 把那个标记括起来,如清单 3 中所示:</p><p>  清单 3. 主模板中包括的简单 header 模板</p><p><code><html><br /><head><br />  <title>{$title|default:'The Marx Brothers'}</title><br />  <style type="text/css"><br />  {literal}<br />  body {<br />    font-family: Verdana, Arial, sans-serif;<br />    margin: 5%;<br />  }<br />  {/literal}<br />  </style><br /></head><br /><body></code></p><p>  如前所述,清单 3 将应用 {literal} 运算符来逐字渲染标记:定界符之间的所有文本都将被传递而无需进一步解释。第 3 行将显示名为 <title> 的占位符的值,该占位符与 PHP 应用程序中的标量变量相关联。如果不与 <title> 关联,则 |default: 修饰符将提供默认值。注意 Smarty 运算符中的空白。通常,必须忽略它。幸运的是,Smarty 编译器将提供有帮助的错误消息。</p>
 <p> </p>

   <p>  清单 4 页脚显示了使用 {if condition} 在渲染时做出决定。在这里,如果占位符 quote 的值已设定,则将显示 {if} 与 {/if} 之间插入的标记。代码行 {$quote|upper} 将用全大写的形式发送 quote 的值。|upper 是改变字符串输出的众多修饰符之一 ―― 同时,它对于分离字符串内容与显示形式十分有用。</p><p>  清单 4. 页脚模板</p><p><code><div style="clear: both;"><br />  <p align="center"><br />    {if $quote}<br />      <{$style|default:'strong'}><br />        {$quote|upper}    <br />      </{$style|default:'strong'}><br />    {/if}<br />  </p><br /></div><br /></body><br /></html></code></p><p>  清单 5 渲染了最终页面。</p><p>  清单 5. 应用程序的主要 Smarty 模板</p><p><code>{include file='header.tpl'}<br /><p><br />  {$person->name} said, "{$person->quote}"<br /></p><br /><ul><br />  {section name=index loop=$movies}<br />  <li><br />    {$movies[index]}<br />  </li><br />  {/section}<br /></ul><br /><table><br />{foreach item=person from=$people name=people}<br />  {if $smarty.foreach.people.first}<br />    <tr><br />      <th>#</th><br />      <th>Name</th><br />      <th>Signature</th><br />    </tr><br />  {/if}<br />  <tr bgcolor="{cycle values="#eeeeee,#d0d0d0}"><br />    <td>{$smarty.foreach.people.iteration}</td><br />    <td>{$person.name}</td><br />    <td>{$person.signature}</td><br />  </tr><br />{foreachelse}<br />  <tr><td>Print this only if there's no data.</td></tr><br />{/foreach}<br /></table><br />{include file='footer.tpl'}</code></p>
 <p> </p>

   <p>  应用程序的这个主要 Smarty 模板采用了若干个 Smarty 运算符:</p><p>  应用程序的这个主要 Smarty 模板采用了若干个 Smarty 运算符: </p><p>  {include file='filename'}</p><p>  像是 PHP 自己的 include() 方法一样运行,在适当的位置立即插入和解释 filename 的内容。虽然并未显示,但是可以将变量从一个模板传递给另一个模板,这样做鼓励重用。</p><p>  {$person->GetProperty('name')}</p><p>  假定 person 与名为 GetProperty() 的方法相关。您可以调用对象的方法和引用对象成员,像 {$person->quote} 所做的那样。</p><p>  {section name=index loop=$placeholder}</p><p>  在数组内迭代。loop 属性将给占位符命名,而 name 属性将指定一个名称以供数组索引使用。在循环内,将把数组元素作为 {$placeholder[index]} 来引用。</p><p>  foreach</p><p>  像 section 一样迭代,但是提供了一个非常优秀的功能来处理一组关联数组,例如数据库查询的行列表。每个关联数组都被 “转换” 到名为 item 的索引中。例如,在清单 5 中,person 被命名为 item。每执行一次循环,person 就会被指定来自数组 people 的关联数组。在那之后,在整个循环过程中,可以通过关键字引用关联数组中的值,如 {$person.signature}。</p><p>  foreach 中的 name 属性</p><p>  类似于 HTML 标记的 id 属性,它将惟一地识别循环。使用此 ID 来引用反映循环状态的特殊变量集。例如,一个特殊变量是 first,它只在循环的第一次迭代时才被设定。因此,值 $smarty.foreach.people.first 将引用与名为 people (people) 的 foreach 循环 (foreach) 关联的特殊 Smarty 变量 (smarty)。正如您可能会想到的那样,还有 last 值和 iteration 值,它们从 1 开始,并随每次迭代增加(如果需要从零开始的计数器,请使用 index 而不要使用 iteration)。</p></p><p>  cycle</p><p>  用于构建表的优秀运算符。如果提供 values 列表,Smarty 将像循环迭代一样在所有值中循环。将循环添加到 bgcolor 中将改变每个表行的颜色可以使表更清晰。</p><p>  {foreachelse}</p><p>  如果要迭代的数组为空,则转而显示 {foreachelse}...{/foreachelse} 的内容。</p><p>  既然您已经预览了模板,那么 清单 2 读起来可能很简单。跟平常一样,清单 2 将执行计算并把渲染页面的工作传递给 Smarty。代码行 $smarty->display('template.tpl') 将渲染模板。要捕捉 Smarty 的输出,请使用 $smarty->fetch('template.tpl')。 </p><p>  使用 Smarty 更聪明一点,而不是更辛苦一点</p><p>  虽然本例是经过设计的,但是它展示了 Smarty 的强大之处和灵活性以及使用它分离标记与代码是多么简单。Smarty 还有更多技巧。Smarty 可能实现您所需要的几乎所有功能。您可以将模板输出捕捉到 Smarty 占位符中。您可以过滤模板,无论是在编译前,还是在编译后,还可以在渲染输出被显示或获取之前先进行处理。而且 Smarty 允许您缓存模板。</p><p>  向 PHP 代码中添加 $smarty->caching = 1; 即可获得上述特性。如果缓存被启用,则调用 display('template.tpl') 将像往常一样渲染模板并将在缓存中保存一份模板副本。下一次调用 display('template.tpl') 将利用缓存的副本,而不再渲染模板。</p><p>  您可以控制缓存的有效期、手动清空缓存,并且如果在保存缓存版本后模板文件发生更改,可以使缓存可以自动刷新。如果希望阅读类似 清单 2 的代码,请把 Smarty 应用到 Web 项目中。</p></p>