-
Notifications
You must be signed in to change notification settings - Fork 20
Expand file tree
/
Copy pathTypeCheck.php
More file actions
123 lines (108 loc) · 4.15 KB
/
Copy pathTypeCheck.php
File metadata and controls
123 lines (108 loc) · 4.15 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
<?php
declare(strict_types=1);
namespace PHPModelGenerator\Utils;
use PHPModelGenerator\Model\Property\PropertyInterface;
use PHPModelGenerator\Model\Validator;
use PHPModelGenerator\Model\Validator\MultiTypeCheckValidator;
use PHPModelGenerator\Model\Validator\PassThroughTypeCheckValidator;
use PHPModelGenerator\Model\Validator\TypeCheckValidator;
/**
* Utility for building runtime type-check expressions in generated PHP code,
* and for upgrading TypeCheckValidator instances to PassThroughTypeCheckValidator.
*
* Converts PHP type names to expressions like is_string($value) for primitives
* or $value instanceof ClassName for classes.
*/
class TypeCheck
{
/**
* Whether the given PHP type name is a primitive (scalar/null/array/object).
*/
public static function isPrimitive(string $typeName): bool
{
return in_array($typeName, ['int', 'float', 'string', 'bool', 'array', 'object', 'null'], true);
}
/**
* Build a positive runtime check for a single type.
*
* Primitives: is_string($value)
* Classes: $value instanceof ClassName
*/
public static function buildCheck(string $typeName): string
{
if (self::isPrimitive($typeName)) {
return "is_{$typeName}(\$value)";
}
$parts = explode('\\', $typeName);
return '$value instanceof ' . end($parts);
}
/**
* Build a compound positive check from multiple type names.
*
* Example: (is_string($value) || $value instanceof DateTime)
*
* @param string[] $typeNames
*/
public static function buildCompound(array $typeNames): string
{
$checks = array_map([self::class, 'buildCheck'], $typeNames);
return '(' . implode(' || ', $checks) . ')';
}
/**
* Build a negated compound check from multiple type names.
*
* For a single primitive, uses !is_string($value) without wrapping parentheses.
* For a single class or multiple types, wraps in !(…).
*
* @param string[] $typeNames
*/
public static function buildNegatedCompound(array $typeNames): string
{
$checks = array_map([self::class, 'buildCheck'], $typeNames);
if (count($checks) === 1) {
$check = reset($checks);
return str_starts_with($check, 'is_') ? '!' . $check : '!(' . $check . ')';
}
return '!(' . implode(' || ', $checks) . ')';
}
/**
* Replace the property's TypeCheckValidator / MultiTypeCheckValidator with a
* PassThroughTypeCheckValidator that also allows the given pass-through type names.
*
* This ensures that an already-transformed value (e.g. an enum instance produced by a
* transforming filter) bypasses the original scalar type check while non-conforming values
* are still rejected.
*
* When called a second time the TypeCheckValidator has already been replaced by a
* PassThroughTypeCheckValidator, which does not match the filter predicate, so the call
* is silently skipped.
*
* @param string[] $passThroughTypeNames Simple PHP type names of the transformed output
* (e.g. ['DateTime'] or ['MyEnum'])
*/
public static function extendTypeCheckValidatorToAllowTransformedValue(
PropertyInterface $property,
array $passThroughTypeNames,
): void {
$typeCheckValidator = null;
$property->filterValidators(static function (Validator $validator) use (&$typeCheckValidator): bool {
if (
is_a($validator->getValidator(), TypeCheckValidator::class) ||
is_a($validator->getValidator(), MultiTypeCheckValidator::class)
) {
$typeCheckValidator = $validator->getValidator();
return false;
}
return true;
});
if (
$typeCheckValidator instanceof TypeCheckValidator
|| $typeCheckValidator instanceof MultiTypeCheckValidator
) {
$property->addValidator(
new PassThroughTypeCheckValidator($passThroughTypeNames, $property, $typeCheckValidator),
2,
);
}
}
}