序列化是將資料結構轉為可儲存/傳輸字串的過程,反序列化則是還原。PHP 內建 serialize() / unserialize() 是核心函式,本文深入解析其運作細節、常見陷阱與解決方案。
<?php
$user = ['Moe', 'Larry', 'Curly'];
// 序列化
$serialized = serialize($user);
// a:3:{i:0;s:3:"Moe";i:1;s:5:"Larry";i:2;s:5:"Curly";}
// 反序列化
$restored = unserialize($serialized);
// ['Moe', 'Larry', 'Curly']
?>
| 格式 | 意義 | 範例 |
|---|---|---|
a:size:{...} | 陣列 | a:2:{i:0;s:3:"foo";i:1;s:3:"bar";} |
O:len:"class":size:{...} | 物件 | O:8:"stdClass":1:{s:3:"foo";s:3:"bar";} |
s:len:"value" | 字串 | s:5:"hello" |
i:value | 整數 | i:42 |
d:value | 浮點數 | d:3.14 |
b:value | 布林 | b:1 |
N | NULL | N |
r:id | 參考 | r:1 |
<?php $data = ['name' => '張三', 'desc' => '說明: "引號"']; $serialized = serialize($data); // 反序列化可能失敗或資料損壞 ?>
解決方案:Base64 編碼包裝
function safe_serialize($data) {
return base64_encode(serialize($data));
}
function safe_unserialize($str) {
return unserialize(base64_decode($str));
}
// 進階:加上壓縮節省空間
function my_serialize($data) {
return base64_encode(gzcompress(serialize($data), 9));
}
function my_unserialize($str) {
return unserialize(gzuncompress(base64_decode($str)));
}
<?php
// 檔案 A:序列化物件
class User { public $name = 'John'; }
$obj = new User();
file_put_contents('user.dat', serialize($obj));
// 檔案 B:反序列化(未引入 User 類別)
$data = file_get_contents('user.dat');
$obj = unserialize($data);
// $obj 變成 __PHP_Incomplete_Class,無法呼叫方法
?>
解決:
require_once 類別定義__sleep() / __wakeup() 控制序列化行為spl_autoload_register() 自動載入unserialize() 第二參數 ['allowed_classes' => ['User', 'Order']](PHP 7.0+)絕不可 對不可信輸入(如 $_GET、$_COOKIE、Redis 快取)直接呼叫 unserialize(),攻擊者可構造惡意物件觸發 __wakeup()、__destruct() 等魔術方法導致 RCE。
// 危險範例 $data = unserialize($_COOKIE['data']); // 絕對禁止! // 安全做法:改用 JSON $data = json_decode($_COOKIE['data'], true); // 若必須用 serialize,嚴格限制允許類別 $data = unserialize($trusted_data, ['allowed_classes' => ['MyApp\\DTO\\User']]);
| 方法 | 速度 | 體積 | 可讀性 | 支援類型 |
|---|---|---|---|---|
| serialize/unserialize | 快 | 中 | 低 | 所有 PHP 型別(含物件、資源) |
| json_encode/decode | 中 | 小 | 高 | 陣列、物件、純量(無資源、循環引用) |
| var_export + include | 慢 | 大 | 高 | 陣列、物件(無資源、閉包) |
| igbinary (擴充) | 最快 | 最小 | 低 | 所有型別,二進位格式 |
| msgpack (擴充) | 極快 | 極小 | 低 | 跨語言相容 |
session.serialize_handler = igbinaryvar_export 產出 PHP 陣列檔,include 直接載入,OPcache 可加速igbinary 或 msgpack 擴充<?php
class User implements Serializable {
public $id;
public $name;
private $password; // 不序列化敏感欄位
public function serialize() {
return serialize(['id' => $this->id, 'name' => $this->name]);
}
public function unserialize($data) {
$arr = unserialize($data);
$this->id = $arr['id'];
$this->name = $arr['name'];
$this->password = null; // 重新登入時設定
}
}
?>
掌握序列化細節,能避免許多難以除錯的問題,也能在效能與安全之間取得最佳平衡。