模板
數據與表現層的標簽分離
smarty是PHP 與 HTML代碼的分離
小型模板類
$smarty 的工作流程:
把需要顯示的全局變量,賦值塞到對象內部的屬性上,一個數組中.
編譯模板,把
{$標簽}
,解析成相應的<?php echo
代碼引入編譯后的PHP文件
使用smarty的步驟:
smarty是一個類,要使用,需要先引入并實例化
assign賦值
display --> 編譯到輸出
smarty缺點:
編譯模板,需要消耗時間
要把變量再重新賦值一份(又倒騰一次,放在對象的屬性上)
<?php/*** 小型模板類*//*** 1. 把標簽解析成PHP輸出語句* 2. 模板文件-->PHP文件* 區分 模板文件和 PHP文件,把模板和編譯后的結果,放置在不同的目錄中.* 用2個不同的屬性來記錄 不同的目錄 */class Tmp {public $template_dir = ''; // 模版文件所在的路徑public $compile_dir = ''; // 模板編譯后存在的路徑public $tpl_var = array(); // 接受外部的變量/*** 存儲全局變量* @param {String} $key 變量名* @param {Mixin} $val 變量值 */public function assign( $key, $val ) {$this->tpl_var[$key] = $val;}/*** 調用 compile模板,和自動引入* @param {String} $template 模板文件名*/public function display( $template ) {$comp = $this->compile($template);include($comp);}/*** 編譯* @param {String} $template 模板文件名 (需要編譯的模板文件的文件名)* @return {String} $comp 編譯后的文件路徑 * * 把指定的模板內容讀取,再編譯成PHP文件* * 最終外部執行的是,編譯后的文件*/public function compile( $template ) {// 讀取模板內容$tmp = $this->template_dir . '/' . $template;$scoure = file_get_contents($tmp); // 替換模板內容$scoure = str_replace('{$', '<?php echo $this->tpl_var[\'', $scoure);$scoure = str_replace('}', '\'];?>', $scoure);// 把編譯后的內容保存成編譯后的文件$comp = $this->compile_dir . '/'. $template . '.php';// 判斷模板是否已經存在, 加上通過 文件修改的時間來判斷,模板是否已經被修改過if ( file_exists($comp) && filemtime($tmp) < filemtime($comp) ) {return $comp;}file_put_contents($comp, $scoure);return $comp; }}
?>
引入賦值和標簽語法
smarty典型使用流程
<?php/*** 原理:* 分析html模板中的標簽,生成相應的PHP文件* 再引入該PHP* * smarty流程:* 1. 引入smarty* 2. 實例化* 3. 配置[最基本的要配置模板目錄和編譯目錄]*/// 引入smartyrequire('../smarty3/libs/Smarty.class.php');// 實例化$smarty = new Smarty();// 配置$smarty->template_dir = './templates';$smarty->compile_dir = './compile';// 賦值$smarty->assign('title', 'T');$smarty->assign('content', 'C');// 編譯$smarty->display('temp01.html');?>
smarty可以賦值為數值,數字等值,可以是數組.
VIEW:
<table style="border: 1px solid lightcyan;"><tr><td>姓名:</td><td>{$name}</td></tr><tr><td>年齡</td><td>{$age}</td></tr><tr><td>兵器</td><td>{$weapon}</td></tr>
</table><table style="border: 1px solid lightcyan;"><tr><td>姓名:</td><td>{$zf.name}</td></tr><tr><td>年齡</td><td>{$zf.age}</td></tr><tr><td>兵器</td><td>{$zf.weapon}</td></tr>
</table><table style="border: 1px solid lightcyan;"><tr><td>姓名:</td><td>{$guanyu[0]}</td></tr><tr><td>年齡</td><td>{$guanyu[1]}</td></tr><tr><td>兵器</td><td>{$guanyu[2]}</td></tr>
</table>
Controller
<?php// 引入smartyrequire('../smarty3/libs/Smarty.class.php');// 實例化$smarty = new Smarty();// 配置$smarty->template_dir = './templates';$smarty->compile_dir = './compile';$user = array('name' => '劉備','age' => 28,'weapon' => '雙劍'); // 賦值$smarty->assign($user);$zf = array('name' => '張飛','age' => 25,'weapon' => '矛'); $smarty->assign('zf', $zf);$guanyu = array('關羽', 25, '青龍');$smarty->assign('guanyu', $guanyu);// 編譯$smarty->display('liubei.html');?>
smarty模板標簽與css標簽防止沖突
如果smarty默認定界符 {}
與 css {}
沖突
可以使用以下二種方法解決
修改smarty默認定界符
{{}}
也可以用
{literal}{/literal}
標簽,來告訴smarty,此處照常輸出,不用解析
// 配置smarty的左右定界符
$smarty->left_delimiter = '{{';
$smarty->right_delimiter = '}}';
<style type="text/css">{literal} table {background: pink;} {/literal}
</style>
模板變量來源
smarty標簽變量的來源:
在模板中,{$title},則說明$title標簽在被assign賦過值。
smarty的標簽變量對應的來源,除了assign,還有那些?
PHP中assign分配變量
smarty的系統保留變量
從配置文件讀取到的配置變量
assign
// 引入require('../smarty3/libs/Smarty.class.php');// 實例化$smarty = new Smarty();// 配置$smarty->template_dir = './templates';$smarty->compile_dir = './compile'; // assign 賦值$smarty->assign('name', '羅隱');$smarty->assign('poem', '我未成名君未嫁,可能俱是不如人');// 編譯$smarty->display('shiju.html');
系統保留變量
系統保留變量,不用賦值,能夠自動獲取
<p>{$smarty.get.id}</p>
$smarty.
開頭的標簽,當成系統變量來解析,如:$smarty.get.id
解析成<?php echo $_GET['id'] ?>
還有以下幾個系統保留變量:
$smarty.post
$smarty.session
$smarty.cookies
常量如何顯示:
$smarty.const.常量名
<p>{$smarty.const.HEI}</p>
配置文件讀取配置變量
注意:
配置文件,一般以
.conf
做后綴配置文件的寫法是:
選項=值
配置smarty的config_dir,并把配置文件放在該目錄下.
配置文件:
site=pink
tel='13164889431'
獲取配置文件中的變量:
// 引入:
{config_load file='site.conf'}// 顯示
<div>
{$smarty.config.site}
</div><div>{#tel#}
</div>
append
連著往某個標簽賦值多個值,可以使用append
賦值:
$smarty->append('color', 'tan'); // _tpl_vars['color'][] = 'tan'
$smarty->append('color', 'pink'); // _tpl_vars['color'][] = 'pink'
使用:
<p>{$color[0]}</p>
<p>{$color[1]}</p>
源碼:
$data->tpl_vars[ $tpl_var ]->value[] = $value;
把一個值壓入一個數組
smarty賦值時還能夠引用賦值
assignByRef('title', $title); // _tpl_vars['title'] = &title; // 引用賦值
這個功能在PHP5以后,意義不大,PHP5以后是寫時賦值
對象賦值和引用
對象賦值
class Human {public $name = 'zf';public $age = 23;public function say() {return 'HELLO WORLD';}}$man = new Human();$smarty->assign('man', $man);
顯示:
<div>{$man->name}</div>
<div>{$man->age}</div>
<div>{$man->say()}</div>
模板的作用:分離PHP代碼,讓代碼簡潔,所以模板中的標簽,應該盡量的只負責變量的輸出.不要負責太多的邏輯判斷,函數調用等.
簡化模板配置
通過繼承來簡化模板配置
<?php/*** 使用對象繼承的方式來完成smarty的配置*/class MySmarty extends Smarty {/*** 構造函數*/public function __construct() {// 調用父類的construct, 否則父類的一些設置就丟失了parent::__construct();$this->setTemplateDir('./templates'); // 模板文件位置$this->setCompileDir('./compile'); // 編譯文件位置$this->setConfigDir('./conf'); // 配置文件位置}} ?>
標簽數學運算
<div>{$age}</div>
<!-- 標簽可以參與運算,但是不推薦 -->
<div>{$age + 2}</div><div>年紀差為:{$age - $diffAge}</div>
邏輯判斷
IF標簽要成對
{if $price < 10000 }
<div>{$color}</div>
{else}
<div>TAN</div>
{/if}
{if $smarty.get.today == 0 || $smarty.get.today == 7}
<div>周日</div>
{elseif $smarty.get.today == 6}
<div>周六</div>
{else}
<div>工作日</div>
{/if}
在模板中使用邏輯判斷的思考:
從分工角度看:模板只負責輸出 ,不負責邏輯判斷。
為什么還需要有邏輯判斷? 在模板上進行邏輯判斷,可以極大的簡化工作.
smarty循環
for循環
for循環基本應用
賦值:
$smarty->assign('start', 1);$smarty->assign('end', 9);
顯示:
{for $i = $start to $end}{$i}{if $i % 3 == 0}<br/>{/if}
{/for}
<h3>1-100所有的奇數</h3>
{for $i=$start to 100}{if $i % 2 != 0 }<span>{$i}</span>{/if}
{/for}
循環總次數
$i@total
,循環索引$i@iteration
<h3>步長屬性 控制</h3>
<div>{for $i=$start to 100 step 2}{if $i@first == $start}<span style="color: cyan;">{$i}</span>{elseif $i@last == $i@total}<span style="color: tan;">{$i}</span>{else} {$i}{/if}{if $i@iteration % 3 == 0}<br/>{/if}{/for}<div>循環總次數:{$i@total}</div><div>循環索引:$i@iteration</div><div>循環的第一次:$i@first</div><div>循環的最后一次:$i@last</div>
</div>
foreach循環
foreach循環
典型場景,二維數組的循環
例如:新聞列表,會員列表,商品列表
賦值數據
<?php/*** foreach循環* 典型場景,二維數組的循環。* 例如:新聞列表,會員列表,商品列表*/ require('../smarty3/libs/Smarty.class.php');require('./MySmarty.class.php');$smarty = new MySmarty();// 鏈接數據庫$conn = mysql_connect('127.0.0.1', 'root', '');// 設置字符集mysql_query('set names utf8', $conn);// 選擇數據庫mysql_query('use boolshop', $conn);// 查詢數據$sql = "select cat_id, cat_name, intro from category limit 5";$result = mysql_query($sql, $conn);$category = array();while ( $row = mysql_fetch_assoc($result) ) {$category[] = $row;}// 關閉數據庫連接mysql_close($conn);// 賦值 $smarty->assign($category);$smarty->display('foreach.html'); ?>
使用foreach:
<h2>商品欄目</h2>
<table border="1"><tr><td>序號</td><td>欄目名</td><td>欄目信息</td></tr>// {foreach from=$category key=key item=$g}{foreach $category as $k=>$g}<tr><td>{$g.cat_id}</td><td>{$g.cat_name}</td><td>{$g.intro}</td></tr>{/foreach}
</table>
循環總次數
$i@total
,循環索引$i@iteration
<h2>商品欄目</h2>
<table border="1"><tr><td>序號</td><td>欄目名</td><td>欄目信息</td></tr>{foreach $category as $k=>$g}<tr {if $g@iteration % 2 == 0} bgcolor="burlywood" {/if} {if $g@first == 1} bgcolor="gray" {/if}><td>{$g@iteration}</td><td>{$g.cat_name}</td> <td>{$g.intro}</td></tr>{/foreach}
</table><div>索引:$g@iteration</div>
<div>首行:$g@first</div>
<div>尾行:$g@last</div>
<div>總條數:{$g@total}</div>
變量調節器
變量調節器:在模板中,修改變量的顯示形式的一種功能.
變量調節器的本質是一個函數,這個函數,以標簽對應的變量值為參數,然后運算,把返回值,顯示在標簽處.
內置變量調節器:
capitalize [首字符大寫]
count_characters [字符計數]
cat [連接字符串]
count_paragraphs [計算段數]
count_sentences [計算句數]
count_words [計算詞數]
date_format [格式化日期]
default [默認值]
escape [編碼]
indent [縮進]
lower [小寫]
nl2br [換行符替換成 <br />]
regex_replace [正則替換]
replace [替換]
spacify [插空]
string_format [字符串格式化]
strip [去除(多余空格)]
strip_tags [去除html標簽]
truncate [截取]
upper [大寫]
wordwrap [行寬約束]
使用變量調節器:
{foreach $goods as $key=>$g}
<tr><td>{$g.goods_id}</td><td>{$g.goods_name|truncate:15:'...'}</td><td>{$g.shop_price}</td><td>{$g.add_time|date_format:"%Y-%m-%d %H:%M:%S"}</td>
</tr>
{/foreach}
調節器的功能,在PHP中也能實現,也可以在模板中進行。比較合適在模板中進行。
體現了業務與顯示的分離,盡量分離。
PHP就負責判斷條件,并取出數據來。
至于顯示的操作,應該盡量往"前"移(越接近用戶).
MySQL-->PHP-->模板-->JavaScript
合適在MySQL存儲原始數據,PHP中處理.
后臺的數據盡量“原始”,不要帶有樣式,格式。顯示的工作盡量靠前.
頁面緩存
緩存,smarty重要概念。
緩存:把頁面內容保存在磁盤上,下次訪問相同頁面,直接返回保存內容。減輕了數據庫的壓力。
smarty緩存的用法:
開啟
配置緩存的生命周期
判斷是否緩存成功并是否從數據庫取出數據
輸出
// 開啟緩存
$smarty->caching = true;// 設置緩存生命周期
$smarty->cache_lifetime = 3600;// 緩存的文件目錄,用戶存儲緩存文件
$smarty->cache_dir = './cache';if ( !$smarty->isCached('cache.html') ) { // 判斷文件是否緩存// 鏈接數據庫$conn = mysql_connect('127.0.0.1', 'root', '');// 設置字符集mysql_query('set names utf8', $conn);// 選擇數據庫mysql_query('use boolshop', $conn);// 查詢數據$sql = "select goods_id, goods_name, shop_price, add_time from goods limit 5";$result = mysql_query($sql, $conn);$goods = array();while ( $row = mysql_fetch_assoc($result) ) {$goods[] = $row;}// 關閉數據庫連接mysql_close($conn);// 賦值 $smarty->assign('goods', $goods);echo '走了數據庫';
} $smarty->display('cache.html');
局部緩存
smarty在頁面緩存的情況下,可以設置部分內容不緩存。頁面中有隨機廣告,時間,股票信息,不適宜緩存起來。
運行的時候,還是PHP代碼,沒有生成靜態數據。
控制局部不緩存:
在標簽中控制,該標簽不緩存。
// {$標簽 nocache}
<h2>{$time|date_format:"%Y-%m-%d %H:%M:%S" nocache}</h2>
控制一段標簽不緩存
{nocache}
<h2>{$time|date_format:"%Y-%m-%d %H:%M:%S"}</h2>
<h2>{$time|date_format:"%Y-%m-%d %H:%M:%S"}</h2>
{/nocache}
在PHP賦值時,就控制不緩存
$smarty->assign('time2', $time, true); // 第三個參數是 nocache,為true,說明不緩存
不緩存標簽,要保證總能從PHP處得到值.
調用函數
定義函數
function insert_welcome( $parm ) {return 'WELCOME HELLO' . ', AGE:' . $parm['age'];
}
模板中使用:
<h2>{insert name="welcome" age=21}</h2>
單模版多緩存
場景:為商品模板設置緩存,當時從url接收的goods_id,當緩存后,所有商品都一樣了,不合適。
能否為同一個模板,生成不同的緩存文件呢?
比如:根據ID的不同,來生成各個商品的緩存頁面。
可以使用:單模板多緩存
,
原理:生成緩存的時候,可以再傳一個緩存ID。如果ID不同,則生成緩存文件不同。
// 編譯
$smarty->display('one_page.html', $goods_id);
一般的,哪些參數要影響頁面的內容,就需要把哪些參數,當成“緩存ID”。
例如:分頁:page=3
, 欄目:cat_id=3
多個參數影響:
// 編譯
$one_page = $goods_id + $page; // 緩存ID,根據自定義規則計算
$smarty->display('one_page.html', $one_page); // 緩存判斷,也需要加緩存ID
if ( !$smarty->isCached('one_page'.html, $one_page) ) {
}
模板緩存:
<?phprequire('../smarty3/libs/Smarty.class.php');require('./MySmarty.class.php');$smarty = new MySmarty();// 開啟緩存 $smarty->caching = true;// 設置緩存生命周期$smarty->cache_lifetime = 10;// 緩存的文件目錄,用戶存儲緩存文件$smarty->cache_dir = './cache';$goods_id = $_GET['goods_id'] + 0;if ( !$smarty->isCached('one_page.html', $goods_id) ) {// 鏈接數據庫$conn = mysql_connect('127.0.0.1', 'root', '');// 設置字符集mysql_query('set names utf8', $conn);// 選擇數據庫mysql_query('use boolshop', $conn);// 查詢數據$sql = "select goods_name, shop_price, goods_desc from goods where goods_id=" . $goods_id;$result = mysql_query($sql, $conn);$goods = mysql_fetch_assoc($result);// 關閉數據庫連接mysql_close($conn);// 賦值 $smarty->assign($goods);} // 編譯 $smarty->display('one_page.html', $goods_id); ?>
強制刪除緩存
簡單刪除緩存.
指定模板名,不指定緩存ID,則該模板對應的緩存都會被刪除.
可以通過緩存ID來控制,模板對應的指定緩存刪除掉.
<?php/*** 強制刪除緩存*/require('../smarty3/libs/Smarty.class.php');require('./MySmarty.class.php');$smarty = new MySmarty();$goods_id = $_GET['goods_id'] + 0;// 參數模板名,和緩存ID$smarty->clearCache('one_page.html', $goods_id);echo '刪除緩存成功';?>
出于調試目的,臨時不緩存文件
$smarty->force_cache = true; // 強迫文件不緩存
數據對象
作用:數據分類,添加命名空間
不使用數據對象,所有數據都存儲在smarty對象中.(重名就覆蓋)
數據對象:把數據分類(不同類別的數據,重名不會覆蓋)
<?phprequire('../smarty3/libs/Smarty.class.php');require('./MySmarty.class.php');$smarty = new MySmarty();$nav_top = array('首頁', '商城', '男裝');$nav_footer = array('友情鏈接', '備案');$smarty->assign('nav', $nav_top);$smarty->assign('nav', $nav_footer); // 數據覆蓋/*** smarty3引入一種新的概念,叫做數據對象。* 數據對象,就是一個裝數據用的框。* * 靠2個數據對象,把2個數據對象里,各賦值一個同名的`nav`,2個`nav`對象互不干擾*/$smarty->display('news.html');?>
使用數據對象,命名空間
創建數據對象
$smarty->createData();
數據掛載到該數據對象上.
$smarty->dispaly();
聲明使用的數據
<?phprequire('../smarty3/libs/Smarty.class.php');require('./MySmarty.class.php');$smarty = new MySmarty();$nav_top = array('首頁', '商城', '男裝');$nav_footer = array('友情鏈接', '備案');// $smarty->assign('nav', $nav_top);
// $smarty->assign('nav', $nav_footer);/*** smarty3引入一種新的概念,叫做數據對象。* 數據對象,就是一個裝數據用的框。* * 靠2個數據對象,把2個數據對象里,各賦值一個同名的`nav`,2個`nav`對象互不干擾*/// 創建一個數據對象$hdata = $smarty->createData();$fdata = $smarty->createData();// 使用數據對象$hdata->assign('nav', $nav_top);$fdata->assign('nav', $nav_footer); // display時,要聲明,這次使用,哪個數據對象。$smarty->display('header.html', $hdata);$smarty->display('news.html');$smarty->display('footer.html', $fdata);?>
對象注冊
場景:在模板中,smartty標簽中,允許調用對象的方法,如果方法是特殊方法,比如修改密碼等方法。(模板調用特殊方法)
使用對象注冊的方式來解決。
作用:允許調用的方法列表。
在View視圖中:
注冊對象的 變量:訪問方法{zf->name}. 注意不加$
,方法后不加()
<?phprequire('../smarty3/libs/Smarty.class.php');require('./MySmarty.class.php');$smarty = new MySmarty();class User {public $name = 'zf';public $age = 23;public function say() {return 'hello:' . $this->name; }public function modPass() {return '修改密碼成功';}}$zf = new User();// $smarty->assign('zf', $zf);// 對象注冊$smarty->registerObject('zf', $zf, array('say')); // 第三個參數可以控制允許調用的方法.$smarty->display('objLogin.html');?>
<!DOCTYPE html>
<html><head><meta charset="UTF-8"><title></title></head><body><h1>姓名:{zf->name}</h1><h2>年齡:{zf->age}</h2><p>SAY:{zf->say}</p><p>{zf->modPass}</p> <!-- 調用之后報錯,已經不能調用該方法,限制方法不能調用 --><!-- 開發中,不建議使用 --></body>
</html>
模板繼承
可以在父模板中,暫留{block name=""}{/block}
注意:
子模板第一句,先聲明繼承
{extends file=''}
子模板的目的,只是填充父模板預留的
block
父模板:
<!DOCTYPE html>
<html><head><meta charset="UTF-8"><title>{block name='title'}父模板標題{/block}</title></head><body>{block name='header'}{/block}<hr />{block name='footer'}{/block}</body>
</html>
子模板1:
{extends file='tplExteds.html'}
{block name="title"}
<ul><li>嘻嘻哈哈</li><li>嘻嘻</li><li>哈哈</li>
</ul>
{/block}
{block name="header"}頭部{/block}
子模板2:
{extends file='tplExted.html'}
{block name="footer"}2016年最后二天{/block}
變量調節器插件開發
調節器:
<?php/*** 調節顏色* @param {String} $string Smarty自動傳遞給進來,待調節的變量* @param {String} $color 參數值*/function Smarty_modifier_modcolor( $string, $color ) {return '<font color="' . $color . '">' . $string . '</font>';}?>
在smarty源碼的目錄中,
plugins
, 開發。文件命名
modifier.函數名.php
定義的調節器的函數名:
Smarty_modifier_modcolor
display與fetch
display()
可以看成 echo fetch();
fecth()
僅僅計算出應輸出的結果,但是不輸出,只把結果返回。
一個PHP頁面如何知道本身的輸出結果: 使用緩沖區
。
利用fetch靜態化
緩沖區的理解:
fetch是如何知道當前頁面的輸出內容?
輸出到緩沖區,在PHP的最后,讀取緩沖區,就是本頁面的內容。
打開緩沖區:ob_start();
讀取緩沖區:ob_get_content();
清空緩沖區:ob_clean();
讀取并清空:ob_get_clenan();
靜態化:緩沖區+文件寫操作
<?php// fetch 如何知道本頁輸出內容if ( file_exists('./html/fetch.html') ) {header('location: ./html/fetch.html');exit;}// 啟動緩沖區ob_start();// 數據 echo 1 , ' ';echo 2 , ' ';echo 4 , ' ';// 讀取緩沖區$html = ob_get_contents();// 清除緩沖區ob_clean(); // 寫入文件file_put_contents('./html/fetch.html', $html);// 輸出echo $html;?>
模板引擎特點
所有模板的共性:解析標簽,解析成PHP
標簽解析的分類:
最多的就是正則替換 。 例如:smarty2.X系列,quickskin
通過字符串函數來解析 例如:dede的模板類.
字符串解析
<?phpclass Mini {protected $left_delimiter = '{';protected $right_delimiter = '}'; protected $right_length = 1; // 右分隔符的長度public function __constrcut() {$this->right_length = strlen($this->right_delimiter);}protected $tags = array(); // 裝載分析到的標簽 // 編譯public function parse( $file ) {$cont = file_get_contents('./templates/' . $file);$offset = 0;// 截取第一個字符 '{'while ( $poss = strpos($cont, $this->left_delimiter, $offset) !== false ) {// 取最后字符 '}'$pose = strpos($cont, $this->right_delimiter, $poss);// 截取標簽$this->tags[] = substr($cont, $poss, $pose-$poss+$this->right_length);$offset = $pose + $this->right_length; }return $this->tags;}}
?>
變量的存儲分類:
最多的是通過assign,把變量再次裝載到模板對象中。
如:smarty,thinkphp只把模板解析出來,再包含模板
如:discuzd的模板,把模板解析后,只返回模板的路徑,再手動包含。(省略了assign過程,速度更快,顯示的粗糙暴力)
語言分類:
絕大部分PHP
C語言以擴展形式寫的模板引擎(Blitz)