Skip to content

Commit 36826f8

Browse files
committed
Add downcasts support for match logic
1 parent 29f6673 commit 36826f8

File tree

9 files changed

+92
-20
lines changed

9 files changed

+92
-20
lines changed

src/Platform/StandardPlatform.php

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -141,23 +141,19 @@ public function getTypeCoercers(): iterable
141141

142142
yield new Coercer\BoolTypeCoercer() => [
143143
Type\BoolType::class,
144-
Type\BoolLiteralType::class,
145144
];
146145

147146
yield new Coercer\FloatTypeCoercer() => [
148147
Type\FloatType::class,
149-
Type\FloatLiteralType::class,
150148
];
151149

152150
yield new Coercer\IntTypeCoercer() => [
153151
Type\IntType::class,
154152
Type\IntRangeType::class,
155-
Type\IntLiteralType::class,
156153
];
157154

158155
yield new Coercer\StringTypeCoercer() => [
159156
Type\StringType::class,
160-
Type\StringLiteralType::class,
161157
];
162158

163159
yield new Coercer\ArrayTypeCoercer() => [

src/Type/Builder/BoolLiteralTypeBuilder.php

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,13 +14,25 @@
1414
*/
1515
class BoolLiteralTypeBuilder implements TypeBuilderInterface
1616
{
17+
public const DEFAULT_PARENT_TYPE = 'bool';
18+
19+
public function __construct(
20+
/**
21+
* @var non-empty-string
22+
*/
23+
protected readonly string $type = self::DEFAULT_PARENT_TYPE,
24+
) {}
25+
1726
public function isSupported(TypeStatement $stmt): bool
1827
{
1928
return $stmt instanceof BoolLiteralNode;
2029
}
2130

2231
public function build(TypeStatement $stmt, BuildingContext $context): BoolLiteralType
2332
{
24-
return new BoolLiteralType($stmt->value);
33+
return new BoolLiteralType(
34+
value: $stmt->value,
35+
type: $context->getTypeByDefinition($this->type),
36+
);
2537
}
2638
}

src/Type/Builder/FloatLiteralTypeBuilder.php

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,13 +14,25 @@
1414
*/
1515
class FloatLiteralTypeBuilder implements TypeBuilderInterface
1616
{
17+
public const DEFAULT_PARENT_TYPE = 'float';
18+
19+
public function __construct(
20+
/**
21+
* @var non-empty-string
22+
*/
23+
protected readonly string $type = self::DEFAULT_PARENT_TYPE,
24+
) {}
25+
1726
public function isSupported(TypeStatement $stmt): bool
1827
{
1928
return $stmt instanceof FloatLiteralNode;
2029
}
2130

2231
public function build(TypeStatement $stmt, BuildingContext $context): FloatLiteralType
2332
{
24-
return new FloatLiteralType($stmt->value);
33+
return new FloatLiteralType(
34+
value: $stmt->value,
35+
type: $context->getTypeByDefinition($this->type),
36+
);
2537
}
2638
}

src/Type/Builder/IntLiteralTypeBuilder.php

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,13 +14,25 @@
1414
*/
1515
class IntLiteralTypeBuilder implements TypeBuilderInterface
1616
{
17+
public const DEFAULT_PARENT_TYPE = 'int';
18+
19+
public function __construct(
20+
/**
21+
* @var non-empty-string
22+
*/
23+
protected readonly string $type = self::DEFAULT_PARENT_TYPE,
24+
) {}
25+
1726
public function isSupported(TypeStatement $stmt): bool
1827
{
1928
return $stmt instanceof IntLiteralNode;
2029
}
2130

2231
public function build(TypeStatement $stmt, BuildingContext $context): IntLiteralType
2332
{
24-
return new IntLiteralType($stmt->value);
33+
return new IntLiteralType(
34+
value: $stmt->value,
35+
type: $context->getTypeByDefinition($this->type),
36+
);
2537
}
2638
}

src/Type/Builder/StringLiteralTypeBuilder.php

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,13 +14,25 @@
1414
*/
1515
class StringLiteralTypeBuilder implements TypeBuilderInterface
1616
{
17+
public const DEFAULT_PARENT_TYPE = 'string';
18+
19+
public function __construct(
20+
/**
21+
* @var non-empty-string
22+
*/
23+
protected readonly string $type = self::DEFAULT_PARENT_TYPE,
24+
) {}
25+
1726
public function isSupported(TypeStatement $stmt): bool
1827
{
1928
return $stmt instanceof StringLiteralNode;
2029
}
2130

2231
public function build(TypeStatement $stmt, BuildingContext $context): StringLiteralType
2332
{
24-
return new StringLiteralType($stmt->value);
33+
return new StringLiteralType(
34+
value: $stmt->value,
35+
type: $context->getTypeByDefinition($this->type),
36+
);
2537
}
2638
}

src/Type/DateTimeType/DateTimeFromStringType.php

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -52,13 +52,14 @@ private function assertDateTimeClassExists(string $class): void
5252
*/
5353
public function match(mixed $value, RuntimeContext $context): bool
5454
{
55-
if (!$this->input->match($value, $context)) {
55+
try {
56+
$coerced = $this->input->cast($value, $context);
57+
} catch (\Throwable) {
5658
return false;
5759
}
5860

5961
try {
60-
/** @var string $value */
61-
return $this->tryParseDateTime($value, $context) !== null;
62+
return $this->tryParseDateTime($coerced, $context) !== null;
6263
} catch (\Throwable) {
6364
return false;
6465
}

src/Type/FloatLiteralType.php

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,11 @@
99
*/
1010
class FloatLiteralType extends LiteralType
1111
{
12-
public function __construct(int|float $value)
12+
/**
13+
* @param TypeInterface<float> $type
14+
*/
15+
public function __construct(int|float $value, TypeInterface $type)
1316
{
14-
parent::__construct((float) $value);
17+
parent::__construct((float) $value, $type);
1518
}
1619
}

src/Type/IntRangeType.php

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -29,14 +29,24 @@ public function __construct(
2929
*/
3030
public function match(mixed $value, RuntimeContext $context): bool
3131
{
32-
return $this->type->match($value, $context)
33-
&& $value >= $this->min
34-
&& $value <= $this->max;
32+
if (\is_int($value)) {
33+
return $value >= $this->min
34+
&& $value <= $this->max;
35+
}
36+
37+
try {
38+
$coerced = $this->type->cast($value, $context);
39+
40+
return $coerced >= $this->min
41+
&& $coerced <= $this->max;
42+
} catch (\Throwable) {
43+
return false;
44+
}
3545
}
3646

3747
public function cast(mixed $value, RuntimeContext $context): int
3848
{
39-
if ($this->match($value, $context)) {
49+
if (\is_int($value) && $value >= $this->min && $value <= $this->max) {
4050
return $value;
4151
}
4252

src/Type/LiteralType.php

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,20 +17,34 @@ public function __construct(
1717
* @var TResult
1818
*/
1919
protected readonly mixed $value,
20+
/**
21+
* @var TypeInterface<TResult>
22+
*/
23+
protected readonly TypeInterface $type,
2024
) {}
2125

2226
/**
2327
* @phpstan-assert-if-true TResult $value
2428
*/
2529
public function match(mixed $value, RuntimeContext $context): bool
2630
{
27-
return $value === $this->value;
31+
if ($value === $this->value) {
32+
return true;
33+
}
34+
35+
try {
36+
return $this->type->cast($value, $context) === $this->value;
37+
} catch (\Throwable) {
38+
return false;
39+
}
2840
}
2941

3042
public function cast(mixed $value, RuntimeContext $context): mixed
3143
{
32-
if ($value === $this->value) {
33-
return $value;
44+
$coerced = $this->type->cast($value, $context);
45+
46+
if ($coerced === $this->value) {
47+
return $coerced;
3448
}
3549

3650
throw InvalidValueException::createFromContext($context);

0 commit comments

Comments
 (0)