unserialize() 失敗常見於序列化資料在不同編碼環境間流轉(如 Latin1 資料庫欄位存 UTF-8 字串),導致字串長度標註 (s:長度) 與實際位元組長度不符。本文分析原因並提供兩個修復函數 mb_unserialize (UTF-8) 與 asc_unserialize (ASCII),利用正則重新計算長度並移除 \r。
$str = 'a:9:{s:4:"time";i:1405306402;s:4:"name";s:6:"新晨";s:5:"url";s:1:"-";s:4:"word";s:1:"-";s:5:"rpage";s:29:"https://www.baidu.com/test.html";s:5:"cpage";s:1:"-";s:2:"ip";s:15:"117.151.180.150";s:7:"ip_city";s:31:"中國北京市 北京市移動";s:4:"miao";s:1:"5";}';
var_dump(unserialize($str)); // bool(false)
// 實際長度:
// "新晨" UTF-8 佔 6 位元組,但標註 s:6 → 正確
// "https://www.baidu.com/test.html" 實際 30 字元,標註 s:29 → 錯誤
// "中國北京市 北京市移動" UTF-8 佔 31 位元組,標註 s:31 → 正確
// UTF-8 環境
function mb_unserialize($serial_str) {
$serial_str = preg_replace('!s:(\d+):"(.*?)";!se', "'s:'.strlen('$2').':\"$2\";'", $serial_str);
$serial_str = str_replace("\r", "", $serial_str);
return unserialize($serial_str);
}
// ASCII/單位元組編碼環境
function asc_unserialize($serial_str) {
$serial_str = preg_replace('!s:(\d+):"(.*?)";!se', '"s:".strlen("$2").":\"$2\";"', $serial_str);
$serial_str = str_replace("\r", "", $serial_str);
return unserialize($serial_str);
}
header('Content-Type: text/html; charset=utf-8');
$bad = 'a:9:{s:4:"time";i:1405306402;s:4:"name";s:6:"新晨";s:5:"url";s:1:"-";s:4:"word";s:1:"-";s:5:"rpage";s:29:"https://www.baidu.com/test.html";s:5:"cpage";s:1:"-";s:2:"ip";s:15:"117.151.180.150";s:7:"ip_city";s:31:"中國北京市 北京市移動";s:4:"miao";s:1:"5";}';
var_dump(unserialize($bad)); // bool(false)
var_dump(mb_unserialize($bad)); // 正確陣列
/* 輸出:
array(9) {
["time"]=> int(1405306402)
["name"]=> string(6) "新晨"
["url"]=> string(1) "-"
["word"]=> string(1) "-"
["rpage"]=> string(30) "https://www.baidu.com/test.html"
["cpage"]=> string(1) "-"
["ip"]=> string(15) "117.151.180.150"
["ip_city"]=> string(31) "中國北京市 北京市移動"
["miao"]=> string(1) "5"
}
*/
s:(\d+):"(.*?)"; 匹配字串欄位,以實際字串內容 $2 的 strlen() 重新計算位元組長度並重建標註。e 修飾符:PHP 5.5+ 棄用,7.0+ 移除。現代版本請改用 preg_replace_callback:function mb_unserialize_safe($str) {
$str = preg_replace_callback('!s:(\d+):"(.*?)";!s', function($m) {
return 's:'.strlen($m[2]).':"'.$m[2].'";';
}, $str);
$str = str_replace("\r", "", $str);
return @unserialize($str);
}
| 原因 | 現象 | 處理 |
|---|---|---|
| 單雙引號未轉義 | 字串中含 " 截斷序列化結構 | 序列化前 addslashes 或改用 JSON |
Null 字元 \0 | C 字串結束符導致長度計算偏差 | 序列化前 str_replace("\0", "", $data) |
\r\n 換行 | Windows 換行符被計入長度 | str_replace(["\r","\n"], "", $str) |
| 物件類別不存在 | __PHP_Incomplete_Class | 反序列化前 require 類別定義 |
json_encode/decode,跨語言、無編碼長度問題、可讀性佳。utf8mb4,避免 Latin1/UTF-8 混用。igbinary 高效二進位序列化。完整測試程式碼可至本站下載。遇到舊系統 unserialize 失敗時,上述修復函數可作為緊急相容方案。