smarty 學習記錄

smarty模版是比較大衆化的一個模版,在php開發過程中被不少開發者視爲最友好的模版之一,學習smarty課程對於不少培訓機構來講也是列入了培訓課程之一,那麼不少方面就須要咱們學習了
一. 安裝
首先打開網頁http://smarty.php.net/download.php,下載最新版本的Smarty。解壓下載的文件(目錄結構還蠻複雜的)。例如:
(1) 我在根目錄下創建了新的目錄learn/,再在learn/裏創建一個文件夾smarty/。將剛纔解壓縮出來的目錄的libs/拷貝到smarty/裏, 再在smarty/裏新建templates目錄,templates裏新建cache/,templates/,templates_c/, config/,
(2) 新建一個模板文件:index.tpl,將此文件放在learn/smarty/templates/templates目錄下,代碼以下:
<!DOCTYPE HTML PUBLIC "-//W3C//DTDHTML 4.01 Transitional//EN""http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<metahttp-equiv="Content-Type" c>
<title>Smarty</title>
</head>
<body>
{$hello}
</body>
</html>
新建index.php,將此文件放在learn/下:
<?php
//引用類文件
require 'smarty/libs/Smarty.class.php';
$smarty = new Smarty;
//設置各個目錄的路徑,這裏是安裝的重點
$smarty->template_dir ="smarty/templates/templates";
$smarty->compile_dir ="smarty/templates/templates_c";
$smarty->config_dir = "smarty/templates/config";
$smarty->cache_dir ="smarty/templates/cache";
//smarty模板有高速緩存的功能,若是這裏是true的話即打開caching,可是會形成網頁不當即更新的問題,固然也能夠經過其餘的辦法解決
$smarty->caching = false;
$hello = "Hello World!";
//賦值
$smarty->assign("hello",$hello);
//引用模板文件
$smarty->display('index.tpl');
?>
(3) 執行index.php就能看到Hello World!了。
二. 賦值
在模板文件中須要替換的值用大括號{}括起來,值的前面還要加$號。例如{$hello}。這裏能夠是數組,好比{$hello.item1},{$hello.item2}…
而PHP源文件中只須要一個簡單的函數assign(var , value)。
簡單的例子:
*.tpl:
Hello,{$exp.name}!Good {$exp.time}
*.php:
$hello[name]= 「Mr. Green」;
$hello[time]=」morning」;
$smarty->assign(「exp」,$hello);
output:
Hello,Mr.Green!Good morning
三. 引用
網站中的網頁通常header和footer是能夠共用的,因此只要在每一個tpl中引用它們就能夠了。
示例:*.tpl:
{include file="header.tpl"}
{* body of template goes here *}
{include file="footer.tpl"}
四. 判斷
模板文件中可使用if else等判斷語句,便可以將一些邏輯程序放在模板裏。"eq","ne", "neq", "gt", "lt","lte", "le", "gte" "ge","is even", "is odd", "is not even", "is notodd", "not", "mod", "div by", "evenby", "odd by","==","!=",">","<","<=",">="這些是if中能夠用到的比較。看看就能知道什麼意思吧。
示例:
{if $name eq"Fred"}
WelcomeSir.
{elseif $name eq"Wilma"}
WelcomeMa'am.
{else}
Welcome,whatever you are.
{/if}
五. 循環
在Smarty裏使用循環遍歷數組的方法是section,如何賦值遍歷都是在模板中解決,php源文件中只要一個assign就能解決問題。
示例:
{* this examplewill print out all the values of the $custid array *}
{secti loop=$custid}
id: {$custid[customer]}<br>
{/section}
OUTPUT:
id: 1000<br>
id: 1001<br>
id: 1002<br>
六. 常見問題
Smarty將全部大括號{}裏的東西都視爲本身的邏輯程序,因而咱們在網頁中想插入javascrīpt函數就須要literal的幫忙了,literal的功能就是忽略大括號{}。
示例:
{literal}
<scrīptlanguage=javascrīpt>
function isblank(field) {
if (field.value == '')
{ return false; }
else
{
document.loginform.submit();
return true;
}
}
</scrīpt>
{/literal}javascript

 

 


這篇文章主要介紹了smarty半小時快速上手入門教程,以實例的形式分析了smarty在實際使用過程當中的屬性、方法及具體用法,須要的朋友能夠參考下
 

本文講述了smarty快速上手入門的方法,可讓讀者在半小時內快速掌握smarty的用法。分享給你們供你們參考。具體實現方法以下:php

1、smarty的程序設計部分:html

在smarty的模板設計部分我簡單的把smarty在模板中的一些經常使用設置作了簡單的介紹,這一節主要來介紹一下如何在smarty中開始咱們程序設計。下載Smarty文件放到大家站點中。java

index.php代碼以下:mysql

 

