將 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:"土人'部落"。經過上述流程,實際存入數據庫的字符串長度與序列化時聲明的長度不一致,導致反序列化解析失敗。
// 存入前
$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);
PDO::prepare() / mysqli_stmt_bind_param(),完全避免手動轉義json_encode()/json_decode(),無長度前綴,不怕轉義破壞,跨語言兼容casts 屬性)// 存入
$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 + 預處理語句,是解決此類問題的現代標準答案。