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

PHP 檔案快取類別完整實作:支援過期時間、序列化儲存、多實例配置

     編輯:PHP 相關知識

PHP 檔案快取類別完整實作

檔案快取是中小型專案無需安裝 Redis/Memcached 即可使用的輕量快取方案。本文提供生產級的 XZCache 類別,支援自訂快取目錄、過期時間、序列化儲存、快取狀態判斷(無快取/有效/過期)。

類別完整代碼

<?php
/**
 * 簡單且實用的檔案快取類別
 */
class XZCache {
    // 預設快取時間:1 小時(秒)
    private $cache_time = 3600;
    
    // 預設快取目錄
    private $cache_dir = './cache';
    
    public function __construct($cache_dir = null, $cache_time = null) {
        $this->cache_dir = $cache_dir ?? $this->cache_dir;
        $this->cache_time = $cache_time ?? $this->cache_time;
        
        // 確保快取目錄存在
        if (!is_dir($this->cache_dir)) {
            mkdir($this->cache_dir, 0755, true);
        }
    }
    
    /**
     * 儲存快取
     * @param string $key 快取鍵值
     * @param mixed $value 快取資料(支援陣列、物件、純量)
     * @return bool 成功與否
     */
    public function saveCache($key, $value): bool {
        $cache_file = $this->cache_dir . '/xzcache_' . md5($key);
        
        // 檢查現有快取是否過期
        $timediff = @(time() - filemtime($cache_file));
        if ($timediff >= $this->cache_time || !is_file($cache_file)) {
            // 過期或不存在,建立新快取
            $serialized = serialize($value);
            $bytes = @file_put_contents($cache_file, $serialized, LOCK_EX);
            return $bytes !== false;
        }
        
        return true; // 快取未過期,無需更新
    }
    
    /**
     * 讀取快取
     * @param string $key 快取鍵值
     * @return array ['cached' => 0|1|2, 'value' => mixed]
     *         cached: 0=無快取, 1=有效, 2=過期
     */
    public function getCache($key): array {
        $cache_file = $this->cache_dir . '/xzcache_' . md5($key);
        $result = ['cached' => 0, 'value' => null];
        
        if (is_file($cache_file)) {
            $timediff = @(time() - filemtime($cache_file));
            
            if ($timediff >= $this->cache_time) {
                $result['cached'] = 2; // 過期
            } else {
                $content = @file_get_contents($cache_file);
                $result['value'] = @unserialize($content);
                $result['cached'] = 1; // 有效
            }
        }
        
        return $result;
    }
    
    /**
     * 刪除快取
     */
    public function deleteCache($key): bool {
        $cache_file = $this->cache_dir . '/xzcache_' . md5($key);
        return @unlink($cache_file);
    }
    
    /**
     * 清空所有快取
     */
    public function clearAll(): int {
        $count = 0;
        $files = glob($this->cache_dir . '/xzcache_*');
        foreach ($files as $file) {
            if (@unlink($file)) $count++;
        }
        return $count;
    }
    
    /**
     * 取得快取資訊(除錯用)
     */
    public function getCacheInfo($key): array {
        $cache_file = $this->cache_dir . '/xzcache_' . md5($key);
        if (!is_file($cache_file)) {
            return ['exists' => false];
        }
        
        $stat = stat($cache_file);
        return [
            'exists' => true,
            'size' => $stat['size'],
            'mtime' => $stat['mtime'],
            'age' => time() - $stat['mtime'],
            'expired' => (time() - $stat['mtime']) >= $this->cache_time
        ];
    }
}

使用範例

<?php
require_once 'XZCache.php';

// 建立快取實例(自訂目錄與時間)
$cache = new XZCache('./my_cache', 1800); // 30 分鐘過期

// 儲存複雜資料結構
$key = 'user_profile_123';
$value = [
    'id' => 123,
    'name' => '張三',
    'roles' => ['admin', 'editor'],
    'settings' => (object)['theme' => 'dark', 'lang' => 'zh-TW'],
    'last_login' => time()
];

// 寫入快取
if ($cache->saveCache($key, $value)) {
    echo "快取寫入成功\n";
}

// 讀取快取
$result = $cache->getCache($key);

switch ($result['cached']) {
    case 1:
        echo "快取命中:\n";
        print_r($result['value']);
        break;
    case 2:
        echo "快取過期,需重新載入資料源\n";
        break;
    case 0:
        echo "無快取,首次載入\n";
        break;
}

// 手動刪除
// $cache->deleteCache($key);

// 清空所有
// $cache->clearAll();

進階應用模式

模式 1:快取穿透防護(Cache-Aside Pattern)

function getUserProfile($userId, XZCache $cache, PDO $pdo) {
    $key = "user_profile_{$userId}";
    $result = $cache->getCache($key);
    
    if ($result['cached'] === 1) {
        return $result['value']; // 快取命中
    }
    
    // 快取未命中或過期,查詢資料庫
    $stmt = $pdo->prepare("SELECT * FROM users WHERE id = ?");
    $stmt->execute([$userId]);
    $user = $stmt->fetch(PDO::FETCH_ASSOC);
    
    if ($user) {
        // 寫入快取(設定較短過期時間防止熱點)
        $cache->saveCache($key, $user);
        return $user;
    }
    
    // 查無資料:快取空值防止穿透(短期快取)
    $cache->saveCache($key, null);
    return null;
}

模式 2:標籤式快取失效(簡易版)

class TaggedCache extends XZCache {
    private $tag_index_file;
    
    public function __construct($dir, $time) {
        parent::__construct($dir, $time);
        $this->tag_index_file = $this->cache_dir . '/_tag_index.php';
    }
    
    public function saveWithTags($key, $value, array $tags) {
        parent::saveCache($key, $value);
        
        // 更新標籤索引
        $index = $this->loadTagIndex();
        foreach ($tags as $tag) {
            $index[$tag][$key] = true;
        }
        $this->saveTagIndex($index);
    }
    
    public function invalidateTag($tag) {
        $index = $this->loadTagIndex();
        if (isset($index[$tag])) {
            foreach (array_keys($index[$tag]) as $key) {
                $this->deleteCache($key);
            }
            unset($index[$tag]);
            $this->saveTagIndex($index);
        }
    }
    
    private function loadTagIndex() {
        return is_file($this->tag_index_file) ? include $this->tag_index_file : [];
    }
    
    private function saveTagIndex(array $index) {
        file_put_contents($this->tag_index_file, '

效能與限制說明

優點 缺點/限制
零依賴、部署簡單 單機架構,不支援分散式/多伺服器共享
支援任意可序列化資料 大量小檔案會消耗 inode,需定期清理
原子寫入(LOCK_EX)防競態 高併發讀寫效能不如記憶體快取
檔案系統天然支援 TTL 無原生標籤/群組失效功能(需自行擴充)

生產環境建議

  • 快取目錄掛載至 tmpfs(記憶體檔案系統)大幅提升 IO 效能
  • 設定 cron 定期清理過期檔案,避免殘留佔用磁碟
  • 監控快取命中率,命中率低於 80% 需檢視策略
  • 關鍵資料雙層快取:記憶體(APCu/Redis)+ 檔案備援
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved