Skip to content

Commit

Permalink
fix: allow integer values in float types
Browse files Browse the repository at this point in the history
A float property/parameter will accept an integer value in PHP, even
when `strict_types` is enabled. PHPStan and Psalm also accept the case
below. To make it consistent, the mapper will now accept integer values
in float types.

```php
(new \CuyZ\Valinor\MapperBuilder())
    ->mapper()
    ->map('float', 42); // returns `42.0`
```
  • Loading branch information
romm committed Apr 6, 2023
1 parent 83eb058 commit c6df24d
Show file tree
Hide file tree
Showing 5 changed files with 20 additions and 4 deletions.
9 changes: 9 additions & 0 deletions src/Mapper/Tree/Builder/TreeNode.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
use CuyZ\Valinor\Mapper\Tree\Message\Message;
use CuyZ\Valinor\Mapper\Tree\Node;
use CuyZ\Valinor\Mapper\Tree\Shell;
use CuyZ\Valinor\Type\FloatType;
use Throwable;

use function array_map;
Expand All @@ -30,6 +31,14 @@ final class TreeNode

private function __construct(Shell $shell, mixed $value)
{
// When the value is an integer and the type is a float, the value needs
// to be cast to float — this special case needs to be handled in case a
// node is not a *native* PHP float type (for instance a class property
// with a `@var float` annotation).
if ($shell->type() instanceof FloatType && is_int($value)) {
$value = (float)$value;
}

$this->shell = $shell;
$this->value = $value;
}
Expand Down
3 changes: 2 additions & 1 deletion src/Type/Types/NativeFloatType.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@

use function assert;
use function is_float;
use function is_integer;
use function is_numeric;

/** @internal */
Expand All @@ -21,7 +22,7 @@ final class NativeFloatType implements FloatType

public function accepts(mixed $value): bool
{
return is_float($value);
return is_float($value) || is_integer($value);
}

public function matches(Type $other): bool
Expand Down
6 changes: 6 additions & 0 deletions tests/Integration/Mapping/Object/ScalarValuesMappingTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ public function test_values_are_mapped_properly(): void
$source = [
'boolean' => true,
'float' => 42.404,
'floatWithInteger' => 42,
'positiveFloatValue' => 42.404,
'negativeFloatValue' => -42.404,
'integer' => 1337,
Expand Down Expand Up @@ -50,6 +51,7 @@ public function test_values_are_mapped_properly(): void

self::assertSame(true, $result->boolean);
self::assertSame(42.404, $result->float);
self::assertSame(42.0, $result->floatWithInteger);
self::assertSame(42.404, $result->positiveFloatValue); // @phpstan-ignore-line
self::assertSame(-42.404, $result->negativeFloatValue); // @phpstan-ignore-line
self::assertSame(1337, $result->integer);
Expand Down Expand Up @@ -89,6 +91,8 @@ class ScalarValues

public float $float = -1.0;

public float $floatWithInteger = -1.0;

/** @var 42.404 */
public float $positiveFloatValue;

Expand Down Expand Up @@ -165,6 +169,7 @@ class ScalarValuesWithConstructor extends ScalarValues
public function __construct(
bool $boolean,
float $float,
float $floatWithInteger,
float $positiveFloatValue,
float $negativeFloatValue,
int $integer,
Expand All @@ -186,6 +191,7 @@ public function __construct(
) {
$this->boolean = $boolean;
$this->float = $float;
$this->floatWithInteger = $floatWithInteger;
$this->positiveFloatValue = $positiveFloatValue;
$this->negativeFloatValue = $negativeFloatValue;
$this->integer = $integer;
Expand Down
4 changes: 2 additions & 2 deletions tests/Integration/Mapping/Object/UnionValuesMappingTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -88,12 +88,12 @@ public function test_values_are_mapped_properly(): void
public function test_invalid_value_is_not_casted_when_casting_mode_is_disabled(): void
{
try {
(new MapperBuilder())->mapper()->map('string|float', 42);
(new MapperBuilder())->mapper()->map('string|int', 42.404);
} catch (MappingError $exception) {
$error = $exception->node()->messages()[0];

self::assertSame('1607027306', $error->code());
self::assertSame('Value 42 does not match any of `string`, `float`.', (string)$error);
self::assertSame('Value 42.404 does not match any of `string`, `int`.', (string)$error);
}
}
}
Expand Down
2 changes: 1 addition & 1 deletion tests/Unit/Type/Types/NativeFloatTypeTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -29,13 +29,13 @@ protected function setUp(): void
public function test_accepts_correct_values(): void
{
self::assertTrue($this->floatType->accepts(42.1337));
self::assertTrue($this->floatType->accepts(404));
}

public function test_does_not_accept_incorrect_values(): void
{
self::assertFalse($this->floatType->accepts(null));
self::assertFalse($this->floatType->accepts('Schwifty!'));
self::assertFalse($this->floatType->accepts(404));
self::assertFalse($this->floatType->accepts(['foo' => 'bar']));
self::assertFalse($this->floatType->accepts(false));
self::assertFalse($this->floatType->accepts(new stdClass()));
Expand Down

0 comments on commit c6df24d

Please sign in to comment.