Skip to content

Commit 0e744d4

Browse files
author
Kirill Nesmeyanov
committed
Add array key tests
1 parent 38a62b9 commit 0e744d4

File tree

3 files changed

+152
-1
lines changed

3 files changed

+152
-1
lines changed

src/Platform/StandardPlatform.php

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,9 @@ public function getTypes(): iterable
4444
// Adds support for the "float" type
4545
yield new Builder\SimpleTypeBuilder(['float', 'double', 'real'], Type\FloatType::class);
4646

47+
// Adds support for the "array-key" type
48+
yield new Builder\SimpleTypeBuilder('array-key', Type\ArrayKeyType::class);
49+
4750
// Adds support for the "array" type
4851
yield new Builder\ArrayTypeBuilder([
4952
'array',

src/Type/ArrayKeyType.php

Lines changed: 35 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,22 +5,56 @@
55
namespace TypeLang\Mapper\Type;
66

77
use TypeLang\Mapper\Exception\Mapping\InvalidValueException;
8+
use TypeLang\Mapper\Exception\Mapping\ValueExceptionInterface;
89
use TypeLang\Mapper\Runtime\Context;
910

1011
class ArrayKeyType implements TypeInterface
1112
{
13+
public function __construct(
14+
protected readonly TypeInterface $string = new StringType(),
15+
protected readonly TypeInterface $int = new IntType(),
16+
) {}
17+
1218
public function match(mixed $value, Context $context): bool
1319
{
14-
return \is_string($value) || \is_int($value);
20+
// TBD (?)
21+
// It is not entirely clear whether a zero ("0") string
22+
// key should be allowed, since it is technically
23+
// impossible to put it in associative array.
24+
//
25+
// if ($value === '0') {
26+
// return false;
27+
// }
28+
29+
return $this->int->match($value, $context)
30+
|| $this->string->match($value, $context);
1531
}
1632

1733
public function cast(mixed $value, Context $context): string|int
1834
{
35+
// PHP does not support zero ("0") string array keys,
36+
// so we need to force-cast it to the integer value.
37+
if ($value === '0') {
38+
return 0;
39+
}
40+
1941
if (\is_string($value) || \is_int($value)) {
2042
/** @var string|int */
2143
return $value;
2244
}
2345

46+
if (\is_float($value)) {
47+
try {
48+
return $this->int->cast($value, $context);
49+
} catch (ValueExceptionInterface) {
50+
return $this->string->cast($value, $context);
51+
}
52+
}
53+
54+
if (!$context->isStrictTypesEnabled()) {
55+
return $this->string->cast($value, $context);
56+
}
57+
2458
throw InvalidValueException::createFromContext(
2559
value: $value,
2660
context: $context,
Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,114 @@
1+
Feature: Checking the "array-key" (TypeLang\Mapper\Type\ArrayKeyType) type behavior
2+
3+
Background:
4+
Given type "TypeLang\Mapper\Type\ArrayKeyType"
5+
6+
Scenario Outline: Matching "<value>"
7+
When normalize
8+
Then match of "<value>" must return <is_matched>
9+
When denormalize
10+
Then match of "<value>" must return <is_matched>
11+
Examples:
12+
| value | is_matched |
13+
| 9223372036854775807 | true |
14+
| -9223372036854775807-1 | true |
15+
| -9223372036854775808 | true |
16+
| 1 | true |
17+
| 0 | true |
18+
| -1 | true |
19+
| 42 | true |
20+
| 42.1 | false |
21+
| 1.0 | false |
22+
| 0.0 | false |
23+
| -1.0 | false |
24+
| INF | false |
25+
| -INF | false |
26+
| NAN | false |
27+
| "1" | true |
28+
| "0" | true |
29+
| "string" | true |
30+
| "true" | true |
31+
| "false" | true |
32+
| "" | true |
33+
| null | false |
34+
| true | false |
35+
| false | false |
36+
| [] | false |
37+
| [1] | false |
38+
| (object)[] | false |
39+
| TypeLang\Mapper\Tests\Stub\IntBackedEnumStub::CASE | false |
40+
| TypeLang\Mapper\Tests\Stub\StringBackedEnumStub::CASE | false |
41+
| TypeLang\Mapper\Tests\Stub\UnitEnumStub::CASE | false |
42+
43+
44+
Scenario Outline: Normalize "<value>"
45+
When normalize
46+
Then cast of "<value>" must return <result>
47+
Examples:
48+
| value | result |
49+
| 9223372036854775807 | 9223372036854775807 |
50+
| -9223372036854775807-1 | -9223372036854775807-1 |
51+
| -9223372036854775808 | -9223372036854775807-1 |
52+
| 1 | 1 |
53+
| 0 | 0 |
54+
| -1 | -1 |
55+
| 42 | 42 |
56+
| 42.1 | "42.1" |
57+
| 1.0 | 1 |
58+
| 0.0 | 0 |
59+
| -1.0 | -1 |
60+
| INF | "inf" |
61+
| -INF | "-inf" |
62+
| NAN | "nan" |
63+
| "1" | "1" |
64+
| "0" | 0 |
65+
| "string" | "string" |
66+
| "true" | "true" |
67+
| "false" | "false" |
68+
| "" | "" |
69+
| null | "" |
70+
| true | "true" |
71+
| false | "false" |
72+
| [] | <error: Passed value [] is invalid> |
73+
| [1] | <error: Passed value [1] is invalid> |
74+
| (object)[] | <error: Passed value {} is invalid> |
75+
| TypeLang\Mapper\Tests\Stub\IntBackedEnumStub::CASE | "3735928559" |
76+
| TypeLang\Mapper\Tests\Stub\StringBackedEnumStub::CASE | "case" |
77+
| TypeLang\Mapper\Tests\Stub\UnitEnumStub::CASE | <error: Passed value {"name": "CASE"} is invalid> |
78+
79+
80+
Scenario Outline: Denormalize "<value>"
81+
When denormalize
82+
Then cast of "<value>" must return <result>
83+
Examples:
84+
| value | result |
85+
| 9223372036854775807 | 9223372036854775807 |
86+
| -9223372036854775807-1 | -9223372036854775807-1 |
87+
| -9223372036854775808 | -9223372036854775807-1 |
88+
| 1 | 1 |
89+
| 0 | 0 |
90+
| -1 | -1 |
91+
| 42 | 42 |
92+
| 42.1 | <error: Passed value 42.1 is invalid> |
93+
| 1.0 | <error: Passed value 1 is invalid> |
94+
| 0.0 | <error: Passed value 0 is invalid> |
95+
| -1.0 | <error: Passed value -1 is invalid> |
96+
| INF | <error: Passed value INF is invalid> |
97+
| -INF | <error: Passed value -INF is invalid> |
98+
| NAN | <error: Passed value NAN is invalid> |
99+
| "1" | "1" |
100+
| "0" | 0 |
101+
| "-1" | "-1" |
102+
| "string" | "string" |
103+
| "true" | "true" |
104+
| "false" | "false" |
105+
| "" | "" |
106+
| null | <error: Passed value null is invalid> |
107+
| true | <error: Passed value true is invalid> |
108+
| false | <error: Passed value false is invalid> |
109+
| [] | <error: Passed value [] is invalid> |
110+
| [1] | <error: Passed value [1] is invalid> |
111+
| (object)[] | <error: Passed value {} is invalid> |
112+
| TypeLang\Mapper\Tests\Stub\IntBackedEnumStub::CASE | <error: Passed value {"name": "CASE", "value": 3735928559} is invalid> |
113+
| TypeLang\Mapper\Tests\Stub\StringBackedEnumStub::CASE | <error: Passed value {"name": "CASE", "value": "case"} is invalid> |
114+
| TypeLang\Mapper\Tests\Stub\UnitEnumStub::CASE | <error: Passed value {"name": "CASE"} is invalid> |

0 commit comments

Comments
 (0)