CI 內建視圖功能簡單但缺乏模板繼承、區塊覆蓋、緩存、修飾符、安全轉義** 等進階功能。Smarty 作為成熟 PHP 模板引擎,提供完整模板系統,與 CI 整合後可保持 $this->assign()、$this->display() 習慣用法,實現「無縫切換」。
下載 Smarty 並放入 application/libraries/Smarty/,或用 Composer:
composer require smarty/smarty
目錄結構:
application/
├── libraries/
│ └── Smarty/
│ ├── Smarty.class.php
│ ├── sysplugins/
│ └── plugins/
├── config/
│ └── smarty.php # Smarty 專用配置
└── core/
└── MY_Controller.php # 擴展控制器
application/libraries/Ci_smarty.php
class Ci_smarty extends Smarty {
protected $ci;
public function __construct() {
parent::__construct();
$this->ci =& get_instance();
// 載入配置
$this->ci->config->load('smarty');
// 套用配置
$this->setTemplateDir($this->ci->config->item('template_dir'));
$this->setCompileDir($this->ci->config->item('compile_dir'));
$this->setCacheDir($this->ci->config->item('cache_dir'));
$this->setConfigDir($this->ci->config->item('config_dir'));
$this->caching = (bool)$this->ci->config->item('caching');
$this->cache_lifetime = (int)$this->ci->config->item('cache_lifetime');
$this->left_delimiter = $this->ci->config->item('left_delimiter');
$this->right_delimiter = $this->ci->config->item('right_delimiter');
// 註冊 CI 專用修飾符/函數
$this->registerPlugin('modifier', 'base_url', function($uri) {
return $this->ci->config->base_url($uri);
});
$this->registerPlugin('modifier', 'site_url', function($uri) {
return $this->ci->config->site_url($uri);
});
}
}
application/config/smarty.php
$config['template_dir'] = APPPATH . 'views/';
$config['compile_dir'] = FCPATH . 'storage/smarty/compile/';
$config['cache_dir'] = FCPATH . 'storage/smarty/cache/';
$config['config_dir'] = APPPATH . 'config/smarty/';
$config['caching'] = false; // true 開啟模板緩存
$config['cache_lifetime'] = 3600;
$config['left_delimiter'] = '{';
$config['right_delimiter'] = '}';
// 確保目錄存在且可寫
foreach (['compile_dir', 'cache_dir'] as $key) {
$dir = $config[$key];
if (!is_dir($dir)) mkdir($dir, 0755, true);
}
application/config/autoload.php
$autoload['libraries'] = ['ci_smarty'];
application/core/MY_Controller.php
class MY_Controller extends CI_Controller {
public function __construct() {
parent::__construct();
}
// 代理 assign() 到 Smarty
public function assign($key, $val = null) {
if (is_array($key)) {
foreach ($key as $k => $v) {
$this->ci_smarty->assign($k, $v);
}
} else {
$this->ci_smarty->assign($key, $val);
}
}
// 代理 display() 到 Smarty
public function display($template) {
// 自動補全副檔名
if (pathinfo($template, PATHINFO_EXTENSION) === '') {
$template .= '.tpl';
}
$this->ci_smarty->display($template);
}
// 額外:fetch() 支援
public function fetch($template) {
if (pathinfo($template, PATHINFO_EXTENSION) === '') {
$template .= '.tpl';
}
return $this->ci_smarty->fetch($template);
}
}
class Welcome extends MY_Controller {
public function index() {
$this->assign('title', '首頁標題');
$this->assign('user', ['name' => 'Admin', 'role' => 'super']);
$this->assign('items', [
['id' => 1, 'name' => '項目一'],
['id' => 2, 'name' => '項目二'],
]);
$this->display('welcome');
}
}
application/views/welcome.tpl<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>{$title}</title>
</head>
<body>
<h1>歡迎,{$user.name} ({$user.role})</h1>
<ul>
{foreach $items as $item}
<li>{$item.id|base_url} - {$item.name}</li>
{/foreach}
</ul>
<!-- 區塊繼承範例 -->
{block name="sidebar"}
<div class="sidebar">預設側邊欄</div>
{/block}
</body>
</html>
application/views/admin/layout.tpl<html>
<head><title>{$title}</title></head>
<body>
<header>管理後台</header>
<div class="layout">
<aside>{block name="sidebar"}{/block}</aside>
<main>{block name="content"}{/block}</main>
</div>
</body>
</html>
<!-- application/views/admin/dashboard.tpl -->
{extends file="admin/layout.tpl"}
{block name="sidebar"}
<nav>管理選單...</nav>
{/block}
{block name="content"}
<h2>儀表板</h2> ...
{/block}
// 配置開啟
$config['caching'] = true;
$config['cache_lifetime'] = 1800; // 30 分鐘
// 控制器中動態控制
$this->ci_smarty->caching = true;
$this->ci_smarty->cache_lifetime = 300;
$this->display('heavy_page');
實作 Smarty_Resource_Custom 從 DB 讀取模板內容,配合 $smarty->registerResource(),實現後台可視化編輯模板。
// Smarty 3 預設啟用自動轉義
$config['auto_escape'] = true; // 模板中 {$var} 自動 htmlspecialchars
// 需原始輸出用 {$var nofilter} 或 {$var|escape:'htmlall'}
| 問題 | 解決 |
|---|---|
| 找不到 Smarty.class.php | 檢查 libraries/Smarty/ 目錄結構、autoload 大小寫 |
| compile_dir 權限錯誤 | chmod 775 storage/smarty/compile/、擁有者 www-data |
| 模板變數不顯示 | 確認 assign() 在 display() 前呼叫、變數名拼寫 |
| 繼承 {extends} 不生效 | Smarty 3 需啟用 $smarty->compile_check = true、路徑正確 |
| 修飾符 base_url 失效 | 檢查 Ci_smarty 建構子 registerPlugin 註冊 |
透過 Ci_smarty 封裝 + MY_Controller 代理 assign/display,實現 CI 與 Smarty **零學習成本整合**:控制器代碼完全不變,視圖檔案僅改為 .tpl 並享受 Smarty 全功能 (繼承、緩存、修飾符、安全轉義、沙箱)。此方案生產驗證穩定,是 CI 專案升級模板層的首選。