複製代碼代碼以下:
<?php 
/** 

* @version $Id: index.php 
* @package 
* @author www.jb51.net 
* @action 顯示實例程序 
*/ 
include_once("./Smarty/Smarty.class.php"); //包含smarty類文件

 

$smarty = new Smarty(); //創建smarty實例對象$smarty 
$smarty->templates("./templates"); //設置模板目錄 
$smarty->templates_c("./templates_c"); //設置編譯目錄 
$smarty->cache("./cache"); //緩存目錄 
$smarty->cache_lifetime = 0; //緩存時間 
$smarty->caching = true; //緩存方式linux

$smarty->left_delimiter = "{#"; 
$smarty->right_delimiter = "#}"; 
$smarty->assign("name", "zaocha"); //進行模板變量替換 
$smarty->display("index.htm"); //編譯並顯示位於./templates下的index.htm模板 
?>web


2、解釋smarty的程序

 

咱們能夠看到,smarty的程序部分實際就是符合php語言規範的一組代碼,咱們依次來解釋一下:sql

1:/**/語句:數據庫

包含的部分爲程序篇頭註釋。主要的內容應該爲對程序的做用,版權與做者及編寫時間作一個簡單的介紹,這在smarty中不是必需的,但從程序的風格來說,這是一個好的風格。小程序

2:include_once語句:

它將安裝到網站的smarty文件包含到當前文件中,注意包含的路徑必定要寫正確。

3:$smarty = new Smarty():

這一句新建一個Smarty對象$smarty,簡單的一個對象的實例化。

4:$smarty->templates(""):

這一句指明$smarty對象使用tpl模板時的路徑,它是一個目錄,在沒有這一句時,Smarty默認的模板路徑爲當前目錄的templates目錄,實際在寫程序時,咱們要將這一句寫明,這也是一種好的程序風格。

5:$smarty->templates_c(""):

這一句指明$smarty對象進行編譯時的目錄。在模板設計篇咱們已經知道Smarty是一種編譯型模板語言,而這個目錄,就是它編譯模板的目錄,要注意,若是站點位於linux服務器上,請確保

teamplates_c裏定義的這個目錄具備可寫可讀權限,默認狀況下它的編譯目錄是當前目錄下的templates_c,出於一樣的理由咱們將其明確的寫出來。

6:$smarty->left_delimiter與$smarty->right_delimiter:

指明在查找模板變量時的左右分割符。默認狀況下爲"{"與"}",但在實際中由於咱們要在模板中使用<script>,Script中的函數定義不免會使用{},雖然它有本身的解決辦法,但習慣上咱們將它從新定義

爲"{#"與"#}"或是"<!--{"與"}-->"或其它標誌符,注意,若是在這裏定義了左右分割符後,在模板文件中相應的要使每個變量使用與定義相同的符號,例如在這裏指定爲"<{"與"}>",htm模板中也要

相應的將{$name}變成<{$name}>,這樣程序才能夠正確的找到模板變量。

7:$smarty->cache("./cache"):

告訴Smarty輸出的模板文件緩存的位置。上一篇咱們知道Smarty最大的優勢在於它能夠緩存,這裏就是設置緩存的目錄。默認狀況下爲當前目錄下的cache目錄,與templates_c目錄至關,在linux系統中

咱們要確保它的可讀可寫性。

8:$smarty->cache_lifetime = 60 * 60 * 24:

這裏將以秒爲單位進行計算緩存有效的時間。第一次緩存時間到期時當Smarty的caching變量設置爲true時緩存將被重建。當它的取值爲-1時表示創建起的緩存從不過時,爲0時表示在程序每次執行時緩

存老是被從新創建。上面的設置表示將cache_lifetime設置爲一天。

9:$smarty->caching = 1:

這個屬性告訴Smarty是否要進行緩存以及緩存的方式。它能夠取3個值,0:Smarty默認值,表示不對模板進行緩存;1:表示Smarty將使用當前定義的cache_lifetime來決定是否結束cache;2:表示

Smarty將使用在cache被創建時使用cache_lifetime這個值。習慣上使用true與false來表示是否進行緩存。

10:$smarty->assign("name", "zaocha"):

該數的原型爲assign(string varname, mixed var),varname爲模板中使用的模板變量,var指出要將模板變量替換的變量名;其第二種原形爲assign(mixed var),咱們要在後面的例子詳細的講解這個成員函數的使用方法,assign是Smarty的核心函數之一,全部對模板變量的替換都要使用它。

11:$smarty->display("index.tpl"):

該函數原形爲display(string varname),做用爲顯示一個模板。簡單的講,它將分析處理過的模板顯示出來,這裏的模板文件不用加路徑,只要使用一個文件名就能夠了,它路徑咱們已經在$smarty->templates(string path)中定義過了。

