PHP 反序列化 unserialize() 與自動載入 spl_autoload_register() 看似獨立,實則存在微妙交互。當反序列化的類定義不存在時,PHP 會產生 __PHP_Incomplete_Class;若此時啟用了 Autoload,可能觸發非預期的類載入邏輯,導致錯誤或效能問題。本文深度剖析機制並提供三種解決方案。
<?php
$string = 'O:6:"Foobar":2:{s:3:"foo";s:1:"1";s:3:"bar";s:1:"2";}';
$result = unserialize($string);
var_dump($result);
/*
object(__PHP_Incomplete_Class)#1 (3) {
["__PHP_Incomplete_Class_Name"]=> string(6) "Foobar"
["foo"]=> string(1) "1"
["bar"]=> string(1) "2"
}
*/
若嘗試存取屬性 $result->foo,會拋出錯誤:
The script tried to execute a method or access a property of an incomplete object. Please ensure that the class definition of the object you are trying to operate on was loaded before unserialize() gets called or provide a __autoload() function to load the class definition.
$arr = (array) unserialize($string);
// 可正常存取 $arr['foo'], $arr['bar']
但會丟失物件型別與方法,僅適合單純資料讀取場景。
若專案註冊了 Autoload:
<?php
spl_autoload_register(function($class) {
var_dump($class); // 會被觸發!
include "/path/to/{$class}.php";
});
$string = 'O:6:"Foobar":2:{s:3:"foo";s:1:"1";s:3:"bar";s:1:"2";}';
$result = (array) unserialize($string); // 觸發 Autoload 嘗試載入 Foobar
關鍵點:即使強制轉型為陣列,PHP 內部仍會嘗試實例化物件,進而觸發 Autoload。若 Autoload 邏輯不當(如路徑錯誤、拋出異常),會導致反序列化失敗。
class Foobar {} // 定義空類,滿足反序列化需求
$result = (array) unserialize($string);
<?php
spl_autoload_register(function($class) {
include "/path/to/{$class}.php";
});
$string = 'O:6:"Foobar":2:{s:3:"foo";s:1:"1";s:3:"bar";s:1:"2";}';
// 1. 備份並卸載所有 autoload 函數
$loaders = spl_autoload_functions();
foreach ($loaders as $loader) {
spl_autoload_unregister($loader);
}
// 2. 安全反序列化
$result = (array) unserialize($string);
// 3. 恢復 autoload
foreach ($loaders as $loader) {
spl_autoload_register($loader);
}
var_dump($result); // 正常輸出陣列,未觸發 autoload
unserialize_callback_func(官方機制)<?php
ini_set('unserialize_callback_func', 'my_autoloader');
function my_autoloader($class) {
// 僅在反序列化缺失類時被呼叫,不影響一般 autoload
if (file_exists("/path/to/{$class}.php")) {
include "/path/to/{$class}.php";
}
}
$string = 'O:6:"Foobar":2:{s:3:"foo";s:1:"1";s:3:"bar";s:1:"2";}';
$result = unserialize($string); // 若找到類定義,回傳完整物件;否則回傳 Incomplete_Class
spl_autoload_registerjson_encode($obj->toArray()))。Serializable 介面,精確控制序列化內容與版本號。__PHP_Incomplete_Class 產生頻率,作為代碼部署一致性指標。PHP 反序列化流程簡述:
Foobarclass_exists('Foobar', false))unserialize_callback_func → 呼叫回調嘗試載入spl_autoload_register → 觸發 autoload 堆疊__PHP_Incomplete_Class理解此流程,才能在複雜自動載入環境下游刃有餘。