頁面快取是提升 PHP 應用效能最直接的手段。本文提供一個輕量、實用的檔案快取類,支援自動過期、參數化快取鍵、手動清除。
<?php
/**
* 簡易檔案快取類
* 常數需預先定義:
* define('_CachePath_', '/path/to/cache/'); // 快取目錄(需可寫)
* define('_CacheEnable_', true); // 是否啟用
* define('_ReCacheTime_', 3600); // 自動重建間隔(秒),0=不自動重建
*/
class Cache {
private $cacheFile; // 當前頁面快取檔
private $cacheIndexFile; // 群組索引檔(記錄最後更新時間)
public function __construct() {
// 以 SCRIPT_NAME + _ActionVar_ 區分不同動作
$script = str_replace(['.', '/'], '_', $_SERVER['SCRIPT_NAME']);
$action = $_GET['_ActionVar_'] ?? 'index';
$this->cacheIndexFile = $script . '_' . $action;
// 具體快取檔含 REQUEST_URI 雜湊(區分不同參數)
$this->cacheFile = $this->cacheIndexFile . '.' . md5($_SERVER['REQUEST_URI']);
}
/** 刪除當前頁面群組所有快取 */
public function delete() {
$dir = _CachePath_;
$prefix = $this->cacheIndexFile;
$len = strlen($prefix);
$handle = opendir($dir);
while (($file = readdir($handle)) !== false) {
if (strncmp($file, $prefix, $len) === 0) {
@unlink($dir . $file);
}
}
closedir($handle);
}
/** 檢查快取是否存在且有效 */
public function check() {
if (!_CacheEnable_) return false;
// 檢查自動重建時間
if (_ReCacheTime_ > 0) {
$indexFile = _CachePath_ . $this->cacheIndexFile;
if (file_exists($indexFile)) {
$lastUpdate = (int)@file_get_contents($indexFile);
if (time() - $lastUpdate > _ReCacheTime_) {
$this->delete();
return false;
}
}
}
return file_exists(_CachePath_ . $this->cacheFile);
}
/** 讀取快取內容並輸出 */
public function read() {
if (!_CacheEnable_) return false;
$file = _CachePath_ . $this->cacheFile;
if (file_exists($file)) {
readfile($file);
return true;
}
return false;
}
/** 寫入快取 */
public function write($content) {
if (!_CacheEnable_) return false;
$file = _CachePath_ . $this->cacheFile;
$fp = @fopen($file, 'w');
if (!$fp || !@fwrite($fp, $content)) {
trigger_error('快取寫入失敗: ' . $file, E_USER_WARNING);
return false;
}
fclose($fp);
// 更新群組索引時間
if (_ReCacheTime_ > 0) {
$indexFile = _CachePath_ . $this->cacheIndexFile;
@file_put_contents($indexFile, time());
}
return true;
}
}
?>
<?php
// 入口檔案或公共引入
define('_CachePath_', __DIR__ . '/cache/');
define('_CacheEnable_', true);
define('_ReCacheTime_', 3600); // 1 小時自動重建
require 'Cache.php';
$cache = new Cache();
// 快取命中:直接輸出並結束
if ($cache->check()) {
$cache->read();
exit;
}
// 快取未命中:開始輸出緩衝
ob_start();
ob_implicit_flush(0);
?>
<!DOCTYPE html>
<html>
<head><title>動態頁面</title></head>
<body>
<h1>目前時間: <?= date('Y-m-d H:i:s') ?></h1>
<p>這段內容會被快取 1 小時</p>
<?php
// 模擬資料庫查詢
sleep(1);
echo "<p>資料載入完成</p>";
?>
</body></html>
<?php
// 結束:取得緩衝內容並寫入快取
$content = ob_get_contents();
$cache->write($content);
ob_end_flush();
?>
| 設計點 | 實作方式 | 優勢 |
|---|---|---|
| 快取鍵區分參數 | md5(REQUEST_URI) | 同腳本不同查詢字串獨立快取 |
| 群組管理 | 索引檔記錄最後更新時間 | 單次 delete() 清除同腳本所有參數快取 |
| 自動過期重建 | _ReCacheTime_ + 索引檔時間比對 | 無需背景程式,請求驅動自動淘汰 |
| 輸出緩衝捕獲 | ob_start() / ob_get_contents() | 零侵入業務邏輯,透明快取 |
| 可關閉開關 | _CacheEnable_ 常數 | 除錯/開發時一鍵停用 |
deleteByTag('user_123') 掃描檔名刪除Redis::setex()/get(),支援叢集gzencode($content, 6) 寫入,gzdecode() 讀取,節省 70% 空間write/read 改為靜態方法,支援區塊級快取 Cache::fragment('sidebar', fn() => renderSidebar())| 適用 | 不適用 |
|---|---|
| 內容較靜態的頁面(文章、列表、首頁) | 高度個人化頁面(購物車、用戶儀表板) |
| 資料庫查詢繁重的報表頁 | 即時性要求極高(股票、聊天) |
| 流量大、讀多寫少 | 頻繁更新的內容 |
此快取類不足 100 行,零依賴,即拿即用,是中小型 PHP 專案效能優化的第一步。