Skip to content

Commit 06909e6

Browse files
author
Kirill Nesmeyanov
committed
Complete bool literals and add tests
1 parent 96bc206 commit 06909e6

File tree

5 files changed

+253
-8
lines changed

5 files changed

+253
-8
lines changed

src/Type/BoolLiteralType.php

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -7,28 +7,30 @@
77
use TypeLang\Mapper\Exception\Mapping\InvalidValueException;
88
use TypeLang\Mapper\Runtime\Context;
99

10-
class BoolLiteralType implements TypeInterface
10+
class BoolLiteralType extends BoolType
1111
{
1212
public function __construct(
1313
private readonly bool $value,
1414
) {}
1515

16+
#[\Override]
1617
public function match(mixed $value, Context $context): bool
1718
{
1819
return $value === $this->value;
1920
}
2021

21-
/**
22-
* Converts incoming value to the bool (in case of strict types is disabled).
23-
*
24-
* @throws InvalidValueException
25-
*/
22+
#[\Override]
2623
public function cast(mixed $value, Context $context): bool
2724
{
2825
if ($value === $this->value) {
2926
return $value;
3027
}
3128

29+
if (!$context->isStrictTypesEnabled()
30+
&& $this->convertToBool($value) === $this->value) {
31+
return $this->value;
32+
}
33+
3234
throw InvalidValueException::createFromContext(
3335
value: $value,
3436
context: $context,

src/Type/BoolType.php

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -37,11 +37,24 @@ public function cast(mixed $value, Context $context): bool
3737

3838
protected function convertToBool(mixed $value): bool
3939
{
40+
//
41+
// Each value should be checked EXPLICITLY, instead
42+
// of converting to a bool like `(bool) $value`.
43+
//
44+
// This will avoid implicit behavior, such as when an empty
45+
// SimpleXMLElement is cast to false, instead of being
46+
// converted to true like any other object:
47+
//
48+
// ```
49+
// (bool) new \SimpleXMLElement('<xml />'); // -> false (WTF?)
50+
// ```
51+
//
4052
return $value !== ''
53+
&& $value !== '0'
4154
&& $value !== []
4255
&& $value !== null
43-
&& $value !== '0'
4456
&& $value !== 0
45-
&& $value !== 0.0;
57+
&& $value !== 0.0
58+
&& $value !== false;
4659
}
4760
}
Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,107 @@
1+
Feature: Checking the FALSE "BoolLiteralType" type behavior
2+
3+
Background:
4+
Given type "TypeLang\Mapper\Type\BoolLiteralType" with [false]
5+
6+
Scenario Outline: Matching "<value>" by the BoolLiteralType
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+
| 1 | false |
14+
| -1 | false |
15+
| 0 | false |
16+
| 42 | false |
17+
| 42.1 | false |
18+
| 1.0 | false |
19+
| 0.0 | false |
20+
| -1.0 | false |
21+
| INF | false |
22+
| NAN | false |
23+
| "1" | false |
24+
| "0" | false |
25+
| "string" | false |
26+
| "true" | false |
27+
| "false" | false |
28+
| "" | false |
29+
| null | false |
30+
| true | false |
31+
| false | true |
32+
| [] | false |
33+
| [1] | false |
34+
| (object)[] | false |
35+
| TypeLang\Mapper\Tests\Stub\IntBackedEnumStub::CASE | false |
36+
| TypeLang\Mapper\Tests\Stub\StringBackedEnumStub::CASE | false |
37+
| TypeLang\Mapper\Tests\Stub\UnitEnumStub::CASE | false |
38+
39+
Scenario Outline: Normalize "<value>" by the BoolLiteralType
40+
When normalize
41+
Then cast of "<value>" must return <result>
42+
# Can be converted to a false:
43+
# - int(0)
44+
# - float(0.0)
45+
# - string("0")
46+
# - empty string ("")
47+
# - empty array ([])
48+
# - null
49+
# Otherwise true
50+
Examples:
51+
| value | result |
52+
| 1 | <error: Passed value 1 is invalid> |
53+
| -1 | <error: Passed value -1 is invalid> |
54+
| 0 | false |
55+
| 42 | <error: Passed value 42 is invalid> |
56+
| 42.1 | <error: Passed value 42.1 is invalid> |
57+
| 1.0 | <error: Passed value 1 is invalid> |
58+
| 0.0 | false |
59+
| -1.0 | <error: Passed value -1 is invalid> |
60+
| INF | <error: Passed value INF is invalid> |
61+
| NAN | <error: Passed value NAN is invalid> |
62+
| "1" | <error: Passed value "1" is invalid> |
63+
| "0" | false |
64+
| "string" | <error: Passed value "string" is invalid> |
65+
| "true" | <error: Passed value "true" is invalid> |
66+
| "false" | <error: Passed value "false" is invalid> |
67+
| "" | false |
68+
| null | false |
69+
| true | <error: Passed value true is invalid> |
70+
| false | false |
71+
| [] | false |
72+
| [1] | <error: Passed value [1] is invalid> |
73+
| (object)[] | <error: Passed value {} is invalid> |
74+
| TypeLang\Mapper\Tests\Stub\IntBackedEnumStub::CASE | <error: Passed value {"name": "CASE", "value": 3735928559} is invalid> |
75+
| TypeLang\Mapper\Tests\Stub\StringBackedEnumStub::CASE | <error: Passed value {"name": "CASE", "value": "case"} is invalid> |
76+
| TypeLang\Mapper\Tests\Stub\UnitEnumStub::CASE | <error: Passed value {"name": "CASE"} is invalid> |
77+
78+
Scenario Outline: Denormalize "<value>" by the BoolLiteralType
79+
When denormalize
80+
Then cast of "<value>" must return <result>
81+
Examples:
82+
| value | result |
83+
| 1 | <error: Passed value 1 is invalid> |
84+
| 0 | <error: Passed value 0 is invalid> |
85+
| -1 | <error: Passed value -1 is invalid> |
86+
| 42 | <error: Passed value 42 is invalid> |
87+
| 42.1 | <error: Passed value 42.1 is invalid> |
88+
| 1.0 | <error: Passed value 1 is invalid> |
89+
| 0.0 | <error: Passed value 0 is invalid> |
90+
| -1.0 | <error: Passed value -1 is invalid> |
91+
| INF | <error: Passed value INF is invalid> |
92+
| NAN | <error: Passed value NAN is invalid> |
93+
| "1" | <error: Passed value "1" is invalid> |
94+
| "0" | <error: Passed value "0" is invalid> |
95+
| "string" | <error: Passed value "string" is invalid> |
96+
| "true" | <error: Passed value "true" is invalid> |
97+
| "false" | <error: Passed value "false" is invalid> |
98+
| "" | <error: Passed value "" is invalid> |
99+
| null | <error: Passed value null is invalid> |
100+
| true | <error: Passed value true is invalid> |
101+
| false | false |
102+
| [] | <error: Passed value [] is invalid> |
103+
| [1] | <error: Passed value [1] is invalid> |
104+
| (object)[] | <error: Passed value {} is invalid> |
105+
| TypeLang\Mapper\Tests\Stub\IntBackedEnumStub::CASE | <error: Passed value {"name": "CASE", "value": 3735928559} is invalid> |
106+
| TypeLang\Mapper\Tests\Stub\StringBackedEnumStub::CASE | <error: Passed value {"name": "CASE", "value": "case"} is invalid> |
107+
| TypeLang\Mapper\Tests\Stub\UnitEnumStub::CASE | <error: Passed value {"name": "CASE"} is invalid> |

0 commit comments

Comments
 (0)