ホームページ  >  記事  >  バックエンド開発  >  [PHP] テンプレート エンジンの簡単かつ詳細な紹介 Smarty_PHP チュートリアル

[PHP] テンプレート エンジンの簡単かつ詳細な紹介 Smarty_PHP チュートリアル

WBOY
WBOYオリジナル
2016-07-21 15:59:31823ブラウズ

Smartyの紹介

テンプレートエンジンとは

いつからか分かりませんが、HTMLにサーバースクリプトを埋め込むことに不満を感じる人が出始めました。ただし、Microsoft の ASP であれ、オープンソースの PHP であれ、それらはすべてサーバー スクリプトが組み込まれた Web サーバーサイド言語です。そこで、プログラムのアプリケーションロジック(または業務アプリケーションのロジック)とWebページのプレゼンテーション(レイアウト)のロジックを分離できれば良いのではないかと考える人もいるのでは?

実際、この問題は長い間存在していました。インタラクティブなWebページが普及したとき、ASPとPHPの両方のユーザーはプログラム開発者であり、ビジュアルデザイナーでもありました。しかし、通常、これらのユーザーはプログラミングが得意か、芸術が得意です。その両方を同時に処理したい場合は、多くの脳細胞を失う必要があります...

そこで、テンプレート エンジンが登場しました。テンプレートエンジンの目的は、上記の論理分離機能を実現することである。これにより、プログラム開発者はデータ制御や機能の実現に集中できる一方、ビジュアルデザイナーは Web ページのレイアウトに集中して、Web ページをよりプロフェッショナルに見せることができます。したがって、テンプレート エンジンは企業の Web サイト開発チームによる使用に適しており、誰もが専門知識を活用できます。

筆者がこれまで接してきたテンプレートエンジンは、データの表現方法に応じて、プログラムで処理する必要があるテンプレートエンジンと、テンプレート自体で完全に決定されるテンプレートエンジンの2種類に大別される。

プログラムで処理する必要があるテンプレート エンジンでは、プログラム開発者は変数のプレゼンテーション ロジックに責任を負わなければなりません。つまり、変数の内容をテンプレートに出力する前に処理する必要があります。課題の仕事。言い換えれば、プログラム開発者は変数の外観を決定するためにさらに多くのプログラムを作成する必要があります。テンプレート エンジンはテンプレート自体によって完全に決定され、変数をテンプレートに直接割り当てることができるため、ビジュアル デザイナーはテンプレートの設計時に変数がどのように表示されるかを決定できます。したがって、制御変数の提示を容易にするために、独自のテンプレート プログラム構文の別のセット (Smarty など) が存在する場合があります。ただし、このように、ビジュアル デザイナーはテンプレート言語の使用方法も学ぶ必要があります。

テンプレートエンジンの動作原理 まず、以下の動作図を見てみましょう:

一般的なテンプレートエンジン(PHPLibなど)は、テンプレートオブジェクトを作成する際に、解析対象のテンプレートを取得し、変数とを挿入します。 use parse() このメソッドはテンプレートを解析し、最終的に Web ページを出力します。

Smarty ユーザーの場合、プログラム内で解析アクションを実行する必要はなく、Smarty が自動的に実行します。さらに、コンパイルされた Web ページのテンプレートが変更されていない場合、Smarty はコンパイル アクションを自動的にスキップし、コンパイルされた Web ページを直接実行してコンパイル時間を節約します。

Smarty のいくつかの概念を使用します

一般的なテンプレート エンジンでは、領域の概念がよく見られ、いわゆるブロックは次のようになります。

領域content


これらのブロックのほとんどは、PHP プログラムでは if または for によって制御されますが、テンプレートははるかに単純に見えますが、テンプレートを変更した後は 1 つだけ必要になります。表示方法が異なる場合は、PHP プログラムを再度変更する必要があります。

Smarty では、すべてが変数に基づいており、すべてのプレゼンテーション ロジックはテンプレートによって制御されます。 Smarty には独自のテンプレート言語があるため、ブロックを表示する必要があるか繰り返す必要があるかに関係なく、ブロックは Smarty のテンプレート構文 (if、foreach、section) と変数コンテンツを使用して表示されます。このように、テンプレートが少し複雑になったように感じますが、きちんと計画を立てれば、PHP プログラムを 1 行も変更する必要がないという利点があります。

