Skip to content

Commit

Permalink
fix: allow leading zeros in numeric string in flexible mode
Browse files Browse the repository at this point in the history
  • Loading branch information
romm committed Dec 26, 2023
1 parent a462fe1 commit f000c10
Show file tree
Hide file tree
Showing 6 changed files with 102 additions and 0 deletions.
9 changes: 9 additions & 0 deletions src/Type/Types/IntegerRangeType.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,9 @@
use CuyZ\Valinor\Type\Parser\Exception\Scalar\SameValueForIntegerRange;
use CuyZ\Valinor\Type\Type;

use function is_string;
use function ltrim;
use function preg_match;
use function sprintf;

/** @internal */
Expand Down Expand Up @@ -79,6 +82,12 @@ public function matches(Type $other): bool

public function canCast(mixed $value): bool
{
if (is_string($value)) {
$value = preg_match('/^0+$/', $value)
? '0'
: ltrim($value, '0');
}

return ! is_bool($value)
&& filter_var($value, FILTER_VALIDATE_INT) !== false
&& $value >= $this->min
Expand Down
9 changes: 9 additions & 0 deletions src/Type/Types/IntegerValueType.php
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,9 @@
use function assert;
use function filter_var;
use function is_bool;
use function is_string;
use function ltrim;
use function preg_match;

/** @internal */
final class IntegerValueType implements IntegerType, FixedType
Expand Down Expand Up @@ -55,6 +58,12 @@ public function matches(Type $other): bool

public function canCast(mixed $value): bool
{
if (is_string($value)) {
$value = preg_match('/^0+$/', $value)
? '0'
: ltrim($value, '0');
}

return ! is_bool($value)
&& filter_var($value, FILTER_VALIDATE_INT) !== false
&& (int)$value === $this->value; // @phpstan-ignore-line;
Expand Down
6 changes: 6 additions & 0 deletions src/Type/Types/NativeIntegerType.php
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@
use function filter_var;
use function is_bool;
use function is_int;
use function is_string;
use function ltrim;

/** @internal */
final class NativeIntegerType implements IntegerType
Expand All @@ -37,6 +39,10 @@ public function matches(Type $other): bool

public function canCast(mixed $value): bool
{
if (is_string($value)) {
$value = ltrim($value, '0') . '0';
}

return ! is_bool($value) && filter_var($value, FILTER_VALIDATE_INT) !== false;
}

Expand Down
7 changes: 7 additions & 0 deletions src/Type/Types/NonNegativeIntegerType.php
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,9 @@
use CuyZ\Valinor\Type\Type;
use CuyZ\Valinor\Utility\IsSingleton;

use function is_string;
use function ltrim;

/** @internal */
final class NonNegativeIntegerType implements IntegerType
{
Expand All @@ -33,6 +36,10 @@ public function matches(Type $other): bool

public function canCast(mixed $value): bool
{
if (is_string($value)) {
$value = ltrim($value, '0') . '0';
}

return ! is_bool($value)
&& filter_var($value, FILTER_VALIDATE_INT) !== false
&& $value >= 0;
Expand Down
6 changes: 6 additions & 0 deletions src/Type/Types/PositiveIntegerType.php
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@
use function filter_var;
use function is_bool;
use function is_int;
use function is_string;
use function ltrim;

/** @internal */
final class PositiveIntegerType implements IntegerType
Expand All @@ -38,6 +40,10 @@ public function matches(Type $other): bool

public function canCast(mixed $value): bool
{
if (is_string($value)) {
$value = ltrim($value, '0');
}

return ! is_bool($value)
&& filter_var($value, FILTER_VALIDATE_INT) !== false
&& $value > 0;
Expand Down
65 changes: 65 additions & 0 deletions tests/Integration/Mapping/Other/FlexibleCastingMappingTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,71 @@ protected function setUp(): void
$this->mapper = (new MapperBuilder())->enableFlexibleCasting()->mapper();
}

public function test_leading_zero_in_numeric_is_mapped_properly(): void
{
$source = ['000', '040', '00040', '0001337.404'];

try {
$result = $this->mapper->map('array<int|float>', $source);
} catch (MappingError $error) {
$this->mappingFail($error);
}

self::assertSame([0, 40, 40, 1337.404], $result);
}

public function test_leading_zero_in_integer_range_is_mapped_properly(): void
{
$source = ['060', '042', '000404'];

try {
$result = $this->mapper->map('array<int<1, 500>>', $source);
} catch (MappingError $error) {
$this->mappingFail($error);
}

self::assertSame([60, 42, 404], $result);
}

public function test_leading_zero_in_integer_value_is_mapped_properly(): void
{
$source = ['000', '040', '000404'];

try {
$result = $this->mapper->map('array<0|40|404>', $source);
} catch (MappingError $error) {
$this->mappingFail($error);
}

self::assertSame([0, 40, 404], $result);
}

public function test_leading_zero_in_positive_integer_is_mapped_properly(): void
{
$source = ['040', '000404'];

try {
$result = $this->mapper->map('array<positive-int>', $source);
} catch (MappingError $error) {
$this->mappingFail($error);
}

self::assertSame([40, 404], $result);
}

public function test_leading_zero_in_non_negative_integer_is_mapped_properly(): void
{
$source = ['000', '040', '000404'];

try {
$result = $this->mapper->map('array<non-negative-int>', $source);
} catch (MappingError $error) {
$this->mappingFail($error);
}

self::assertSame([0, 40, 404], $result);
}

public function test_array_of_scalars_is_mapped_properly(): void
{
$source = ['foo', 42, 1337.404];
Expand Down

0 comments on commit f000c10

Please sign in to comment.