-
-
Notifications
You must be signed in to change notification settings - Fork 1.4k
Change BigIntType Casting #6143
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thank you. Please add tests.
Please also document this change in |
I'll fix the issues with the phpcs |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this calls for a functional test on MySQL with a BIGINT UNSIGNED
field.
src/Types/BigIntType.php
Outdated
] | ||
]; | ||
|
||
$safeInt = filter_var($value, FILTER_VALIDATE_INT, $options); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let's not use ext-filter here. Your previous code that simply compared the value to PHP_INT_MIN
and PHP_INT_MAX
was just fine.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
When I was creating the unit tests I was getting a strange behavior.
If the value is a string and exceeds the PHP_INT_MAX then it passes the comparison.
See this PHP shell example:
php > var_dump(PHP_INT_MAX);
int(9223372036854775807)
php > var_dump(('9223372036854775808' <= PHP_INT_MAX));
bool(true)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The problem is the <=
operator. If you have a string with an integer lager than PHP_INT_MAX
, an integer cast gives you PHP_INT_MAX
in PHP.
I'm afraid the ext-filter methods are to slow to put them on a hot execution path. Let's stick to <
and >
. I realize that this creates an edge case for PHP_INT_MAX
itself because we cannot tell apart PHP_INT_MAX
from an integer overflow, but I think we can live with that.
tests/Types/BigIntTest.php
Outdated
|
||
public function testShouldConvertPhpIntMinToInteger(): void | ||
{ | ||
self::assertIsInt($this->type->convertToPHPValue(PHP_INT_MIN, $this->platform)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
assertIsInt()
is too weak. An implementation that always returns 42
would pass this test. Why not assertSame()
? We pretty much know that value to expect.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ok then, I'll change that.
tests/Types/BigIntTest.php
Outdated
|
||
public function testShouldConvertSafeNegativeValueToInteger(): void | ||
{ | ||
self::assertIsInt($this->type->convertToPHPValue(PHP_INT_MIN + 1, $this->platform)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This test is bogus. PHP_INT_MIN + 1
is a float and a database would never return a float for a BIGINT
field.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No according to PHP shell.
php > var_dump(PHP_INT_MIN + 1);
int(-9223372036854775807)
The same goes for the other test
php > var_dump(PHP_INT_MAX - 1);
int(9223372036854775806)
The idea is to test scenarios around the limits of PHP.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@derrabus PHP_INT_MIN + 1
is not a float. You confused with PHP_INT_MAX + 1
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@stof You're right.
tests/Types/BigIntTest.php
Outdated
|
||
public function testShouldConvertSafeNegativeStringValueToInteger(): void | ||
{ | ||
self::assertIsInt($this->type->convertToPHPValue((string) (PHP_INT_MIN + 1), $this->platform)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That's also not a valid scenario.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Alright, I'll remove these kinds of tests.
Could you tell me how to run integration tests? When I do this most tests are skipped. php vendor/bin/phpunit --testdox tests/Functional |
@cizordj by default, tests are run with SQLite. You can add the |
@derrabus Hi, can you please take a look at this test? It's failing on my machine. Does that mean we should change our implementation? |
protected function setUp(): void | ||
{ | ||
if ( | ||
! $this->connection->getDatabasePlatform() instanceof MySQLPlatform |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
the sqlite platform also support unsigned integer fields
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks, I didn't know that 😁
|
||
$this->connection->executeStatement( | ||
<<<'SQL' | ||
CREATE TABLE IF NOT EXISTS doctrine_tests.mysql_unsigned_test ( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This should use the schema API so that it does not hardcode support for mysql only
|
||
Not all of the database vendors support unsigned integers, so such an assumption | ||
might not be propagated to the database. | ||
Values retrieved from the database are always converted to PHP's ``integer`` type |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
it might be worth mentioning that getting a string can only happen on PHP 32-bit architecture or for unsigned integers, as PHP 64-bit has an integer type that corresponds to the range of signed bigint
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I agree, I had no idea on how to express this in the documentation.
- Cast to int within safe range, else to string. - Update the documentation regarding the BigIntType - Add unit tests using assertSame - Add integration test with both signed and unsigned integers - Add the breaking change to the UPGRADE.md file
if ($value === null) { | ||
return null; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
if ($value === null) { | |
return null; | |
} | |
if ($value === null || is_int($value)) { | |
return $value; | |
} |
Let's exit here already if we already hold an integer in our hand. This prevents accidental int-to-string casts. This should also help us getting the tests to pass.
Closing in favor of #6177. |
| Q | A |------------- | ----------- | Type | improvement | Fixed issues | Replaces doctrine#6143, closes doctrine#6126 #### Summary `BigIntType` casts values retrieved from the database to int if they're inside the integer range of PHP. Previously, those values were always cast to string. This PR continues the work done by @cizordj in doctrine#6143. Co-authored-by: cizordj <[email protected]>
Summary