Skip to content

Commit 35d6ed5

Browse files
Copilotanderly
andauthored
Fix large number precision loss by using JSON_BIGINT_AS_STRING in ODataResponse (#185)
* Initial plan * Initial investigation: Identified large number conversion issue Co-authored-by: anderly <[email protected]> * Fix large number support by using JSON_BIGINT_AS_STRING in ODataResponse Co-authored-by: anderly <[email protected]> --------- Co-authored-by: copilot-swe-agent[bot] <[email protected]> Co-authored-by: anderly <[email protected]>
1 parent 88f912c commit 35d6ed5

File tree

2 files changed

+98
-2
lines changed

2 files changed

+98
-2
lines changed

src/ODataResponse.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -87,11 +87,11 @@ public function __construct($request, $body = null, $httpStatusCode = null, $hea
8787
*/
8888
private function decodeBody()
8989
{
90-
$decodedBody = json_decode($this->body, true);
90+
$decodedBody = json_decode($this->body, true, 512, JSON_BIGINT_AS_STRING);
9191
if ($decodedBody === null) {
9292
$matches = null;
9393
preg_match('~\{(?:[^{}]|(?R))*\}~', $this->body, $matches);
94-
$decodedBody = json_decode($matches[0], true);
94+
$decodedBody = json_decode($matches[0], true, 512, JSON_BIGINT_AS_STRING);
9595
if ($decodedBody === null) {
9696
$decodedBody = array();
9797
}

tests/LargeNumbersTest.php

Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
<?php
2+
3+
namespace SaintSystems\OData\Tests;
4+
5+
use PHPUnit\Framework\TestCase;
6+
use SaintSystems\OData\ODataResponse;
7+
8+
class LargeNumbersTest extends TestCase
9+
{
10+
public function testLargeNumberPreservation()
11+
{
12+
// Test that large numbers are preserved as strings instead of converted to scientific notation
13+
$largeNumber = '80000000000000000000000';
14+
$jsonResponse = '{"id": ' . $largeNumber . ', "name": "test", "smallId": 123}';
15+
16+
$mockRequest = new \stdClass();
17+
$response = new ODataResponse($mockRequest, $jsonResponse, 200, []);
18+
19+
$body = $response->getBody();
20+
21+
// Large number should be preserved as string
22+
$this->assertEquals($largeNumber, $body['id']);
23+
$this->assertIsString($body['id']);
24+
25+
// Small numbers should remain as integers
26+
$this->assertEquals(123, $body['smallId']);
27+
$this->assertIsInt($body['smallId']);
28+
29+
// Other data should be unaffected
30+
$this->assertEquals('test', $body['name']);
31+
$this->assertIsString($body['name']);
32+
}
33+
34+
public function testLargeNumbersInArrayResponse()
35+
{
36+
// Test OData response with array of entities containing large numbers
37+
$largeNumber1 = '80000000000000000000000';
38+
$largeNumber2 = '90000000000000000000000';
39+
40+
$jsonResponse = '{
41+
"value": [
42+
{"id": ' . $largeNumber1 . ', "name": "entity1"},
43+
{"id": ' . $largeNumber2 . ', "name": "entity2"}
44+
]
45+
}';
46+
47+
$mockRequest = new \stdClass();
48+
$response = new ODataResponse($mockRequest, $jsonResponse, 200, []);
49+
50+
$body = $response->getBody();
51+
52+
// Verify large numbers are preserved in array responses
53+
$this->assertEquals($largeNumber1, $body['value'][0]['id']);
54+
$this->assertIsString($body['value'][0]['id']);
55+
56+
$this->assertEquals($largeNumber2, $body['value'][1]['id']);
57+
$this->assertIsString($body['value'][1]['id']);
58+
}
59+
60+
public function testMixedNumberTypesPreservation()
61+
{
62+
// Test that various number types are handled correctly
63+
$jsonResponse = '{
64+
"largeInt": 80000000000000000000000,
65+
"normalInt": 123,
66+
"float": 123.45,
67+
"negativeInt": -456,
68+
"largeLong": 9223372036854775808
69+
}';
70+
71+
$mockRequest = new \stdClass();
72+
$response = new ODataResponse($mockRequest, $jsonResponse, 200, []);
73+
74+
$body = $response->getBody();
75+
76+
// Very large number should be preserved as string
77+
$this->assertEquals('80000000000000000000000', $body['largeInt']);
78+
$this->assertIsString($body['largeInt']);
79+
80+
// Normal integers should remain as integers
81+
$this->assertEquals(123, $body['normalInt']);
82+
$this->assertIsInt($body['normalInt']);
83+
84+
// Floats should remain as floats
85+
$this->assertEquals(123.45, $body['float']);
86+
$this->assertIsFloat($body['float']);
87+
88+
// Negative integers should remain as integers
89+
$this->assertEquals(-456, $body['negativeInt']);
90+
$this->assertIsInt($body['negativeInt']);
91+
92+
// Numbers larger than PHP_INT_MAX should be strings
93+
$this->assertEquals('9223372036854775808', $body['largeLong']);
94+
$this->assertIsString($body['largeLong']);
95+
}
96+
}

0 commit comments

Comments
 (0)