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

PHP unserialize 返回 false 解決方案:編碼不一致、長度錯誤、特殊字元處理

     編輯:PHP 綜合知識

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 字元 \0C 字串結束符導致長度計算偏差序列化前 str_replace("\0", "", $data)
\r\n 換行Windows 換行符被計入長度str_replace(["\r","\n"], "", $str)
物件類別不存在__PHP_Incomplete_Class反序列化前 require 類別定義

最佳實務建議

  • 棄用 serialize:新專案一律使用 json_encode/decode,跨語言、無編碼長度問題、可讀性佳。
  • 資料庫欄位:統一使用 utf8mb4,避免 Latin1/UTF-8 混用。
  • 快取層:Redis/Memcached 直接存 JSON 字串,或使用 igbinary 高效二進位序列化。

完整測試程式碼可至本站下載。遇到舊系統 unserialize 失敗時,上述修復函數可作為緊急相容方案。

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