上記の説明から、Smarty を使用する場合は、プログラム アプリケーション ロジックと Web ページ レンダリング ロジックを明確に分離するという 1 つの原則を習得する必要があることがわかります。つまり、PHP プログラム内に HTML コードが多すぎてはなりません。プログラムでは、どの変数をテンプレートに挿入するかを決定するだけでよく、これらの変数をどのように表示するか (またはまったく表示しないことも) はテンプレートに決定させます。

Smartyの基礎

Smartyのインストール

まずはプログラムをどこに配置するかを決めます。

Windows では、「d:appservwebdemo」のような場所がある場合があります。

Linux では、場所は「/home/jaceju/public_html/」のようになります。

Smarty の公式 Web サイトにアクセスして、最新の Smarty パッケージをダウンロードします: http://smarty.php.net。

Smarty 2.6.0 のロックを解除すると、libs フォルダーを含む多くのファイルが表示されます。在 libs 中应该会有 3 个 class.php 檔 + 1 个 debug.tpl + 1 个 plugin 资料夹 + 1 个 core 资料夹。然后直接将 libs 复制到您的程序主资料夹下,再更名为 class 就可以了。就这样?没错!这种安装法比较简单,适合一般没有自己主机的使用者。
  
  至于 Smarty 官方手册中为什么要介绍一些比较复杂的安装方式呢?基本上依照官方的方式安装,可以只在主机安装一次,然后提供给该主机下所有设计者开发不同程序时直接引用,而不会重复安装太多的 Smarty 复本。而笔者所提供的方式则是适合要把程序带过来移过去的程序开发者使用,这样不用烦恼主机有没有安装 Smarty 。
  
  程序的资料夹设定
  
  以笔者在Windows安装Appserv为例,程序的主资料夹是「d:\appserv\web\demo\」。安装好Smarty后,我们在主资料夹下再建立这样的资料夹:

  在 Linux 底下,请记得将 templates_c 的权限变更为 777 。Windows 下则将其只读取消。
  
  第一个用Smarty写的小程序
  
  我们先设定 Smarty 的路径,请将以下这个档案命名为 main.php ,并放置到主资料夹下:
  
  main.php:
  


  include "class/Smarty.class.php";
  define(@#__SITE_ROOT@#, @#d:/appserv/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:/appserv/web/demo/templates/@# ) 。然后我们用 display() 这个 Smarty 方法来显示我们的模版。
  
  接下来我们在 templates 资料夹下放置一个 test.htm:(扩展名叫什么都无所谓,但便于视觉设计师开发,笔者都还是以 .htm 为主。)
  
  templates/test.htm:


   
   
   
  <{$title}> 
   
   
  <{$content}> 
   
   
   

  现在我们要将上面的模版显示出来,并将网页标题 ($title) 与内容 ($content) 更换,请将以下档案内容命名为 test.php ,并放置在主资料夹下:
  
  test.php:


  require "main.php";
  $tpl->assign("title", "测试用的网页标题"); 
  $tpl->assign("content", "测试用的网页内容"); 
  // 上面两行也可以用这行代替 
  // $tpl->assign(array("title" => "测试用的网页标题", "content" => "测试用的网页内容")); 
  $tpl->display(@#test.htm@#); 
  ?> 


请打开浏览器,输入 http://localhost/demo/test.php 试试看(依您的环境决定网址),应该会看到以下的画面:
   
 

  再到 templates_c 底下,我们会看到一个奇怪的资料夹 (%%179) ,再点选下去也是一个奇怪的资料夹 (%%1798044067) ,而其中有一个档案:
  
  templates_c/%%179/%%1798044067/test.htm.php:


 
   
   
   
  <?php echo $this->_tpl_vars[@#title@#]; ?\> 
   
   
  _tpl_vars[@#content@#]; ?\> 
   
   
   


 没错,这就是 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 檔,这样在 main.php 就可以将它们引入了,如下:
  
  main.php:
  



  include "class/Smarty.class.php";
  define(@#__SITE_ROOT@#, @#d:/appserv/web/demo@#); // 最后没有斜线
  // 以 main.php 的位置为基准
  require_once "includes/functions.php";
  require_once "includes/include.php";
  $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 这个资料夹则是用来放置程序模块的,如此一来便不会把程序丢得到处都是,整体架构一目了然。
  
  上面我们也提到 main.php ,这是整个程序的主要核心,不论是常数定义、外部程序加载、共享变量建立等,都是在这里开始的。所以之后的模块都只要将这个档案包含进来就可以啦。因此在程序流程规划期间,就必须好好构思 main.php 中应该要放那些东西;当然利用 include 或 require 指令,把每个环节清楚分离是再好不过了。
   
 

  在上节提到的 Smarty 程序 5 步骤, main.php 就会帮我们先将前 3 个步骤做好,后面的模块程序只要做后面两个步骤就可以了。
  
  从变量开始
  
  如何使用变量
  
  从上一章范例中,我们可以清楚地看到我们利用 <{ 及 }> 这两个标示符号将变量包起来。预设的标示符号为 { 及 } ,但为了中文冲码及 javascript 的关系,因此笔者还是模仿 XOOPS ,将标示符号换掉。变量的命名方式和 PHP 的变量命名方式是一模一样的,前面也有个 $ 字号 (这和一般的模版引擎不同)。标示符号就有点像是 PHP 中的
(事实上它们的确会被替换成这个) ,所以以下的模版变量写法都是可行的:
  
  1. <{$var}>
  
  2. <{ $var }> 
  
  3. <{$var
  
  }> 
  在 Smarty 里,变量预设是全域的,也就是说你只要指定一次就好了。指定两次以上的话,变量内容会以最后指定的为主。就算我们在主模版中加载了外部的子模版,子模版中同样的变量一样也会被替代,这样我们就不用再针对子模版再做一次解析的动作。
  
  而在 PHP 程序中,我们用 Smarty 的 assign 来将变量置放到模版中。 assign 的用法官方手册中已经写得很多了,用法就如同上一节的范例所示。不过在重复区块时,我们就必须将变量做一些手脚后,才能将变量 assign 到模版中,这在下一章再提。
  
  修饰你的变量
  
  上面我们提到 Smarty 变量呈现的风貌是由模版自行决定的,所以 Smarty 提供了许多修饰变量的函式。使用的方法如下:
  
  <{变量|修饰函式}> 
  
  <{变量|修饰函式:"参数(非必要,视函式而定)"}> 
  范例如下:
  
  <{$var|nl2br}> 
  
  <{$var|string_format:"%02d"}> 
  好,那为什么要让模版自行决定变量呈现的风貌?先看看底下的 HTML ,这是某个购物车结帐的部份画面。
  
  
  
  总金额:21,000 元
  一般模版引擎的模版可能会这样写:
  
  
  
  总金额:{format_total} 元
  它们的 PHP 程序中要这样写:


  $total = 21000;
  $tpl->assign("total", $total); 
  $tpl->assign("format_total", number_format($total)); 
  ?> 



而 Smarty 的模版就可以这样写: (number_format 修饰函式请到Smarty 官方网页下载)
  
  
  
  总金额:<{$total|number_format:""}> 元
  Smarty 的 PHP 程序中只要这样写:



  $total = 21000;
  $tpl->assign("total", $total); 
  ?> 


所以在 Smarty 中我们只要指定一次变量,剩下的交给模版自行决定即可。这样了解了吗?这就是让模版自行决定变量呈现风貌的好处!
  
  控制模版的内容
  
  重复的区块
  
  在 Smarty 样板中,我们要重复一个区块有两种方式: foreach 及 section 。而在程序中我们则要 assign 一个数组,这个数组中可以包含数组数组。就像下面这个例子:
  
  首先我们来看 PHP 程序是如何写的:
  
  test2.php:


  require "main.php";
  $array1 = array(1 => "苹果", 2 => "菠萝", 3 => "香蕉", 4 => "芭乐"); 
  $tpl->assign("array1", $array1); 
  $array2 = array( 
  array("index1" => "data1-1", "index2" => "data1-2", "index3" => "data1-3"), 
  array("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("test2.htm"); 
  ?> 

而模版的写法如下:
  
  templates/test2.htm:



   
   
   
  测试重复区块 
   
   
  
  <br>  利用 foreach 来呈现 array1  <br>  <{foreach item=item1 from=$array1}>  <br>  <{$item1}>  <br>  <{/foreach}>  <br>  利用 section 来呈现 array1  <br>  <{section name=sec1 loop=$array1}>  <br>  <{$array1[sec1]}>  <br>  <{/section}>  <br>  利用 foreach 来呈现 array2  <br>  <{foreach item=index2 from=$array2}>  <br>  <{foreach key=key2 item=item2 from=$index2}>  <br>  <{$key2}>: <{$item2}>  <br>  <{/foreach}>  <br>  <{/foreach}>  <br>  利用 section 来呈现 array1  <br>  <{section name=sec2 loop=$array2}>  <br>  index1: <{$array2[sec2].index1}>  <br>  index2: <{$array2[sec2].index2}>  <br>  index3: <{$array2[sec2].index3}>  <br>  <{/section}>  <br>  
 
   
   
   

执行上例后,我们发现不管是 foreach 或 section 两个执行结果是一样的。那么两者到底有何不同呢?
  
  第一个差别很明显,就是foreach 要以巢状处理的方式来呈现我们所 assign 的两层数组变量,而 section 则以「主数组[循环名称].子数组索引」即可将整个数组呈现出来。由此可知, Smarty 在模版中的 foreach 和 PHP 中的 foreach 是一样的;而 section 则是 Smarty 为了处理如上列的数组变量所发展出来的叙述。当然 section 的功能还不只如此,除了下一节所谈到的巢状资料呈现外,官方手册中也提供了好几个 section 的应用范例。
  
  不过要注意的是,丢给 section 的数组索引必须是从 0 开始的正整数,即 0, 1, 2, 3, ...。如果您的数组索引不是从 0 开始的正整数,那么就得改用 foreach 来呈现您的资料。您可以参考官方讨论区中的此篇讨论,其中探讨了 section 和 foreach 的用法。
  
  巢状资料的呈现
  
  模版引擎里最令人伤脑筋的大概就是巢状资料的呈现吧,许多著名的模版引擎都会特意强调这点,不过这对 Smarty 来说却是小儿科。
  
  最常见到的巢状资料,就算论譠程序中的讨论主题区吧。假设要呈现的结果如下:
  
  公告区
  
  站务公告
  
  文学专区
  
  好书介绍
  
  奇文共赏
  
  计算机专区
  
  硬件外围
  
  软件讨论
  
  程序中我们先以静态资料为例:
  
  test3.php:


  require "main.php";
  $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("test3.htm"); 
  ?> 

模版的写法如下:
  
  templates/test3.htm:



   
   
  巢状循环测试 
   
   
   
  <{section name=sec1 loop=$forum}> 
   
   
   
  <{section name=sec2 loop=$forum[sec1].topic}> 
   
   
   
   
  <{/section}> 
  <{/section}> 
  
<{$forum[sec1].category_name}>
 <{$forum[sec1].topic[sec2].topic_name}>
 
   
   
   

执行的结果就像笔者举的例子一样。
  
  因此呢,在程序中我们只要想办法把所要重复值一层一层的塞到数组中,再利用 <{第一层数组[循环1].第二层数组[循环2].第三层数组[循环3]. ... .数组索引}> 这样的方式来显示每一个巢状循环中的值。至于用什么方法呢?下一节使用数据库时我们再提。
  
  转换数据库中的资料
  
  上面提到如何显示巢状循环,而实际上应用时我们的资料可能是从数据库中抓取出来的,所以我们就得想办法把数据库的资料变成上述的多重数组的形式。这里笔者用一个 DB 类别来抓取数据库中的资料,您可以自行用您喜欢的方法。
  
  我们只修改 PHP 程序,模版还是上面那个 (这就是模版引擎的好处~),其中 $db 这个对象假设已经在 main.php 中建立好了,而且抓出来的资料就是上面的例子。
  
  test3.php:
  


  require "main.php";
  // 先建立第一层数组
  $category = array();
  $db->setSQL($SQL1, @#CATEGORY@#); 
  if (!$db->query(@#CATEGORY@#)) die($db->error()); 
  // 抓取第一层循环的资料 
  while ($item_category = $db->fetchAssoc(@#CATEGORY@#)) 
  { 
  // 建立第二层数组 
  $topic = array(); 
  $db->setSQL(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("test3.htm"); 
  ?> 

在数据库抓取一笔资料后,我们得到的是一个包含该笔数据的数组。透过 while 叙述及 array_push 函式,我们将数据库中的资料一笔一笔塞到数组里。如果您只用到单层循环,就把第二层循环 (红色的部份) 去掉即可。
  
  决定内容是否显示
  
  要决定是否显示内容,我们可以使用 if 这个语法来做选择。例如如果使用者已经登入的话,我们的模版就可以这样写:
  
  <{if $is_login == true}>
  显示使用者操作选单
  <{else}>
  显示输入帐号和密码的窗体
  <{/if}>
  
  要注意的是,「==」号两边一定要各留至少一个空格符,否则 Smarty 会无法解析。
  
  if 语法一般的应用可以参照官方使用说明,所以笔者在这里就不详加介绍了。不过笔者发现了一个有趣的应用:常常会看到程序里要产生这样的一个表格: (数字代表的是资料集的顺序)
  
  1 2
  
  3 4
  
  5 6
  
  7 8
  
  这个笔者称之为「横向重复表格」。它的特色和传统的纵向重复不同,前几节我们看到的重复表格都是从上而下,一列只有一笔资料。而横向重复表格则可以横向地在一列中产生 n 笔资料后,再换下一列,直到整个循环结束。要达到这样的功能,最简单的方式只需要 section 和 if 搭配即可。
  
  我们来看看下面这个例子:
  
  test4.php:
  


  require "main.php";
  $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(@#test4.htm@#); 
  ?> 


模版的写法如下:
  
  templates/test4.htm:


   
   
  横向重复表格测试 
   
   
   
   
  <{section name=sec1 loop=$my_array}> 
   
  <{if $smarty.section.sec1.rownum is div by 2}> 
   
   
  <{/if}> 
  <{/section}> 
   
  
<{$my_array[sec1].value}>
 
   
   
   

 重点在于 $smarty.section.sec1.rownum 这个 Smarty 变量,在 section 循环中这个变量会取得从 1 开始的索引值,所以当 rownum 能被 2 除尽时,就输出  使表格换列 (注意!是  在前面 在后面) 。因此数字 2 就是我们在一列中想要呈现的资料笔数。各位可以由此去变化其它不同的呈现方式。
  
  加载外部内容
  
  我们可以在模版内加载 PHP 程序代码或是另一个子模版,分别是使用 include_php 及 include 这两个 Smarty 模版语法; include_php 笔者较少用,使用方式可以查询官方手册,这里不再叙述。
  
  在使用 include 时,我们可以预先加载子模版,或是动态加载子模版。预先加载通常使用在有共同的文件标头及版权宣告;而动态加载则可以用在统一的框架页,而进一步达到如 Winamp 般可换 Skin 。当然这两种我们也可以混用,视状况而定。
  
  我们来看看下面这个例子:
  
  test5.php:


  require "main.php";
  $tpl->assign("title", "Include 测试"); 
  $tpl->assign("content", "这是模版 2 中的变量"); 
  $tpl->assign("dyn_page", "test5_3.htm"); 
  $tpl->display(@#test5_1.htm@#); 
  ?> 

模版 1 的写法如下:
  
  templates/test5_1.htm:


   
   
   
  <{$title}> 
   
   
  <{include file="test5_2.htm"}>
 
  <{include file=$dyn_page}> 
  <{include file="test5_4.htm" custom_var="自订变量的内容"}> 
   
   
   

模版 2 的写法如下:
  
  templates/test5_2.htm:
  
  <{$content}>
  模版 3 的写法如下:
  
  templates/test5_3.htm:
  
  这是模版 3 的内容
  模版 4 的写法如下:
  
  templates/test5_4.htm:
  
  <{$custom_var}>
  这里注意几个重点:1. 模版的位置都是以先前定义的 template_dir 为基准;2. 所有 include 进来的子模版中,其变量也会被解译。;3. include 中可以用「变量名称=变量内容」来指定引含进来的模版中所包含的变量,如同上面模版 4 的做法。
用PHP实现MVC开发模式的逻辑层和表示层有多种模板引擎可供选择,但是官方引擎SMARTY诞生后,选择就有了变化。它的理念和实现都是相当"前卫"的。本文主要讨论SMARTY之于其他模板引擎的不同特点,简要介绍了该引擎的安装及使用,并用一个小的测试案例对比了SMARTY和PHPLIB template的速度和易用性。

一、MVC需要模板

MVC最早是在SmallTalk语言的开发过程中总结出的一种设计模式,MVC分别代表了"模型"、"视图"和"控制",目的就是让不同的开发角色在大中型项目中各司其职。在网络应用程序的开发中,可以用下图来表示各概念之间的关系。 
/uploadfiles/image006_65505.gif

该图展示了一个简单的WEB应用程序,用户在浏览器上看到信息是数据库服务器上的内容,但在这之前经过了应用服务器加工。开发人员负责的就是建立数据结构、处理数据的逻辑以及表示数据的方法。 

96年CGI在中国开始流行的时候,早期的WEB程序员都是从HTML开始自学成材的,在PERL中print一行行的HTML并不是一件难事,但是随着网络的一步步提速,页面大小也从当初的二、三十K暴涨了十倍。写CGI程序就产生了一个迫切的要求:分开PERL和HTML源码。于是,社会进步体现在开发小组内部的分工上。由于美工和程序员对互相的工作并不是十分熟悉,在进行合作的过程中需要用一种约定的"语言"进行交流。 

这种语言并不是我们的母语或者英语,术语叫做"模板",逻辑和表示依靠它联系。它是结合了HTML和脚本语言特征的一种表达方式。通过这种方式,表示层可以按照用户所希望的格式来显示经过逻辑层处理过的数据。如果你有Windows平台下MFC的开发经验,那么一定会很熟悉Document/Document Template/View的封装,这就是一个很典型的MVC例子。对于Web应用来说,个人认为J2EE中的EJB/servlets/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几乎是兼容的,从模板的语法到对模板的处理同出一辙:都是将模板读入内存然后调用parse()函数,用数据对预置的标记进行替换。


下面看看SMARTY是怎么做的。接到request后,先判断是否第一次请求该url,如果是,将该url所需的模板文件"编译"成php脚本,然后redirect;如果不是,就是说该url的模板已经被"编译"过了,检查不需要重编译后可以马上redirect,重编译条件可以自己设定为固定时限,默认的是模板文件被修改。 

怎么样,看起来是不是有点眼熟?想起来了──这不就是JSP的原理嘛!的确,这种"编译"用在PHP这样的解释性脚本引擎上显得匪夷所思,但是仔细想想,JAVA不也是由JVM解释执行的吗?这就叫"没有做不到,只有想不到"。 

既然谈到了JAVA,就再对PHP的未来发表一点看法。PHP官方网站上宣布了要在2003年年底发布PHP5.0版。这个版本拥有很多崭新的特性:比如异常处理,命名空间,更加面向对象等等。可以说越来越向JAVA靠拢,SMARTY也是新特性之一,使得PHP更适用于大中型项目的开发。但是似乎离我当初选择它的原因──灵巧易用──越来越远了。但就一个软件的生存周期来看,PHP正处在成长期,开发者赋予它更多的功能,以期能胜任商业应用是利大于弊的。作为PHP的忠实用户,肯定不希望PHP总是被人指责"能力不足"吧?

为什么选择SMARTY,仅仅因为它很像JSP?当然有更为充分的理由。首先,除了第一次编译的成本比较高之外,只要不修改模板文件,编译好的cache脚本就随时可用,省去了大量的parse()时间;其次SMARTY像PHP一样有丰富的函数库,从统计字数到自动缩进、文字环绕以及正则表达式都可以直接使用;如果觉得不够,比如需要数据结果集分页显示的功能,SMARTY还有很强的扩展能力,可以通过插件的形式进行扩充。 

事实胜于雄辩。我设计了一个测试程序,通过速度和开发难度这两个因素对比了一下SMARTY和PHPLIB template,选PHPLIB template的原因是在patrick的文章 《在PHP世界中选择最合适的模板》中有一个PHPLIB template对Fast template的竞赛,结果PHPLIB template大获全胜,这使得SMARTY有了一个很好的对手。在测试之前,先谈一下在安装过程中需要注意的问题。


三、可能遇到的问题 

在SMARTY的 官方网站上,有详尽的用户手册,可以选择在线HTML和PDF格式的版本。这里就不再涉及手册上已有的内容,只是把初次使用可能遇到的问题做个解释。 

第一个问题就很要命:提示说找不到所需文件?并不是每一个人都按照SMARTY默认目录结构来写应用的。这里需要手工指定,假设目录结构如下:
/uploadfiles/image008_67318.gif

就需要在index.php里指定目录结构:

$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则称section。别小看这称呼的不同,易用性标准分两块:模板文件和脚本文件的语法是否简明易用。

/uploadfiles/image010_85833.jpg

テストを始めましょう。まず、2 つのテンプレート ファイルの構文を見てみましょう。青いバーの左側は PHPLIB テンプレートで、右側は SMARTY に属します。個人の好みはさまざまなので、ここではコメントしません。スクリプト内の処理ステートメントの比較に重点を置き、まず PHPLIB テンプレートを確認します。

$tpl->set_file('phplib', 'bigfile.htm');
$tpl->set_block('phplib', 'row', 'rows'); j $tpl->set_var('tag',"$j");
$tpl->parse('rows', 'row', true); tpl->parse('out', 'phplib');
$tpl->p('out'); 以下はスマートです:


$smart->assign('row',$row);
$smart->display('bigfile.htm');

SMARTY はタグと行の 2 つの変数のみを使用しますが、PHPLIB テンプレートにはテンプレート ファイルの追加ハンドラーがあり、不可解なエラーが発生します。正直に言うと、最初にこのアウトが存在する理由がわかりませんでしたが、今でも違和感があります。 SMARTY の処理ステートメントが非常に少ないのはなぜですか?答えは、仕事はエンジンによって行われるということです。ソース プログラムを詳しく調べたい場合は、Smarty_compiler.class.php に _compile_tag() という関数があることがわかります。この関数はセクション タグを PHP ステートメントに変換します。通常のラベルとは異なり、パラメータやデータが用意されているため、スクリプトプログラミングの負荷が軽減されます。使いやすさでは、SMARTY の方が優れていると判断できます。

今度はスピードに焦点を当てる番です。結局のところ、熟練した Web 開発者にとって、テンプレート エンジンは言うまでもなく、習得が容易なテクノロジーであるため、どんな難しいツールもマスターするのは時間の問題です。速度は Web アプリケーションの命であり、特に同時アクセス数が多いサイトでテンプレート エンジンが使用されている場合には、さらに重要になります。テストが始まる前は、何度もアップグレードされており、基本的にバグがないため、この点では PHPLIB テンプレートが勝つだろうと思っていました。また、ファイルが 2 つしかない相手とは異なり、SMARTY のエンジンは大きすぎます。

案の定、テスト結果は以下のとおりで、PHPLIB テンプレートには 25% の速度の利点があります。


しかし、常にこのようになるわけではありません。もう一度更新を押したところ、今度は異なる結果が得られました。



PHPLIB は基本的に変更ありませんが、SMARTY は 25% 高速化されました。更新を続けると、2 回目と同様の結果が得られます。SMARTY は PHPLIB テンプレートよりも 10% 近く高速です。これが、コンパイルされたバージョンがインタープリタされたバージョンよりも高速である理由だと思います。 SMARTY エンジン自体は非常に大きいため、テンプレートを php ファイルにコンパイルする必要があります。当然、速度はコンパクトな PHPLIB テンプレートほど速くありません。しかし、これは初めての場合に限ります。 2 回目のリクエストを受け取ったとき、SMARTY はテンプレートがすでにコンパイルされていることを発見したため、最も時間のかかるステップがスキップされ、相手は検索と置換を段階的に実行する必要がありました。これは、編集の原則で述べた「空間と時間の交換」の典型的な例です。
/uploadfiles/image012_51369.gif5. 結論


結論としては、SMARTY に夢中になったら、何を待っているでしょうか?もちろん、これが万能であるという意味ではありません。MVC モデルを使用して個人の Web サイトを作成する場合と同様、作業負荷が軽減されないだけでなく、異なるレベル間の結合について常に考慮する必要があります。
/uploadfiles/image014_89692.gifSMARTYが適さないのは何ですか?マニュアルにある典型的な例として、天気予報 Web サイトを取り上げます。もう一つ思い浮かぶのは株式市場です。この種の Web サイトで SMARTY を使用すると、再コンパイルが頻繁に行われるため非効率になるため、PHPLIB テンプレートの方が適しています。

この記事は 2 つのエンジンを比較するものではなく、SMARTY の利点を説明するものです。これを使用する上で最も意味があるのは、これが新しい PHP システムの一部として、.NET と JAVA ONE の 2 つの主要なシステムに加えて、大規模および中規模の Web 開発のための他のオプションがあることです。 GNU プロジェクトにとって、その重要性は、劉氏と鄧小平の軍隊が数千マイルをデービー山脈に飛び込むのと何ら変わりません。



http://www.bkjia.com/PHPjc/317353.html

www.bkjia.com

tru​​e
http://www.bkjia.com/PHPjc/317353.html

技術記事 Smarty は、HTML に ServerScript を埋め込むことに不満を感じ始めた時期から、テンプレート エンジンを導入しました。ただし、Microsoft ASP であろうと、オープンソース PHP であろうと、...
声明:
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。