首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> 網頁程式設計 >> PHP 程式設計 >> PHP 相關知識

CodeIgniter 靜態頁面生成實作:擴充 Loader 類實現視圖快取

     編輯:PHP 相關知識

前言

CodeIgniter 預設每次請求都動態載入視圖並輸出。對於內容較靜態、流量大的頁面 (如文章詳情、產品頁),生成靜態 HTML 檔案可大幅降低 PHP 執行負載與資料庫查詢。本文示範如何透過繼承 CI_Loader 並覆寫 _ci_load() 實現自動靜態化。

核心實作:MY_Loader.php

<?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。
Gzip 預壓縮
生成時同步 gzencode($buffer, 9) 產生 .html.gz,Nginx gzip_static on 直接傳送。

注意事項

  • 動態內容不可靜態化:包含使用者專屬資訊 (購物車、未讀通知)、CSRF Token、表單驗證錯誤的頁面請勿啟用。
  • 權限:確保 PHP 進程對靜態檔目錄有寫入權限。
  • 部署同步:多伺服器環境下,靜態檔生成僅在當前節點,需配合 rsync、NFS 或部署腳本同步,或改用共享儲存 (EFS/NAS)。

總結

透過繼承 CI_Loader 並插入 輸出緩衝捕獲 → 條件寫入靜態檔 → 直接包含靜態檔 的邏輯,即可在 CodeIgniter 中以最小改動實現全自動靜態頁面生成。此方案相容框架原有視圖機制,並提供智慧更新、併發安全等生產級特性,是中小型專案快速提升效能的實用選擇。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved