Smarty 的快取機制是其核心優勢之一,透過「編譯模板為 PHP」+「快取輸出結果」雙層加速,大幅降低 CPU 與資料庫負載。本文深入解析快取層級、動態區塊處理、清除策略與實戰調優。
| 層級 | 名稱 | 儲存內容 | 更新觸發 |
|---|---|---|---|
| L1 | 編譯快取 | 模板編譯後的 PHP 代碼 (`templates_c/`) | 模板檔修改時間變更 |
| L2 | 輸出快取 | 模板執行後的 HTML 輸出 (`cache/`) | 快取過期 / 手動清除 / 依賴資料變更 |
$smarty->caching = true; // 開啟 L2 快取
$smarty->cache_lifetime = 3600; // 快取壽命:3600 秒 (1小時)
$smarty->cache_dir = '/path/to/cache/'; // 快取目錄 (需寫入權限)
// 檢查是否有效快取 (避免不必要的資料庫查詢)
if (!$smarty->isCached('index.tpl')) {
// 只有無快取時才查詢資料庫
$data = $db->query('SELECT * FROM products')->fetchAll();
$smarty->assign('products', $data);
}
$smarty->display('index.tpl');
適用場景:同一模板顯示不同資料(如:不同分類商品列表、不同用戶個人頁)
// 使用分類 ID 作為 cache_id
$cacheId = 'cat_' . (int)$_GET['cat_id'];
if (!$smarty->isCached('product_list.tpl', $cacheId)) {
$products = $db->query('SELECT * FROM products WHERE cat_id = ?', [$cacheId])->fetchAll();
$smarty->assign('products', $products);
}
$smarty->display('product_list.tpl', $cacheId);
// 清除所有快取
$smarty->clearAllCache();
// 清除特定模板所有快取
$smarty->clearCache('index.tpl');
// 清除特定模板特定 cache_id
$smarty->clearCache('product_list.tpl', 'cat_5');
// 清除過期快取 (可排程每小時執行)
$smarty->clearCache(null, null, time() - 3600); // 清除 1 小時前的快取
// 依關鍵字清除 (Smarty 3.1+)
$smarty->clearCache('index.tpl', '', 'product_'); // 清除 cache_id 含 product_ 的快取
<!-- 模板 -->
<div class="header">
<{insert name="user_panel"}>
</div>
<?php
// PHP 註冊 insert 函數 (命名規則: insert_標籤名)
function insert_user_panel($params, $smarty) {
// 這裡的程式碼每次都會執行,不受快取影響
if (isset($_SESSION['user'])) {
return '歡迎,' . $_SESSION['user']['name'] . ' | 登出';
}
return '登入 | 註冊';
}
// 使用前不需額外註冊,Smarty 會自動尋找 insert_xxx 函數
?>
<?php
// 定義 block 處理函數
function smarty_block_dynamic($params, $content, $smarty) {
return $content; // 直接回傳內容,但此區塊不被快取
}
// 註冊 block,第 3 參數 false = 不快取
$smarty->registerBlock('dynamic', 'smarty_block_dynamic', false);
?>
<!-- 模板 -->
<div class="sidebar">
<{dynamic}>
目前線上人數: <{$online_count}> <!-- 這裡每次都重新渲染 -->
<{/dynamic}>
</div>
<?php
function smarty_function_current_time($params, $smarty) {
return date('Y-m-d H:i:s');
}
// 註冊函數,第 3 參數 false = 不快取輸出
$smarty->registerPlugin('function', 'current_time', 'smarty_function_current_time', false);
?>
<!-- 模板 -->
<div class="footer">
目前時間: <{current_time}>
</div>
核心問題:資料庫資料異動時,如何讓對應快取失效?
// 取得資料表最後更新時間
$lastUpdate = $db->query("SELECT MAX(updated_at) FROM products")->fetchColumn();
$cacheId = 'products_v' . $lastUpdate;
if (!$smarty->isCached('product_list.tpl', $cacheId)) {
$products = $db->query('SELECT * FROM products')->fetchAll();
$smarty->assign('products', $products);
}
$smarty->display('product_list.tpl', $cacheId);
// 商品更新後
function updateProduct($id, $data) {
global $smarty;
$db->update('products', $data, ['id' => $id]);
// 清除所有相關快取
$smarty->clearCache('product_list.tpl'); // 列表頁
$smarty->clearCache('product_detail.tpl', 'prod_' . $id); // 詳情頁
$smarty->clearCache('home.tpl', '', 'product_'); // 首頁推薦區塊
}
Smarty 內建不支援標籤式清除,可自行實作:
// 簡易版:快取檔命名規則 {template}_{cacheId}_{tags}.php
// 寫入時記錄 tags,清除時掃描 cache/ 目錄刪除含特定 tag 的檔案
| 場景 | 建議配置 |
|---|---|
| 高流量首頁 | `cache_lifetime = 300` (5分鐘) + 靜態化輸出 HTML 到 nginx 直接服務 |
| 用戶個人頁 | 使用 `cache_id = 'user_' . $uid`,登入狀態區塊用 `{insert}` |
| 商品列表頁 | `cache_id = 'cat_' . $catId . '_p' . $page`,分頁各自快取 |
| 資料頻繁變動 | 縮短 `cache_lifetime` 或改用策略 2 主動清除 |
| 大量模板 | 定期清理 `templates_c/` 避免 inode 耗盡 |
Smarty 快取機制威力強大,但關鍵在於精準控制快取顆粒度(Cache ID)、正確標記動態區塊(insert/block/function, false)、建立資料異動同步清除機制。掌握這三點,即能在開發效率與執行效能間取得最佳平衡。