本文為《Core PHP Programming》筆記第十三節,詳細講解 PHP 對象序列化機制:serialize()/unserialize()、魔術方法 __sleep()/__wakeup() 控制序列化行為、排除資源屬性、重建連接、版本演進兼容、安全風險與現代替代方案。
<?php
class User {
public $name;
public $id; // 臨時 ID,不需持久化
public function __construct() { $this->id = uniqid(); }
}
$u = new User();
$u->name = "Leon";
$s = serialize($u); // 字符串:O:4:"User":2:{s:4:"name";s:4:"Leon";s:2:"id";s:13:"5f8d7a1b2c3d4";}
$u2 = unserialize($s); // 新對象,id 為序列化時的值
serialize() 返回字節流字符串,包含類名、屬性名值對。unserialize() 重建對象,**不調用構造函數**,直接恢復屬性值。__sleep():自定義序列化屬性class User {
public $name;
public $id; // 臨時會話 ID
public $pdo; // PDO 連接資源,不可序列化
public function __sleep(): array {
// 僅返回需序列化的屬性名數組
return ['name']; // 排除 id、pdo
}
}
$u = new User();
$u->name = "Leon";
$s = serialize($u); // 僅包含 name
$u2 = unserialize($s);
echo $u2->name; // Leon
// $u2->id 為 null (未序列化)
// $u2->pdo 為 null
null)。__sleep,默認序列化所有非靜態屬性。__wakeup():反序列化後重建狀態class User {
public $name;
public $id;
public $pdo;
public function __wakeup() {
// 重新生成臨時 ID
$this->id = uniqid();
// 重建資料庫連接
$this->pdo = new PDO('mysql:host=localhost;dbname=test', 'user', 'pass');
}
}
$u2 = unserialize($s);
// $u2->id 為新生成 ID
// $u2->pdo 為新連接
<?php
class User {
public $name;
public $id;
public function __construct() { $this->id = uniqid(); }
public function __sleep() { return ['name']; }
public function __wakeup() { $this->id = uniqid(); }
}
$u = new User(); $u->name = "Leon";
$s = serialize($u);
$u2 = unserialize($s);
var_dump($u); // id: 舊值
var_dump($u2); // id: 新值
const SERIALIZATION_VERSION = 1;,__sleep 返回包含版本,__wakeup 根據版本遷移數據。__serialize()/__unserialize() 新 API,返回關聯數組,更靈活、避免 __sleep 數組順序問題。unserialize()!攻擊者可構造惡意字節流實例化任意類、觸發 __wakeup/__destruct 鏈(POP 鏈)導致 RCE。unserialize($data, ['allowed_classes' => ['User', 'Product']]) 白名單類(PHP 7.0+)。json_encode/decode)、MessagePack、Protocol Buffers 等純數據格式,無代碼執行風險。| 方案 | 優點 | 缺點 | 適用場景 |
|---|---|---|---|
| serialize/unserialize | 原生、保留類型、對象圖 | 安全風險、跨語言難、版本敏感 | 內部緩存、Session 存儲(受信數據) |
| json_encode/decode | 跨語言、安全、人類可讀、標準 | 無類型信息、無對象行為、循環引用需處理 | API 傳輸、配置存儲、前端交互 |
| igbinary | 二進制、比 serialize 小 50%、快、支持 PHP 類型 | 需擴展、仍有反序列化風險 | 高性能緩存(Redis/Memcached 值) |
| MessagePack/Protobuf | 極致緊湊、跨語言、Schema 演進 | 需定義 Schema、學習成本 | 微服務通信、大數據序列化 |