2022年12月10日发(作者:男的qq名字)什么是模版引擎 不知道从什么时候开始, 有人开始对?HTML?内嵌入?Server?Script?觉得不太满意。 然而不论 是微软的?ASP?或是开放源码的?PHP,都是属于内嵌?Server?Script?的网页伺服端语言。因 此也就有人想到,如果能把程序应用逻辑?(或称商业应用逻辑)?与网页呈现?(Layout)?逻辑 分离的话,是不是会比较好呢? 其实这个问题早就存在已久,从交互式网页开始风行时,不论是?ASP?或是?PHP?的使用者都 是身兼程序开发者与视觉设计师两种身份。可是通常这些使用者不是程序强就是美工强,如 果要两者同时兼顾,那可得死掉不少脑细胞... 所以模版引擎就应运而生啦!模版引擎的目的,就是要达到上述提到的逻辑分离的功能。它 能让程序开发者专注于资料的控制或是功能的达成;而视觉设计师则可专注于网页排版,让 网页看起来更具有专业感!因此模版引擎很适合公司的网站开发团队使用,使每个人都能发 挥其专长! 就笔者接触过的模版引擎来说,依资料呈现方式大概分成:需搭配程序处理的模版引擎和完 全由模版本身自行决定的模版引擎两种形式。 在需搭配程序处理的模版引擎中,程序开发者必须要负责变量的呈现逻辑,也就是说他必须 把变量的内容在输出到模版前先处理好,才能做?assign?的工作。换句话说,程序开发者还 是得多写一些程序来决定变量呈现的风貌。而完全由模版本身自行决定的模版引擎,它允许 变量直接?assign?到模版中,让视觉设计师在设计模版时再决定变量要如何呈现。因此它就 可能会有另一套属于自己的模版程序语法?(如?Smarty)?, 以方便控制变量的呈现。 但这样一 来,视觉设计师也得学习如何使用模版语言。 模版引擎的运作原理,首先我们先看看以下的运行图: 一般的模版引擎?(如?PHPLib)?都是在建立模版对象时取得要解析的模版,然后把变量套入 后,透过?par()?这个方法来解析模版,最后再将网页输出。 对?Smarty?的使用者来说, 程序里也不需要做任何?par?的动作了, 这些?Smarty?自动会帮 我们做。而且已经编译过的网页,如果模版没有变动的话,?Smarty?就自动跳过编译的动作, 直接执行编译过的网页,以节省编译的时间。 使用 Smarty 的一些概念 在一般模版引擎中,我们常看到区域的观念,所谓区块大概都会长成这样: 区域内容 这些区块大部份都会在?PHP?程序中以?if?或?for,?while?来控制它们的显示状态, 虽然模版 看起来简洁多了,但只要一换了显示方式不同的模版,?PHP?程序势必要再改一次!
x0c在?Smarty?中,一切以变量为主,所有的呈现逻辑都让模版自行控制。因为?Smarty?
会有自 己 的 模 版 语 言 , 所 以 不 管 是 区 块 是 否 要 显 示 还 是 要 重 复 , 都 是 用 ?Smarty? 的 模 版 语 法?(if,?foreach,?ction)?搭配变量内容作呈现。 这样一来感觉上好象模版变得有点复杂, 但好处是只要规划得当,?PHP?程序一行都不必改。 由上面的说明,我们可以知道使用 Smarty?要掌握一个原则:将程序应用逻辑与网页呈现逻 辑明确地分离。 就是说?PHP?程序里不要有太多的?HTML?码。 程序中只要决定好那些变量要塞 到模版里,让模版自己决定该如何呈现这些变量?(甚至不出现也行)?。 Smarty 的基础 安装 Smarty 首先,我们先决定程序放置的位置。 Windows 下可能会类似这样的位置: 「?d:apprvwebdemo?」 。 Linux 下可能会类似这样的位置: 「?/home/jaceju/public_html/?」 。 到 Smarty 的官方网站下载最新的 Smarty 套件:。 解开?Smarty?2.6.0?后,会看到很多档案,其中有个?libs?资料夹。在?libs?中应该会有?3? 个??檔?+?1?个??+?1?个?plugin?资料夹?+?1?个?core?资料夹。然后直 接将?libs?复制到您的程序主资料夹下,再更名为?class?就可以了。就这样?没错!这种安 装法比较简单,适合一般没有自己主机的使用者。 至于?Smarty?官方手册中为什么要介绍一些比较复杂的安装方式呢?基本上依照官方的方式 安装,可以只在主机安装一次,然后提供给该主机下所有设计者开发不同程序时直接引用, 而不会重复安装太多的?Smarty?复本。而笔者所提供的方式则是适合要把程序带过来移过去 的程序开发者使用,这样不用烦恼主机有没有安装?Smarty?。 程序的资料夹设定 以笔者在 Windows 安装 Apprv 为例,程序的主资料夹是「d:apprvwebdemo」 。安装好 Smarty 后,我们在主资料夹下再建立这样的资料夹: 在?Linux?底下,请记得将?templates_c?的权限变更为?777?。Windows?下则将其只读取消。 第一个用 Smarty 写的小程序 我们先设定?Smarty?的路径,请将以下这个档案命名为??,并放置到主资料夹下: :
x0c<?php? include?"class/";? define(@#__SITE_ROOT@#,?@#d:/apprv/web/demo@#);?//?最后没有斜线? $tpl?=?new?Smarty();? $tpl->template_dir?=?__SITE_ROOT?.?"/templates/";? $tpl->compile_dir?=?__SITE_ROOT?.?"/templates_c/";? $tpl->config_dir?=?__SITE_ROOT?.?"/configs/";? $tpl->cache_dir?=?__SITE_ROOT?.?"/cache/";? $tpl->left_delimiter?=?@#<{@#;? $tpl->right_delimiter?=?@#}>@#;? ?>?
照上面方式设定的用意在于,程序如果要移植到其它地方,只要改?__SITE_ROOT?就可以 啦。?(这里是参考?XOOPS?的?) Smarty?的模版路径设定好后,程序会依照这个路径来抓所有模版的相对位置 ?(范例中 是?@#d:/apprv/web/demo/templates/@#?)?。 然后我们用?display()?这个?S
marty?方法来 显示我们的模版。 接下来我们在?templates?资料夹下放置一个?: (扩展名叫什么都无所谓, 但便于视 觉设计师开发,笔者都还是以?.htm?为主。) templates/:
? ?
?
<{$title}>? ? ? <{$content}>? ? ?
x0c现在我们要将上面的模版显示出来,并将网页标题?($title)?与内容?($content)?更换,请 将以下档案内容命名为??,并放置在主资料夹下: :
<?php? require?"";? $tpl->assign("title",?"测试用的网页标题");? $tpl->assign("content",?"测试用的网页内容");? //?上面两行也可以用这行代替? //?$tpl->assign(array("title"?=>?"测试用的网页标题",?"content"?=>?"测试用的网页 内容"));? $tpl->display(@#@#);? ?>?
请打开浏览器,输入?localhost/demo/?试试看(依您的环境决定网址),应 该会看到以下的画面:
再到?templates_c?底下,我们会看到一个奇怪的资料夹?(%%179)?,再点选下去也是一个奇 怪的资料夹?(%%1798044067)?,而其中有一个档案: templates_c/%%179/%%1798044067/:
<?php?/*?Smarty?version?2.6.0,?created?on?2003-12-15?22:19:45?compiled?from?test .htm?*/??>? ? ?
?
<?php?echo?$this->_tpl_vars[@#title@#];??>? ? ? <?php?echo?$this->_tpl_vars[@#content@#];??>?
x0c? ?
没错, 这就是?Smarty?编译过的档案。 它将我们在模版中的变量转换成了?PHP?的语法来执行, 下次再读取同样的内容时,?Smarty?就会直接抓取这个档案来执行了。 最后我们整理一下整个?Smarty?程序撰写步骤: Step?1.?加载?Smarty?模版引擎。 Step?2.?建立?Smarty?对象。 Step?3.?设定?Smarty?对象的参数。 Step?4.?在程序中处理变量后,再用?Smarty?的?assign?方法将变量置入模版里。 Step?5.?利用?Smarty?的?display?方法将网页秀出。 如何安排你的程序架构 上 面 我 们 看 到 除 了 ?Smarty? 所 需 要 的 资 料 夹 外 ?(class? 、 ?configs? 、 ?templates? 、 ?templates_c)? , 还 有 两 个 资 料 夹:?includes?、?modules?。其实这是笔者模仿?XOOPS?的架构所建立出来的,因为?XOOPS? 是笔者所接触到的程序中,少数使用?Smarty?模版引擎的架站程序。所谓西瓜偎大边,笔者 这样的程序架构虽没有?XOOPS?的百分之一强,但至少给人看时还有?XOOPS?撑腰。 includes?这个资料夹主要是用来放置一些?function?、?sql?檔,这样在??就可以 将它们引入了,如下: :
<?php? include?"class/";? define(@#__SITE_ROOT@#,?@#d:/apprv/web/demo@#);?//?最后没有斜线? //?以??的位置为基准?
x0crequire_once?"includes/";? require_once?"includes/incl
";? $tpl?=?new?Smarty();? $tpl->template_dir?=?__SITE_ROOT?.?"/templates/";? $tpl->compile_dir?=?__SITE_ROOT?.?"/templates_c/";? $tpl->config_dir?=?__SITE_ROOT?.?"/configs/";? $tpl->cache_dir?=?__SITE_ROOT?.?"/cache/";? $tpl->left_delimiter?=?@#<{@#;? $tpl->right_delimiter?=?@#}>@#;? ?>?
modules?这个资料夹则是用来放置程序模块的,如此一来便不会把程序丢得到处都是,整体 架构一目了然。 上面我们也提到??,这是整个程序的主要核心,不论是常数定义、外部程序加载、 共享变量建立等, 都是在这里开始的。 所以之后的模块都只要将这个档案包含进来就可以啦。 因此在程序流程规划期间,就必须好好构思 ??中应该要放那些东西;当然利 用?include?或?require?指令,把每个环节清楚分离是再好不过了。
在上节提到的?Smarty?程序?5?步骤,??就会帮我们先将前?3?个步骤做好,后面的 模块程序只要做后面两个步骤就可以了。 从变量开始 如何使用变量 从上一章范例中,我们可以清楚地看到我们利用?<{?及?}>?这两个标示符号将变量包起来。 预设的标示符号为?{?及?}?,但为了中文冲码及?javascript?的关系,因此笔者还是模 仿?XOOPS?,将标示符号换掉。变量的命名方式和?PHP?的变量命名方式是一模一样的,前面 也有个?$?字号?(这和一般的模版引擎不同)。标示符号就有点像是?PHP?中的 (事实上它们的确会被替换成这个)?,所以以下的模版变量写法都是可行的: 1.?<{$var}> 2.?<{?$var?}>? 3.?<{$var }>? 在?Smarty?里,变量预设是全域的,也就是说你只要指定一次就好了。指定两次以上的话,
x0c变量内容会以最后指定的为主。就算我们在主模版中加载了外部的子模版,子模版中同样的 变量一样也会被替代,这样我们就不用再针对子模版再做一次解析的动作。 而在?PHP?程序中, 我们用?Smarty?的?assign?来将变量置放到模版中。 ?assign?的用法官方 手册中已经写得很多了,用法就如同上一节的范例所示。不过在重复区块时,我们就必须将 变量做一些手脚后,才能将变量?assign?到模版中,这在下一章再提。 修饰你的变量 上面我们提到?Smarty?变量呈现的风貌是由模版自行决定的,所以?Smarty?提供了许多修饰 变量的函式。使用的方法如下: <{变量|修饰函式}>? <{变量|修饰函式:"参数(非必要,视函式而定)"}>? 范例如下: <{$var|nl2br}>? <{$var|string_format:"%02d"}>? 好,那为什么要让模版自行决定变量呈现的风貌?先看看底下的?HTML?,这是某个购物车结
帐的部份画面。
总金额:21,000?元 一般模版引擎的模版可能会这样写:
总金额:{format_total}?元 它们的?PHP?程序中要这样写:
<?php? $total?=?21000;? $tpl->assign("total",?$total);? $tpl->assign("format_total",?number_format($total));? ?>?
x0c而?Smarty?的模版就可以这样写:?(number_format?修饰函式请到 Smarty?官方网页下载)
"?/> 总金额:<{$total|number_format:""}>?元 Smarty?的?PHP?程序中只要这样写:
<?php? $total?=?21000;? $tpl->assign("total",?$total);? ?>?
所以在?Smarty?中我们只要指定一次变量,剩下的交给模版自行决定即可。这样了解了吗? 这就是让模版自行决定变量呈现风貌的好处! 控制模版的内容 重复的区块 在?Smarty?样板中,我们要重复一个区块有两种方式:?foreach?及?ction?。而在程序中 我们则要?assign?一个数组,这个数组中可以包含数组数组。就像下面这个例子: 首先我们来看?PHP?程序是如何写的: :
<?php? require?"";? $array1?=?array(1?=>?"苹果",?2?=>?"菠萝",?3?=>?"香蕉",?4?=>?"芭乐");? $tpl->assign("array1",?$array1);? $array2?=?array(? array("index1"?=>?"data1-1",?"index2"?=>?"data1-2",?"index3"?=>?"data1-3"),?
x0carray("index1"?=>?"data2-1",?"index2"?=>?"data2-2",?"index3"?=>?"data2-3"),? array("index1"?=>?"data3-1",?"index2"?=>?"data3-2",?"index3"?=>?"data3-3"),? array("index1"?=>?"data4-1",?"index2"?=>?"data4-2",?"index3"?=>?"data4-3"),? array("index1"?=>?"data5-1",?"index2"?=>?"data5-2",?"index3"?=>?"data5-3"));? $tpl->assign("array2",?$array2);? $tpl->display("");? ?>?
而模版的写法如下: templates/:
? ?
?
测试重复区块? ? ?
? 利用?foreach?来呈现?array1? <{foreach?item=item1?from=$array1}>? <{$item1}>? <{/foreach}>? 利用?ction?来呈现?array1? <{ction?name=c1?loop=$array1}>? <{$array1[c1]}>? <{/ction}>? 利用?foreach?来呈现?array2? <{foreach?item=index2?from=$array2}>? <{foreach?key=key2?item=item2?from=$index2}>? <{$key2}>:?<{$item2}>? <{/foreach}>? <{/foreach}>? 利用?ction?来呈现?array1? <{ction?name=c2?loop=$array2}>? index1:?<{$array2[c2].index1}>? index2:?<{$array2[c2].index2}>? index3:?<{$array2[c2].index3}>?
x0c<{/ction}>?
? ? ?
执行上例后,我们发现不管是?foreach?或?ction?两个执行结果是一样的。那么两者到底 有何不同呢? 第一个差别很明显, 就是 foreach?要以巢状处理的方式来呈现我们所?assign?的两层数组变 量,而?ction?则以「主数组[循环名称].子数组索引」即可将整个数组呈现出来。由此可 知 , ?Smarty? 在 模 版 中 的 ?foreach? 和 ?PHP? 中 的 ?for
each? 是 一 样 的 ; 而 ?ction? 则 是?Smarty?为了处理如上列的数组变量所发展出来的叙述。当然?ction?的功能还不只如 此, 除了下一节所谈到的巢状资料呈现外, 官方手册中也提供了好几个?ction?的应用范例。 不 过 要 注 意 的 是 , 丢 给 ?ction? 的 数 组 索 引 必 须 是 从 ?0? 开 始 的 正 整 数 , 即?0,?1,?2,?3,?...。如果您的数组索引不是从?0?开始的正整数,那么就得改用?foreach? 来呈现您的资料。您可以参考官方讨论区中的此篇讨论,其中探讨了?ction?和?foreach? 的用法。 巢状资料的呈现 模版引擎里最令人伤脑筋的大概就是巢状资料的呈现吧,许多著名的模版引擎都会特意强调 这点,不过这对?Smarty?来说却是小儿科。 最常见到的巢状资料,就算论譠程序中的讨论主题区吧。假设要呈现的结果如下: 公告区 站务公告 文学专区 好书介绍 奇文共赏 计算机专区 硬件外围 软件讨论
x0c程序中我们先以静态资料为例: :
<?php? require?"";? $forum?=?array(? array("category_id"?=>?1,?"category_name"?=>?"公告区",? "topic"?=>?array(? array("topic_id"?=>?1,?"topic_name"?=>?"站务公告")? )? ),? array("category_id"?=>?2,?"category_name"?=>?"文学专区",? "topic"?=>?array(? array("topic_id"?=>?2,?"topic_name"?=>?"好书介绍"),? array("topic_id"?=>?3,?"topic_name"?=>?"奇文共赏")? )? ),? array("category_id"?=>?3,?"category_name"?=>?"计算机专区",? "topic"?=>?array(? array("topic_id"?=>?4,?"topic_name"?=>?"硬件外围"),? array("topic_id"?=>?5,?"topic_name"?=>?"软件讨论")? )? )? );? $tpl->assign("forum",?$forum);? $tpl->display("");? ?>?
模版的写法如下: templates/:
?
x0c?
巢状循环测试? ? ?
? <{ction?name=c1?loop=$forum}>? ? <{$forum[c1].category_name}> | ?
? <{ction?name=c2?loop=$forum[c1].topic}>? ? ? | ? <{$forum[c1].topic[c2].topic_name}> | ?
? <{/ction}>? <{/ction}>?
? ? ?
执行的结果就像笔者举的例子一样。 因此呢, 在程序中我们只要想办法把所要重复值一层一层的塞到数组中, 再利用?<{第一层数 组[循环 1].第二层数组[循环 2].第三层数组[循环 3].?...?.数组索引}>?这样的方式来显示 每一个巢状循环中的值。至于用什么方法呢?下一节使用数据库时我们再提。 转换数据库中的资料 上面提到如何显示巢状循环,而实际上应用时我们的资料可能是从数据库中抓取出来的,所 以我们就得想办法把数据库的资料变成上述的多重数组的形式。这里笔者用一个?DB?类别来 抓取数据库中的资料,您可以自行用您喜欢的方法。 我们只修改?PHP?
程序,模版还是上面那个?(这就是模版引擎的好处~),其中?$db?这个对象 假设已经在??中建立好了,而且抓出来的资料就是上面的例子。 :
x0c<?php? require?"";? //?先建立第一层数组? $category?=?array();? $db->tSQL($SQL1,?@#CATEGORY@#);? if?(!$db->query(@#CATEGORY@#))?die($db->error());? //?抓取第一层循环的资料? while?($item_category?=?$db->fetchAssoc(@#CATEGORY@#))? {? //?建立第二层数组? $topic?=?array();? $db->tSQL(sprintf($SQL2,?$item_category[@#category_id@#]),?@#TOPIC@#);? if?(!$db->query(@#TOPIC@#))?die($db->error());? //?抓取第二层循环的资料? while?($item_topic?=?$db->fetchAssoc(@#TOPIC@#))? {? //?把抓取的数据推入第二层数组中? array_push($topic,?$item_topic);? }? //?把第二层数组指定为第一层数组所抓取的数据中的一个成员? $item_category[@#topic@#]?=?$topic;? //?把第一层数据推入第一层数组中? array_push($category,?$item_category);? }? $tpl->assign("forum",?$category);? $tpl->display("");? ?>?
在数据库抓取一笔资料后,我们得到的是一个包含该笔数据的数组。透过?while?叙述 及?array_push?函式, 我们将数据库中的资料一笔一笔塞到数组里。 如果您只用到单层循环, 就把第二层循环?(红色的部份)?去掉即可。 决定内容是否显示 要决定是否显示内容, 我们可以使用?if?这个语法来做选择。 例如如果使用者已经登入的话, 我们的模版就可以这样写: <{if?$is_login?==?true}> 显示使用者操作选单 <{el}> 显示输入帐号和密码的窗体 <{/if}>
x0c要注意的是, 「==」号两边一定要各留至少一个空格符,否则?Smarty?会无法解析。 if?语法一般的应用可以参照官方使用说明, 所以笔者在这里就不详加介绍了。 不过笔者发现 了一个有趣的应用:常常会看到程序里要产生这样的一个表格:?(数字代表的是资料集的顺 序) 1?2 3?4 5?6 7?8 这个笔者称之为「横向重复表格」 。它的特色和传统的纵向重复不同,前几节我们看到的重复 表格都是从上而下, 一列只有一笔资料。 而横向重复表格则可以横向地在一列中产生?n?笔资 料后,再换下一列,直到整个循环结束。要达到这样的功能,最简单的方式只需要?ction? 和?if?搭配即可。 我们来看看下面这个例子: :
<?php? require?"";? $my_array?=?array(? array("value"?=>?"0"),? array("value"?=>?"1"),? array("value"?=>?"2"),? array("value"?=>?"3"),? array("value"?=>?"4"),? array("value"?=>?"5"),? array("value"?=>?"6"),? array("value"?=>?"7"),? array("value"?=>?"8"),? array("value"?=>?"9"));? $tpl->assign("my_array",?$my_array);? $tpl->display(@#@#);? ?>?
x0c模版的写法如下: templates/:
? ?
横向重复表格测试? ? ?
lpadding="3">? ? <{ction?name=c1?loop=$my_array}>? <{$my_array[c1].value}> | ? <{if?$?is?div?by?2}>?
? ? <{/if}>? <{/ction}>?
?
? ? ?
重点在于?$?这个?Smarty?变量,在?ction?循环中这个变量 会取得从?1?开始的索引值,所以当?rownum?能被?2?除尽时,就输出?
?使表格换 列?(注意! 是?
?在前面
?在后面)?。 因此数字?2?就是我们在一列中想要呈现的资料 笔数。各位可以由此去变化其它不同的呈现方式。 加载外部内容 我 们 可以 在模 版内 加 载 ?PHP? 程序 代码 或是 另 一个 子 模版 ,分 别是 使 用 ?include_php? 及?include?这两个?Smarty?模版语法;?include_php?笔者较少用,使用方式可以查询官方 手册,这里不再叙述。
x0c在使用?include?时,我们可以预先加载子模版,或是动态加载子模版。预先加载通常使用在 有共同的文件标头及版权宣告;而动态加载则可以用在统一的框架页,而进一步达到 如?Winamp?般可换?Skin?。当然这两种我们也可以混用,视状况而定。 我们来看看下面这个例子: :
<?php? require?"";? $tpl->assign("title",?"Include?测试");? $tpl->assign("content",?"这是模版?2?中的变量");? $tpl->assign("dyn_page",?"test5_");? $tpl->display(@#test5_@#);? ?>?
模版?1?的写法如下: templates/test5_:
? ? ? <{$title}>? ? ? <{include?file="test5_"}>
? <{include?file=$dyn_page}>? <{include?file="test5_"?custom_var="自订变量的内容"}>? ? ?
模版?2?的写法如下:
x0ctemplates/test5_: <{$content}> 模版?3?的写法如下: templates/test5_: 这是模版?3?的内容 模版?4?的写法如下: templates/test5_: <{$custom_var}> 这 里 注意 几个 重点 : 1.?模 版 的位 置都 是以 先 前定 义 的 ?template_dir? 为基 准 ;2.?所 有?include?进来的子模版中,其变量也会被解译。 ;3.?include?中可以用「变量名称=变量 内容」来指定引含进来的模版中所包含的变量,如同上面模版?4?的做法。 用 PHP 实现 MVC 开发模式的逻辑层和表示层有多种模板引擎可供选择, 但是官方引擎 SMARTY 诞生后,选择就有了变化。它的理念和实现都是相当"前卫"的。本文主要讨论 SMARTY 之于其 他模板引擎的不同特点,简要介绍了该引擎的安装及使用,并用一个小的测试案例对比了 SMARTY 和 PHPLIB?template 的速度和易用性。 一、MVC 需要模板 MVC 最早是在 SmallTalk 语言的开发过程中总结出的一种设计模式,MVC 分别代表了"模型"、 "视图"和"控制",目的就是让不同的开发角色在大中型项目中各司其职。在网络应用程序的 开发中
,可以用下图来表示各概念之间的关系。?
该图展示了一个简单的 WEB 应用程序,用户在浏览器上看到信息是数据库服务器上的内容, 但在这之前经过了应用服务器加工。开发人员负责的就是建立数据结构、处理数据的逻辑以 及表示数据的方法。? 96 年 CGI 在中国开始流行的时候,早期的 WEB 程序员都是从 HTML 开始自学成材的,在 PERL 中 print 一行行的 HTML 并不是一件难事, 但是随着网络的一步步提速, 页面大小也从当初的 二、三十 K 暴涨了十倍。写 CGI 程序就产生了一个迫切的要求:分开 PERL 和 HTML 源码。于 是,社会进步体现在开发小组内部的分工上。由于美工和程序员对互相的工作并不是十分熟 悉,在进行合作的过程中需要用一种约定的"语言"进行交流。?
x0c这种语言并不是我们的母语或者英语,术语叫做"模板",逻辑和表示依靠它联系。它是结合 了 HTML 和脚本语言特征的一种表达方式。 通过这种方式, 表示层可以按照用户所希望的格式 来显示经过逻辑层处理过的数据。 如果你有 Windows 平台下 MFC 的开发经验,那么一定会很熟 悉 Document/Document?Template/View 的封装,这就是一个很典型的 MVC 例子。对于 Web 应 用来说,个人认为 J2EE 中的 EJB/rvlets/JSP 是最强大的,当然还有简洁优美的 Structs。 另一个很有名的实现就是 COM/DCOM+ASP,这个组合在我国是最多人使用的。? 通过几种 MVC 实现在 WEB 应用程序里的对比, 可以得到一个关于模板的概念: 一组插入了 HTML 的脚本或者说是插入了脚本 HTML,通过这种插入的内容来表示变化的数据。下面给出一个模 板文件的例子,这个模板经过处理后在浏览器里显示"Hello,?world!"
$greetings $greetings
这里暂且省略处理方式,在后面做专门对比讨论。
二、为什么选 SMARTY?? 对 PHP 来 说 , 有 很 多 模 板 引 擎 可 供 选 择 , 比 如 最 早 的 PHPLIB?template 和 后 起 之 秀 Fast?template,经过数次升级,已经相当成熟稳定。如果你对目前手中的模板引擎很满意, 那么......也请往下看,相信你作为一个自由软件爱好者或者追求效率和优雅的开发者,下 面的 SMARTY 介绍多少会有点意思。? 除了个人偏好的影响,我一直倾向于使用官方标准的实现,比如 APACHE 的 XML 引擎 Axis。 好处就是可以获得尽可能好的兼容性(比如早期 MFC 对于 Win3x 的兼容性就比其它的应用程序 框架好,当然现在各种版本都很完善了)。SMARTY 发布之前我一直使用的是?PEAR?中的 Integrated?Template?eXtension。这个引擎和 PHPLIB?template、Fast?template 几乎是兼 容的,从模板的语法到对模板的处理同出一辙:都是将模板读入内存然后
调用 par()函数, 用数据对预置的标记进行替换。
x0c下面看看 SMARTY 是怎么做的。接到 request 后,先判断是否第一次请求该 url,如果是,将 该 url 所需的模板文件"编译"成 php 脚本,然后 redirect;如果不是,就是说该 url 的模板 已经被"编译"过了,检查不需要重编译后可以马上 redirect,重编译条件可以自己设定为固 定时限,默认的是模板文件被修改。?
怎么样,看起来是不是有点眼熟?想起来了──这不就是 JSP 的原理嘛!的确,这种"编译" 用在 PHP 这样的解释性脚本引擎上显得匪夷所思,但是仔细想想,JAVA 不也是由 JVM 解释执 行的吗?这就叫"没有做不到,只有想不到"。? 既然谈到了 JAVA, 就再对 PHP 的未来发表一点看法。 官方网站上宣布了要在 2003 年年底 PHP 发布 PHP5.0 版。这个版本拥有很多崭新的特性:比如异常处理,命名空间,更加面向对象等 等。可以说越来越向 JAVA 靠拢,SMARTY 也是新特性之一,使得 PHP 更适用于大中型项目的 开发。但是似乎离我当初选择它的原因──灵巧易用──越来越远了。但就一个软件的生存 周期来看,PHP 正处在成长期,开发者赋予它更多的功能,以期能胜任商业应用是利大于弊 的。作为 PHP 的忠实用户,肯定不希望 PHP 总是被人指责"能力不足"吧? 为什么选择 SMARTY,仅仅因为它很像 JSP?当然有更为充分的理由。首先,除了第一次编译 的成本比较高之外,只要不修改模板文件,编译好的 cache 脚本就随时可用,省去了大量的 par()时间;其次 SMARTY 像 PHP 一样有丰富的函数库,从统计字数到自动缩进、文字环绕 以及正则表达式都可以直接使用;如果觉得不够,比如需要数据结果集分页显示的功能, SMARTY 还有很强的扩展能力,可以通过插件的形式进行扩充。? 事实胜于雄辩。我设计了一个测试程序,通过速度和开发难度这两个因素对比了一下 SMARTY 和 PHPLIB?template,选 PHPLIB?template 的原因是在 patrick 的文章?《在 PHP 世界中选择 最合适的模板》 中有一个 PHPLIB?template 对 Fast?template 的竞赛, 结果 PHPLIB?template 大获全胜,这使得 SMARTY 有了一个很好的对手。在测试之前,先谈一下在安装过程中需要注 意的问题。
三、可能遇到的问题? 在 SMARTY 的?官方网站上,有详尽的用户手册,可以选择在线 HTML 和 PDF 格式的版本。这里 就不再涉及手册上已有的内容,只是把初次使用可能遇到的问题做个解释。? 第一个问题就很要命: 提示说找不到所需文件?并不是每一个人都按照 SMARTY 默认目录结构 来写应用的。这里需要手工指定,假设目录结构如下:
x0c就需要在 里指定目录结构:
$smart->template_dir?=?"smarty/templates/";
$smart->compile_dir?=?"smarty/templates_c/"; $smart->config_dir?=?"smarty/configs/"; $smart->cache_dir?=?"smarty/cache/";
第一个问题解决了,紧接着就是第二个:我刚用 Dreamweaver 生成的漂亮模板怎么不能用? 并不是模板文件有什么问题, 而是因为 SMARTY 默认的标记分隔符是{}, 不巧的是 Javascript 肯定包含这个标记。好在我们可以用任意字符当作分隔符,再加上这两句:
$smart->left_delimiter?=?"{/"; $smart->right_delimiter?=?"/}";
这下安装就基本完成,没问题了。
四、反衬和类比? 先构思一下对测试的设计。主要的评比因素当然是速度了。为了进行速度测试,采取了算术 平均数的作法。在测试页面中重复将页面生成 N 遍,再对比总页面生成时间。另一个重要因 素是易用性(至于扩展性不用比较已经有结果了),所以使用的模板不能太小。我用的是我个 人主页的的页面,一个用 Firework+Dreamweaver 生成的 HTML 文件,大小约 7K。其中的变量 设置也采取最常用的区块, PHPLIB?template 里叫 block,而 SMARTY 则称 ction。 在 别小看 这称呼的不同,易用性标准分两块:模板文件和脚本文件的语法是否简明易用。
下面就深入到测试中来。 先看看两种模板文件的语法: 蓝条左边是 PHPLIB?template 的模板,
x0c右边属于 SMARTY。个人偏好是不一样的,所以这里不作评论。着重对比一下脚本里的处理语 句,先看 PHPLIB?template 的:
$tpl->t_file(‘phplib‘,?‘‘); $tpl->t_block(‘phplib‘,?‘row‘,?‘rows‘); for?($j?=?0;?$j?<?10;?$j++){ $tpl->t_var(‘tag‘?,"$j"); $tpl->par(‘rows‘,?‘row‘,?true); } $tpl->par(‘out‘,?‘phplib‘); $tpl->p(‘out‘);
下面是 SMARTY 的:
$smart->assign(‘row‘,$row); $smart->display(‘‘);
SMARTY 只用了 tags 和 row 两个变量, PHPLIB?template 则多了模板文件的 handler, 而 还有 一个莫名其妙的 out。说实在的这个 out 我当初学的时候就不知道为什么要存在,现在看起 来,还是别扭。为什么 SMARTY 少那么多处理语句呢?答案是工作由引擎完成了。如果你喜欢 钻研源程序, 可以发现在 Smarty_ 里有一个名叫_compile_tag()的函数, 由它负责把 ction 这个标签转换成 php 语句。 这不是一个普通的标签, 它带有参数和数据, 节省了脚本编程的工作量,而模板标签上的工作量相差又不大,可以判定在易用性上 SMARTY 高出一畴。? 下面该轮到我们最关注的速度了,毕竟对于一个熟练的 web 开发者来说,掌握再困难的工具 不过是时间问题, 何况模板引擎这种学习曲线平缓的技术。 而速度则是 web 应用程序的生命, 尤其是模板引擎使用在并发访问量很大的站点上,这点就更重要了。测试开始前,我觉得 PHPLIB?temp
late 会在这一环节上胜出,因为它经历了很多次升级,已经基本没有什么 bug, 而且 SMARTY 的引擎个头太大,不像它的对手只有两个文件。? 果然,测试结果如下图,PHPLIB?template 有 25%的速度优势:?
x0c但不会一直这样,我又按了一次刷新,这次得到了不一样的结果:
PHPLIB 基本没变化,但是 SMARTY 提高了 25%的速度。继续刷新,得到的都是类似于第二次的 结果:SMARTY 比 PHPLIB?template?快上近 10%。我想这就是编译型比解释型快的原理了。 SMARTY 引擎本身就很大,加上还要把模板编译成 php 文件,速度当然比不上小巧的 PHPLIB?template。但这只是第一次的情况。第二次接到请求的时候,SMARTY 发现该模板已 经被编译过了,于是最耗时的一步被跳过了,而对手还要按部就班地进行查找和替换工作。 这是编译原理里讲到的很经典的"用空间换时间"例子
x0c