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

PHP unserialize 失敗排查:magic_quotes_gpc 與數據庫轉義導致的序列化數據損壞

     編輯:PHP 綜合知識

序列化數據入庫後反序列化失敗?根因是雙重轉義導致數據損壞

將 PHP 數組 serialize() 後存入 MySQL,讀出時 unserialize() 報錯或數據錯誤,是經典坑。本文還原問題場景,分析 magic_quotes_gpc、addslashes、MySQL 轉義三重疊加導致的數據變異,給出三種解決方案。

問題還原場景

$message = [
    'uid' => 1,
    'nickname' => '土豆男',
    'sitename' => "土人'部落",  // 包含單引號
    // ... 其他字段
];

數據流向與變異分析

階段操作數據狀態說明
1. 表單提交magic_quotes_gpc=On 自動 addslashes土人\'部落單引號被轉義
2. 入庫前代碼再次 addslashes(雙重轉義)土人\\'部落反斜杠被轉義
3. MySQL 存儲MySQL 解析轉義字符土人\'部落MySQL 只存實際字符,反斜杠不入庫
4. 查詢讀出無轉義,直接返回土人'部落還原為原始單引號
5. 反序列化unserialize()失敗序列化格式被破壞,長度不匹配

根本原因

serialize() 格式中包含字符串長度標識,如 s:12:"土人'部落"。經過上述流程,實際存入數據庫的字符串長度與序列化時聲明的長度不一致,導致反序列化解析失敗。

三種解決方案對比

方案一:拆表存儲(徹底但改動大)

  • 將數組每個鍵值拆分為獨立字段
  • 優點:避免序列化問題,利於查詢索引
  • 缺點:系統已上線,改動巨大,需遷移數據、修改大量查詢
  • 適用:重構期、新模塊設計階段

方案二:雙重 addslashes + 反序列化後 stripslashes(推薦快速修復)

// 存入前
$serialized = serialize($data);
$escaped = addslashes($serialized);  // 額外轉義
// 執行 INSERT...

// 讀出後
$raw = $row['data'];  // 已被 MySQL 還原一次
$data = unserialize($raw);  // 此時可能含殘留反斜杠
// 遞歸 stripslashes 數組所有字符串值
function recursive_stripslashes(&$val) {
    if (is_array($val)) {
        foreach ($val as &$v) recursive_stripslashes($v);
    } elseif (is_string($val)) {
        $val = stripslashes($val);
    }
}
recursive_stripslashes($data);
  • 優點:不改表結構,代碼改動小,快速上線
  • 缺點:邏輯繞彎,需確保所有讀寫路徑一致

方案三:輸入過濾禁止特殊字符(治標不治本)

  • 前端/後端攔截單引號、雙引號、反斜杠
  • 缺點:損害用戶體驗(如人名、地址合法包含引號),不推薦

最佳實踐建議(現代 PHP)

  1. 關閉 magic_quotes_gpc(PHP 5.4+ 已移除該特性)
  2. 使用預處理語句:PDO::prepare() / mysqli_stmt_bind_param(),完全避免手動轉義
  3. 序列化改用 JSON:json_encode()/json_decode(),無長度前綴,不怕轉義破壞,跨語言兼容
  4. 或使用成熟 ORM:自動處理序列化/反序列化(如 Laravel 的 casts 屬性)

遷移到 JSON 示例

// 存入
$data_json = json_encode($data, JSON_UNESCAPED_UNICODE);
$stmt = $pdo->prepare("INSERT INTO config (data) VALUES (?)");
$stmt->execute([$data_json]);

// 讀出
$stmt = $pdo->prepare("SELECT data FROM config WHERE id=?");
$stmt->execute([$id]);
$data = json_decode($stmt->fetchColumn(), true);

拋棄 serialize() 改用 JSON + 預處理語句,是解決此類問題的現代標準答案。

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