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

PHP 檔案快取類實作:支援目錄自動建立、序列化儲存、過期時間、檔案鎖併發安全與全量清理

     編輯:PHP 相關知識

類別設計目標

  • 輕量、無依賴、單檔案即用。
  • 支援多實例、自定義快取目錄。
  • 原子寫入 (flock LOCK_EX) 避免併發損壞。
  • 內建過期機制 (分鐘精度)。
  • 提供 clearAll() 一鍵清空。

完整代碼 (PSR-4 友善、型別提示)

<?php
namespace App\Cache;

class FileCache {
    private string $directory;
    private string $keySalt;
    
    public function __construct(string $directory = '', string $keySalt = 'cache_salt_v1') {
        $this->keySalt = $keySalt;
        if ($directory) $this->setDirectory($directory);
    }
    
    public function setDirectory(string $path): void {
        $this->directory = rtrim($path, '/') . '/';
        $this->ensureDirectory($this->directory);
    }
    
    /** 讀取快取,未命中或過期回傳 false */
    public function get(string $key, int $ttlMinutes = 1): mixed {
        $file = $this->buildPath($key);
        if (!is_file($file)) return false;
        
        $data = @unserialize(file_get_contents($file));
        if (!$data || !isset($data['time'], $data['value'])) return false;
        
        if (time() - $data['time'] > $ttlMinutes * 60) {
            @unlink($file); // 順手清理過期檔
            return false;
        }
        return $data['value'];
    }
    
    /** 寫入快取,回傳 bool */
    public function set(string $key, mixed $value): bool {
        $file = $this->buildPath($key);
        $fp = fopen($file, 'cb'); // 'c' 不截斷, 'b' 二進位
        if (!$fp) return false;
        
        if (flock($fp, LOCK_EX)) {
            $payload = serialize(['value' => $value, 'time' => time()]);
            $written = fwrite($fp, $payload);
            ftruncate($fp, $written); // 確保無殘留
            flock($fp, LOCK_UN);
            fclose($fp);
            return $written !== false;
        }
        fclose($fp);
        return false;
    }
    
    /** 刪除單筆 */
    public function delete(string $key): bool {
        $file = $this->buildPath($key);
        return is_file($file) && @unlink($file);
    }
    
    /** 清空整個快取目錄 */
    public function clearAll(): void {
        $this->rrmdir($this->directory);
        $this->ensureDirectory($this->directory);
    }
    
    private function buildPath(string $key): string {
        $hash = md5($key . $this->keySalt);
        return $this->directory . $key . '_' . $hash . '.cache';
    }
    
    private function ensureDirectory(string $path): void {
        if (!is_dir($path)) {
            if (!mkdir($path, 0777, true) && !is_dir($path)) {
                throw new \RuntimeException("無法建立快取目錄: $path");
            }
        }
    }
    
    private function rrmdir(string $dir): void {
        if (!is_dir($dir)) return;
        $items = new RecursiveIteratorIterator(
            new RecursiveDirectoryIterator($dir, FilesystemIterator::SKIP_DOTS),
            RecursiveIteratorIterator::CHILD_FIRST
        );
        foreach ($items as $item) {
            $item->isDir() ? rmdir($item->getPathname()) : unlink($item->getPathname());
        }
        rmdir($dir);
    }
}

// 使用範例
$cache = new FileCache(__DIR__ . '/storage/cache', 'my_app_v2');
$data = $cache->get('homepage_stats', 5); // 5 分鐘快取
if ($data === false) {
    $data = ['pv' => 12345, 'uv' => 6789, 'updated' => date('c')];
    $cache->set('homepage_stats', $data);
}
var_dump($data);

進階擴充建議

  • 標籤/分組失效:維護 tag -> [keys] 映射檔,invalidateTag('user_123') 批量刪除相關鍵。
  • 防止快取穿透:getOrSet($key, $callable, $ttl) 單飛模式,併發時僅一個請求執行 $callable,其餘等待結果。
  • 效能優化:高頻熱鍵改用 Redis/Memcached,本類適合中低頻、結構化、需持久化的資料 (設定檔、報表、第三方 API 回應)。
  • 序列化替代:igbinary_serialize (更小、更快) 或 json_encode (跨語言、可讀),需注意資料類型還原 (JSON 無資源型別)。

生產環境部署注意

  1. 快取目錄**不可**在 Web 公開根目錄下,防止直接下載 .cache 洩露敏感資料。
  2. 設定 open_basedir 限制 PHP 僅能讀寫指定目錄。
  3. 定期清理過期檔 (cron 執行 find /cache -name '*.cache' -mmin +1440 -delete) 或改用 tmpfs 掛載快取目錄 (重啟自動清空、極速 IO)。
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved