php.net |  support |  documentation |  report a bug |  advanced search |  search howto |  statistics |  random bug |  login
Bug #66707 big int conversion on json_encode/json_decode
Submitted: 2014-02-13 11:07 UTC Modified: 2014-02-13 11:38 UTC
From: sergey dot shilko at gmail dot com Assigned:
Status: Not a bug Package: JSON related
PHP Version: 5.5.7 OS: Linux
Private report: No CVE-ID: None
Welcome back! If you're the original bug submitter, here's where you can edit the bug or add additional notes.
If you forgot your password, you can retrieve your password here.
Password:
Status:
Package:
Bug Type:
Summary:
From: sergey dot shilko at gmail dot com
New email:
PHP Version: OS:

 

 [2014-02-13 11:07 UTC] sergey dot shilko at gmail dot com
Description:
------------
http://php.net/json_encode
http://php.net/json_decode

json_decode(json_encode(A)) != A

json_encode + json_decode breaks large integers.



Test script:
---------------
<?php

$facebookIds = array(48572380745623087456, '48572380745623087456');
$encoded = json_encode($facebookIds);
$decoded = json_decode($encoded);

var_dump($facebookIds);
var_dump($decoded);

if ($facebookIds != $decoded) {
        echo "\nBroken\n";
}



Patches

Pull Requests

History

AllCommentsChangesGit/SVN commitsRelated reports
 [2014-02-13 11:12 UTC] sergey dot shilko at gmail dot com
-Summary: Int to float conversion on json_encode +Summary: big int conversion on json_encode/json_decode -PHP Version: 5.5.9 +PHP Version: 5.5.7
 [2014-02-13 11:12 UTC] sergey dot shilko at gmail dot com
#$decoded = json_decode($encoded, false, 512, JSON_BIGINT_AS_STRING);
Makes no difference.


Test script output:

array(2) {
  [0]=>
  float(4.8572380745623E+19)
  [1]=>
  string(20) "48572380745623087456"
}
array(2) {
  [0]=>
  float(4.8572380745623E+19)
  [1]=>
  string(20) "48572380745623087456"
}

Broken
 [2014-02-13 11:19 UTC] sergey dot shilko at gmail dot com
Trying to use:
$encoded = json_encode($facebookIds, JSON_NUMERIC_CHECK);

makes things even worse:

samearray(2) {
  [0]=>
  float(4.8572380745623E+19)
  [1]=>
  string(20) "48572380745623087456"
}
array(2) {
  [0]=>
  float(4.8572380745623E+19)
  [1]=>
  float(4.8572380745623E+19)
}


It converts "big int" to float, but this is at least visible in var dump.
 [2014-02-13 11:33 UTC] derick@php.net
-Status: Open +Status: Not a bug
 [2014-02-13 11:33 UTC] derick@php.net
Thank you for taking the time to write to us, but this is not
a bug. Please double-check the documentation available at
http://www.php.net/manual/ and the instructions on how to report
a bug at http://bugs.php.net/how-to-report.php

Your integer is larger than the 64bit signed range allows. PHP can not represent this as an int, and just as a float.
 [2014-02-13 11:35 UTC] sergey dot shilko at gmail dot com
echo json_last_error();
//0

echo json_last_error_msg();
//No error
 [2014-02-13 11:38 UTC] sergey dot shilko at gmail dot com
okay then, so its expected.
Already using strict datatypes to threat large numbers as strings.
Thanks.
 [2015-08-28 03:47 UTC] dev at skynar dot co
A JSON numeric size is not limited by the specification and should not to be related to PHP_INT_MAX, just put numeric strings into json without quotes, if JSON_NUMERIC_CHECK flag exists.
i'm sending data to another server, and it needs numeric parameters to be numeric. Am i the only developer in the world who care about variable types?

function bigint_json_fix(&$array, $depth=512){
  foreach($array as $key=>$value){
    if(is_string($value) && is_numeric($value) && $value <= PHP_INT_MAX){
        if((int)$value == $value){
            $array[$key]=(int)$value;
        }elseif((float)$value == $value){
            $array[$key]=(float)$value;
        }
    }elseif(is_array($value) && $depth > 0){
        bigint_json_fix($value, $depth-1);
        $array[$key]=$value;
    }
  }
}

function bigint_json_encode($array, $options=0, $depth=512){
  if(($options & JSON_NUMERIC_CHECK) == 0)
    return json_encode($array, $options, $depth);
  bigint_json_fix($array, $depth);
  $json = json_encode($array, $options - JSON_NUMERIC_CHECK, $depth);
  return preg_replace('!\:\"([0-9]*)\"([\}\,])!', ':$1$2', $json);
}

echo bigint_json_encode(['myint'=>'43274923749237498273443579'], JSON_NUMERIC_CHECK); // {"myint":43274923749237498273443579}

okay now i wonna get numeric string out of json:

$data = json_decode('{"myint":43274923749237498273443579}', true, 512, JSON_BIGINT_AS_STRING)); //PHP Notice:  json_decode(): integer overflow detected
echo $data['myint']; // 9223372036854775807 as i expected, result is corrupted.

function bigint_json_decode($json, $assoc=false, $depth=512, $useless_option=0){
  if(($useless_option & JSON_BIGINT_AS_STRING) != 0){
    $json = preg_replace('!\:([0-9]*)([\}\,])!', ':"$1"$2', $json);
  }
  $data = json_decode($json, $assoc, $depth, $useless_option);
  bigint_json_fix($data);
  return $data;
}

$data = bigint_json_decode('{"myint":43274923749237498273443579}');
echo $data['myint']; //43274923749237498273443579

why not to do the same without regular expressions?
 [2015-08-28 05:46 UTC] sergey dot shilko at gmail dot com
working with big numbers was never a strong side of php, i just defined them as strings in datatype desc.
also look into is_finite() for numeric check.

would help if it throws json_last_error at least.
 
PHP Copyright © 2001-2024 The PHP Group
All rights reserved.
Last updated: Sun Dec 22 00:01:30 2024 UTC