PHP 檔案快取常見三種資料序列化格式:serialize、json_encode、var_export。本文分析三者優劣、實測效能差異,並提供一個完整的檔案快取驅動類別實作。
| 格式 | 序列化函數 | 反序列化函數 | 特點 | 效能排序 |
|---|---|---|---|---|
| PHP 原生序列化 | serialize() | unserialize() | 支援所有 PHP 型別 (含物件、資源)、二進位安全、PHP 專用 | ⭐⭐⭐ 最快 |
| JSON | json_encode() | json_decode() | 跨語言通用、可讀性好、不支援資源/物件私有屬性 | ⭐⭐ 中等 |
| PHP 程式碼 | var_export() | include/require | 可讀性最好、可直接作為 PHP 檔案載入、需 eval 或 include 解析 | ⭐ 最慢 |
serialize/unserialize:~0.8 秒 (寫入/讀取總和)json_encode/decode:~1.2 秒var_export + include:~2.5 秒 (含 opcode 解析)結論:純 PHP 環境下,serialize 綜合效能最佳,建議作為檔案快取預設格式。
<?php
class Cache_Driver {
protected $_cachePath;
public function __construct($config = array()) {
if (isset($config['cache_path'])) {
$this->_cachePath = rtrim($config['cache_path'], '/') . '/';
} else {
$this->_cachePath = realpath(dirname(__FILE__)) . '/cache/';
}
// 確保目錄存在
if (!is_dir($this->_cachePath)) {
mkdir($this->_cachePath, 0755, true);
}
}
/**
* 讀取快取
* @param string $id 快取鍵 (檔案名)
* @return mixed|false 資料或 false (不存在/過期/損壞)
*/
public function get($id) {
$file = $this->_cachePath . $id;
if (!file_exists($file)) return false;
$data = @file_get_contents($file);
if ($data === false) return false;
$data = @unserialize($data);
if (!is_array($data)) return false;
// 結構驗證
if (!isset($data['time'], $data['ttl'], $data['data'])) return false;
// 過期檢查 (ttl=0 表示永不過期)
if ($data['ttl'] > 0 && time() > $data['time'] + $data['ttl']) {
@unlink($file);
return false;
}
return $data['data'];
}
/**
* 寫入快取
* @param string $id 快取鍵
* @param mixed $data 任意可序列化資料
* @param int $ttl 存活秒數 (0=永久)
* @return bool
*/
public function set($id, $data, $ttl = 60) {
$contents = array(
'time' => time(),
'ttl' => $ttl,
'data' => $data,
);
$serialized = serialize($contents);
$file = $this->_cachePath . $id;
if (@file_put_contents($file, $serialized) !== false) {
@chmod($file, 0644);
return true;
}
return false;
}
/**
* 刪除快取
*/
public function delete($id) {
$file = $this->_cachePath . $id;
return @unlink($file);
}
/**
* 清空所有快取
*/
public function clean() {
$dh = @opendir($this->_cachePath);
if (!$dh) return false;
while (($file = @readdir($dh)) !== false) {
if ($file === '.' || $file === '..') continue;
$path = $this->_cachePath . $file;
if (is_file($path)) @unlink($path);
}
@closedir($dh);
return true;
}
}
?>
<?php
require_once 'Cache_Driver.php';
$cache = new Cache_Driver(array('cache_path' => '/tmp/myapp_cache/'));
// 寫入快取 (TTL 300 秒)
$userData = array('id'=>1, 'name'=>'Admin', 'roles'=>array('admin','editor'));
$cache->set('user_1', $userData, 300);
// 讀取快取
$cached = $cache->get('user_1');
if ($cached !== false) {
echo "快取命中: "; print_r($cached);
} else {
echo "快取未命中,查詢資料庫...";
// $data = $db->query(...);
// $cache->set('user_1', $data, 300);
}
// 刪除快取
$cache->delete('user_1');
?>
md5('prefix_' . $id) 避免檔名過長/特殊字元rename() 覆蓋,避免並發讀取到半寫入資料cache/a/b/abcdef... 避免單目錄檔案過多影響檔案系統效能unserialize() 可能觸發物件注入 (PHP 7+ allowed_classes 參數限制)需要跨語言共享快取? → 是 → json_encode 純 PHP 內部快取? → 是 → serialize (預設) 需人工閱讀/除錯快取檔? → 是 → var_export (僅開發環境)
選擇正確的序列化格式並封裝為可複用的驅動類別,是建構高效能 PHP 應用的基礎功夫。隨著流量增長,平滑遷移至 Redis/Memcached 才是長久之計。