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

PHP 5 對象序列化:serialize/unserialize、__sleep/__wakeup、資源處理與版本兼容

     編輯:PHP 相關知識

PHP 5 對象序列化:serialize/unserialize、__sleep/__wakeup、資源處理與版本兼容

本文為《Core PHP Programming》筆記第十三節,詳細講解 PHP 對象序列化機制:serialize()/unserialize()、魔術方法 __sleep()/__wakeup() 控制序列化行為、排除資源屬性、重建連接、版本演進兼容、安全風險與現代替代方案。

1. 基礎序列化

<?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() 重建對象,**不調用構造函數**,直接恢復屬性值。
  • 反序列化前**必須已定義類**(自動加載器需生效)。

2. __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,默認序列化所有非靜態屬性。

3. __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 為新連接
  • 反序列化完成後自動調用,**無參數**。
  • 用於:重建資源連接、初始化臨時狀態、觸發事件通知、驗證數據完整性。

4. 完整示例:自包含演示

<?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: 新值

5. 版本兼容與演進

  • PHP 內部序列化格式跨版本相對穩定,但類結構變更(增減屬性、修改訪問修飾符)可能導致反序列化異常。
  • 最佳實踐:在類中定義 const SERIALIZATION_VERSION = 1;,__sleep 返回包含版本,__wakeup 根據版本遷移數據。
  • PHP 7.4+ 支持 __serialize()/__unserialize() 新 API,返回關聯數組,更靈活、避免 __sleep 數組順序問題。

6. 安全風險:反序列化漏洞

  • 絕不對不可信輸入調用 unserialize()!攻擊者可構造惡意字節流實例化任意類、觸發 __wakeup/__destruct 鏈(POP 鏈)導致 RCE。
  • 防護:unserialize($data, ['allowed_classes' => ['User', 'Product']]) 白名單類(PHP 7.0+)。
  • 替代方案:使用 JSON (json_encode/decode)、MessagePack、Protocol Buffers 等純數據格式,無代碼執行風險。

7. 現代替代方案對比

總結

__sleep/__wakeup 提供細粒度序列化控制,解決資源屬性持久化難題。但現代架構傾向於**無狀態服務 + 外部存儲 (Redis/DB)**,減少對象序列化依賴。若必須使用,嚴格限制允許類白名單,並考慮遷移至 JSON/igbinary。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved
方案優點缺點適用場景
serialize/unserialize原生、保留類型、對象圖安全風險、跨語言難、版本敏感內部緩存、Session 存儲(受信數據)
json_encode/decode跨語言、安全、人類可讀、標準無類型信息、無對象行為、循環引用需處理API 傳輸、配置存儲、前端交互
igbinary二進制、比 serialize 小 50%、快、支持 PHP 類型需擴展、仍有反序列化風險高性能緩存(Redis/Memcached 值)
MessagePack/Protobuf極致緊湊、跨語言、Schema 演進需定義 Schema、學習成本微服務通信、大數據序列化