CodeIgniter 預設每次請求都動態載入視圖並輸出。對於內容較靜態、流量大的頁面 (如文章詳情、產品頁),生成靜態 HTML 檔案可大幅降低 PHP 執行負載與資料庫查詢。本文示範如何透過繼承 CI_Loader 並覆寫 _ci_load() 實現自動靜態化。
<?php
defined('BASEPATH') OR exit('No direct script access allowed');
class MY_Loader extends CI_Loader {
// 公開方法:保持原有介面相容
public function m_view($view, $vars = [], $return = FALSE) {
return $this->_m_ci_load([
'_ci_view' => $view,
'_ci_vars' => $this->_ci_object_to_array($vars),
'_ci_return'=> $return
]);
}
// 核心載入邏輯 (複製自 CI_Loader::_ci_load 並插入靜態化邏輯)
protected function _m_ci_load($_ci_data) {
// --- 1. 解析參數 (同原版) ---
foreach (['_ci_view', '_ci_vars', '_ci_path', '_ci_return'] as $val) {
$$val = isset($_ci_data[$val]) ? $_ci_data[$val] : FALSE;
}
$file_exists = FALSE;
// --- 2. 解析視圖路徑 ---
if (is_string($_ci_path) && $_ci_path !== '') {
$_ci_x = explode('/', $_ci_path);
$_ci_file = end($_ci_x);
} else {
$_ci_ext = pathinfo($_ci_view, PATHINFO_EXTENSION);
$_ci_file = ($_ci_ext === '') ? $_ci_view . '.php' : $_ci_view;
// 靜態檔名:同名 .html
$_ci_html_file = ($_ci_ext === '') ? $_ci_view . '.html' : $_ci_view;
foreach ($this->_ci_view_paths as $_ci_view_file => $cascade) {
if (file_exists($_ci_view_file . $_ci_file)) {
$_ci_path = $_ci_view_file . $_ci_file;
$_ci_html_path = $_ci_view_file . $_ci_html_file; // 靜態檔完整路徑
$file_exists = TRUE;
break;
}
if (!$cascade) break;
}
}
if (!$file_exists && !file_exists($_ci_path)) {
show_error('Unable to load the requested file: ' . $_ci_file);
}
// --- 3. 變數提取與快取 (同原版) ---
$_ci_CI =& get_instance();
foreach (get_object_vars($_ci_CI) as $key => $var) {
if (!isset($this->$key)) $this->$key =& $_ci_CI->$key;
}
if (is_array($_ci_vars)) {
$this->_ci_cached_vars = array_merge($this->_ci_cached_vars, $_ci_vars);
}
extract($this->_ci_cached_vars);
// --- 4. 輸出緩衝區開始 ---
ob_start();
// 相容短標籤
if (!is_php('5.4') && (bool)@ini_get('short_open_tag') === FALSE
&& config_item('rewrite_short_tags') === TRUE && function_usable('eval')) {
echo eval('?>' . preg_replace('/;*\s*\?>/', '; ?>', str_replace('=', ' $this->_ci_ob_level + 1) {
ob_end_flush();
} else {
$_ci_CI->output->append_output(ob_get_contents());
@ob_end_clean();
}
}
}
在 application/config/config.php 新增:
$config['html'] = TRUE; // 啟用靜態頁面生成
<?php
class Article extends CI_Controller {
public function view($id) {
$data['article'] = $this->article_model->get($id);
// 使用自訂 m_view 而非預設 view
$this->load->m_view('article/detail', $data);
}
}
請求進入
│
▼
Controller -> $this->load->m_view()
│
▼
MY_Loader::_m_ci_load()
│
├── 載入視圖 PHP -> ob_start() 捕獲輸出
│
├── 判斷 config['html'] === TRUE ?
│ │
│ ├── YES → 檢查靜態 .html 是否存在且內容一致
│ │ │
│ │ ├── 否/不一致 → 寫入新靜態檔 (flock 鎖定)
│ │ │
│ │ └── 是 → 直接 include 靜態檔輸出 (繞過 PHP 解析)
│ │
│ └── NO → 正常輸出緩衝區內容
│
▼
回應輸出
| 特性 | 實作細節 |
|---|---|
| 零侵入式 | 僅需將 $this->load->view() 改為 $this->load->m_view(),原有變數傳遞、巢狀視圖、短標籤處理完全保留 |
| 智慧更新 | 比對 filesize() 與 strlen($buffer),視圖檔異動自動重新生成,無需手動清除快取 |
| 併發安全 | 寫入靜態檔使用 flock(LOCK_EX) 獨佔鎖,防止高併發下檔案損毀 |
| 效能提升 | 命中靜態檔時直接 include,省去 PHP 編譯視圖、執行邏輯、資料庫查詢 (若控制器也快取) 全流程 |
| 相容巢狀視圖 | 保留原版 ob_get_level() 判斷邏輯,支援視圖中嵌套 $this->load->view() |
$_ci_html_path 指向 FCPATH . 'static_html/',配合 Nginx try_files 直接由 Web Server 服務靜態檔,完全繞過 PHP-FPM。filemtime($view_file) > filemtime($html_file) 判斷,或提供後台「清除快取」按鈕呼叫 unlink()。detail.en.html, detail.mobile.html。gzencode($buffer, 9) 產生 .html.gz,Nginx gzip_static on 直接傳送。透過繼承 CI_Loader 並插入 輸出緩衝捕獲 → 條件寫入靜態檔 → 直接包含靜態檔 的邏輯,即可在 CodeIgniter 中以最小改動實現全自動靜態頁面生成。此方案相容框架原有視圖機制,並提供智慧更新、併發安全等生產級特性,是中小型專案快速提升效能的實用選擇。