程序執行完後咱們能夠打開當前目錄下的templates_c與cache目錄,就會發如今下邊多出一些%%的目錄,這些目錄就是Smarty的編譯與緩存目錄,它由程序自動生成,不要直接對這些生成的文件進行修改。

以上我簡單的把Smarty程序中的一些經常使用的基本元素介紹了一下,在後邊的例子中你能夠看到將它們將被屢次的使用。

3、模板說明

接下來介紹一個section循環塊與foreach循環塊,原本它應該屬於模板部分,可是因爲它們是smarty的精華所在,並且與smarty程序設計部分聯繫很是緊密,因此就在本節單獨拿出來說一下。

1: foreach:用於循環簡單數組,它是一個選擇性的section循環,它的定義格式爲:

 

複製代碼代碼以下:
{foreach from=$array item=array_id} 
{foreachelse} 
{/foreach}

其中,from 指出要循環的數組變量,item爲要循環的變量名稱,循環次數由from所指定的數組變量的個數所決定。{foreachelse}用來當程序中傳遞過來的數組爲空時的處理,下面是一個簡單的例子:

 

模板文件:

example.htm頁面以下:

 

複製代碼代碼以下:
foreach 輸出一個「二維關聯數組」的數據:

 

{#foreach item=new from=$news#}

新聞編號:{#$new.id#}

新聞內容:{#$new.title#}

{#foreachelse#}
數據庫中沒有新聞輸出!

{#/foreach#}

{foreach from=$newsArray item=newsID}
新聞編號:{$newsID}
新聞內容:{$newsTitle}
{foreachelse}
對不起,數據庫中沒有新聞輸出!

{/foreach}


這是一個錯誤的不顯示數據,本文作了更正。

 

程序文件:example.php以下:

 

複製代碼代碼以下:
<?php
/********************************************* 

* 文件名: example.php 
* 做 用: 顯示實例程序2 
*********************************************/ 
include_once("./Smarty/Smarty.class.php"); 
$smarty = new Smarty(); 
$smarty->templates("./templates"); 
$smarty->templates_c("./templates_c"); 
$smarty->cache("./cache"); 
$smarty->cache_lifetime = 0; 
$smarty->caching = true; 
$smarty->left_delimiter = "{#"; 
$smarty->right_delimiter = "#}"; 
$array[] = array("newsID"=>1, "newsTitle"=>"第1條新聞"); 
$array[] = array("newsID"=>2, "newsTitle"=>"第2條新聞"); 
$array[] = array("newsID"=>3, "newsTitle"=>"第3條新聞"); 
$array[] = array("newsID"=>4, "newsTitle"=>"第4條新聞"); 
$array[] = array("newsID"=>5, "newsTitle"=>"第5條新聞"); 
$array[] = array("newsID"=>6, "newsTitle"=>"第6條新聞"); 
//這是一個二維關聯數組 
$smarty->assign("newsArray", $array); 
//編譯並顯示位於./templates下的index.htm模板 
$smarty->display("example.htm"); 
?>

輸入結果:example.php輸出以下:

 

 

複製代碼代碼以下:
這裏將輸出一個數組:

 

新聞編號:1

新聞內容:第1條新聞

新聞編號:2

新聞內容:第2條新聞

新聞編號:3

新聞內容:第3條新聞

新聞編號:4

新聞內容:第4條新聞

新聞編號:5

新聞內容:第5條新聞

新聞編號:6

新聞內容:第6條新聞

 

foreach還能夠用foreachelse來匹配,用foreachelse來表示當傳遞給foreach的數組爲空值時程序要執行的操做,具體的使用方法,請參考手冊的說明。

2. section:

section的產生是爲解決foreach的不足的,與foreach同樣,它用於設計模板內的循環塊,它較爲複雜,可極大程序上知足程序須要,因此在程序中我習慣使用它而不使用foreach,基本原形爲:

 

複製代碼代碼以下:
{section name = name loop = $varName[, start = $start, step = $step, max = $max, show = true]}

 

參數解釋以下:

name: section的名稱,不用加$

$loop: 要循環的變量,在程序中要使用assign對這個變量進行操做。

$start: 開始循環的下標,循環下標默認由0開始

$step: 每次循環時下標的增數

$max: 最大循環下標

$show: boolean類型,決定是否對這個塊進行顯示,默認爲true
這裏有個名詞須要說明:

循環下標:實際它的英文名稱爲index,是索引的意思,這裏我將它譯成"下標",主要是爲了好理解。它表示在顯示這個循環塊時當前的循環索引,默認從0開始,受$start的影響,若是將$start設爲5,它也將從5開始計數,在模板設計部分咱們使用過它,這是當前{section}的一個屬性,調用方式爲Smarty.section.sectionName.index,這裏的sectionName指的是函數原型中的name屬性。

{section}塊具備的屬性值,分別爲:

1. index: 上邊咱們介紹的"循環下標",默認爲0

2. index_prev: 當前下標的前一個值,默認爲-1

3. index_next: 當前下標的下一個值,默認爲1

4. first: 是否爲第一下循環

5. last: 是否爲最後一個循環

6. iteration: 循環次數

7. rownum: 當前的行號,iteration的另外一個別名

8. loop: 最後一個循環號,可用在section塊後統計section的循環次數

9. total: 循環次數,可用在section塊後統計循環次數

10. show: 在函數的聲明中有它,用於判斷section是否顯示

它們的具體屬性你們能夠參考手冊,在程序中可靈活使用它的這些屬性,模板部分我就使用過index屬性,你們能夠回過頭去看看。

一樣,{section}也能夠配合使用{sectionelse},用來表示傳入的數組變量爲空時對模板進行的處理。

咱們把上邊的那個例子使用{section}來替代{foreach}來實現現樣的功能,注意,在這個例子中我只將tpl模板中的{foreach}用{section}來實現,php程序文件中沒有任何改動,同時加了{sectionelse}處理塊:

example.tpl模板文件以下:

 

複製代碼代碼以下:
這裏將輸出一個數組:

 

{section name=loop loop=$News} 
新聞編號:{$News[loop].newsID} 
新聞標題:{$News[loop].newsTitle} 
{sectionelse} 
對不起,沒有任何新聞輸入! 
{/section}

 

example.php文件以下:

 

複製代碼代碼以下:
<?php
/*********************************************

 

*

* 文件名: example7.php

* 做 用: 顯示實例程序2

*********************************************/

include_once("./comm/Smarty.class.php");

$smarty = new Smarty();

$smarty->templates("./templates");

$smarty->templates_c("./templates_c");

$smarty->cache("./cache");

$smarty->cache_lifetime = 0;

$smarty->caching = true;

$smarty->left_delimiter = "{";

$smarty->right_delimiter = "}";

$array[] = array("newsID"=>1, "newsTitle"=>"第1條新聞");

$array[] = array("newsID"=>2, "newsTitle"=>"第2條新聞");

$array[] = array("newsID"=>3, "newsTitle"=>"第3條新聞");

$array[] = array("newsID"=>4, "newsTitle"=>"第4條新聞");

$array[] = array("newsID"=>5, "newsTitle"=>"第5條新聞");

$array[] = array("newsID"=>6, "newsTitle"=>"第6條新聞");

$smarty->assign("newsArray", $array);

//編譯並顯示位於./templates下的index.tpl模板

$smarty->display("example.tpl");

?>


example.php 輸出文件以下:

 

 

複製代碼代碼以下:
這裏將輸出一個數組:

 

新聞編號:1

新聞內容:第1條新聞

新聞編號:2

新聞內容:第2條新聞

新聞編號:3

新聞內容:第3條新聞

新聞編號:4

新聞內容:第4條新聞

新聞編號:5

新聞內容:第5條新聞

新聞編號:6

新聞內容:第6條新聞


這裏的{section}塊的對於變量的命名方式感受有些彆扭,不過不要緊,你只要記住模板變量使用:

 

$loopName[name].var這種模式就好了,loopName爲loop處賦予的變量名,[name]爲name處賦予的字符串,.後爲爲你要在程序數組中設定要與值相對應的下標名稱就好了。

好了,本文關於smarty程序設計學習指南就寫到這裏,對於通常的應用,這些知識已經夠用了,其它的一些高級技巧你們請參看手冊中的例子,此外相關的還有Smarty在實際應用中的例子,以php內置的mysql語句,phplib中的DB類,ADODB,Pear中DB類等等。感興趣的朋友能夠關注一下相關內容。

但願本文所述對你們的PHP程序設計有所幫助。

您可能感興趣的文章:

 



smarty模板引擎基礎知識入門

做者:詩未冷 字體:[增長 減少] 類型:轉載 時間:2015-03-30 我要評論

這篇文章主要介紹了smarty模板引擎基礎知識入門,較爲詳細的分析了smarty的基本概念並實例分析了相關的基本用法,具備必定參考借鑑價值,須要的朋友能夠參考下
 

本文實例講述了smarty模板引擎基礎知識。分享給你們供你們參考。具體以下:

1、基本概念

1.什麼是mvc?
mvc是一種開發模式,核心思想是:數據的輸入、數據的處理、數據顯示的強制分離。
2.什麼是smarty?
smarty是一個php的模板引擎。更明確的來講,它能夠幫助開發者更好的分離程序邏輯和頁面顯示。

3.smarty運行原理
模板文件,就是一個顯示數據的模板,其中須要顯示的數據用佔位符代替。
smarty運行時,會讀取模板文件,將模板文件中的佔位符替換成真正的數據,並輸出一個處理後的php文件,交由服務器運行。

2、本身寫一個smarty模板

爲了更好的理解smarty模板,如今本身先寫一個本身的smarty模板-minismarty,讓本身更加深刻的瞭解smarty運行原理。

1.新建項目minismarty
新建模板文件路徑:templates
新建模板文件被編譯後的文件路徑:templates_c
新建模板文件:intro.tpl
新建運行的文件:index.php
新建本身的smarty,即處理模板的文件:cls_MiniSmarty.php

2.編寫index.php文件

<?php 
  require_once './cls_MiniSmarty.php';
  $miniSmarty = new MiniSmarty();
  //傳遞數據
  $miniSmarty->assign("title","hello minismarty!");
  $miniSmarty->assign("content","<font color='red'>this is content!</font>");
  //傳遞數據到哪一個頁面顯示
  $miniSmarty->display("intro.tpl");
?>

3.編寫intro.tpl文件

<!--這是個模板文件--> 
<html> 
<head> 
<meta http-equiv="Content-Language" content="en" />
<meta name="GENERATOR" content="PHPEclipse 1.0" />
<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1" />
<title>{$title}</title>
</head>
<body bgcolor="#FFFFFF" text="#000000" link="#FF9966" vlink="#FF9966" alink="#FFCC99">
{$content}
</body>
</html>

這裏面的內容是用佔位符的形式,smarty的做用就是將佔位符的內容替換成真正的數據。
這樣就能夠實現模板文件和數據文件強制分離,經過smarty進行數據的傳遞。

4.編寫cls_MiniSmarty.php文件

<?php 
/** 
 * 
 * 本來是經過smarty模板引擎給模板提供數據的 
 * 如今本身模仿寫一個模板,給模板提供數據的類 
 * smarty運行時,讀取模板文件,將模板文件替換成可運行的php文件
 * 服務器真正運行的文件是處理後的文件
 */ 
class MiniSmarty { 
  //模板文件路徑 
  var $template_dir = "./templates/"; 
  //模板文件被替換後的文件路徑 
  var $templates_c_dir = "./templates_c/"; 
  //存放變量值 
  var $tpl_vars = array (); 
  //主要模擬2個方法 
  /** 
   * 添加數據 
   * 參數1:鍵 
   * 參數2:值,默認爲null 
   */ 
  function assign($tpl_var, $var = null) { 
    if ($tpl_var != '') { 
      $this->tpl_vars[$tpl_var] = $var; //將數據添加到數組中 
    } 
  } 
  /** 
   * 顯示數據 
   * 參數1:顯示到哪一個模板文件中 
   */ 
  function display($tpl_file) { 
    //得到模板文件的路徑 
    $tpl_file_path = $this->template_dir . $tpl_file; 
    //得到模板文件被編譯後的文件路徑 
    $compile_file_path = $this->templates_c_dir . "com_" . $tpl_file . ".php"; 
    //判斷文件是否存在 
    if (!file_exists($tpl_file_path)) { 
      return false; 
    } 
    //不用每次都生成編譯文件,只有編譯文件不存在或者模板文件被修改了才生成新的編譯文件 
    //至關於緩存了編譯文件 
    //filemtime函數:得到文件的生成時間 
    if (!file_exists($compile_file_path) || filemtime($tpl_file_path) > filemtime($compile_file_path)) { 
      //讀取模板文件的內容 
      $fpl_file_content = file_get_contents($tpl_file_path); 
      $newStr = myReplace($fpl_file_content); 
      //將替換後的字符串生成新的文件,也就是編譯後的文件 
      file_put_contents($compile_file_path, $newStr); 
    } 
    //引入編譯後的文件 
    include $compile_file_path; 
  } 
  /** 
   * 對模板文件中的內容進行替換,得到新的字符串 
   */ 
  function myReplace($fpl_file_content) { 
    $pattern = array ( 
      '/\{\s*\$([a-zA-Z_][a-zA-Z0-9_]*)\s*\}/i' 
    ); 
    $replace = array ( 
      '<?php echo $this->tpl_vars["${1}"] ?>' 
    ); 
    $newStr = preg_replace($pattern, $replace, $fpl_file_content); 
    return $newStr; 
  } 
} 
?>

preg_replace方法介紹:
參數1:替換的規則
參數2:替換成的內容
參數3:替換操做的內容

5.運行結果

標題和內容都顯示出來了:

結論:

真正運行的文件,既不是index.php,也不是intro.php,而是兩者經過smarty做用後的文件:
com_intro.tpl.php。這個文件中數據來源於index.php,顯示的佈局來自intro.tpl,中間的橋樑是smarty。
smarty的做用是接受數據、填充數據(替換模板中的佔位符)、並加載替換後的文件。

3、講解smarty使用細節

1.如何配置smarty?

解壓後,將libs文件夾拷貝到項目目錄下便可,而後再建立2個文件夾templates和templates_c,分別放模板文件和模板編譯後文件。

2.使用smarty注意事項

①替換變量的標示符。
由於默認的表示符是{}這個和style樣式中的{}發生衝突,因此須要修改一下默認的標識符,通常修改成:{<>}
②修改標識符的方法。
方法一:直接修改smarty類源碼:不推薦。
方法二:使用smarty提供的方法進行修改。

$smarty->left_delimiter="{<";
$smarty->right_delimiter=">}";

③smarty的一些基本配置

$smarty->template_dir="./templates";//模板路徑
$smarty->compile_dir="./templates_c";//編譯路徑
$smarty->caching=false;  //是否使用緩存
$smarty->cache_dir="./smarty_cache";//若是使用緩存的話:緩存的路徑

3.smarty模板技術分配變量的細節問題

一句話:能夠分配php支持的各類數據。
php基本數據:int double string bool
複合數據類型:array object
特殊數據類型:resource null

但願本文所述對你們的php程序設計有所幫助。

 



菜鳥學PHP之Smarty入門

做者: 字體:[增長 減少] 類型:轉載 時間:2007-01-04 我要評論

 
 

剛開始接觸模版引擎的 PHP 設計師,聽到 Smarty 時,都會以爲很難。其實筆者也不例外,碰都不敢碰一下。可是後來在剖析 XOOPS 的程序架構時,開始發現 Smarty 其實並不難。只要將 Smarty 基礎功練好,在通常應用上就已經至關足夠了。固然基礎能打好,後面的進階應用也就不用怕了。 這篇文章的主要用意並不是要深刻探討 Smarty 的使用,這在官方使用說明中都已經寫得很完整了。筆者僅在此寫下一些本身使用上的心得,讓想要了解 Smarty 卻不得其門而入的朋友,能夠從中獲得一些啓示。就由於這篇文章的內容不是很是深刻,會使用 Smarty 的朋友們可能會以爲簡單了點。 目前本文已經第三次修訂了,本想多加一些料進來;不過礙於時間的關係,不少 Smarty 的進階技巧筆者並無研究得很透徹,因此也不敢拿出來現眼,但筆者相信這篇文章應該可以知足大多數想學習 Smarty 的初學者了。固然本文有謬誤的地方也歡迎告知,筆者會在下一次的修訂中更正的。 Smarty介紹 什麼是模版引擎 不知道從何時開始,有人開始對 HTML 內嵌入 Server Script 以爲不太滿意。然而不管是微軟的 ASP 或是開放源碼的 PHP,都是屬於內嵌 Server Script 的網頁伺服端語言。所以也就有人想到,若是能把程序應用邏輯 (或稱商業應用邏輯) 與網頁呈現 (Layout) 邏輯分離的話,是否是會比較好呢? 其實這個問題早就存在已久,從交互式網頁開始風行時,不管是 ASP 或是 PHP 的使用者都是身兼程序開發者與視覺設計師兩種身份。但是一般這些使用者不是程序強就是美工強,若是要二者同時兼顧,那可得死掉很多腦細胞... 因此模版引擎就應運而生啦!模版引擎的目的,就是要達到上述提到的邏輯分離的功能。它能讓程序開發者專一於資料的控制或是功能的達成;而視覺設計師則可專一於網頁排版,讓網頁看起來更具備專業感!所以模版引擎很適合公司的網站開發團隊使用,使每一個人都能發揮其專長! 就筆者接觸過的模版引擎來講,依資料呈現方式大概分紅:需搭配程序處理的模版引擎和徹底由模版自己自行決定的模版引擎兩種形式。 在需搭配程序處理的模版引擎中,程序開發者必需要負責變量的呈現邏輯,也就是說他必須把變量的內容在輸出到模版前先處理好,才能作 assign 的工做。換句話說,程序開發者仍是得多寫一些程序來決定變量呈現的風貌。而徹底由模版自己自行決定的模版引擎,它容許變量直接 assign 到模版中,讓視覺設計師在設計模版時再決定變量要如何呈現。所以它就可能會有另外一套屬於本身的模版程序語法 (如 Smarty) ,以方便控制變量的呈現。但這樣一來,視覺設計師也得學習如何使用模版語言。 模版引擎的運做原理,首先咱們先看看如下的運行圖: 通常的模版引擎 (如 PHPLib) 都是在創建模版對象時取得要解析的模版,而後把變量套入後,透過 parse() 這個方法來解析模版,最後再將網頁輸出。 對 Smarty 的使用者來講,程序裏也不須要作任何 parse 的動做了,這些 Smarty 自動會幫咱們作。並且已經編譯過的網頁,若是模版沒有變更的話, Smarty 就自動跳過編譯的動做,直接執行編譯過的網頁,以節省編譯的時間。 使用Smarty的一些概念 在通常模版引擎中,咱們常看到區域的觀念,所謂區塊大概都會長成這樣: <!-- START : Block name --> 區域內容 <!-- END : Block name --> 這些區塊大部份都會在 PHP 程序中以 if 或 for, while 來控制它們的顯示狀態,雖然模版看起來簡潔多了,但只要一換了顯示方式不一樣的模版, PHP 程序勢必要再改一次! 在 Smarty 中,一切以變量爲主,全部的呈現邏輯都讓模版自行控制。由於 Smarty 會有本身的模版語言,因此無論是區塊是否要顯示仍是要重複,都是用 Smarty 的模版語法 (if, foreach, section) 搭配變量內容做呈現。這樣一來感受上好象模版變得有點複雜,但好處是隻要規劃得當, PHP 程序一行都沒必要改。 由上面的說明,咱們能夠知道使用Smarty 要掌握一個原則:將程序應用邏輯與網頁呈現邏輯明確地分離。就是說 PHP 程序裏不要有太多的 HTML 碼。程序中只要決定好那些變量要塞到模版裏,讓模版本身決定該如何呈現這些變量 (甚至不出現也行) 。 Smarty的基礎 安裝Smarty 首先,咱們先決定程序放置的位置。 Windows下可能會相似這樣的位置:「 d:appservwebdemo 」。 Linux下可能會相似這樣的位置:「 /home/jaceju/public_html/ 」。 到Smarty的官方網站下載最新的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:appservwebdemo」。安裝好Smarty後,咱們在主資料夾下再創建這樣的資料夾: 在 Linux 底下,請記得將 templates_c 的權限變動爲 777 。Windows 下則將其只讀取消。 第一個用Smarty寫的小程序 咱們先設定 Smarty 的路徑,請將如下這個檔案命名爲 main.php ,並放置到主資料夾下: main.php: <?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: <html>  <head>  <meta http-equiv="Content-Type" content="text/html; charset=big5">  <title><{$title}></title>  </head>  <body>  <{$content}>  </body>  </html>  如今咱們要將上面的模版顯示出來,並將網頁標題 ($title) 與內容 ($content) 更換,請將如下檔案內容命名爲 test.php ,並放置在主資料夾下: test.php: <?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 /* Smarty version 2.6.0, created on 2003-12-15 22:19:45 compiled from test.htm */ ?>  <html>  <head>  <meta http-equiv="Content-Type" content="text/html; charset=big5">  <title><?php echo $this->_tpl_vars[@#title@#]; ?></title>  </head>  <body>  <?php echo $this->_tpl_vars[@#content@#]; ?>  </body>  </html>  沒錯,這就是 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: <?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 中的 <?php 及 ?>  (事實上它們的確會被替換成這個) ,因此如下的模版變量寫法都是可行的: 1. <{$var}> 2. <{ $var }> <!-- 和變量之間有空格 --> 3. <{$var }> <!-- 啓始的標示符號和結束的標示符號不在同一行 --> 在 Smarty 裏,變量預設是全域的,也就是說你只要指定一次就行了。指定兩次以上的話,變量內容會以最後指定的爲主。就算咱們在主模版中加載了外部的子模版,子模版中一樣的變量同樣也會被替代,這樣咱們就不用再針對子模版再作一次解析的動做。 而在 PHP 程序中,咱們用 Smarty 的 assign 來將變量置放到模版中。 assign 的用法官方手冊中已經寫得不少了,用法就如同上一節的範例所示。不過在重複區塊時,咱們就必須將變量作一些手腳後,才能將變量 assign 到模版中,這在下一章再提。 修飾你的變量 上面咱們提到 Smarty 變量呈現的風貌是由模版自行決定的,因此 Smarty 提供了許多修飾變量的函式。使用的方法以下: <{變量|修飾函式}> <!-- 當修飾函式沒有參數時 --> <{變量|修飾函式:"參數(非必要,視函式而定)"}> <!-- 當修飾函式有參數時 --> 範例以下: <{$var|nl2br}> <!-- 將變量中的換行字符換成 <br /> --> <{$var|string_format:"%02d"}> <!-- 將變量格式化 --> 好,那爲何要讓模版自行決定變量呈現的風貌?先看看底下的 HTML ,這是某個購物車結賬的部份畫面。<input name="total" type="hidden" value="21000" /> 總金額:21,000 元 通常模版引擎的模版可能會這樣寫: <input name="total" type="hidden" value="{total}" /> 總金額:{format_total} 元 它們的 PHP 程序中要這樣寫: <?php  $total = 21000;  $tpl->assign("total", $total);  $tpl->assign("format_total", number_format($total));  ?>  而 Smarty 的模版就能夠這樣寫: (number_format 修飾函式請到Smarty 官方網頁下載) <input name="total" type="hidden" value="<{$total}>" /> 總金額:<{$total|number_format:""}> 元 Smarty 的 PHP 程序中只要這樣寫: <?php  $total = 21000;  $tpl->assign("total", $total);  ?>  因此在 Smarty 中咱們只要指定一次變量,剩下的交給模版自行決定便可。這樣瞭解了嗎?這就是讓模版自行決定變量呈現風貌的好處! 控制模版的內容 重複的區塊 在 Smarty 樣板中,咱們要重複一個區塊有兩種方式: foreach 及 section 。而在程序中咱們則要 assign 一個數組,這個數組中能夠包含數組數組。就像下面這個例子: 首先咱們來看 PHP 程序是如何寫的: test2.php: <?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: <html>  <head>  <meta http-equiv="Content-Type" content="text/html; charset=big5">  <title>測試重複區塊</title>  </head>  <body>  <pre>  利用 foreach 來呈現 array1  <{foreach item=item1 from=$array1}>  <{$item1}>  <{/foreach}>  利用 section 來呈現 array1  <{section name=sec1 loop=$array1}>  <{$array1[sec1]}>  <{/section}>  利用 foreach 來呈現 array2  <{foreach item=index2 from=$array2}>  <{foreach key=key2 item=item2 from=$index2}>  <{$key2}>: <{$item2}>  <{/foreach}>  <{/foreach}>  利用 section 來呈現 array1  <{section name=sec2 loop=$array2}>  index1: <{$array2[sec2].index1}>  index2: <{$array2[sec2].index2}>  index3: <{$array2[sec2].index3}>  <{/section}>  </pre>  </body>  </html>  執行上例後,咱們發現無論是 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: <?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: <html>  <head>  <title>巢狀循環測試</title>  </head>  <body>  <table width="200" border="0" align="center" cellpadding="3" cellspacing="0">  <{section name=sec1 loop=$forum}>  <tr>  <td colspan="2"><{$forum[sec1].category_name}></td>  </tr>  <{section name=sec2 loop=$forum[sec1].topic}>  <tr>  <td width="25"> </td>  <td width="164"><{$forum[sec1].topic[sec2].topic_name}></td>  </tr>  <{/section}>  <{/section}>  </table>  </body>  </html>  執行的結果就像筆者舉的例子同樣。 所以呢,在程序中咱們只要想辦法把所要重複值一層一層的塞到數組中,再利用 <{第一層數組[循環1].第二層數組[循環2].第三層數組[循環3]. ... .數組索引}> 這樣的方式來顯示每個巢狀循環中的值。至於用什麼方法呢?下一節使用數據庫時咱們再提。 轉換數據庫中的資料 上面提到如何顯示巢狀循環,而實際上應用時咱們的資料多是從數據庫中抓取出來的,因此咱們就得想辦法把數據庫的資料變成上述的多重數組的形式。這裏筆者用一個 DB 類別來抓取數據庫中的資料,您能夠自行用您喜歡的方法。 咱們只修改 PHP 程序,模版仍是上面那個 (這就是模版引擎的好處~),其中 $db 這個對象假設已經在 main.php 中創建好了,並且抓出來的資料就是上面的例子。 test3.php: <?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: <?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: <html>  <head>  <title>橫向重複表格測試</title>  </head>  <body>  <table width="500" border="1" cellspacing="0" cellpadding="3">  <tr>  <{section name=sec1 loop=$my_array}>  <td><{$my_array[sec1].value}></td>  <{if $smarty.section.sec1.rownum is div by 2}>  </tr>  <tr>  <{/if}>  <{/section}>  </tr>  </table>  </body>  </html>  重點在於 $smarty.section.sec1.rownum 這個 Smarty 變量,在 section 循環中這個變量會取得從 1 開始的索引值,因此當 rownum 能被 2 除盡時,就輸出 </tr><tr> 使表格換列 (注意!是 </tr> 在前面<tr> 在後面) 。所以數字 2 就是咱們在一列中想要呈現的資料筆數。各位能夠由此去變化其它不一樣的呈現方式。 加載外部內容 咱們能夠在模版內加載 PHP 程序代碼或是另外一個子模版,分別是使用 include_php 及 include 這兩個 Smarty 模版語法; include_php 筆者較少用,使用方式能夠查詢官方手冊,這裏再也不敘述。 在使用 include 時,咱們能夠預先加載子模版,或是動態加載子模版。預先加載一般使用在有共同的文件標頭及版權宣告;而動態加載則能夠用在統一的框架頁,而進一步達到如 Winamp 般可換 Skin 。固然這兩種咱們也能夠混用,視情況而定。 咱們來看看下面這個例子: test5.php: <?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: <html>  <head>  <meta http-equiv="Content-Type" content="text/html; charset=big5">  <title><{$title}></title>  </head>  <body>  <{include file="test5_2.htm"}><br />  <{include file=$dyn_page}>  <{include file="test5_4.htm" custom_var="自訂變量的內容"}>  </body>  </html>  模版 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 的作法。

相關文章
相關標籤/搜索