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

PHP 全頁靜態快取:Output Buffering 實作、過期策略、佈署注意與現代替代方案

     編輯:PHP 相關知識

核心原理:Output Buffering 攔截輸出

`ob_start()` 開啟緩衝區 → PHP 執行產生 HTML → `ob_get_contents()` 取得內容 → 存檔 → 後續請求直接讀檔輸出,跳過 PHP 執行。

完整實作 (支援 GET/POST 參數區分、過期時間、原子寫入)

<?php
function pageCache(int $ttl = 300): void {
    // 僅快取 GET 請求,POST 通常有副作用
    if ($_SERVER['REQUEST_METHOD'] !== 'GET') return;

    $cacheDir = __DIR__ . '/../var/cache/pages';
    if (!is_dir($cacheDir)) mkdir($cacheDir, 0750, true);

    // Key 包含路徑 + 查詢字串 (排除追蹤參數 utm_*)
    $query = $_SERVER['QUERY_STRING'];
    parse_str($query, $params);
    unset($params['utm_source'], $params['utm_medium'], $params['utm_campaign'], $params['fbclid'], $params['gclid']);
    $key = $_SERVER['REQUEST_URI'] . '?' . http_build_query($params);
    $key = 'page_' . hash('sha256', $key) . '.php';
    $file = $cacheDir . '/' . $key;

    // 檢查快取是否存在且未過期
    if (is_file($file) && (time() - filemtime($file) < $ttl)) {
        // 直接輸出快取內容 (已含 )
        include $file;
        exit;
    }

    // 註冊關閉函式:請求結束時寫入快取
    register_shutdown_function(function () use ($file) {
        $content = ob_get_contents();
        ob_end_clean();
        // 快取檔案格式: + HTML 內容
        $cacheContent = "<?php exit; ?>" . $content;
        $tmp = $file . '.tmp.' . random_bytes(4);
        if (file_put_contents($tmp, $cacheContent) !== false) {
            @rename($tmp, $file);
            @chmod($file, 0640);
        }
    });

    // 開啟緩衝區
    ob_start();
}

// 入口檔案 (index.php / front controller) 最頂端呼叫
pageCache(300); // 5 分鐘快取
?>

快取檔案結構說明

<?php exit; ?><!DOCTYPE html>
<html><head><title>Page Title</title></head>
<body>...靜態 HTML 內容...</body></html>

直接 `include` 會因 `exit;` 終止執行,輸出後續 HTML,極快 (OPcache 緩存編譯後 opcode)。

關鍵避坑指南

坑點後果解決
快取了動態內容 (用戶名、購物車、CSRF Token)用戶 A 看到用戶 B 的資料、表單失效**僅快取純公開頁面** (首頁、列表、文章詳情)。動態區塊用 AJAX/ESI/SSI 片段快取
POST 請求被快取重複提交、副作用重複執行強制 `REQUEST_METHOD === 'GET'`
查詢字串導致快取爆炸每個 utm_* 參數產生新快取正規化 Key:移除追蹤參數、排序參數
多伺服器快取不一致用戶輪詢到不同機器見到舊資料共享儲存 (NFS/Redis) 或主動清除 (部署時 `clear()`)
權限問題無法寫入/讀取快取目錄目錄擁有者 www-data,權限 750/640

現代替代方案 (優先考慮)

  1. Nginx FastCGI Cache:Nginx 直接緩存 FastCGI 回應,完全繞過 PHP,效能最高,支援 `fastcgi_cache_key`、`fastcgi_cache_valid`、`fastcgi_cache_purge`。
  2. Varnish / NGINX Plus / Cloudflare CDN:邊緣快取,全球分發,支援 ESI 片段組合。
  3. 框架內建:Laravel `Cache::remember()`、Symfony `HttpCache`、Yii `FragmentCache`。
  4. 靜態站點生成 (SSG):內容不常變更時,建構時期生成純 HTML (Astro, Next.js, Hugo, VitePress)。

總結

PHP `ob_start` 全頁快取適合**中小型、單機、純公開內容**場景。核心在於 **Key 正規化、原子寫入、排除動態內容、部署時清除**。生產環境**強烈建議升級至 Nginx FastCGI Cache 或 CDN 邊緣快取**,架構更清晰、效能更極致。

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