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
View Add Comment Developer Edit
Welcome! If you don't have a Git account, you can't do anything here.
You can add a comment by following this link or if you reported this bug, you can edit this bug over here.
(description)
Block user comment
Status: Assign to:
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

Add a Patch

Pull Requests

Add a Pull Request

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-2019 The PHP Group
All rights reserved.
Last updated: Fri Dec 13 10:01:24 2019 UTC