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

PHP 檔案快取工具函式:讀寫刪除、序列化、原子操作與防禦性設計

     編輯:PHP 相關知識

核心需求

  • 支援任意 PHP 變數 (陣列、物件、標量) 快取。
  • 原子寫入 (避免並發讀到半寫入檔案)。
  • 自動序列化/反序列化、過期時間、標籤/群組清除 (可選)。

健壯實作 (PHP 8.0+、嚴格型別、PSR-16 相容介面)

<?php
declare(strict_types=1);

class FileCache implements \Psr\SimpleCache\CacheInterface {
    private string $directory;
    private int $defaultTtl;

    public function __construct(string $directory, int $defaultTtl = 3600) {
        $this->directory = rtrim($directory, '/');
        $this->defaultTtl = $defaultTtl;
        if (!is_dir($this->directory)) {
            mkdir($this->directory, 0750, true);
        }
    }

    private function getFilePath(string $key): string {
        // Key 安全化:僅允許字母數字底線,避免目錄穿越
        $safeKey = preg_replace('/[^a-zA-Z0-9_]/', '_', $key);
        return $this->directory . '/' . $safeKey . '.cache.php';
    }

    private function serializeData(mixed $data, int $ttl): string {
        $payload = [
            'data' => $data,
            'expire' => $ttl > 0 ? time() + $ttl : 0, // 0 = 永久
            'created' => time(),
        ];
        // 使用 var_export 產生合法 PHP 代碼,效能優于 serialize/igbinary
        $code = '<?php return ' . var_export($payload, true) . '; ?>';
        return $code;
    }

    public function get(string $key, mixed $default = null): mixed {
        $file = $this->getFilePath($key);
        if (!is_file($file)) return $default;

        $payload = include $file; // 直接 include 回傳陣列,極快
        if (!is_array($payload) || !isset($payload['data'], $payload['expire'])) {
            $this->delete($key); // 損壞檔案自動清理
            return $default;
        }
        if ($payload['expire'] > 0 && time() > $payload['expire']) {
            $this->delete($key);
            return $default;
        }
        return $payload['data'];
    }

    public function set(string $key, mixed $value, int $ttl = null): bool {
        $ttl = $ttl ?? $this->defaultTtl;
        $file = $this->getFilePath($key);
        $tmpFile = $file . '.tmp.' . random_bytes(8); // 隨機暫存檔名

        $content = $this->serializeData($value, $ttl);
        // 原子寫入:寫暫存檔 → rename (同檔案系統保證原子性)
        if (file_put_contents($tmpFile, $content) === false) return false;
        if (!@rename($tmpFile, $file)) {
            @unlink($tmpFile);
            return false;
        }
        @chmod($file, 0640);
        return true;
    }

    public function delete(string $key): bool {
        $file = $this->getFilePath($key);
        return is_file($file) && @unlink($file);
    }

    public function clear(): bool {
        $files = glob($this->directory . '/*.cache.php');
        $ok = true;
        foreach ($files as $f) { if (!@unlink($f)) $ok = false; }
        return $ok;
    }

    // PSR-16 其他介面方法 (getMultiple, setMultiple, deleteMultiple, has, hasMultiple) 略...
}
?>

關鍵設計亮點

特性實作方式效益
原子寫入暫存檔 + `rename()`並發讀寫不會讀到損壞內容
安全 Key`preg_replace('/[^a-zA-Z0-9_]/', '_', $key)`防目錄穿越 (`../`)、注入
高效序列化`var_export` + `include`OPcache 可緩存編譯後 opcode,比 `unserialize` 快 2-5 倍
自動過期讀取時檢查 `expire` 時間戳無需背景 GC,懶惰清理
防禦性讀取`include` 失敗/格式錯誤自動 `delete`自癒,避免髒資料殘留

使用範例

<?php
$cache = new FileCache('/var/www/cache', 1800);

// 寫入
$cache->set('user:123', ['name' => 'John', 'roles' => ['admin']], 3600);

// 讀取
$user = $cache->get('user:123'); // 陣列或 null

// 刪除
$cache->delete('user:123');

// 批次 (PSR-16)
$cache->setMultiple(['a'=>1, 'b'=>2]);
$vals = $cache->getMultiple(['a','b']); // ['a'=>1, 'b'=>2]
?>

生產環境擴充建議

  1. 標籤/群組清除:維護 `tag => [keys]` 映射檔,`deleteByTag('user')` 批次刪除。
  2. 分散式部署:掛載共享儲存 (NFS/EFS) 或改用 Redis/Memcached (相容 PSR-16 介面無縫切換)。
  3. 監控:統計命中率、檔案數、目錄大小、寫入延遲。
  4. 權限隔離:快取目錄僅 PHP-FPM 使用者可讀寫 (750/640),防止其他服務竄改。

總結

檔案快取適合中小型專案、單機部署、開發環境。核心在於 **原子寫入 (rename)、安全 Key、高效序列化 (var_export+include)、懶惰過期清理**。實作 PSR-16 介面可無縫升級至 Redis/Memcached,架構更彈性。

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