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

PHP json_decode 整型溢出問題:32位系統大整數處理方案

     編輯:PHP 相關知識

問題現象

在 32 位系統上,PHP 整數最大值為 2147483647(PHP_INT_MAX)。當 JSON 包含超過此值的整數時,json_decode() 會發生溢出,導致數值變為負數或浮點數。

<?php
$array = array(
    "id1" => 2147483647,  // PHP_INT_MAX
    "id2" => 2147483648,  // 超過最大值
);
$json = json_encode($array);
$out = json_decode($json, true);
var_dump($out);

/* 32位系統輸出:
array(2) {
  ["id1"]=> int(2147483647)
  ["id2"]=> int(-2147483646)  // 溢出變負數!
}
*/
?>

根本原因

  • PHP 整數範圍依賴作業系統位數:32位系統為 ±2147483647,64位系統為 ±9223372036854775807
  • 直接賦值大數值時 PHP 會自動轉為 float:$x = 2147483648; // float
  • 但 json_decode()(PHP 5.3 前)不做此類檢測,直接按有符號 32 位整數解析,導致溢出

解決方案對比

方案一:升級 PHP ≥ 5.3(推薦,根治)

PHP 5.3+ 已修復此 Bug,大整數會自動解析為 float。現代專案應至少使用 PHP 7.4+。

方案二:JSON 編碼階段將大數值轉為字串

<?php
$array = array(
    "id1" => 2147483647,
    "id2" => 2147483648,
);

// 方案 A:預處理陣列,大數值轉字串
function stringify_bigints(&$value) {
    if (is_int($value) && ($value > PHP_INT_MAX || $value < -PHP_INT_MAX)) {
        $value = (string)$value;
    } elseif (is_array($value)) {
        array_walk_recursive($value, 'stringify_bigints');
    }
}
stringify_bigints($array);

$json = json_encode($array);
// 結果:{"id1":2147483647,"id2":"2147483648"}

$out = json_decode($json, true);
var_dump($out);
/* 輸出:
array(2) {
  ["id1"]=> int(2147483647)
  ["id2"]=> string(10) "2147483648"  // 保持字串,不溢出
}
*/
?>

方案三:正則替換 JSON 字串(原文方案,需謹慎)

<?php
$json = json_encode($array);
// 針對特定欄位名(如 id、uid、snowflake_id)的大數值加引號
$json = preg_replace('/("(?:id|uid|snowflake_id)"\s*:\s*)(\d{10,})/i', '${1}"${2}"', $json);

$out = json_decode($json, true);
?>

風險:正則可能誤匹配非 ID 欄位的大數值,或漏匹配巢狀結構。建議用方案二的遞迴預處理。

方案四:使用 JSON_BIGINT_AS_STRING 旗標(PHP 5.4+)

<?php
// PHP 5.4.0 新增選項
$out = json_decode($json, true, 512, JSON_BIGINT_AS_STRING);
// 或
$out = json_decode($json, true, 512, JSON_BIGINT_AS_STRING | JSON_OBJECT_AS_ARRAY);

var_dump($out);
/* 輸出:
array(2) {
  ["id1"]=> int(2147483647)
  ["id2"]=> string(10) "2147483648"
}
*/

這是最標準、最高效的解決方式,無需預處理,直接在解碼階段處理。

最佳實踐總結

PHP 版本推薦方案備註
PHP 5.4+json_decode($json, true, 512, JSON_BIGINT_AS_STRING)官方旗標,零代碼入侵
PHP 5.3 - 5.3.x預處理陣列轉字串 + json_encode手動處理大數值
PHP 5.2 或更老升級 PHP!安全、效能、功能全面落後

前端對接建議

  • API 回傳大整數(如訂單號、雪花 ID)時,**統一以字串形式輸出**
  • 前端 JavaScript Number.MAX_SAFE_INTEGER = 9007199254740991,超過此值也會精度丟失
  • 規範:{"order_id": "123456789012345678", "amount": 100} —— ID 用字串,金額用數字

總結

32 位系統 json_decode 整型溢出是歷史遺留問題。**現代專案請直接使用 JSON_BIGINT_AS_STRING 旗標**,或統一升級至 64 位 PHP 7.4+ 環境。API 設計階段即約定「大整數走字串」,從源頭消除跨語言精度問題。

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