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

PHP 物件序列化深度解析:__sleep 與 __wakeup 魔術方法控制序列化行為

     編輯:PHP 綜合知識

前言:什麼是物件序列化?

序列化將物件 (含屬性與方法) 轉換為連續位元組字串,可儲存於檔案、資料庫或網路傳輸。反序列化則還原為記憶體中完整的物件。PHP 透過 serialize() 與 unserialize() 實作,並提供 __sleep()、__wakeup() 兩個魔術方法精細控制過程。

核心機制

魔術方法觸發時機職責回傳值
__sleep()serialize($obj) 呼叫前決定哪些屬性需序列化、關閉資源、提交交易等清理工作必須回傳字串陣列,列出要序列化的屬性名稱;未列入者將被捨棄
__wakeup()unserialize($str) 完成後重建資源連線、初始化臨時屬性、解密資料等還原工作無回傳值

完整範例:排除臨時 ID 屬性

<?php
class User
{
    public $name;
    public $id; // 臨時屬性,不需持久化

    public function __construct()
    {
        $this->id = uniqid(); // 建立唯一暫時 ID
    }

    // 序列化前:明確宣告只儲存 name
    public function __sleep()
    {
        return ["name"]; // id 將不被序列化
    }

    // 反序列化後:重新產生新的唯一 ID
    public function __wakeup()
    {
        $this->id = uniqid();
    }
}

// 建立物件
$u = new User();
$u->name = "Leon";

// 序列化 (id 被捨棄)
$s = serialize($u);
echo "序列化字串: $s\n";
// 輸出: O:4:"User":1:{s:4:"name";s:4:"Leon";}

// 反序列化 (id 重新產生)
$u2 = unserialize($s);

// 驗證
print_r($u);
print_r($u2);
/*
輸出差異:
User Object ( [name] => Leon [id] => 5f3a1b2c... )  // 原物件
User Object ( [name] => Leon [id] => 5f3a1d9e... )  // 新物件,id 不同
*/
?>

實務應用場景

1. 資料庫連線資源 (PDO/MySQLi)

class Database
{
    private $pdo;
    private $dsn, $user, $pass;

    public function __construct($dsn, $user, $pass)
    {
        $this->dsn = $dsn; $this->user = $user; $this->pass = $pass;
        $this->connect();
    }

    private function connect()
    {
        $this->pdo = new PDO($this->dsn, $this->user, $this->pass);
    }

    public function __sleep()
    {
        // PDO 物件無法序列化,只儲存連線參數
        return ["dsn", "user", "pass"];
    }

    public function __wakeup()
    {
        // 反序列化後自動重連
        $this->connect();
    }
}

2. 機密資料加密儲存

class Credentials
{
    public $username;
    private $encryptedPassword;

    public function setPassword($plain)
    {
        $this->encryptedPassword = openssl_encrypt($plain, 'AES-256-CBC', ENCRYPTION_KEY);
    }

    public function __sleep()
    {
        return ["username", "encryptedPassword"]; // 明文密碼永不序列化
    }

    public function __wakeup()
    {
        // 解密邏輯可在這裡或 getter 中實作
    }
}

重要注意事項

  • 類別定義必須存在: 反序列化時,類別定義必須已載入 (建議配合 spl_autoload_register),否則會產生 __PHP_Incomplete_Class。
  • 存取控制不影響序列化: private/protected 屬性同樣會被序列化 (名稱會經過 name mangling),__sleep 回傳陣列時使用原始屬性名即可。
  • 父類別 __sleep: 子類別實作 __sleep 時,需自行包含父類別屬性,或呼叫 parent::__sleep() 合併陣列。
  • 效能考量: 大型物件圖序列化耗時且產生巨大字串,避免在高頻路徑 (如 Session 儲存大物件) 使用。
  • 安全風險: 絕不可 對不可信輸入呼叫 unserialize(),可能導致物件注入攻擊 (POP Chain)。PHP 7+ 提供 allowed_classes 選項限制可還原類別。

現代替代方案

  • JSON 序列化: 實作 JsonSerializable 介面,json_encode() 更安全、跨語言、人類可讀。
  • igbinary / msgpack: 高效能二進位序列化擴展,支援 PHP 7/8,體積更小、速度更快。
  • DTO + 手動轉換: 領域模型與傳輸物件分離,明確控制資料流向。

結語

__sleep 與 __wakeup 賦予 PHP 物件序列化精細控制力,是處理資源管理、敏感資料保護、臨時狀態排除的關鍵工具。但需謹記安全風險,現代架構建議優先考慮 JSON 或專用序列化庫,僅在必要時使用原生序列化。

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