Code Coverage
 
Lines
Branches
Paths
Functions and Methods
Classes and Traits
Total
100.00% covered (success)
100.00%
149 / 149
96.39% covered (success)
96.39%
187 / 194
19.55% covered (danger)
19.55%
69 / 353
76.47% covered (warning)
76.47%
13 / 17
CRAP
0.00% covered (danger)
0.00%
0 / 1
TypeCoercer
100.00% covered (success)
100.00%
149 / 149
96.39% covered (success)
96.39%
187 / 194
19.55% covered (danger)
19.55%
69 / 353
100.00% covered (success)
100.00%
17 / 17
5099.30
100.00% covered (success)
100.00%
1 / 1
 coerce
100.00% covered (success)
100.00%
7 / 7
100.00% covered (success)
100.00%
7 / 7
60.00% covered (warning)
60.00%
3 / 5
100.00% covered (success)
100.00%
1 / 1
5.02
 coerceUnion
100.00% covered (success)
100.00%
15 / 15
100.00% covered (success)
100.00%
18 / 18
4.55% covered (danger)
4.55%
2 / 44
100.00% covered (success)
100.00%
1 / 1
63.66
 coerceNamed
100.00% covered (success)
100.00%
7 / 7
100.00% covered (success)
100.00%
7 / 7
100.00% covered (success)
100.00%
5 / 5
100.00% covered (success)
100.00%
1 / 1
6
 coerceSpecial
100.00% covered (success)
100.00%
7 / 7
100.00% covered (success)
100.00%
7 / 7
100.00% covered (success)
100.00%
4 / 4
100.00% covered (success)
100.00%
1 / 1
4
 coerceInt
100.00% covered (success)
100.00%
7 / 7
100.00% covered (success)
100.00%
6 / 6
100.00% covered (success)
100.00%
4 / 4
100.00% covered (success)
100.00%
1 / 1
4
 coerceFloat
100.00% covered (success)
100.00%
11 / 11
90.91% covered (success)
90.91%
20 / 22
6.31% covered (danger)
6.31%
7 / 111
100.00% covered (success)
100.00%
1 / 1
75.62
 coerceBool
100.00% covered (success)
100.00%
16 / 16
100.00% covered (success)
100.00%
15 / 15
25.00% covered (danger)
25.00%
7 / 28
100.00% covered (success)
100.00%
1 / 1
43.17
 coerceString
100.00% covered (success)
100.00%
7 / 7
91.67% covered (success)
91.67%
11 / 12
55.56% covered (warning)
55.56%
5 / 9
100.00% covered (success)
100.00%
1 / 1
9.16
 coerceImmutableDate
100.00% covered (success)
100.00%
10 / 10
100.00% covered (success)
100.00%
14 / 14
12.00% covered (danger)
12.00%
3 / 25
100.00% covered (success)
100.00%
1 / 1
40.39
 coerceMutableDate
100.00% covered (success)
100.00%
10 / 10
100.00% covered (success)
100.00%
14 / 14
8.00% covered (danger)
8.00%
2 / 25
100.00% covered (success)
100.00%
1 / 1
45.16
 coerceEnum
100.00% covered (success)
100.00%
18 / 18
100.00% covered (success)
100.00%
22 / 22
27.78% covered (danger)
27.78%
5 / 18
100.00% covered (success)
100.00%
1 / 1
47.67
 satisfies
100.00% covered (success)
100.00%
7 / 7
100.00% covered (success)
100.00%
9 / 9
100.00% covered (success)
100.00%
6 / 6
100.00% covered (success)
100.00%
1 / 1
7
 satisfiesSpecial
100.00% covered (success)
100.00%
7 / 7
100.00% covered (success)
100.00%
7 / 7
100.00% covered (success)
100.00%
4 / 4
100.00% covered (success)
100.00%
1 / 1
4
 matchesKind
100.00% covered (success)
100.00%
5 / 5
100.00% covered (success)
100.00%
5 / 5
75.00% covered (warning)
75.00%
3 / 4
100.00% covered (success)
100.00%
1 / 1
4.25
 intFromString
100.00% covered (success)
100.00%
5 / 5
87.50% covered (warning)
87.50%
7 / 8
50.00% covered (danger)
50.00%
3 / 6
100.00% covered (success)
100.00%
1 / 1
4.12
 integerStringFits
100.00% covered (success)
100.00%
9 / 9
85.00% covered (warning)
85.00%
17 / 20
9.26% covered (danger)
9.26%
5 / 54
100.00% covered (success)
100.00%
1 / 1
23.68
 fail
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
1<?php
2
3declare(strict_types=1);
4
5namespace Celema\Quma\Hydration;
6
7use BackedEnum;
8use Celema\Quma\Exception\InvalidTypeCoercion;
9use DateTime;
10use DateTimeImmutable;
11use DateTimeZone;
12use ReflectionEnum;
13use ReflectionNamedType;
14use ValueError;
15
16/** @internal */
17final class TypeCoercer
18{
19    private const array DATE_FORMATS = [
20        'Y-m-d H:i:s.uP',
21        'Y-m-d H:i:sP',
22        'Y-m-d\TH:i:s.uP',
23        'Y-m-d\TH:i:sP',
24        'Y-m-d H:i:s.u',
25        'Y-m-d H:i:s',
26        'Y-m-d',
27    ];
28
29    public function coerce(mixed $value, TypeMetadata $type, HydrationContext $context): mixed
30    {
31        if ($value === null) {
32            if ($type->allowsNull) {
33                return null;
34            }
35
36            $this->fail($value, $context, $type->describe(), 'null is not allowed');
37        }
38
39        if ($type->kind === 'union') {
40            return $this->coerceUnion($value, $type, $context);
41        }
42
43        return $this->coerceNamed($value, $type->names[0], $context, $type->describe());
44    }
45
46    private function coerceUnion(mixed $value, TypeMetadata $type, HydrationContext $context): mixed
47    {
48        foreach ($type->names as $name) {
49            if ($this->satisfies($value, $name)) {
50                return $value;
51            }
52        }
53
54        $lastFailure = null;
55
56        foreach (['enum', 'immutable', 'mutable', 'int', 'float', 'bool', 'string'] as $kind) {
57            foreach ($type->names as $name) {
58                if (!$this->matchesKind($name, $kind)) {
59                    continue;
60                }
61
62                try {
63                    return $this->coerceNamed($value, $name, $context, $type->describe());
64                } catch (InvalidTypeCoercion $e) {
65                    $lastFailure = $e->reason();
66                }
67            }
68        }
69
70        $reason = 'no union arm accepted the value';
71
72        if ($lastFailure !== null) {
73            $reason .= '; last failure: ' . $lastFailure;
74        }
75
76        $this->fail($value, $context, $type->describe(), $reason);
77    }
78
79    private function coerceNamed(
80        mixed $value,
81        NamedTypeMetadata $name,
82        HydrationContext $context,
83        string $description,
84    ): mixed {
85        return match ($name->scalar) {
86            'int' => $this->coerceInt($value, $context, $description),
87            'float' => $this->coerceFloat($value, $context, $description),
88            'bool' => $this->coerceBool($value, $context, $description),
89            'string' => $this->coerceString($value, $context, $description),
90            default => $this->coerceSpecial($value, $name, $context, $description),
91        };
92    }
93
94    private function coerceSpecial(
95        mixed $value,
96        NamedTypeMetadata $name,
97        HydrationContext $context,
98        string $description,
99    ): mixed {
100        if ($name->date === 'immutable') {
101            return $this->coerceImmutableDate($value, $context, $description);
102        }
103
104        if ($name->date === 'mutable') {
105            return $this->coerceMutableDate($value, $context, $description);
106        }
107
108        if ($name->enum !== null) {
109            return $this->coerceEnum($value, $name->enum, $context, $description);
110        }
111
112        $this->fail($value, $context, $description, 'unsupported declared type');
113    }
114
115    private function coerceInt(mixed $value, HydrationContext $context, string $description): int
116    {
117        if (is_int($value)) {
118            return $value;
119        }
120
121        if (is_string($value)) {
122            $int = $this->intFromString($value);
123
124            if ($int !== null) {
125                return $int;
126            }
127        }
128
129        $this->fail($value, $context, $description, 'expected int or decimal integer string');
130    }
131
132    private function coerceFloat(mixed $value, HydrationContext $context, string $description): float
133    {
134        if (is_float($value)) {
135            if (is_finite($value)) {
136                return $value;
137            }
138
139            $this->fail($value, $context, $description, 'float must be finite');
140        }
141
142        if (is_int($value)) {
143            return (float) $value;
144        }
145
146        if (is_string($value) && $value !== '' && trim($value) === $value && is_numeric($value)) {
147            $float = (float) $value;
148
149            if (is_finite($float)) {
150                return $float;
151            }
152        }
153
154        $this->fail($value, $context, $description, 'expected finite float, int, or numeric string');
155    }
156
157    private function coerceBool(mixed $value, HydrationContext $context, string $description): bool
158    {
159        if (is_bool($value)) {
160            return $value;
161        }
162
163        if (is_int($value) && ($value === 0 || $value === 1)) {
164            return $value === 1;
165        }
166
167        if (is_string($value)) {
168            return match ($value) {
169                '0', 'false', 'f' => false,
170                '1', 'true', 't' => true,
171                default => $this->fail(
172                    $value,
173                    $context,
174                    $description,
175                    'expected bool, 0/1, or lowercase true/false token',
176                ),
177            };
178        }
179
180        $this->fail($value, $context, $description, 'expected bool, 0/1, or lowercase true/false token');
181    }
182
183    private function coerceString(mixed $value, HydrationContext $context, string $description): string
184    {
185        if (is_string($value)) {
186            return $value;
187        }
188
189        if (is_int($value) || is_float($value)) {
190            return (string) $value;
191        }
192
193        if (is_bool($value)) {
194            return $value ? '1' : '0';
195        }
196
197        $this->fail($value, $context, $description, 'expected string or scalar value');
198    }
199
200    private function coerceImmutableDate(
201        mixed $value,
202        HydrationContext $context,
203        string $description,
204    ): DateTimeImmutable {
205        if ($value instanceof DateTimeImmutable) {
206            return $value;
207        }
208
209        if (!is_string($value) || $value === '') {
210            $this->fail($value, $context, $description, 'expected non-empty date string');
211        }
212
213        $timezone = new DateTimeZone(date_default_timezone_get());
214
215        foreach (self::DATE_FORMATS as $format) {
216            $date = DateTimeImmutable::createFromFormat('!' . $format, $value, $timezone);
217
218            // The round trip also rejects input that parsed with warnings or errors.
219            if ($date !== false && $date->format($format) === $value) {
220                return $date;
221            }
222        }
223
224        $this->fail($value, $context, $description, 'unsupported date/time format');
225    }
226
227    private function coerceMutableDate(
228        mixed $value,
229        HydrationContext $context,
230        string $description,
231    ): DateTime {
232        if ($value instanceof DateTime) {
233            return $value;
234        }
235
236        if (!is_string($value) || $value === '') {
237            $this->fail($value, $context, $description, 'expected non-empty date string');
238        }
239
240        $timezone = new DateTimeZone(date_default_timezone_get());
241
242        foreach (self::DATE_FORMATS as $format) {
243            $date = DateTime::createFromFormat('!' . $format, $value, $timezone);
244
245            // The round trip also rejects input that parsed with warnings or errors.
246            if ($date !== false && $date->format($format) === $value) {
247                return $date;
248            }
249        }
250
251        $this->fail($value, $context, $description, 'unsupported date/time format');
252    }
253
254    /** @param class-string<BackedEnum> $enum */
255    private function coerceEnum(
256        mixed $value,
257        string $enum,
258        HydrationContext $context,
259        string $description,
260    ): BackedEnum {
261        if ($value instanceof $enum) {
262            return $value;
263        }
264
265        $backing = new ReflectionEnum($enum)->getBackingType();
266        $backingType = $backing instanceof ReflectionNamedType ? $backing->getName() : null;
267
268        if ($backingType === 'int') {
269            if (is_int($value)) {
270                $backingValue = $value;
271            } elseif (is_string($value) && ($int = $this->intFromString($value)) !== null) {
272                $backingValue = $int;
273            } else {
274                $this->fail($value, $context, $description, 'expected int enum backing value');
275            }
276        } elseif ($backingType === 'string') {
277            if (!is_string($value)) {
278                $this->fail($value, $context, $description, 'expected string enum backing value');
279            }
280
281            $backingValue = $value;
282        } else {
283            $this->fail($value, $context, $description, 'enum is not backed');
284        }
285
286        try {
287            return $enum::from($backingValue);
288        } catch (ValueError) {
289            $this->fail($value, $context, $description, 'no enum case matches the backing value');
290        }
291    }
292
293    private function satisfies(mixed $value, NamedTypeMetadata $name): bool
294    {
295        return match ($name->scalar) {
296            'int' => is_int($value),
297            'float' => is_float($value) && is_finite($value),
298            'bool' => is_bool($value),
299            'string' => is_string($value),
300            default => $this->satisfiesSpecial($value, $name),
301        };
302    }
303
304    private function satisfiesSpecial(mixed $value, NamedTypeMetadata $name): bool
305    {
306        if ($name->date === 'immutable') {
307            return $value instanceof DateTimeImmutable;
308        }
309
310        if ($name->date === 'mutable') {
311            return $value instanceof DateTime;
312        }
313
314        if ($name->enum !== null) {
315            return $value instanceof $name->enum;
316        }
317
318        return false;
319    }
320
321    private function matchesKind(NamedTypeMetadata $name, string $kind): bool
322    {
323        return match ($kind) {
324            'enum' => $name->enum !== null,
325            'immutable', 'mutable' => $name->date === $kind,
326            default => $name->scalar === $kind,
327        };
328    }
329
330    private function intFromString(string $value): ?int
331    {
332        if (!preg_match('/^-?\d+$/', $value)) {
333            return null;
334        }
335
336        if (!$this->integerStringFits($value)) {
337            return null;
338        }
339
340        return (int) $value;
341    }
342
343    private function integerStringFits(string $value): bool
344    {
345        $negative = str_starts_with($value, '-');
346        $digits = $negative ? substr($value, 1) : $value;
347        $digits = ltrim($digits, '0');
348        $limit = $negative ? substr((string) PHP_INT_MIN, 1) : (string) PHP_INT_MAX;
349
350        return (
351            strlen($digits) < strlen($limit)
352                || strlen($digits) === strlen($limit)
353                && strcmp($digits, $limit) <= 0
354        );
355    }
356
357    private function fail(
358        mixed $value,
359        HydrationContext $context,
360        string $description,
361        string $reason,
362    ): never {
363        throw InvalidTypeCoercion::forContext($context, $value, $description, $reason);
364    }
365}

Paths

Below are the source code lines that represent each code path as identified by Xdebug. Please note a path is not necessarily coterminous with a line, a line may contain multiple paths and therefore show up more than once. Please also be aware that some paths may include implicit rather than explicit branches, e.g. an if statement always has an else as part of its logical flow even if you didn't write one.

TypeCoercer->coerce
29    public function coerce(mixed $value, TypeMetadata $type, HydrationContext $context): mixed
30    {
31        if ($value === null) {
 
32            if ($type->allowsNull) {
 
33                return null;
29    public function coerce(mixed $value, TypeMetadata $type, HydrationContext $context): mixed
30    {
31        if ($value === null) {
 
32            if ($type->allowsNull) {
 
36            $this->fail($value, $context, $type->describe(), 'null is not allowed');
37        }
38
39        if ($type->kind === 'union') {
 
39        if ($type->kind === 'union') {
 
40            return $this->coerceUnion($value, $type, $context);
29    public function coerce(mixed $value, TypeMetadata $type, HydrationContext $context): mixed
30    {
31        if ($value === null) {
 
32            if ($type->allowsNull) {
 
36            $this->fail($value, $context, $type->describe(), 'null is not allowed');
37        }
38
39        if ($type->kind === 'union') {
 
39        if ($type->kind === 'union') {
 
43        return $this->coerceNamed($value, $type->names[0], $context, $type->describe());
44    }
29    public function coerce(mixed $value, TypeMetadata $type, HydrationContext $context): mixed
30    {
31        if ($value === null) {
 
39        if ($type->kind === 'union') {
 
40            return $this->coerceUnion($value, $type, $context);
29    public function coerce(mixed $value, TypeMetadata $type, HydrationContext $context): mixed
30    {
31        if ($value === null) {
 
39        if ($type->kind === 'union') {
 
43        return $this->coerceNamed($value, $type->names[0], $context, $type->describe());
44    }
TypeCoercer->coerceBool
157    private function coerceBool(mixed $value, HydrationContext $context, string $description): bool
158    {
159        if (is_bool($value)) {
 
160            return $value;
157    private function coerceBool(mixed $value, HydrationContext $context, string $description): bool
158    {
159        if (is_bool($value)) {
 
163        if (is_int($value) && ($value === 0 || $value === 1)) {
 
163        if (is_int($value) && ($value === 0 || $value === 1)) {
 
163        if (is_int($value) && ($value === 0 || $value === 1)) {
 
163        if (is_int($value) && ($value === 0 || $value === 1)) {
 
163        if (is_int($value) && ($value === 0 || $value === 1)) {
 
164            return $value === 1;
157    private function coerceBool(mixed $value, HydrationContext $context, string $description): bool
158    {
159        if (is_bool($value)) {
 
163        if (is_int($value) && ($value === 0 || $value === 1)) {
 
163        if (is_int($value) && ($value === 0 || $value === 1)) {
 
163        if (is_int($value) && ($value === 0 || $value === 1)) {
 
163        if (is_int($value) && ($value === 0 || $value === 1)) {
 
163        if (is_int($value) && ($value === 0 || $value === 1)) {
 
167        if (is_string($value)) {
 
168            return match ($value) {
 
169                '0', 'false', 'f' => false,
 
175                    'expected bool, 0/1, or lowercase true/false token',
157    private function coerceBool(mixed $value, HydrationContext $context, string $description): bool
158    {
159        if (is_bool($value)) {
 
163        if (is_int($value) && ($value === 0 || $value === 1)) {
 
163        if (is_int($value) && ($value === 0 || $value === 1)) {
 
163        if (is_int($value) && ($value === 0 || $value === 1)) {
 
163        if (is_int($value) && ($value === 0 || $value === 1)) {
 
163        if (is_int($value) && ($value === 0 || $value === 1)) {
 
167        if (is_string($value)) {
 
168            return match ($value) {
 
169                '0', 'false', 'f' => false,
 
175                    'expected bool, 0/1, or lowercase true/false token',
157    private function coerceBool(mixed $value, HydrationContext $context, string $description): bool
158    {
159        if (is_bool($value)) {
 
163        if (is_int($value) && ($value === 0 || $value === 1)) {
 
163        if (is_int($value) && ($value === 0 || $value === 1)) {
 
163        if (is_int($value) && ($value === 0 || $value === 1)) {
 
163        if (is_int($value) && ($value === 0 || $value === 1)) {
 
163        if (is_int($value) && ($value === 0 || $value === 1)) {
 
167        if (is_string($value)) {
 
168            return match ($value) {
 
169                '0', 'false', 'f' => false,
 
175                    'expected bool, 0/1, or lowercase true/false token',
157    private function coerceBool(mixed $value, HydrationContext $context, string $description): bool
158    {
159        if (is_bool($value)) {
 
163        if (is_int($value) && ($value === 0 || $value === 1)) {
 
163        if (is_int($value) && ($value === 0 || $value === 1)) {
 
163        if (is_int($value) && ($value === 0 || $value === 1)) {
 
163        if (is_int($value) && ($value === 0 || $value === 1)) {
 
163        if (is_int($value) && ($value === 0 || $value === 1)) {
 
167        if (is_string($value)) {
 
168            return match ($value) {
 
170                '1', 'true', 't' => true,
 
175                    'expected bool, 0/1, or lowercase true/false token',
157    private function coerceBool(mixed $value, HydrationContext $context, string $description): bool
158    {
159        if (is_bool($value)) {
 
163        if (is_int($value) && ($value === 0 || $value === 1)) {
 
163        if (is_int($value) && ($value === 0 || $value === 1)) {
 
163        if (is_int($value) && ($value === 0 || $value === 1)) {
 
163        if (is_int($value) && ($value === 0 || $value === 1)) {
 
163        if (is_int($value) && ($value === 0 || $value === 1)) {
 
167        if (is_string($value)) {
 
168            return match ($value) {
 
170                '1', 'true', 't' => true,
 
175                    'expected bool, 0/1, or lowercase true/false token',
157    private function coerceBool(mixed $value, HydrationContext $context, string $description): bool
158    {
159        if (is_bool($value)) {
 
163        if (is_int($value) && ($value === 0 || $value === 1)) {
 
163        if (is_int($value) && ($value === 0 || $value === 1)) {
 
163        if (is_int($value) && ($value === 0 || $value === 1)) {
 
163        if (is_int($value) && ($value === 0 || $value === 1)) {
 
163        if (is_int($value) && ($value === 0 || $value === 1)) {
 
167        if (is_string($value)) {
 
168            return match ($value) {
 
170                '1', 'true', 't' => true,
 
175                    'expected bool, 0/1, or lowercase true/false token',
157    private function coerceBool(mixed $value, HydrationContext $context, string $description): bool
158    {
159        if (is_bool($value)) {
 
163        if (is_int($value) && ($value === 0 || $value === 1)) {
 
163        if (is_int($value) && ($value === 0 || $value === 1)) {
 
163        if (is_int($value) && ($value === 0 || $value === 1)) {
 
163        if (is_int($value) && ($value === 0 || $value === 1)) {
 
163        if (is_int($value) && ($value === 0 || $value === 1)) {
 
167        if (is_string($value)) {
 
168            return match ($value) {
 
171                default => $this->fail(
172                    $value,
173                    $context,
174                    $description,
175                    'expected bool, 0/1, or lowercase true/false token',
 
175                    'expected bool, 0/1, or lowercase true/false token',
157    private function coerceBool(mixed $value, HydrationContext $context, string $description): bool
158    {
159        if (is_bool($value)) {
 
163        if (is_int($value) && ($value === 0 || $value === 1)) {
 
163        if (is_int($value) && ($value === 0 || $value === 1)) {
 
163        if (is_int($value) && ($value === 0 || $value === 1)) {
 
163        if (is_int($value) && ($value === 0 || $value === 1)) {
 
163        if (is_int($value) && ($value === 0 || $value === 1)) {
 
167        if (is_string($value)) {
 
180        $this->fail($value, $context, $description, 'expected bool, 0/1, or lowercase true/false token');
181    }
157    private function coerceBool(mixed $value, HydrationContext $context, string $description): bool
158    {
159        if (is_bool($value)) {
 
163        if (is_int($value) && ($value === 0 || $value === 1)) {
 
163        if (is_int($value) && ($value === 0 || $value === 1)) {
 
163        if (is_int($value) && ($value === 0 || $value === 1)) {
 
163        if (is_int($value) && ($value === 0 || $value === 1)) {
 
164            return $value === 1;
157    private function coerceBool(mixed $value, HydrationContext $context, string $description): bool
158    {
159        if (is_bool($value)) {
 
163        if (is_int($value) && ($value === 0 || $value === 1)) {
 
163        if (is_int($value) && ($value === 0 || $value === 1)) {
 
163        if (is_int($value) && ($value === 0 || $value === 1)) {
 
163        if (is_int($value) && ($value === 0 || $value === 1)) {
 
167        if (is_string($value)) {
 
168            return match ($value) {
 
169                '0', 'false', 'f' => false,
 
175                    'expected bool, 0/1, or lowercase true/false token',
157    private function coerceBool(mixed $value, HydrationContext $context, string $description): bool
158    {
159        if (is_bool($value)) {
 
163        if (is_int($value) && ($value === 0 || $value === 1)) {
 
163        if (is_int($value) && ($value === 0 || $value === 1)) {
 
163        if (is_int($value) && ($value === 0 || $value === 1)) {
 
163        if (is_int($value) && ($value === 0 || $value === 1)) {
 
167        if (is_string($value)) {
 
168            return match ($value) {
 
169                '0', 'false', 'f' => false,
 
175                    'expected bool, 0/1, or lowercase true/false token',
157    private function coerceBool(mixed $value, HydrationContext $context, string $description): bool
158    {
159        if (is_bool($value)) {
 
163        if (is_int($value) && ($value === 0 || $value === 1)) {
 
163        if (is_int($value) && ($value === 0 || $value === 1)) {
 
163        if (is_int($value) && ($value === 0 || $value === 1)) {
 
163        if (is_int($value) && ($value === 0 || $value === 1)) {
 
167        if (is_string($value)) {
 
168            return match ($value) {
 
169                '0', 'false', 'f' => false,
 
175                    'expected bool, 0/1, or lowercase true/false token',
157    private function coerceBool(mixed $value, HydrationContext $context, string $description): bool
158    {
159        if (is_bool($value)) {
 
163        if (is_int($value) && ($value === 0 || $value === 1)) {
 
163        if (is_int($value) && ($value === 0 || $value === 1)) {
 
163        if (is_int($value) && ($value === 0 || $value === 1)) {
 
163        if (is_int($value) && ($value === 0 || $value === 1)) {
 
167        if (is_string($value)) {
 
168            return match ($value) {
 
170                '1', 'true', 't' => true,
 
175                    'expected bool, 0/1, or lowercase true/false token',
157    private function coerceBool(mixed $value, HydrationContext $context, string $description): bool
158    {
159        if (is_bool($value)) {
 
163        if (is_int($value) && ($value === 0 || $value === 1)) {
 
163        if (is_int($value) && ($value === 0 || $value === 1)) {
 
163        if (is_int($value) && ($value === 0 || $value === 1)) {
 
163        if (is_int($value) && ($value === 0 || $value === 1)) {
 
167        if (is_string($value)) {
 
168            return match ($value) {
 
170                '1', 'true', 't' => true,
 
175                    'expected bool, 0/1, or lowercase true/false token',
157    private function coerceBool(mixed $value, HydrationContext $context, string $description): bool
158    {
159        if (is_bool($value)) {
 
163        if (is_int($value) && ($value === 0 || $value === 1)) {
 
163        if (is_int($value) && ($value === 0 || $value === 1)) {
 
163        if (is_int($value) && ($value === 0 || $value === 1)) {
 
163        if (is_int($value) && ($value === 0 || $value === 1)) {
 
167        if (is_string($value)) {
 
168            return match ($value) {
 
170                '1', 'true', 't' => true,
 
175                    'expected bool, 0/1, or lowercase true/false token',
157    private function coerceBool(mixed $value, HydrationContext $context, string $description): bool
158    {
159        if (is_bool($value)) {
 
163        if (is_int($value) && ($value === 0 || $value === 1)) {
 
163        if (is_int($value) && ($value === 0 || $value === 1)) {
 
163        if (is_int($value) && ($value === 0 || $value === 1)) {
 
163        if (is_int($value) && ($value === 0 || $value === 1)) {
 
167        if (is_string($value)) {
 
168            return match ($value) {
 
171                default => $this->fail(
172                    $value,
173                    $context,
174                    $description,
175                    'expected bool, 0/1, or lowercase true/false token',
 
175                    'expected bool, 0/1, or lowercase true/false token',
157    private function coerceBool(mixed $value, HydrationContext $context, string $description): bool
158    {
159        if (is_bool($value)) {
 
163        if (is_int($value) && ($value === 0 || $value === 1)) {
 
163        if (is_int($value) && ($value === 0 || $value === 1)) {
 
163        if (is_int($value) && ($value === 0 || $value === 1)) {
 
163        if (is_int($value) && ($value === 0 || $value === 1)) {
 
167        if (is_string($value)) {
 
180        $this->fail($value, $context, $description, 'expected bool, 0/1, or lowercase true/false token');
181    }
157    private function coerceBool(mixed $value, HydrationContext $context, string $description): bool
158    {
159        if (is_bool($value)) {
 
163        if (is_int($value) && ($value === 0 || $value === 1)) {
 
163        if (is_int($value) && ($value === 0 || $value === 1)) {
 
164            return $value === 1;
157    private function coerceBool(mixed $value, HydrationContext $context, string $description): bool
158    {
159        if (is_bool($value)) {
 
163        if (is_int($value) && ($value === 0 || $value === 1)) {
 
163        if (is_int($value) && ($value === 0 || $value === 1)) {
 
167        if (is_string($value)) {
 
168            return match ($value) {
 
169                '0', 'false', 'f' => false,
 
175                    'expected bool, 0/1, or lowercase true/false token',
157    private function coerceBool(mixed $value, HydrationContext $context, string $description): bool
158    {
159        if (is_bool($value)) {
 
163        if (is_int($value) && ($value === 0 || $value === 1)) {
 
163        if (is_int($value) && ($value === 0 || $value === 1)) {
 
167        if (is_string($value)) {
 
168            return match ($value) {
 
169                '0', 'false', 'f' => false,
 
175                    'expected bool, 0/1, or lowercase true/false token',
157    private function coerceBool(mixed $value, HydrationContext $context, string $description): bool
158    {
159        if (is_bool($value)) {
 
163        if (is_int($value) && ($value === 0 || $value === 1)) {
 
163        if (is_int($value) && ($value === 0 || $value === 1)) {
 
167        if (is_string($value)) {
 
168            return match ($value) {
 
169                '0', 'false', 'f' => false,
 
175                    'expected bool, 0/1, or lowercase true/false token',
157    private function coerceBool(mixed $value, HydrationContext $context, string $description): bool
158    {
159        if (is_bool($value)) {
 
163        if (is_int($value) && ($value === 0 || $value === 1)) {
 
163        if (is_int($value) && ($value === 0 || $value === 1)) {
 
167        if (is_string($value)) {
 
168            return match ($value) {
 
170                '1', 'true', 't' => true,
 
175                    'expected bool, 0/1, or lowercase true/false token',
157    private function coerceBool(mixed $value, HydrationContext $context, string $description): bool
158    {
159        if (is_bool($value)) {
 
163        if (is_int($value) && ($value === 0 || $value === 1)) {
 
163        if (is_int($value) && ($value === 0 || $value === 1)) {
 
167        if (is_string($value)) {
 
168            return match ($value) {
 
170                '1', 'true', 't' => true,
 
175                    'expected bool, 0/1, or lowercase true/false token',
157    private function coerceBool(mixed $value, HydrationContext $context, string $description): bool
158    {
159        if (is_bool($value)) {
 
163        if (is_int($value) && ($value === 0 || $value === 1)) {
 
163        if (is_int($value) && ($value === 0 || $value === 1)) {
 
167        if (is_string($value)) {
 
168            return match ($value) {
 
170                '1', 'true', 't' => true,
 
175                    'expected bool, 0/1, or lowercase true/false token',
157    private function coerceBool(mixed $value, HydrationContext $context, string $description): bool
158    {
159        if (is_bool($value)) {
 
163        if (is_int($value) && ($value === 0 || $value === 1)) {
 
163        if (is_int($value) && ($value === 0 || $value === 1)) {
 
167        if (is_string($value)) {
 
168            return match ($value) {
 
171                default => $this->fail(
172                    $value,
173                    $context,
174                    $description,
175                    'expected bool, 0/1, or lowercase true/false token',
 
175                    'expected bool, 0/1, or lowercase true/false token',
157    private function coerceBool(mixed $value, HydrationContext $context, string $description): bool
158    {
159        if (is_bool($value)) {
 
163        if (is_int($value) && ($value === 0 || $value === 1)) {
 
163        if (is_int($value) && ($value === 0 || $value === 1)) {
 
167        if (is_string($value)) {
 
180        $this->fail($value, $context, $description, 'expected bool, 0/1, or lowercase true/false token');
181    }
TypeCoercer->coerceEnum
256        mixed $value,
257        string $enum,
258        HydrationContext $context,
259        string $description,
260    ): BackedEnum {
261        if ($value instanceof $enum) {
 
262            return $value;
256        mixed $value,
257        string $enum,
258        HydrationContext $context,
259        string $description,
260    ): BackedEnum {
261        if ($value instanceof $enum) {
 
265        $backing = new ReflectionEnum($enum)->getBackingType();
266        $backingType = $backing instanceof ReflectionNamedType ? $backing->getName() : null;
 
266        $backingType = $backing instanceof ReflectionNamedType ? $backing->getName() : null;
 
266        $backingType = $backing instanceof ReflectionNamedType ? $backing->getName() : null;
267
268        if ($backingType === 'int') {
 
269            if (is_int($value)) {
 
269            if (is_int($value)) {
270                $backingValue = $value;
 
268        if ($backingType === 'int') {
 
286        try {
287            return $enum::from($backingValue);
256        mixed $value,
257        string $enum,
258        HydrationContext $context,
259        string $description,
260    ): BackedEnum {
261        if ($value instanceof $enum) {
 
265        $backing = new ReflectionEnum($enum)->getBackingType();
266        $backingType = $backing instanceof ReflectionNamedType ? $backing->getName() : null;
 
266        $backingType = $backing instanceof ReflectionNamedType ? $backing->getName() : null;
 
266        $backingType = $backing instanceof ReflectionNamedType ? $backing->getName() : null;
267
268        if ($backingType === 'int') {
 
269            if (is_int($value)) {
 
271            } elseif (is_string($value) && ($int = $this->intFromString($value)) !== null) {
 
271            } elseif (is_string($value) && ($int = $this->intFromString($value)) !== null) {
 
271            } elseif (is_string($value) && ($int = $this->intFromString($value)) !== null) {
 
271            } elseif (is_string($value) && ($int = $this->intFromString($value)) !== null) {
272                $backingValue = $int;
 
268        if ($backingType === 'int') {
 
286        try {
287            return $enum::from($backingValue);
256        mixed $value,
257        string $enum,
258        HydrationContext $context,
259        string $description,
260    ): BackedEnum {
261        if ($value instanceof $enum) {
 
265        $backing = new ReflectionEnum($enum)->getBackingType();
266        $backingType = $backing instanceof ReflectionNamedType ? $backing->getName() : null;
 
266        $backingType = $backing instanceof ReflectionNamedType ? $backing->getName() : null;
 
266        $backingType = $backing instanceof ReflectionNamedType ? $backing->getName() : null;
267
268        if ($backingType === 'int') {
 
269            if (is_int($value)) {
 
271            } elseif (is_string($value) && ($int = $this->intFromString($value)) !== null) {
 
271            } elseif (is_string($value) && ($int = $this->intFromString($value)) !== null) {
 
271            } elseif (is_string($value) && ($int = $this->intFromString($value)) !== null) {
 
268        if ($backingType === 'int') {
269            if (is_int($value)) {
270                $backingValue = $value;
271            } elseif (is_string($value) && ($int = $this->intFromString($value)) !== null) {
272                $backingValue = $int;
273            } else {
274                $this->fail($value, $context, $description, 'expected int enum backing value');
 
268        if ($backingType === 'int') {
 
286        try {
287            return $enum::from($backingValue);
256        mixed $value,
257        string $enum,
258        HydrationContext $context,
259        string $description,
260    ): BackedEnum {
261        if ($value instanceof $enum) {
 
265        $backing = new ReflectionEnum($enum)->getBackingType();
266        $backingType = $backing instanceof ReflectionNamedType ? $backing->getName() : null;
 
266        $backingType = $backing instanceof ReflectionNamedType ? $backing->getName() : null;
 
266        $backingType = $backing instanceof ReflectionNamedType ? $backing->getName() : null;
267
268        if ($backingType === 'int') {
 
269            if (is_int($value)) {
 
271            } elseif (is_string($value) && ($int = $this->intFromString($value)) !== null) {
 
271            } elseif (is_string($value) && ($int = $this->intFromString($value)) !== null) {
 
271            } elseif (is_string($value) && ($int = $this->intFromString($value)) !== null) {
272                $backingValue = $int;
 
268        if ($backingType === 'int') {
 
286        try {
287            return $enum::from($backingValue);
256        mixed $value,
257        string $enum,
258        HydrationContext $context,
259        string $description,
260    ): BackedEnum {
261        if ($value instanceof $enum) {
 
265        $backing = new ReflectionEnum($enum)->getBackingType();
266        $backingType = $backing instanceof ReflectionNamedType ? $backing->getName() : null;
 
266        $backingType = $backing instanceof ReflectionNamedType ? $backing->getName() : null;
 
266        $backingType = $backing instanceof ReflectionNamedType ? $backing->getName() : null;
267
268        if ($backingType === 'int') {
 
269            if (is_int($value)) {
 
271            } elseif (is_string($value) && ($int = $this->intFromString($value)) !== null) {
 
271            } elseif (is_string($value) && ($int = $this->intFromString($value)) !== null) {
 
268        if ($backingType === 'int') {
269            if (is_int($value)) {
270                $backingValue = $value;
271            } elseif (is_string($value) && ($int = $this->intFromString($value)) !== null) {
272                $backingValue = $int;
273            } else {
274                $this->fail($value, $context, $description, 'expected int enum backing value');
 
268        if ($backingType === 'int') {
 
286        try {
287            return $enum::from($backingValue);
256        mixed $value,
257        string $enum,
258        HydrationContext $context,
259        string $description,
260    ): BackedEnum {
261        if ($value instanceof $enum) {
 
265        $backing = new ReflectionEnum($enum)->getBackingType();
266        $backingType = $backing instanceof ReflectionNamedType ? $backing->getName() : null;
 
266        $backingType = $backing instanceof ReflectionNamedType ? $backing->getName() : null;
 
266        $backingType = $backing instanceof ReflectionNamedType ? $backing->getName() : null;
267
268        if ($backingType === 'int') {
 
276        } elseif ($backingType === 'string') {
 
277            if (!is_string($value)) {
 
278                $this->fail($value, $context, $description, 'expected string enum backing value');
279            }
280
281            $backingValue = $value;
 
276        } elseif ($backingType === 'string') {
277            if (!is_string($value)) {
278                $this->fail($value, $context, $description, 'expected string enum backing value');
279            }
280
281            $backingValue = $value;
 
286        try {
287            return $enum::from($backingValue);
256        mixed $value,
257        string $enum,
258        HydrationContext $context,
259        string $description,
260    ): BackedEnum {
261        if ($value instanceof $enum) {
 
265        $backing = new ReflectionEnum($enum)->getBackingType();
266        $backingType = $backing instanceof ReflectionNamedType ? $backing->getName() : null;
 
266        $backingType = $backing instanceof ReflectionNamedType ? $backing->getName() : null;
 
266        $backingType = $backing instanceof ReflectionNamedType ? $backing->getName() : null;
267
268        if ($backingType === 'int') {
 
276        } elseif ($backingType === 'string') {
 
277            if (!is_string($value)) {
 
276        } elseif ($backingType === 'string') {
277            if (!is_string($value)) {
278                $this->fail($value, $context, $description, 'expected string enum backing value');
279            }
280
281            $backingValue = $value;
 
286        try {
287            return $enum::from($backingValue);
256        mixed $value,
257        string $enum,
258        HydrationContext $context,
259        string $description,
260    ): BackedEnum {
261        if ($value instanceof $enum) {
 
265        $backing = new ReflectionEnum($enum)->getBackingType();
266        $backingType = $backing instanceof ReflectionNamedType ? $backing->getName() : null;
 
266        $backingType = $backing instanceof ReflectionNamedType ? $backing->getName() : null;
 
266        $backingType = $backing instanceof ReflectionNamedType ? $backing->getName() : null;
267
268        if ($backingType === 'int') {
 
276        } elseif ($backingType === 'string') {
 
283            $this->fail($value, $context, $description, 'enum is not backed');
284        }
285
286        try {
 
286        try {
287            return $enum::from($backingValue);
256        mixed $value,
257        string $enum,
258        HydrationContext $context,
259        string $description,
260    ): BackedEnum {
261        if ($value instanceof $enum) {
 
265        $backing = new ReflectionEnum($enum)->getBackingType();
266        $backingType = $backing instanceof ReflectionNamedType ? $backing->getName() : null;
 
266        $backingType = $backing instanceof ReflectionNamedType ? $backing->getName() : null;
 
266        $backingType = $backing instanceof ReflectionNamedType ? $backing->getName() : null;
267
268        if ($backingType === 'int') {
 
269            if (is_int($value)) {
 
269            if (is_int($value)) {
270                $backingValue = $value;
 
268        if ($backingType === 'int') {
 
286        try {
287            return $enum::from($backingValue);
256        mixed $value,
257        string $enum,
258        HydrationContext $context,
259        string $description,
260    ): BackedEnum {
261        if ($value instanceof $enum) {
 
265        $backing = new ReflectionEnum($enum)->getBackingType();
266        $backingType = $backing instanceof ReflectionNamedType ? $backing->getName() : null;
 
266        $backingType = $backing instanceof ReflectionNamedType ? $backing->getName() : null;
 
266        $backingType = $backing instanceof ReflectionNamedType ? $backing->getName() : null;
267
268        if ($backingType === 'int') {
 
269            if (is_int($value)) {
 
271            } elseif (is_string($value) && ($int = $this->intFromString($value)) !== null) {
 
271            } elseif (is_string($value) && ($int = $this->intFromString($value)) !== null) {
 
271            } elseif (is_string($value) && ($int = $this->intFromString($value)) !== null) {
 
271            } elseif (is_string($value) && ($int = $this->intFromString($value)) !== null) {
272                $backingValue = $int;
 
268        if ($backingType === 'int') {
 
286        try {
287            return $enum::from($backingValue);
256        mixed $value,
257        string $enum,
258        HydrationContext $context,
259        string $description,
260    ): BackedEnum {
261        if ($value instanceof $enum) {
 
265        $backing = new ReflectionEnum($enum)->getBackingType();
266        $backingType = $backing instanceof ReflectionNamedType ? $backing->getName() : null;
 
266        $backingType = $backing instanceof ReflectionNamedType ? $backing->getName() : null;
 
266        $backingType = $backing instanceof ReflectionNamedType ? $backing->getName() : null;
267
268        if ($backingType === 'int') {
 
269            if (is_int($value)) {
 
271            } elseif (is_string($value) && ($int = $this->intFromString($value)) !== null) {
 
271            } elseif (is_string($value) && ($int = $this->intFromString($value)) !== null) {
 
271            } elseif (is_string($value) && ($int = $this->intFromString($value)) !== null) {
 
268        if ($backingType === 'int') {
269            if (is_int($value)) {
270                $backingValue = $value;
271            } elseif (is_string($value) && ($int = $this->intFromString($value)) !== null) {
272                $backingValue = $int;
273            } else {
274                $this->fail($value, $context, $description, 'expected int enum backing value');
 
268        if ($backingType === 'int') {
 
286        try {
287            return $enum::from($backingValue);
256        mixed $value,
257        string $enum,
258        HydrationContext $context,
259        string $description,
260    ): BackedEnum {
261        if ($value instanceof $enum) {
 
265        $backing = new ReflectionEnum($enum)->getBackingType();
266        $backingType = $backing instanceof ReflectionNamedType ? $backing->getName() : null;
 
266        $backingType = $backing instanceof ReflectionNamedType ? $backing->getName() : null;
 
266        $backingType = $backing instanceof ReflectionNamedType ? $backing->getName() : null;
267
268        if ($backingType === 'int') {
 
269            if (is_int($value)) {
 
271            } elseif (is_string($value) && ($int = $this->intFromString($value)) !== null) {
 
271            } elseif (is_string($value) && ($int = $this->intFromString($value)) !== null) {
 
271            } elseif (is_string($value) && ($int = $this->intFromString($value)) !== null) {
272                $backingValue = $int;
 
268        if ($backingType === 'int') {
 
286        try {
287            return $enum::from($backingValue);
256        mixed $value,
257        string $enum,
258        HydrationContext $context,
259        string $description,
260    ): BackedEnum {
261        if ($value instanceof $enum) {
 
265        $backing = new ReflectionEnum($enum)->getBackingType();
266        $backingType = $backing instanceof ReflectionNamedType ? $backing->getName() : null;
 
266        $backingType = $backing instanceof ReflectionNamedType ? $backing->getName() : null;
 
266        $backingType = $backing instanceof ReflectionNamedType ? $backing->getName() : null;
267
268        if ($backingType === 'int') {
 
269            if (is_int($value)) {
 
271            } elseif (is_string($value) && ($int = $this->intFromString($value)) !== null) {
 
271            } elseif (is_string($value) && ($int = $this->intFromString($value)) !== null) {
 
268        if ($backingType === 'int') {
269            if (is_int($value)) {
270                $backingValue = $value;
271            } elseif (is_string($value) && ($int = $this->intFromString($value)) !== null) {
272                $backingValue = $int;
273            } else {
274                $this->fail($value, $context, $description, 'expected int enum backing value');
 
268        if ($backingType === 'int') {
 
286        try {
287            return $enum::from($backingValue);
256        mixed $value,
257        string $enum,
258        HydrationContext $context,
259        string $description,
260    ): BackedEnum {
261        if ($value instanceof $enum) {
 
265        $backing = new ReflectionEnum($enum)->getBackingType();
266        $backingType = $backing instanceof ReflectionNamedType ? $backing->getName() : null;
 
266        $backingType = $backing instanceof ReflectionNamedType ? $backing->getName() : null;
 
266        $backingType = $backing instanceof ReflectionNamedType ? $backing->getName() : null;
267
268        if ($backingType === 'int') {
 
276        } elseif ($backingType === 'string') {
 
277            if (!is_string($value)) {
 
278                $this->fail($value, $context, $description, 'expected string enum backing value');
279            }
280
281            $backingValue = $value;
 
276        } elseif ($backingType === 'string') {
277            if (!is_string($value)) {
278                $this->fail($value, $context, $description, 'expected string enum backing value');
279            }
280
281            $backingValue = $value;
 
286        try {
287            return $enum::from($backingValue);
256        mixed $value,
257        string $enum,
258        HydrationContext $context,
259        string $description,
260    ): BackedEnum {
261        if ($value instanceof $enum) {
 
265        $backing = new ReflectionEnum($enum)->getBackingType();
266        $backingType = $backing instanceof ReflectionNamedType ? $backing->getName() : null;
 
266        $backingType = $backing instanceof ReflectionNamedType ? $backing->getName() : null;
 
266        $backingType = $backing instanceof ReflectionNamedType ? $backing->getName() : null;
267
268        if ($backingType === 'int') {
 
276        } elseif ($backingType === 'string') {
 
277            if (!is_string($value)) {
 
276        } elseif ($backingType === 'string') {
277            if (!is_string($value)) {
278                $this->fail($value, $context, $description, 'expected string enum backing value');
279            }
280
281            $backingValue = $value;
 
286        try {
287            return $enum::from($backingValue);
256        mixed $value,
257        string $enum,
258        HydrationContext $context,
259        string $description,
260    ): BackedEnum {
261        if ($value instanceof $enum) {
 
265        $backing = new ReflectionEnum($enum)->getBackingType();
266        $backingType = $backing instanceof ReflectionNamedType ? $backing->getName() : null;
 
266        $backingType = $backing instanceof ReflectionNamedType ? $backing->getName() : null;
 
266        $backingType = $backing instanceof ReflectionNamedType ? $backing->getName() : null;
267
268        if ($backingType === 'int') {
 
276        } elseif ($backingType === 'string') {
 
283            $this->fail($value, $context, $description, 'enum is not backed');
284        }
285
286        try {
 
286        try {
287            return $enum::from($backingValue);
288        } catch (ValueError) {
 
289            $this->fail($value, $context, $description, 'no enum case matches the backing value');
290        }
291    }
TypeCoercer->coerceImmutableDate
201        mixed $value,
202        HydrationContext $context,
203        string $description,
204    ): DateTimeImmutable {
205        if ($value instanceof DateTimeImmutable) {
 
206            return $value;
201        mixed $value,
202        HydrationContext $context,
203        string $description,
204    ): DateTimeImmutable {
205        if ($value instanceof DateTimeImmutable) {
 
209        if (!is_string($value) || $value === '') {
 
209        if (!is_string($value) || $value === '') {
 
209        if (!is_string($value) || $value === '') {
 
210            $this->fail($value, $context, $description, 'expected non-empty date string');
211        }
212
213        $timezone = new DateTimeZone(date_default_timezone_get());
 
213        $timezone = new DateTimeZone(date_default_timezone_get());
214
215        foreach (self::DATE_FORMATS as $format) {
 
215        foreach (self::DATE_FORMATS as $format) {
 
216            $date = DateTimeImmutable::createFromFormat('!' . $format, $value, $timezone);
217
218            // The round trip also rejects input that parsed with warnings or errors.
219            if ($date !== false && $date->format($format) === $value) {
 
219            if ($date !== false && $date->format($format) === $value) {
 
219            if ($date !== false && $date->format($format) === $value) {
 
220                return $date;
201        mixed $value,
202        HydrationContext $context,
203        string $description,
204    ): DateTimeImmutable {
205        if ($value instanceof DateTimeImmutable) {
 
209        if (!is_string($value) || $value === '') {
 
209        if (!is_string($value) || $value === '') {
 
209        if (!is_string($value) || $value === '') {
 
210            $this->fail($value, $context, $description, 'expected non-empty date string');
211        }
212
213        $timezone = new DateTimeZone(date_default_timezone_get());
 
213        $timezone = new DateTimeZone(date_default_timezone_get());
214
215        foreach (self::DATE_FORMATS as $format) {
 
215        foreach (self::DATE_FORMATS as $format) {
 
216            $date = DateTimeImmutable::createFromFormat('!' . $format, $value, $timezone);
217
218            // The round trip also rejects input that parsed with warnings or errors.
219            if ($date !== false && $date->format($format) === $value) {
 
219            if ($date !== false && $date->format($format) === $value) {
 
219            if ($date !== false && $date->format($format) === $value) {
 
215        foreach (self::DATE_FORMATS as $format) {
 
215        foreach (self::DATE_FORMATS as $format) {
 
215        foreach (self::DATE_FORMATS as $format) {
216            $date = DateTimeImmutable::createFromFormat('!' . $format, $value, $timezone);
217
218            // The round trip also rejects input that parsed with warnings or errors.
219            if ($date !== false && $date->format($format) === $value) {
220                return $date;
221            }
222        }
223
224        $this->fail($value, $context, $description, 'unsupported date/time format');
225    }
201        mixed $value,
202        HydrationContext $context,
203        string $description,
204    ): DateTimeImmutable {
205        if ($value instanceof DateTimeImmutable) {
 
209        if (!is_string($value) || $value === '') {
 
209        if (!is_string($value) || $value === '') {
 
209        if (!is_string($value) || $value === '') {
 
210            $this->fail($value, $context, $description, 'expected non-empty date string');
211        }
212
213        $timezone = new DateTimeZone(date_default_timezone_get());
 
213        $timezone = new DateTimeZone(date_default_timezone_get());
214
215        foreach (self::DATE_FORMATS as $format) {
 
215        foreach (self::DATE_FORMATS as $format) {
 
216            $date = DateTimeImmutable::createFromFormat('!' . $format, $value, $timezone);
217
218            // The round trip also rejects input that parsed with warnings or errors.
219            if ($date !== false && $date->format($format) === $value) {
 
219            if ($date !== false && $date->format($format) === $value) {
 
220                return $date;
201        mixed $value,
202        HydrationContext $context,
203        string $description,
204    ): DateTimeImmutable {
205        if ($value instanceof DateTimeImmutable) {
 
209        if (!is_string($value) || $value === '') {
 
209        if (!is_string($value) || $value === '') {
 
209        if (!is_string($value) || $value === '') {
 
210            $this->fail($value, $context, $description, 'expected non-empty date string');
211        }
212
213        $timezone = new DateTimeZone(date_default_timezone_get());
 
213        $timezone = new DateTimeZone(date_default_timezone_get());
214
215        foreach (self::DATE_FORMATS as $format) {
 
215        foreach (self::DATE_FORMATS as $format) {
 
216            $date = DateTimeImmutable::createFromFormat('!' . $format, $value, $timezone);
217
218            // The round trip also rejects input that parsed with warnings or errors.
219            if ($date !== false && $date->format($format) === $value) {
 
219            if ($date !== false && $date->format($format) === $value) {
 
215        foreach (self::DATE_FORMATS as $format) {
 
215        foreach (self::DATE_FORMATS as $format) {
 
215        foreach (self::DATE_FORMATS as $format) {
216            $date = DateTimeImmutable::createFromFormat('!' . $format, $value, $timezone);
217
218            // The round trip also rejects input that parsed with warnings or errors.
219            if ($date !== false && $date->format($format) === $value) {
220                return $date;
221            }
222        }
223
224        $this->fail($value, $context, $description, 'unsupported date/time format');
225    }
201        mixed $value,
202        HydrationContext $context,
203        string $description,
204    ): DateTimeImmutable {
205        if ($value instanceof DateTimeImmutable) {
 
209        if (!is_string($value) || $value === '') {
 
209        if (!is_string($value) || $value === '') {
 
209        if (!is_string($value) || $value === '') {
 
210            $this->fail($value, $context, $description, 'expected non-empty date string');
211        }
212
213        $timezone = new DateTimeZone(date_default_timezone_get());
 
213        $timezone = new DateTimeZone(date_default_timezone_get());
214
215        foreach (self::DATE_FORMATS as $format) {
 
215        foreach (self::DATE_FORMATS as $format) {
 
215        foreach (self::DATE_FORMATS as $format) {
216            $date = DateTimeImmutable::createFromFormat('!' . $format, $value, $timezone);
217
218            // The round trip also rejects input that parsed with warnings or errors.
219            if ($date !== false && $date->format($format) === $value) {
220                return $date;
221            }
222        }
223
224        $this->fail($value, $context, $description, 'unsupported date/time format');
225    }
201        mixed $value,
202        HydrationContext $context,
203        string $description,
204    ): DateTimeImmutable {
205        if ($value instanceof DateTimeImmutable) {
 
209        if (!is_string($value) || $value === '') {
 
209        if (!is_string($value) || $value === '') {
 
209        if (!is_string($value) || $value === '') {
 
210            $this->fail($value, $context, $description, 'expected non-empty date string');
211        }
212
213        $timezone = new DateTimeZone(date_default_timezone_get());
 
213        $timezone = new DateTimeZone(date_default_timezone_get());
214
215        foreach (self::DATE_FORMATS as $format) {
 
215        foreach (self::DATE_FORMATS as $format) {
216            $date = DateTimeImmutable::createFromFormat('!' . $format, $value, $timezone);
217
218            // The round trip also rejects input that parsed with warnings or errors.
219            if ($date !== false && $date->format($format) === $value) {
220                return $date;
221            }
222        }
223
224        $this->fail($value, $context, $description, 'unsupported date/time format');
225    }
201        mixed $value,
202        HydrationContext $context,
203        string $description,
204    ): DateTimeImmutable {
205        if ($value instanceof DateTimeImmutable) {
 
209        if (!is_string($value) || $value === '') {
 
209        if (!is_string($value) || $value === '') {
 
209        if (!is_string($value) || $value === '') {
 
213        $timezone = new DateTimeZone(date_default_timezone_get());
214
215        foreach (self::DATE_FORMATS as $format) {
 
215        foreach (self::DATE_FORMATS as $format) {
 
216            $date = DateTimeImmutable::createFromFormat('!' . $format, $value, $timezone);
217
218            // The round trip also rejects input that parsed with warnings or errors.
219            if ($date !== false && $date->format($format) === $value) {
 
219            if ($date !== false && $date->format($format) === $value) {
 
219            if ($date !== false && $date->format($format) === $value) {
 
220                return $date;
201        mixed $value,
202        HydrationContext $context,
203        string $description,
204    ): DateTimeImmutable {
205        if ($value instanceof DateTimeImmutable) {
 
209        if (!is_string($value) || $value === '') {
 
209        if (!is_string($value) || $value === '') {
 
209        if (!is_string($value) || $value === '') {
 
213        $timezone = new DateTimeZone(date_default_timezone_get());
214
215        foreach (self::DATE_FORMATS as $format) {
 
215        foreach (self::DATE_FORMATS as $format) {
 
216            $date = DateTimeImmutable::createFromFormat('!' . $format, $value, $timezone);
217
218            // The round trip also rejects input that parsed with warnings or errors.
219            if ($date !== false && $date->format($format) === $value) {
 
219            if ($date !== false && $date->format($format) === $value) {
 
219            if ($date !== false && $date->format($format) === $value) {
 
215        foreach (self::DATE_FORMATS as $format) {
 
215        foreach (self::DATE_FORMATS as $format) {
 
215        foreach (self::DATE_FORMATS as $format) {
216            $date = DateTimeImmutable::createFromFormat('!' . $format, $value, $timezone);
217
218            // The round trip also rejects input that parsed with warnings or errors.
219            if ($date !== false && $date->format($format) === $value) {
220                return $date;
221            }
222        }
223
224        $this->fail($value, $context, $description, 'unsupported date/time format');
225    }
201        mixed $value,
202        HydrationContext $context,
203        string $description,
204    ): DateTimeImmutable {
205        if ($value instanceof DateTimeImmutable) {
 
209        if (!is_string($value) || $value === '') {
 
209        if (!is_string($value) || $value === '') {
 
209        if (!is_string($value) || $value === '') {
 
213        $timezone = new DateTimeZone(date_default_timezone_get());
214
215        foreach (self::DATE_FORMATS as $format) {
 
215        foreach (self::DATE_FORMATS as $format) {
 
216            $date = DateTimeImmutable::createFromFormat('!' . $format, $value, $timezone);
217
218            // The round trip also rejects input that parsed with warnings or errors.
219            if ($date !== false && $date->format($format) === $value) {
 
219            if ($date !== false && $date->format($format) === $value) {
 
220                return $date;
201        mixed $value,
202        HydrationContext $context,
203        string $description,
204    ): DateTimeImmutable {
205        if ($value instanceof DateTimeImmutable) {
 
209        if (!is_string($value) || $value === '') {
 
209        if (!is_string($value) || $value === '') {
 
209        if (!is_string($value) || $value === '') {
 
213        $timezone = new DateTimeZone(date_default_timezone_get());
214
215        foreach (self::DATE_FORMATS as $format) {
 
215        foreach (self::DATE_FORMATS as $format) {
 
216            $date = DateTimeImmutable::createFromFormat('!' . $format, $value, $timezone);
217
218            // The round trip also rejects input that parsed with warnings or errors.
219            if ($date !== false && $date->format($format) === $value) {
 
219            if ($date !== false && $date->format($format) === $value) {
 
215        foreach (self::DATE_FORMATS as $format) {
 
215        foreach (self::DATE_FORMATS as $format) {
 
215        foreach (self::DATE_FORMATS as $format) {
216            $date = DateTimeImmutable::createFromFormat('!' . $format, $value, $timezone);
217
218            // The round trip also rejects input that parsed with warnings or errors.
219            if ($date !== false && $date->format($format) === $value) {
220                return $date;
221            }
222        }
223
224        $this->fail($value, $context, $description, 'unsupported date/time format');
225    }
201        mixed $value,
202        HydrationContext $context,
203        string $description,
204    ): DateTimeImmutable {
205        if ($value instanceof DateTimeImmutable) {
 
209        if (!is_string($value) || $value === '') {
 
209        if (!is_string($value) || $value === '') {
 
209        if (!is_string($value) || $value === '') {
 
213        $timezone = new DateTimeZone(date_default_timezone_get());
214
215        foreach (self::DATE_FORMATS as $format) {
 
215        foreach (self::DATE_FORMATS as $format) {
 
215        foreach (self::DATE_FORMATS as $format) {
216            $date = DateTimeImmutable::createFromFormat('!' . $format, $value, $timezone);
217
218            // The round trip also rejects input that parsed with warnings or errors.
219            if ($date !== false && $date->format($format) === $value) {
220                return $date;
221            }
222        }
223
224        $this->fail($value, $context, $description, 'unsupported date/time format');
225    }
201        mixed $value,
202        HydrationContext $context,
203        string $description,
204    ): DateTimeImmutable {
205        if ($value instanceof DateTimeImmutable) {
 
209        if (!is_string($value) || $value === '') {
 
209        if (!is_string($value) || $value === '') {
 
209        if (!is_string($value) || $value === '') {
 
213        $timezone = new DateTimeZone(date_default_timezone_get());
214
215        foreach (self::DATE_FORMATS as $format) {
 
215        foreach (self::DATE_FORMATS as $format) {
216            $date = DateTimeImmutable::createFromFormat('!' . $format, $value, $timezone);
217
218            // The round trip also rejects input that parsed with warnings or errors.
219            if ($date !== false && $date->format($format) === $value) {
220                return $date;
221            }
222        }
223
224        $this->fail($value, $context, $description, 'unsupported date/time format');
225    }
201        mixed $value,
202        HydrationContext $context,
203        string $description,
204    ): DateTimeImmutable {
205        if ($value instanceof DateTimeImmutable) {
 
209        if (!is_string($value) || $value === '') {
 
209        if (!is_string($value) || $value === '') {
 
210            $this->fail($value, $context, $description, 'expected non-empty date string');
211        }
212
213        $timezone = new DateTimeZone(date_default_timezone_get());
 
213        $timezone = new DateTimeZone(date_default_timezone_get());
214
215        foreach (self::DATE_FORMATS as $format) {
 
215        foreach (self::DATE_FORMATS as $format) {
 
216            $date = DateTimeImmutable::createFromFormat('!' . $format, $value, $timezone);
217
218            // The round trip also rejects input that parsed with warnings or errors.
219            if ($date !== false && $date->format($format) === $value) {
 
219            if ($date !== false && $date->format($format) === $value) {
 
219            if ($date !== false && $date->format($format) === $value) {
 
220                return $date;
201        mixed $value,
202        HydrationContext $context,
203        string $description,
204    ): DateTimeImmutable {
205        if ($value instanceof DateTimeImmutable) {
 
209        if (!is_string($value) || $value === '') {
 
209        if (!is_string($value) || $value === '') {
 
210            $this->fail($value, $context, $description, 'expected non-empty date string');
211        }
212
213        $timezone = new DateTimeZone(date_default_timezone_get());
 
213        $timezone = new DateTimeZone(date_default_timezone_get());
214
215        foreach (self::DATE_FORMATS as $format) {
 
215        foreach (self::DATE_FORMATS as $format) {
 
216            $date = DateTimeImmutable::createFromFormat('!' . $format, $value, $timezone);
217
218            // The round trip also rejects input that parsed with warnings or errors.
219            if ($date !== false && $date->format($format) === $value) {
 
219            if ($date !== false && $date->format($format) === $value) {
 
219            if ($date !== false && $date->format($format) === $value) {
 
215        foreach (self::DATE_FORMATS as $format) {
 
215        foreach (self::DATE_FORMATS as $format) {
 
215        foreach (self::DATE_FORMATS as $format) {
216            $date = DateTimeImmutable::createFromFormat('!' . $format, $value, $timezone);
217
218            // The round trip also rejects input that parsed with warnings or errors.
219            if ($date !== false && $date->format($format) === $value) {
220                return $date;
221            }
222        }
223
224        $this->fail($value, $context, $description, 'unsupported date/time format');
225    }
201        mixed $value,
202        HydrationContext $context,
203        string $description,
204    ): DateTimeImmutable {
205        if ($value instanceof DateTimeImmutable) {
 
209        if (!is_string($value) || $value === '') {
 
209        if (!is_string($value) || $value === '') {
 
210            $this->fail($value, $context, $description, 'expected non-empty date string');
211        }
212
213        $timezone = new DateTimeZone(date_default_timezone_get());
 
213        $timezone = new DateTimeZone(date_default_timezone_get());
214
215        foreach (self::DATE_FORMATS as $format) {
 
215        foreach (self::DATE_FORMATS as $format) {
 
216            $date = DateTimeImmutable::createFromFormat('!' . $format, $value, $timezone);
217
218            // The round trip also rejects input that parsed with warnings or errors.
219            if ($date !== false && $date->format($format) === $value) {
 
219            if ($date !== false && $date->format($format) === $value) {
 
220                return $date;
201        mixed $value,
202        HydrationContext $context,
203        string $description,
204    ): DateTimeImmutable {
205        if ($value instanceof DateTimeImmutable) {
 
209        if (!is_string($value) || $value === '') {
 
209        if (!is_string($value) || $value === '') {
 
210            $this->fail($value, $context, $description, 'expected non-empty date string');
211        }
212
213        $timezone = new DateTimeZone(date_default_timezone_get());
 
213        $timezone = new DateTimeZone(date_default_timezone_get());
214
215        foreach (self::DATE_FORMATS as $format) {
 
215        foreach (self::DATE_FORMATS as $format) {
 
216            $date = DateTimeImmutable::createFromFormat('!' . $format, $value, $timezone);
217
218            // The round trip also rejects input that parsed with warnings or errors.
219            if ($date !== false && $date->format($format) === $value) {
 
219            if ($date !== false && $date->format($format) === $value) {
 
215        foreach (self::DATE_FORMATS as $format) {
 
215        foreach (self::DATE_FORMATS as $format) {
 
215        foreach (self::DATE_FORMATS as $format) {
216            $date = DateTimeImmutable::createFromFormat('!' . $format, $value, $timezone);
217
218            // The round trip also rejects input that parsed with warnings or errors.
219            if ($date !== false && $date->format($format) === $value) {
220                return $date;
221            }
222        }
223
224        $this->fail($value, $context, $description, 'unsupported date/time format');
225    }
201        mixed $value,
202        HydrationContext $context,
203        string $description,
204    ): DateTimeImmutable {
205        if ($value instanceof DateTimeImmutable) {
 
209        if (!is_string($value) || $value === '') {
 
209        if (!is_string($value) || $value === '') {
 
210            $this->fail($value, $context, $description, 'expected non-empty date string');
211        }
212
213        $timezone = new DateTimeZone(date_default_timezone_get());
 
213        $timezone = new DateTimeZone(date_default_timezone_get());
214
215        foreach (self::DATE_FORMATS as $format) {
 
215        foreach (self::DATE_FORMATS as $format) {
 
215        foreach (self::DATE_FORMATS as $format) {
216            $date = DateTimeImmutable::createFromFormat('!' . $format, $value, $timezone);
217
218            // The round trip also rejects input that parsed with warnings or errors.
219            if ($date !== false && $date->format($format) === $value) {
220                return $date;
221            }
222        }
223
224        $this->fail($value, $context, $description, 'unsupported date/time format');
225    }
201        mixed $value,
202        HydrationContext $context,
203        string $description,
204    ): DateTimeImmutable {
205        if ($value instanceof DateTimeImmutable) {
 
209        if (!is_string($value) || $value === '') {
 
209        if (!is_string($value) || $value === '') {
 
210            $this->fail($value, $context, $description, 'expected non-empty date string');
211        }
212
213        $timezone = new DateTimeZone(date_default_timezone_get());
 
213        $timezone = new DateTimeZone(date_default_timezone_get());
214
215        foreach (self::DATE_FORMATS as $format) {
 
215        foreach (self::DATE_FORMATS as $format) {
216            $date = DateTimeImmutable::createFromFormat('!' . $format, $value, $timezone);
217
218            // The round trip also rejects input that parsed with warnings or errors.
219            if ($date !== false && $date->format($format) === $value) {
220                return $date;
221            }
222        }
223
224        $this->fail($value, $context, $description, 'unsupported date/time format');
225    }
201        mixed $value,
202        HydrationContext $context,
203        string $description,
204    ): DateTimeImmutable {
205        if ($value instanceof DateTimeImmutable) {
 
209        if (!is_string($value) || $value === '') {
 
209        if (!is_string($value) || $value === '') {
 
213        $timezone = new DateTimeZone(date_default_timezone_get());
214
215        foreach (self::DATE_FORMATS as $format) {
 
215        foreach (self::DATE_FORMATS as $format) {
 
216            $date = DateTimeImmutable::createFromFormat('!' . $format, $value, $timezone);
217
218            // The round trip also rejects input that parsed with warnings or errors.
219            if ($date !== false && $date->format($format) === $value) {
 
219            if ($date !== false && $date->format($format) === $value) {
 
219            if ($date !== false && $date->format($format) === $value) {
 
220                return $date;
201        mixed $value,
202        HydrationContext $context,
203        string $description,
204    ): DateTimeImmutable {
205        if ($value instanceof DateTimeImmutable) {
 
209        if (!is_string($value) || $value === '') {
 
209        if (!is_string($value) || $value === '') {
 
213        $timezone = new DateTimeZone(date_default_timezone_get());
214
215        foreach (self::DATE_FORMATS as $format) {
 
215        foreach (self::DATE_FORMATS as $format) {
 
216            $date = DateTimeImmutable::createFromFormat('!' . $format, $value, $timezone);
217
218            // The round trip also rejects input that parsed with warnings or errors.
219            if ($date !== false && $date->format($format) === $value) {
 
219            if ($date !== false && $date->format($format) === $value) {
 
219            if ($date !== false && $date->format($format) === $value) {
 
215        foreach (self::DATE_FORMATS as $format) {
 
215        foreach (self::DATE_FORMATS as $format) {
 
215        foreach (self::DATE_FORMATS as $format) {
216            $date = DateTimeImmutable::createFromFormat('!' . $format, $value, $timezone);
217
218            // The round trip also rejects input that parsed with warnings or errors.
219            if ($date !== false && $date->format($format) === $value) {
220                return $date;
221            }
222        }
223
224        $this->fail($value, $context, $description, 'unsupported date/time format');
225    }
201        mixed $value,
202        HydrationContext $context,
203        string $description,
204    ): DateTimeImmutable {
205        if ($value instanceof DateTimeImmutable) {
 
209        if (!is_string($value) || $value === '') {
 
209        if (!is_string($value) || $value === '') {
 
213        $timezone = new DateTimeZone(date_default_timezone_get());
214
215        foreach (self::DATE_FORMATS as $format) {
 
215        foreach (self::DATE_FORMATS as $format) {
 
216            $date = DateTimeImmutable::createFromFormat('!' . $format, $value, $timezone);
217
218            // The round trip also rejects input that parsed with warnings or errors.
219            if ($date !== false && $date->format($format) === $value) {
 
219            if ($date !== false && $date->format($format) === $value) {
 
220                return $date;
201        mixed $value,
202        HydrationContext $context,
203        string $description,
204    ): DateTimeImmutable {
205        if ($value instanceof DateTimeImmutable) {
 
209        if (!is_string($value) || $value === '') {
 
209        if (!is_string($value) || $value === '') {
 
213        $timezone = new DateTimeZone(date_default_timezone_get());
214
215        foreach (self::DATE_FORMATS as $format) {
 
215        foreach (self::DATE_FORMATS as $format) {
 
216            $date = DateTimeImmutable::createFromFormat('!' . $format, $value, $timezone);
217
218            // The round trip also rejects input that parsed with warnings or errors.
219            if ($date !== false && $date->format($format) === $value) {
 
219            if ($date !== false && $date->format($format) === $value) {
 
215        foreach (self::DATE_FORMATS as $format) {
 
215        foreach (self::DATE_FORMATS as $format) {
 
215        foreach (self::DATE_FORMATS as $format) {
216            $date = DateTimeImmutable::createFromFormat('!' . $format, $value, $timezone);
217
218            // The round trip also rejects input that parsed with warnings or errors.
219            if ($date !== false && $date->format($format) === $value) {
220                return $date;
221            }
222        }
223
224        $this->fail($value, $context, $description, 'unsupported date/time format');
225    }
201        mixed $value,
202        HydrationContext $context,
203        string $description,
204    ): DateTimeImmutable {
205        if ($value instanceof DateTimeImmutable) {
 
209        if (!is_string($value) || $value === '') {
 
209        if (!is_string($value) || $value === '') {
 
213        $timezone = new DateTimeZone(date_default_timezone_get());
214
215        foreach (self::DATE_FORMATS as $format) {
 
215        foreach (self::DATE_FORMATS as $format) {
 
215        foreach (self::DATE_FORMATS as $format) {
216            $date = DateTimeImmutable::createFromFormat('!' . $format, $value, $timezone);
217
218            // The round trip also rejects input that parsed with warnings or errors.
219            if ($date !== false && $date->format($format) === $value) {
220                return $date;
221            }
222        }
223
224        $this->fail($value, $context, $description, 'unsupported date/time format');
225    }
201        mixed $value,
202        HydrationContext $context,
203        string $description,
204    ): DateTimeImmutable {
205        if ($value instanceof DateTimeImmutable) {
 
209        if (!is_string($value) || $value === '') {
 
209        if (!is_string($value) || $value === '') {
 
213        $timezone = new DateTimeZone(date_default_timezone_get());
214
215        foreach (self::DATE_FORMATS as $format) {
 
215        foreach (self::DATE_FORMATS as $format) {
216            $date = DateTimeImmutable::createFromFormat('!' . $format, $value, $timezone);
217
218            // The round trip also rejects input that parsed with warnings or errors.
219            if ($date !== false && $date->format($format) === $value) {
220                return $date;
221            }
222        }
223
224        $this->fail($value, $context, $description, 'unsupported date/time format');
225    }
TypeCoercer->coerceInt
115    private function coerceInt(mixed $value, HydrationContext $context, string $description): int
116    {
117        if (is_int($value)) {
 
118            return $value;
115    private function coerceInt(mixed $value, HydrationContext $context, string $description): int
116    {
117        if (is_int($value)) {
 
121        if (is_string($value)) {
 
122            $int = $this->intFromString($value);
123
124            if ($int !== null) {
 
125                return $int;
115    private function coerceInt(mixed $value, HydrationContext $context, string $description): int
116    {
117        if (is_int($value)) {
 
121        if (is_string($value)) {
 
122            $int = $this->intFromString($value);
123
124            if ($int !== null) {
 
129        $this->fail($value, $context, $description, 'expected int or decimal integer string');
130    }
115    private function coerceInt(mixed $value, HydrationContext $context, string $description): int
116    {
117        if (is_int($value)) {
 
121        if (is_string($value)) {
 
129        $this->fail($value, $context, $description, 'expected int or decimal integer string');
130    }
TypeCoercer->coerceMutableDate
228        mixed $value,
229        HydrationContext $context,
230        string $description,
231    ): DateTime {
232        if ($value instanceof DateTime) {
 
233            return $value;
228        mixed $value,
229        HydrationContext $context,
230        string $description,
231    ): DateTime {
232        if ($value instanceof DateTime) {
 
236        if (!is_string($value) || $value === '') {
 
236        if (!is_string($value) || $value === '') {
 
236        if (!is_string($value) || $value === '') {
 
237            $this->fail($value, $context, $description, 'expected non-empty date string');
238        }
239
240        $timezone = new DateTimeZone(date_default_timezone_get());
 
240        $timezone = new DateTimeZone(date_default_timezone_get());
241
242        foreach (self::DATE_FORMATS as $format) {
 
242        foreach (self::DATE_FORMATS as $format) {
 
243            $date = DateTime::createFromFormat('!' . $format, $value, $timezone);
244
245            // The round trip also rejects input that parsed with warnings or errors.
246            if ($date !== false && $date->format($format) === $value) {
 
246            if ($date !== false && $date->format($format) === $value) {
 
246            if ($date !== false && $date->format($format) === $value) {
 
247                return $date;
228        mixed $value,
229        HydrationContext $context,
230        string $description,
231    ): DateTime {
232        if ($value instanceof DateTime) {
 
236        if (!is_string($value) || $value === '') {
 
236        if (!is_string($value) || $value === '') {
 
236        if (!is_string($value) || $value === '') {
 
237            $this->fail($value, $context, $description, 'expected non-empty date string');
238        }
239
240        $timezone = new DateTimeZone(date_default_timezone_get());
 
240        $timezone = new DateTimeZone(date_default_timezone_get());
241
242        foreach (self::DATE_FORMATS as $format) {
 
242        foreach (self::DATE_FORMATS as $format) {
 
243            $date = DateTime::createFromFormat('!' . $format, $value, $timezone);
244
245            // The round trip also rejects input that parsed with warnings or errors.
246            if ($date !== false && $date->format($format) === $value) {
 
246            if ($date !== false && $date->format($format) === $value) {
 
246            if ($date !== false && $date->format($format) === $value) {
 
242        foreach (self::DATE_FORMATS as $format) {
 
242        foreach (self::DATE_FORMATS as $format) {
 
242        foreach (self::DATE_FORMATS as $format) {
243            $date = DateTime::createFromFormat('!' . $format, $value, $timezone);
244
245            // The round trip also rejects input that parsed with warnings or errors.
246            if ($date !== false && $date->format($format) === $value) {
247                return $date;
248            }
249        }
250
251        $this->fail($value, $context, $description, 'unsupported date/time format');
252    }
228        mixed $value,
229        HydrationContext $context,
230        string $description,
231    ): DateTime {
232        if ($value instanceof DateTime) {
 
236        if (!is_string($value) || $value === '') {
 
236        if (!is_string($value) || $value === '') {
 
236        if (!is_string($value) || $value === '') {
 
237            $this->fail($value, $context, $description, 'expected non-empty date string');
238        }
239
240        $timezone = new DateTimeZone(date_default_timezone_get());
 
240        $timezone = new DateTimeZone(date_default_timezone_get());
241
242        foreach (self::DATE_FORMATS as $format) {
 
242        foreach (self::DATE_FORMATS as $format) {
 
243            $date = DateTime::createFromFormat('!' . $format, $value, $timezone);
244
245            // The round trip also rejects input that parsed with warnings or errors.
246            if ($date !== false && $date->format($format) === $value) {
 
246            if ($date !== false && $date->format($format) === $value) {
 
247                return $date;
228        mixed $value,
229        HydrationContext $context,
230        string $description,
231    ): DateTime {
232        if ($value instanceof DateTime) {
 
236        if (!is_string($value) || $value === '') {
 
236        if (!is_string($value) || $value === '') {
 
236        if (!is_string($value) || $value === '') {
 
237            $this->fail($value, $context, $description, 'expected non-empty date string');
238        }
239
240        $timezone = new DateTimeZone(date_default_timezone_get());
 
240        $timezone = new DateTimeZone(date_default_timezone_get());
241
242        foreach (self::DATE_FORMATS as $format) {
 
242        foreach (self::DATE_FORMATS as $format) {
 
243            $date = DateTime::createFromFormat('!' . $format, $value, $timezone);
244
245            // The round trip also rejects input that parsed with warnings or errors.
246            if ($date !== false && $date->format($format) === $value) {
 
246            if ($date !== false && $date->format($format) === $value) {
 
242        foreach (self::DATE_FORMATS as $format) {
 
242        foreach (self::DATE_FORMATS as $format) {
 
242        foreach (self::DATE_FORMATS as $format) {
243            $date = DateTime::createFromFormat('!' . $format, $value, $timezone);
244
245            // The round trip also rejects input that parsed with warnings or errors.
246            if ($date !== false && $date->format($format) === $value) {
247                return $date;
248            }
249        }
250
251        $this->fail($value, $context, $description, 'unsupported date/time format');
252    }
228        mixed $value,
229        HydrationContext $context,
230        string $description,
231    ): DateTime {
232        if ($value instanceof DateTime) {
 
236        if (!is_string($value) || $value === '') {
 
236        if (!is_string($value) || $value === '') {
 
236        if (!is_string($value) || $value === '') {
 
237            $this->fail($value, $context, $description, 'expected non-empty date string');
238        }
239
240        $timezone = new DateTimeZone(date_default_timezone_get());
 
240        $timezone = new DateTimeZone(date_default_timezone_get());
241
242        foreach (self::DATE_FORMATS as $format) {
 
242        foreach (self::DATE_FORMATS as $format) {
 
242        foreach (self::DATE_FORMATS as $format) {
243            $date = DateTime::createFromFormat('!' . $format, $value, $timezone);
244
245            // The round trip also rejects input that parsed with warnings or errors.
246            if ($date !== false && $date->format($format) === $value) {
247                return $date;
248            }
249        }
250
251        $this->fail($value, $context, $description, 'unsupported date/time format');
252    }
228        mixed $value,
229        HydrationContext $context,
230        string $description,
231    ): DateTime {
232        if ($value instanceof DateTime) {
 
236        if (!is_string($value) || $value === '') {
 
236        if (!is_string($value) || $value === '') {
 
236        if (!is_string($value) || $value === '') {
 
237            $this->fail($value, $context, $description, 'expected non-empty date string');
238        }
239
240        $timezone = new DateTimeZone(date_default_timezone_get());
 
240        $timezone = new DateTimeZone(date_default_timezone_get());
241
242        foreach (self::DATE_FORMATS as $format) {
 
242        foreach (self::DATE_FORMATS as $format) {
243            $date = DateTime::createFromFormat('!' . $format, $value, $timezone);
244
245            // The round trip also rejects input that parsed with warnings or errors.
246            if ($date !== false && $date->format($format) === $value) {
247                return $date;
248            }
249        }
250
251        $this->fail($value, $context, $description, 'unsupported date/time format');
252    }
228        mixed $value,
229        HydrationContext $context,
230        string $description,
231    ): DateTime {
232        if ($value instanceof DateTime) {
 
236        if (!is_string($value) || $value === '') {
 
236        if (!is_string($value) || $value === '') {
 
236        if (!is_string($value) || $value === '') {
 
240        $timezone = new DateTimeZone(date_default_timezone_get());
241
242        foreach (self::DATE_FORMATS as $format) {
 
242        foreach (self::DATE_FORMATS as $format) {
 
243            $date = DateTime::createFromFormat('!' . $format, $value, $timezone);
244
245            // The round trip also rejects input that parsed with warnings or errors.
246            if ($date !== false && $date->format($format) === $value) {
 
246            if ($date !== false && $date->format($format) === $value) {
 
246            if ($date !== false && $date->format($format) === $value) {
 
247                return $date;
228        mixed $value,
229        HydrationContext $context,
230        string $description,
231    ): DateTime {
232        if ($value instanceof DateTime) {
 
236        if (!is_string($value) || $value === '') {
 
236        if (!is_string($value) || $value === '') {
 
236        if (!is_string($value) || $value === '') {
 
240        $timezone = new DateTimeZone(date_default_timezone_get());
241
242        foreach (self::DATE_FORMATS as $format) {
 
242        foreach (self::DATE_FORMATS as $format) {
 
243            $date = DateTime::createFromFormat('!' . $format, $value, $timezone);
244
245            // The round trip also rejects input that parsed with warnings or errors.
246            if ($date !== false && $date->format($format) === $value) {
 
246            if ($date !== false && $date->format($format) === $value) {
 
246            if ($date !== false && $date->format($format) === $value) {
 
242        foreach (self::DATE_FORMATS as $format) {
 
242        foreach (self::DATE_FORMATS as $format) {
 
242        foreach (self::DATE_FORMATS as $format) {
243            $date = DateTime::createFromFormat('!' . $format, $value, $timezone);
244
245            // The round trip also rejects input that parsed with warnings or errors.
246            if ($date !== false && $date->format($format) === $value) {
247                return $date;
248            }
249        }
250
251        $this->fail($value, $context, $description, 'unsupported date/time format');
252    }
228        mixed $value,
229        HydrationContext $context,
230        string $description,
231    ): DateTime {
232        if ($value instanceof DateTime) {
 
236        if (!is_string($value) || $value === '') {
 
236        if (!is_string($value) || $value === '') {
 
236        if (!is_string($value) || $value === '') {
 
240        $timezone = new DateTimeZone(date_default_timezone_get());
241
242        foreach (self::DATE_FORMATS as $format) {
 
242        foreach (self::DATE_FORMATS as $format) {
 
243            $date = DateTime::createFromFormat('!' . $format, $value, $timezone);
244
245            // The round trip also rejects input that parsed with warnings or errors.
246            if ($date !== false && $date->format($format) === $value) {
 
246            if ($date !== false && $date->format($format) === $value) {
 
247                return $date;
228        mixed $value,
229        HydrationContext $context,
230        string $description,
231    ): DateTime {
232        if ($value instanceof DateTime) {
 
236        if (!is_string($value) || $value === '') {
 
236        if (!is_string($value) || $value === '') {
 
236        if (!is_string($value) || $value === '') {
 
240        $timezone = new DateTimeZone(date_default_timezone_get());
241
242        foreach (self::DATE_FORMATS as $format) {
 
242        foreach (self::DATE_FORMATS as $format) {
 
243            $date = DateTime::createFromFormat('!' . $format, $value, $timezone);
244
245            // The round trip also rejects input that parsed with warnings or errors.
246            if ($date !== false && $date->format($format) === $value) {
 
246            if ($date !== false && $date->format($format) === $value) {
 
242        foreach (self::DATE_FORMATS as $format) {
 
242        foreach (self::DATE_FORMATS as $format) {
 
242        foreach (self::DATE_FORMATS as $format) {
243            $date = DateTime::createFromFormat('!' . $format, $value, $timezone);
244
245            // The round trip also rejects input that parsed with warnings or errors.
246            if ($date !== false && $date->format($format) === $value) {
247                return $date;
248            }
249        }
250
251        $this->fail($value, $context, $description, 'unsupported date/time format');
252    }
228        mixed $value,
229        HydrationContext $context,
230        string $description,
231    ): DateTime {
232        if ($value instanceof DateTime) {
 
236        if (!is_string($value) || $value === '') {
 
236        if (!is_string($value) || $value === '') {
 
236        if (!is_string($value) || $value === '') {
 
240        $timezone = new DateTimeZone(date_default_timezone_get());
241
242        foreach (self::DATE_FORMATS as $format) {
 
242        foreach (self::DATE_FORMATS as $format) {
 
242        foreach (self::DATE_FORMATS as $format) {
243            $date = DateTime::createFromFormat('!' . $format, $value, $timezone);
244
245            // The round trip also rejects input that parsed with warnings or errors.
246            if ($date !== false && $date->format($format) === $value) {
247                return $date;
248            }
249        }
250
251        $this->fail($value, $context, $description, 'unsupported date/time format');
252    }
228        mixed $value,
229        HydrationContext $context,
230        string $description,
231    ): DateTime {
232        if ($value instanceof DateTime) {
 
236        if (!is_string($value) || $value === '') {
 
236        if (!is_string($value) || $value === '') {
 
236        if (!is_string($value) || $value === '') {
 
240        $timezone = new DateTimeZone(date_default_timezone_get());
241
242        foreach (self::DATE_FORMATS as $format) {
 
242        foreach (self::DATE_FORMATS as $format) {
243            $date = DateTime::createFromFormat('!' . $format, $value, $timezone);
244
245            // The round trip also rejects input that parsed with warnings or errors.
246            if ($date !== false && $date->format($format) === $value) {
247                return $date;
248            }
249        }
250
251        $this->fail($value, $context, $description, 'unsupported date/time format');
252    }
228        mixed $value,
229        HydrationContext $context,
230        string $description,
231    ): DateTime {
232        if ($value instanceof DateTime) {
 
236        if (!is_string($value) || $value === '') {
 
236        if (!is_string($value) || $value === '') {
 
237            $this->fail($value, $context, $description, 'expected non-empty date string');
238        }
239
240        $timezone = new DateTimeZone(date_default_timezone_get());
 
240        $timezone = new DateTimeZone(date_default_timezone_get());
241
242        foreach (self::DATE_FORMATS as $format) {
 
242        foreach (self::DATE_FORMATS as $format) {
 
243            $date = DateTime::createFromFormat('!' . $format, $value, $timezone);
244
245            // The round trip also rejects input that parsed with warnings or errors.
246            if ($date !== false && $date->format($format) === $value) {
 
246            if ($date !== false && $date->format($format) === $value) {
 
246            if ($date !== false && $date->format($format) === $value) {
 
247                return $date;
228        mixed $value,
229        HydrationContext $context,
230        string $description,
231    ): DateTime {
232        if ($value instanceof DateTime) {
 
236        if (!is_string($value) || $value === '') {
 
236        if (!is_string($value) || $value === '') {
 
237            $this->fail($value, $context, $description, 'expected non-empty date string');
238        }
239
240        $timezone = new DateTimeZone(date_default_timezone_get());
 
240        $timezone = new DateTimeZone(date_default_timezone_get());
241
242        foreach (self::DATE_FORMATS as $format) {
 
242        foreach (self::DATE_FORMATS as $format) {
 
243            $date = DateTime::createFromFormat('!' . $format, $value, $timezone);
244
245            // The round trip also rejects input that parsed with warnings or errors.
246            if ($date !== false && $date->format($format) === $value) {
 
246            if ($date !== false && $date->format($format) === $value) {
 
246            if ($date !== false && $date->format($format) === $value) {
 
242        foreach (self::DATE_FORMATS as $format) {
 
242        foreach (self::DATE_FORMATS as $format) {
 
242        foreach (self::DATE_FORMATS as $format) {
243            $date = DateTime::createFromFormat('!' . $format, $value, $timezone);
244
245            // The round trip also rejects input that parsed with warnings or errors.
246            if ($date !== false && $date->format($format) === $value) {
247                return $date;
248            }
249        }
250
251        $this->fail($value, $context, $description, 'unsupported date/time format');
252    }
228        mixed $value,
229        HydrationContext $context,
230        string $description,
231    ): DateTime {
232        if ($value instanceof DateTime) {
 
236        if (!is_string($value) || $value === '') {
 
236        if (!is_string($value) || $value === '') {
 
237            $this->fail($value, $context, $description, 'expected non-empty date string');
238        }
239
240        $timezone = new DateTimeZone(date_default_timezone_get());
 
240        $timezone = new DateTimeZone(date_default_timezone_get());
241
242        foreach (self::DATE_FORMATS as $format) {
 
242        foreach (self::DATE_FORMATS as $format) {
 
243            $date = DateTime::createFromFormat('!' . $format, $value, $timezone);
244
245            // The round trip also rejects input that parsed with warnings or errors.
246            if ($date !== false && $date->format($format) === $value) {
 
246            if ($date !== false && $date->format($format) === $value) {
 
247                return $date;
228        mixed $value,
229        HydrationContext $context,
230        string $description,
231    ): DateTime {
232        if ($value instanceof DateTime) {
 
236        if (!is_string($value) || $value === '') {
 
236        if (!is_string($value) || $value === '') {
 
237            $this->fail($value, $context, $description, 'expected non-empty date string');
238        }
239
240        $timezone = new DateTimeZone(date_default_timezone_get());
 
240        $timezone = new DateTimeZone(date_default_timezone_get());
241
242        foreach (self::DATE_FORMATS as $format) {
 
242        foreach (self::DATE_FORMATS as $format) {
 
243            $date = DateTime::createFromFormat('!' . $format, $value, $timezone);
244
245            // The round trip also rejects input that parsed with warnings or errors.
246            if ($date !== false && $date->format($format) === $value) {
 
246            if ($date !== false && $date->format($format) === $value) {
 
242        foreach (self::DATE_FORMATS as $format) {
 
242        foreach (self::DATE_FORMATS as $format) {
 
242        foreach (self::DATE_FORMATS as $format) {
243            $date = DateTime::createFromFormat('!' . $format, $value, $timezone);
244
245            // The round trip also rejects input that parsed with warnings or errors.
246            if ($date !== false && $date->format($format) === $value) {
247                return $date;
248            }
249        }
250
251        $this->fail($value, $context, $description, 'unsupported date/time format');
252    }
228        mixed $value,
229        HydrationContext $context,
230        string $description,
231    ): DateTime {
232        if ($value instanceof DateTime) {
 
236        if (!is_string($value) || $value === '') {
 
236        if (!is_string($value) || $value === '') {
 
237            $this->fail($value, $context, $description, 'expected non-empty date string');
238        }
239
240        $timezone = new DateTimeZone(date_default_timezone_get());
 
240        $timezone = new DateTimeZone(date_default_timezone_get());
241
242        foreach (self::DATE_FORMATS as $format) {
 
242        foreach (self::DATE_FORMATS as $format) {
 
242        foreach (self::DATE_FORMATS as $format) {
243            $date = DateTime::createFromFormat('!' . $format, $value, $timezone);
244
245            // The round trip also rejects input that parsed with warnings or errors.
246            if ($date !== false && $date->format($format) === $value) {
247                return $date;
248            }
249        }
250
251        $this->fail($value, $context, $description, 'unsupported date/time format');
252    }
228        mixed $value,
229        HydrationContext $context,
230        string $description,
231    ): DateTime {
232        if ($value instanceof DateTime) {
 
236        if (!is_string($value) || $value === '') {
 
236        if (!is_string($value) || $value === '') {
 
237            $this->fail($value, $context, $description, 'expected non-empty date string');
238        }
239
240        $timezone = new DateTimeZone(date_default_timezone_get());
 
240        $timezone = new DateTimeZone(date_default_timezone_get());
241
242        foreach (self::DATE_FORMATS as $format) {
 
242        foreach (self::DATE_FORMATS as $format) {
243            $date = DateTime::createFromFormat('!' . $format, $value, $timezone);
244
245            // The round trip also rejects input that parsed with warnings or errors.
246            if ($date !== false && $date->format($format) === $value) {
247                return $date;
248            }
249        }
250
251        $this->fail($value, $context, $description, 'unsupported date/time format');
252    }
228        mixed $value,
229        HydrationContext $context,
230        string $description,
231    ): DateTime {
232        if ($value instanceof DateTime) {
 
236        if (!is_string($value) || $value === '') {
 
236        if (!is_string($value) || $value === '') {
 
240        $timezone = new DateTimeZone(date_default_timezone_get());
241
242        foreach (self::DATE_FORMATS as $format) {
 
242        foreach (self::DATE_FORMATS as $format) {
 
243            $date = DateTime::createFromFormat('!' . $format, $value, $timezone);
244
245            // The round trip also rejects input that parsed with warnings or errors.
246            if ($date !== false && $date->format($format) === $value) {
 
246            if ($date !== false && $date->format($format) === $value) {
 
246            if ($date !== false && $date->format($format) === $value) {
 
247                return $date;
228        mixed $value,
229        HydrationContext $context,
230        string $description,
231    ): DateTime {
232        if ($value instanceof DateTime) {
 
236        if (!is_string($value) || $value === '') {
 
236        if (!is_string($value) || $value === '') {
 
240        $timezone = new DateTimeZone(date_default_timezone_get());
241
242        foreach (self::DATE_FORMATS as $format) {
 
242        foreach (self::DATE_FORMATS as $format) {
 
243            $date = DateTime::createFromFormat('!' . $format, $value, $timezone);
244
245            // The round trip also rejects input that parsed with warnings or errors.
246            if ($date !== false && $date->format($format) === $value) {
 
246            if ($date !== false && $date->format($format) === $value) {
 
246            if ($date !== false && $date->format($format) === $value) {
 
242        foreach (self::DATE_FORMATS as $format) {
 
242        foreach (self::DATE_FORMATS as $format) {
 
242        foreach (self::DATE_FORMATS as $format) {
243            $date = DateTime::createFromFormat('!' . $format, $value, $timezone);
244
245            // The round trip also rejects input that parsed with warnings or errors.
246            if ($date !== false && $date->format($format) === $value) {
247                return $date;
248            }
249        }
250
251        $this->fail($value, $context, $description, 'unsupported date/time format');
252    }
228        mixed $value,
229        HydrationContext $context,
230        string $description,
231    ): DateTime {
232        if ($value instanceof DateTime) {
 
236        if (!is_string($value) || $value === '') {
 
236        if (!is_string($value) || $value === '') {
 
240        $timezone = new DateTimeZone(date_default_timezone_get());
241
242        foreach (self::DATE_FORMATS as $format) {
 
242        foreach (self::DATE_FORMATS as $format) {
 
243            $date = DateTime::createFromFormat('!' . $format, $value, $timezone);
244
245            // The round trip also rejects input that parsed with warnings or errors.
246            if ($date !== false && $date->format($format) === $value) {
 
246            if ($date !== false && $date->format($format) === $value) {
 
247                return $date;
228        mixed $value,
229        HydrationContext $context,
230        string $description,
231    ): DateTime {
232        if ($value instanceof DateTime) {
 
236        if (!is_string($value) || $value === '') {
 
236        if (!is_string($value) || $value === '') {
 
240        $timezone = new DateTimeZone(date_default_timezone_get());
241
242        foreach (self::DATE_FORMATS as $format) {
 
242        foreach (self::DATE_FORMATS as $format) {
 
243            $date = DateTime::createFromFormat('!' . $format, $value, $timezone);
244
245            // The round trip also rejects input that parsed with warnings or errors.
246            if ($date !== false && $date->format($format) === $value) {
 
246            if ($date !== false && $date->format($format) === $value) {
 
242        foreach (self::DATE_FORMATS as $format) {
 
242        foreach (self::DATE_FORMATS as $format) {
 
242        foreach (self::DATE_FORMATS as $format) {
243            $date = DateTime::createFromFormat('!' . $format, $value, $timezone);
244
245            // The round trip also rejects input that parsed with warnings or errors.
246            if ($date !== false && $date->format($format) === $value) {
247                return $date;
248            }
249        }
250
251        $this->fail($value, $context, $description, 'unsupported date/time format');
252    }
228        mixed $value,
229        HydrationContext $context,
230        string $description,
231    ): DateTime {
232        if ($value instanceof DateTime) {
 
236        if (!is_string($value) || $value === '') {
 
236        if (!is_string($value) || $value === '') {
 
240        $timezone = new DateTimeZone(date_default_timezone_get());
241
242        foreach (self::DATE_FORMATS as $format) {
 
242        foreach (self::DATE_FORMATS as $format) {
 
242        foreach (self::DATE_FORMATS as $format) {
243            $date = DateTime::createFromFormat('!' . $format, $value, $timezone);
244
245            // The round trip also rejects input that parsed with warnings or errors.
246            if ($date !== false && $date->format($format) === $value) {
247                return $date;
248            }
249        }
250
251        $this->fail($value, $context, $description, 'unsupported date/time format');
252    }
228        mixed $value,
229        HydrationContext $context,
230        string $description,
231    ): DateTime {
232        if ($value instanceof DateTime) {
 
236        if (!is_string($value) || $value === '') {
 
236        if (!is_string($value) || $value === '') {
 
240        $timezone = new DateTimeZone(date_default_timezone_get());
241
242        foreach (self::DATE_FORMATS as $format) {
 
242        foreach (self::DATE_FORMATS as $format) {
243            $date = DateTime::createFromFormat('!' . $format, $value, $timezone);
244
245            // The round trip also rejects input that parsed with warnings or errors.
246            if ($date !== false && $date->format($format) === $value) {
247                return $date;
248            }
249        }
250
251        $this->fail($value, $context, $description, 'unsupported date/time format');
252    }
TypeCoercer->coerceNamed
80        mixed $value,
81        NamedTypeMetadata $name,
82        HydrationContext $context,
83        string $description,
84    ): mixed {
85        return match ($name->scalar) {
 
86            'int' => $this->coerceInt($value, $context, $description),
 
90            default => $this->coerceSpecial($value, $name, $context, $description),
91        };
92    }
80        mixed $value,
81        NamedTypeMetadata $name,
82        HydrationContext $context,
83        string $description,
84    ): mixed {
85        return match ($name->scalar) {
 
87            'float' => $this->coerceFloat($value, $context, $description),
 
90            default => $this->coerceSpecial($value, $name, $context, $description),
91        };
92    }
80        mixed $value,
81        NamedTypeMetadata $name,
82        HydrationContext $context,
83        string $description,
84    ): mixed {
85        return match ($name->scalar) {
 
88            'bool' => $this->coerceBool($value, $context, $description),
 
90            default => $this->coerceSpecial($value, $name, $context, $description),
91        };
92    }
80        mixed $value,
81        NamedTypeMetadata $name,
82        HydrationContext $context,
83        string $description,
84    ): mixed {
85        return match ($name->scalar) {
 
89            'string' => $this->coerceString($value, $context, $description),
 
90            default => $this->coerceSpecial($value, $name, $context, $description),
91        };
92    }
80        mixed $value,
81        NamedTypeMetadata $name,
82        HydrationContext $context,
83        string $description,
84    ): mixed {
85        return match ($name->scalar) {
 
90            default => $this->coerceSpecial($value, $name, $context, $description),
 
90            default => $this->coerceSpecial($value, $name, $context, $description),
91        };
92    }
TypeCoercer->coerceSpecial
95        mixed $value,
96        NamedTypeMetadata $name,
97        HydrationContext $context,
98        string $description,
99    ): mixed {
100        if ($name->date === 'immutable') {
 
101            return $this->coerceImmutableDate($value, $context, $description);
95        mixed $value,
96        NamedTypeMetadata $name,
97        HydrationContext $context,
98        string $description,
99    ): mixed {
100        if ($name->date === 'immutable') {
 
104        if ($name->date === 'mutable') {
 
105            return $this->coerceMutableDate($value, $context, $description);
95        mixed $value,
96        NamedTypeMetadata $name,
97        HydrationContext $context,
98        string $description,
99    ): mixed {
100        if ($name->date === 'immutable') {
 
104        if ($name->date === 'mutable') {
 
108        if ($name->enum !== null) {
 
109            return $this->coerceEnum($value, $name->enum, $context, $description);
95        mixed $value,
96        NamedTypeMetadata $name,
97        HydrationContext $context,
98        string $description,
99    ): mixed {
100        if ($name->date === 'immutable') {
 
104        if ($name->date === 'mutable') {
 
108        if ($name->enum !== null) {
 
112        $this->fail($value, $context, $description, 'unsupported declared type');
113    }
TypeCoercer->coerceString
183    private function coerceString(mixed $value, HydrationContext $context, string $description): string
184    {
185        if (is_string($value)) {
 
186            return $value;
183    private function coerceString(mixed $value, HydrationContext $context, string $description): string
184    {
185        if (is_string($value)) {
 
189        if (is_int($value) || is_float($value)) {
 
189        if (is_int($value) || is_float($value)) {
 
189        if (is_int($value) || is_float($value)) {
 
190            return (string) $value;
183    private function coerceString(mixed $value, HydrationContext $context, string $description): string
184    {
185        if (is_string($value)) {
 
189        if (is_int($value) || is_float($value)) {
 
189        if (is_int($value) || is_float($value)) {
 
189        if (is_int($value) || is_float($value)) {
 
193        if (is_bool($value)) {
 
194            return $value ? '1' : '0';
 
194            return $value ? '1' : '0';
 
194            return $value ? '1' : '0';
183    private function coerceString(mixed $value, HydrationContext $context, string $description): string
184    {
185        if (is_string($value)) {
 
189        if (is_int($value) || is_float($value)) {
 
189        if (is_int($value) || is_float($value)) {
 
189        if (is_int($value) || is_float($value)) {
 
193        if (is_bool($value)) {
 
194            return $value ? '1' : '0';
 
194            return $value ? '1' : '0';
 
194            return $value ? '1' : '0';
183    private function coerceString(mixed $value, HydrationContext $context, string $description): string
184    {
185        if (is_string($value)) {
 
189        if (is_int($value) || is_float($value)) {
 
189        if (is_int($value) || is_float($value)) {
 
189        if (is_int($value) || is_float($value)) {
 
193        if (is_bool($value)) {
 
197        $this->fail($value, $context, $description, 'expected string or scalar value');
198    }
183    private function coerceString(mixed $value, HydrationContext $context, string $description): string
184    {
185        if (is_string($value)) {
 
189        if (is_int($value) || is_float($value)) {
 
189        if (is_int($value) || is_float($value)) {
 
190            return (string) $value;
183    private function coerceString(mixed $value, HydrationContext $context, string $description): string
184    {
185        if (is_string($value)) {
 
189        if (is_int($value) || is_float($value)) {
 
189        if (is_int($value) || is_float($value)) {
 
193        if (is_bool($value)) {
 
194            return $value ? '1' : '0';
 
194            return $value ? '1' : '0';
 
194            return $value ? '1' : '0';
183    private function coerceString(mixed $value, HydrationContext $context, string $description): string
184    {
185        if (is_string($value)) {
 
189        if (is_int($value) || is_float($value)) {
 
189        if (is_int($value) || is_float($value)) {
 
193        if (is_bool($value)) {
 
194            return $value ? '1' : '0';
 
194            return $value ? '1' : '0';
 
194            return $value ? '1' : '0';
183    private function coerceString(mixed $value, HydrationContext $context, string $description): string
184    {
185        if (is_string($value)) {
 
189        if (is_int($value) || is_float($value)) {
 
189        if (is_int($value) || is_float($value)) {
 
193        if (is_bool($value)) {
 
197        $this->fail($value, $context, $description, 'expected string or scalar value');
198    }
TypeCoercer->coerceUnion
46    private function coerceUnion(mixed $value, TypeMetadata $type, HydrationContext $context): mixed
47    {
48        foreach ($type->names as $name) {
 
48        foreach ($type->names as $name) {
 
49            if ($this->satisfies($value, $name)) {
 
50                return $value;
46    private function coerceUnion(mixed $value, TypeMetadata $type, HydrationContext $context): mixed
47    {
48        foreach ($type->names as $name) {
 
48        foreach ($type->names as $name) {
 
49            if ($this->satisfies($value, $name)) {
 
48        foreach ($type->names as $name) {
 
48        foreach ($type->names as $name) {
 
48        foreach ($type->names as $name) {
49            if ($this->satisfies($value, $name)) {
50                return $value;
51            }
52        }
53
54        $lastFailure = null;
55
56        foreach (['enum', 'immutable', 'mutable', 'int', 'float', 'bool', 'string'] as $kind) {
 
56        foreach (['enum', 'immutable', 'mutable', 'int', 'float', 'bool', 'string'] as $kind) {
 
57            foreach ($type->names as $name) {
 
57            foreach ($type->names as $name) {
 
58                if (!$this->matchesKind($name, $kind)) {
 
59                    continue;
 
57            foreach ($type->names as $name) {
 
56        foreach (['enum', 'immutable', 'mutable', 'int', 'float', 'bool', 'string'] as $kind) {
57            foreach ($type->names as $name) {
 
56        foreach (['enum', 'immutable', 'mutable', 'int', 'float', 'bool', 'string'] as $kind) {
 
56        foreach (['enum', 'immutable', 'mutable', 'int', 'float', 'bool', 'string'] as $kind) {
57            foreach ($type->names as $name) {
58                if (!$this->matchesKind($name, $kind)) {
59                    continue;
60                }
61
62                try {
63                    return $this->coerceNamed($value, $name, $context, $type->describe());
64                } catch (InvalidTypeCoercion $e) {
65                    $lastFailure = $e->reason();
66                }
67            }
68        }
69
70        $reason = 'no union arm accepted the value';
71
72        if ($lastFailure !== null) {
 
73            $reason .= '; last failure: ' . $lastFailure;
74        }
75
76        $this->fail($value, $context, $type->describe(), $reason);
 
76        $this->fail($value, $context, $type->describe(), $reason);
77    }
46    private function coerceUnion(mixed $value, TypeMetadata $type, HydrationContext $context): mixed
47    {
48        foreach ($type->names as $name) {
 
48        foreach ($type->names as $name) {
 
49            if ($this->satisfies($value, $name)) {
 
48        foreach ($type->names as $name) {
 
48        foreach ($type->names as $name) {
 
48        foreach ($type->names as $name) {
49            if ($this->satisfies($value, $name)) {
50                return $value;
51            }
52        }
53
54        $lastFailure = null;
55
56        foreach (['enum', 'immutable', 'mutable', 'int', 'float', 'bool', 'string'] as $kind) {
 
56        foreach (['enum', 'immutable', 'mutable', 'int', 'float', 'bool', 'string'] as $kind) {
 
57            foreach ($type->names as $name) {
 
57            foreach ($type->names as $name) {
 
58                if (!$this->matchesKind($name, $kind)) {
 
59                    continue;
 
57            foreach ($type->names as $name) {
 
56        foreach (['enum', 'immutable', 'mutable', 'int', 'float', 'bool', 'string'] as $kind) {
57            foreach ($type->names as $name) {
 
56        foreach (['enum', 'immutable', 'mutable', 'int', 'float', 'bool', 'string'] as $kind) {
 
56        foreach (['enum', 'immutable', 'mutable', 'int', 'float', 'bool', 'string'] as $kind) {
57            foreach ($type->names as $name) {
58                if (!$this->matchesKind($name, $kind)) {
59                    continue;
60                }
61
62                try {
63                    return $this->coerceNamed($value, $name, $context, $type->describe());
64                } catch (InvalidTypeCoercion $e) {
65                    $lastFailure = $e->reason();
66                }
67            }
68        }
69
70        $reason = 'no union arm accepted the value';
71
72        if ($lastFailure !== null) {
 
76        $this->fail($value, $context, $type->describe(), $reason);
77    }
46    private function coerceUnion(mixed $value, TypeMetadata $type, HydrationContext $context): mixed
47    {
48        foreach ($type->names as $name) {
 
48        foreach ($type->names as $name) {
 
49            if ($this->satisfies($value, $name)) {
 
48        foreach ($type->names as $name) {
 
48        foreach ($type->names as $name) {
 
48        foreach ($type->names as $name) {
49            if ($this->satisfies($value, $name)) {
50                return $value;
51            }
52        }
53
54        $lastFailure = null;
55
56        foreach (['enum', 'immutable', 'mutable', 'int', 'float', 'bool', 'string'] as $kind) {
 
56        foreach (['enum', 'immutable', 'mutable', 'int', 'float', 'bool', 'string'] as $kind) {
 
57            foreach ($type->names as $name) {
 
57            foreach ($type->names as $name) {
 
58                if (!$this->matchesKind($name, $kind)) {
 
62                try {
63                    return $this->coerceNamed($value, $name, $context, $type->describe());
46    private function coerceUnion(mixed $value, TypeMetadata $type, HydrationContext $context): mixed
47    {
48        foreach ($type->names as $name) {
 
48        foreach ($type->names as $name) {
 
49            if ($this->satisfies($value, $name)) {
 
48        foreach ($type->names as $name) {
 
48        foreach ($type->names as $name) {
 
48        foreach ($type->names as $name) {
49            if ($this->satisfies($value, $name)) {
50                return $value;
51            }
52        }
53
54        $lastFailure = null;
55
56        foreach (['enum', 'immutable', 'mutable', 'int', 'float', 'bool', 'string'] as $kind) {
 
56        foreach (['enum', 'immutable', 'mutable', 'int', 'float', 'bool', 'string'] as $kind) {
 
57            foreach ($type->names as $name) {
 
57            foreach ($type->names as $name) {
 
56        foreach (['enum', 'immutable', 'mutable', 'int', 'float', 'bool', 'string'] as $kind) {
57            foreach ($type->names as $name) {
 
56        foreach (['enum', 'immutable', 'mutable', 'int', 'float', 'bool', 'string'] as $kind) {
 
56        foreach (['enum', 'immutable', 'mutable', 'int', 'float', 'bool', 'string'] as $kind) {
57            foreach ($type->names as $name) {
58                if (!$this->matchesKind($name, $kind)) {
59                    continue;
60                }
61
62                try {
63                    return $this->coerceNamed($value, $name, $context, $type->describe());
64                } catch (InvalidTypeCoercion $e) {
65                    $lastFailure = $e->reason();
66                }
67            }
68        }
69
70        $reason = 'no union arm accepted the value';
71
72        if ($lastFailure !== null) {
 
73            $reason .= '; last failure: ' . $lastFailure;
74        }
75
76        $this->fail($value, $context, $type->describe(), $reason);
 
76        $this->fail($value, $context, $type->describe(), $reason);
77    }
46    private function coerceUnion(mixed $value, TypeMetadata $type, HydrationContext $context): mixed
47    {
48        foreach ($type->names as $name) {
 
48        foreach ($type->names as $name) {
 
49            if ($this->satisfies($value, $name)) {
 
48        foreach ($type->names as $name) {
 
48        foreach ($type->names as $name) {
 
48        foreach ($type->names as $name) {
49            if ($this->satisfies($value, $name)) {
50                return $value;
51            }
52        }
53
54        $lastFailure = null;
55
56        foreach (['enum', 'immutable', 'mutable', 'int', 'float', 'bool', 'string'] as $kind) {
 
56        foreach (['enum', 'immutable', 'mutable', 'int', 'float', 'bool', 'string'] as $kind) {
 
57            foreach ($type->names as $name) {
 
57            foreach ($type->names as $name) {
 
56        foreach (['enum', 'immutable', 'mutable', 'int', 'float', 'bool', 'string'] as $kind) {
57            foreach ($type->names as $name) {
 
56        foreach (['enum', 'immutable', 'mutable', 'int', 'float', 'bool', 'string'] as $kind) {
 
56        foreach (['enum', 'immutable', 'mutable', 'int', 'float', 'bool', 'string'] as $kind) {
57            foreach ($type->names as $name) {
58                if (!$this->matchesKind($name, $kind)) {
59                    continue;
60                }
61
62                try {
63                    return $this->coerceNamed($value, $name, $context, $type->describe());
64                } catch (InvalidTypeCoercion $e) {
65                    $lastFailure = $e->reason();
66                }
67            }
68        }
69
70        $reason = 'no union arm accepted the value';
71
72        if ($lastFailure !== null) {
 
76        $this->fail($value, $context, $type->describe(), $reason);
77    }
46    private function coerceUnion(mixed $value, TypeMetadata $type, HydrationContext $context): mixed
47    {
48        foreach ($type->names as $name) {
 
48        foreach ($type->names as $name) {
 
49            if ($this->satisfies($value, $name)) {
 
48        foreach ($type->names as $name) {
 
48        foreach ($type->names as $name) {
 
48        foreach ($type->names as $name) {
49            if ($this->satisfies($value, $name)) {
50                return $value;
51            }
52        }
53
54        $lastFailure = null;
55
56        foreach (['enum', 'immutable', 'mutable', 'int', 'float', 'bool', 'string'] as $kind) {
 
56        foreach (['enum', 'immutable', 'mutable', 'int', 'float', 'bool', 'string'] as $kind) {
 
57            foreach ($type->names as $name) {
 
56        foreach (['enum', 'immutable', 'mutable', 'int', 'float', 'bool', 'string'] as $kind) {
57            foreach ($type->names as $name) {
 
56        foreach (['enum', 'immutable', 'mutable', 'int', 'float', 'bool', 'string'] as $kind) {
 
56        foreach (['enum', 'immutable', 'mutable', 'int', 'float', 'bool', 'string'] as $kind) {
57            foreach ($type->names as $name) {
58                if (!$this->matchesKind($name, $kind)) {
59                    continue;
60                }
61
62                try {
63                    return $this->coerceNamed($value, $name, $context, $type->describe());
64                } catch (InvalidTypeCoercion $e) {
65                    $lastFailure = $e->reason();
66                }
67            }
68        }
69
70        $reason = 'no union arm accepted the value';
71
72        if ($lastFailure !== null) {
 
73            $reason .= '; last failure: ' . $lastFailure;
74        }
75
76        $this->fail($value, $context, $type->describe(), $reason);
 
76        $this->fail($value, $context, $type->describe(), $reason);
77    }
46    private function coerceUnion(mixed $value, TypeMetadata $type, HydrationContext $context): mixed
47    {
48        foreach ($type->names as $name) {
 
48        foreach ($type->names as $name) {
 
49            if ($this->satisfies($value, $name)) {
 
48        foreach ($type->names as $name) {
 
48        foreach ($type->names as $name) {
 
48        foreach ($type->names as $name) {
49            if ($this->satisfies($value, $name)) {
50                return $value;
51            }
52        }
53
54        $lastFailure = null;
55
56        foreach (['enum', 'immutable', 'mutable', 'int', 'float', 'bool', 'string'] as $kind) {
 
56        foreach (['enum', 'immutable', 'mutable', 'int', 'float', 'bool', 'string'] as $kind) {
 
57            foreach ($type->names as $name) {
 
56        foreach (['enum', 'immutable', 'mutable', 'int', 'float', 'bool', 'string'] as $kind) {
57            foreach ($type->names as $name) {
 
56        foreach (['enum', 'immutable', 'mutable', 'int', 'float', 'bool', 'string'] as $kind) {
 
56        foreach (['enum', 'immutable', 'mutable', 'int', 'float', 'bool', 'string'] as $kind) {
57            foreach ($type->names as $name) {
58                if (!$this->matchesKind($name, $kind)) {
59                    continue;
60                }
61
62                try {
63                    return $this->coerceNamed($value, $name, $context, $type->describe());
64                } catch (InvalidTypeCoercion $e) {
65                    $lastFailure = $e->reason();
66                }
67            }
68        }
69
70        $reason = 'no union arm accepted the value';
71
72        if ($lastFailure !== null) {
 
76        $this->fail($value, $context, $type->describe(), $reason);
77    }
46    private function coerceUnion(mixed $value, TypeMetadata $type, HydrationContext $context): mixed
47    {
48        foreach ($type->names as $name) {
 
48        foreach ($type->names as $name) {
 
49            if ($this->satisfies($value, $name)) {
 
48        foreach ($type->names as $name) {
 
48        foreach ($type->names as $name) {
 
48        foreach ($type->names as $name) {
49            if ($this->satisfies($value, $name)) {
50                return $value;
51            }
52        }
53
54        $lastFailure = null;
55
56        foreach (['enum', 'immutable', 'mutable', 'int', 'float', 'bool', 'string'] as $kind) {
 
56        foreach (['enum', 'immutable', 'mutable', 'int', 'float', 'bool', 'string'] as $kind) {
 
56        foreach (['enum', 'immutable', 'mutable', 'int', 'float', 'bool', 'string'] as $kind) {
57            foreach ($type->names as $name) {
58                if (!$this->matchesKind($name, $kind)) {
59                    continue;
60                }
61
62                try {
63                    return $this->coerceNamed($value, $name, $context, $type->describe());
64                } catch (InvalidTypeCoercion $e) {
65                    $lastFailure = $e->reason();
66                }
67            }
68        }
69
70        $reason = 'no union arm accepted the value';
71
72        if ($lastFailure !== null) {
 
73            $reason .= '; last failure: ' . $lastFailure;
74        }
75
76        $this->fail($value, $context, $type->describe(), $reason);
 
76        $this->fail($value, $context, $type->describe(), $reason);
77    }
46    private function coerceUnion(mixed $value, TypeMetadata $type, HydrationContext $context): mixed
47    {
48        foreach ($type->names as $name) {
 
48        foreach ($type->names as $name) {
 
49            if ($this->satisfies($value, $name)) {
 
48        foreach ($type->names as $name) {
 
48        foreach ($type->names as $name) {
 
48        foreach ($type->names as $name) {
49            if ($this->satisfies($value, $name)) {
50                return $value;
51            }
52        }
53
54        $lastFailure = null;
55
56        foreach (['enum', 'immutable', 'mutable', 'int', 'float', 'bool', 'string'] as $kind) {
 
56        foreach (['enum', 'immutable', 'mutable', 'int', 'float', 'bool', 'string'] as $kind) {
 
56        foreach (['enum', 'immutable', 'mutable', 'int', 'float', 'bool', 'string'] as $kind) {
57            foreach ($type->names as $name) {
58                if (!$this->matchesKind($name, $kind)) {
59                    continue;
60                }
61
62                try {
63                    return $this->coerceNamed($value, $name, $context, $type->describe());
64                } catch (InvalidTypeCoercion $e) {
65                    $lastFailure = $e->reason();
66                }
67            }
68        }
69
70        $reason = 'no union arm accepted the value';
71
72        if ($lastFailure !== null) {
 
76        $this->fail($value, $context, $type->describe(), $reason);
77    }
46    private function coerceUnion(mixed $value, TypeMetadata $type, HydrationContext $context): mixed
47    {
48        foreach ($type->names as $name) {
 
48        foreach ($type->names as $name) {
 
49            if ($this->satisfies($value, $name)) {
 
48        foreach ($type->names as $name) {
 
48        foreach ($type->names as $name) {
 
48        foreach ($type->names as $name) {
49            if ($this->satisfies($value, $name)) {
50                return $value;
51            }
52        }
53
54        $lastFailure = null;
55
56        foreach (['enum', 'immutable', 'mutable', 'int', 'float', 'bool', 'string'] as $kind) {
 
56        foreach (['enum', 'immutable', 'mutable', 'int', 'float', 'bool', 'string'] as $kind) {
57            foreach ($type->names as $name) {
58                if (!$this->matchesKind($name, $kind)) {
59                    continue;
60                }
61
62                try {
63                    return $this->coerceNamed($value, $name, $context, $type->describe());
64                } catch (InvalidTypeCoercion $e) {
65                    $lastFailure = $e->reason();
66                }
67            }
68        }
69
70        $reason = 'no union arm accepted the value';
71
72        if ($lastFailure !== null) {
 
73            $reason .= '; last failure: ' . $lastFailure;
74        }
75
76        $this->fail($value, $context, $type->describe(), $reason);
 
76        $this->fail($value, $context, $type->describe(), $reason);
77    }
46    private function coerceUnion(mixed $value, TypeMetadata $type, HydrationContext $context): mixed
47    {
48        foreach ($type->names as $name) {
 
48        foreach ($type->names as $name) {
 
49            if ($this->satisfies($value, $name)) {
 
48        foreach ($type->names as $name) {
 
48        foreach ($type->names as $name) {
 
48        foreach ($type->names as $name) {
49            if ($this->satisfies($value, $name)) {
50                return $value;
51            }
52        }
53
54        $lastFailure = null;
55
56        foreach (['enum', 'immutable', 'mutable', 'int', 'float', 'bool', 'string'] as $kind) {
 
56        foreach (['enum', 'immutable', 'mutable', 'int', 'float', 'bool', 'string'] as $kind) {
57            foreach ($type->names as $name) {
58                if (!$this->matchesKind($name, $kind)) {
59                    continue;
60                }
61
62                try {
63                    return $this->coerceNamed($value, $name, $context, $type->describe());
64                } catch (InvalidTypeCoercion $e) {
65                    $lastFailure = $e->reason();
66                }
67            }
68        }
69
70        $reason = 'no union arm accepted the value';
71
72        if ($lastFailure !== null) {
 
76        $this->fail($value, $context, $type->describe(), $reason);
77    }
46    private function coerceUnion(mixed $value, TypeMetadata $type, HydrationContext $context): mixed
47    {
48        foreach ($type->names as $name) {
 
48        foreach ($type->names as $name) {
 
48        foreach ($type->names as $name) {
49            if ($this->satisfies($value, $name)) {
50                return $value;
51            }
52        }
53
54        $lastFailure = null;
55
56        foreach (['enum', 'immutable', 'mutable', 'int', 'float', 'bool', 'string'] as $kind) {
 
56        foreach (['enum', 'immutable', 'mutable', 'int', 'float', 'bool', 'string'] as $kind) {
 
57            foreach ($type->names as $name) {
 
57            foreach ($type->names as $name) {
 
58                if (!$this->matchesKind($name, $kind)) {
 
59                    continue;
 
57            foreach ($type->names as $name) {
 
56        foreach (['enum', 'immutable', 'mutable', 'int', 'float', 'bool', 'string'] as $kind) {
57            foreach ($type->names as $name) {
 
56        foreach (['enum', 'immutable', 'mutable', 'int', 'float', 'bool', 'string'] as $kind) {
 
56        foreach (['enum', 'immutable', 'mutable', 'int', 'float', 'bool', 'string'] as $kind) {
57            foreach ($type->names as $name) {
58                if (!$this->matchesKind($name, $kind)) {
59                    continue;
60                }
61
62                try {
63                    return $this->coerceNamed($value, $name, $context, $type->describe());
64                } catch (InvalidTypeCoercion $e) {
65                    $lastFailure = $e->reason();
66                }
67            }
68        }
69
70        $reason = 'no union arm accepted the value';
71
72        if ($lastFailure !== null) {
 
73            $reason .= '; last failure: ' . $lastFailure;
74        }
75
76        $this->fail($value, $context, $type->describe(), $reason);
 
76        $this->fail($value, $context, $type->describe(), $reason);
77    }
46    private function coerceUnion(mixed $value, TypeMetadata $type, HydrationContext $context): mixed
47    {
48        foreach ($type->names as $name) {
 
48        foreach ($type->names as $name) {
 
48        foreach ($type->names as $name) {
49            if ($this->satisfies($value, $name)) {
50                return $value;
51            }
52        }
53
54        $lastFailure = null;
55
56        foreach (['enum', 'immutable', 'mutable', 'int', 'float', 'bool', 'string'] as $kind) {
 
56        foreach (['enum', 'immutable', 'mutable', 'int', 'float', 'bool', 'string'] as $kind) {
 
57            foreach ($type->names as $name) {
 
57            foreach ($type->names as $name) {
 
58                if (!$this->matchesKind($name, $kind)) {
 
59                    continue;
 
57            foreach ($type->names as $name) {
 
56        foreach (['enum', 'immutable', 'mutable', 'int', 'float', 'bool', 'string'] as $kind) {
57            foreach ($type->names as $name) {
 
56        foreach (['enum', 'immutable', 'mutable', 'int', 'float', 'bool', 'string'] as $kind) {
 
56        foreach (['enum', 'immutable', 'mutable', 'int', 'float', 'bool', 'string'] as $kind) {
57            foreach ($type->names as $name) {
58                if (!$this->matchesKind($name, $kind)) {
59                    continue;
60                }
61
62                try {
63                    return $this->coerceNamed($value, $name, $context, $type->describe());
64                } catch (InvalidTypeCoercion $e) {
65                    $lastFailure = $e->reason();
66                }
67            }
68        }
69
70        $reason = 'no union arm accepted the value';
71
72        if ($lastFailure !== null) {
 
76        $this->fail($value, $context, $type->describe(), $reason);
77    }
46    private function coerceUnion(mixed $value, TypeMetadata $type, HydrationContext $context): mixed
47    {
48        foreach ($type->names as $name) {
 
48        foreach ($type->names as $name) {
 
48        foreach ($type->names as $name) {
49            if ($this->satisfies($value, $name)) {
50                return $value;
51            }
52        }
53
54        $lastFailure = null;
55
56        foreach (['enum', 'immutable', 'mutable', 'int', 'float', 'bool', 'string'] as $kind) {
 
56        foreach (['enum', 'immutable', 'mutable', 'int', 'float', 'bool', 'string'] as $kind) {
 
57            foreach ($type->names as $name) {
 
57            foreach ($type->names as $name) {
 
58                if (!$this->matchesKind($name, $kind)) {
 
62                try {
63                    return $this->coerceNamed($value, $name, $context, $type->describe());
46    private function coerceUnion(mixed $value, TypeMetadata $type, HydrationContext $context): mixed
47    {
48        foreach ($type->names as $name) {
 
48        foreach ($type->names as $name) {
 
48        foreach ($type->names as $name) {
49            if ($this->satisfies($value, $name)) {
50                return $value;
51            }
52        }
53
54        $lastFailure = null;
55
56        foreach (['enum', 'immutable', 'mutable', 'int', 'float', 'bool', 'string'] as $kind) {
 
56        foreach (['enum', 'immutable', 'mutable', 'int', 'float', 'bool', 'string'] as $kind) {
 
57            foreach ($type->names as $name) {
 
57            foreach ($type->names as $name) {
 
56        foreach (['enum', 'immutable', 'mutable', 'int', 'float', 'bool', 'string'] as $kind) {
57            foreach ($type->names as $name) {
 
56        foreach (['enum', 'immutable', 'mutable', 'int', 'float', 'bool', 'string'] as $kind) {
 
56        foreach (['enum', 'immutable', 'mutable', 'int', 'float', 'bool', 'string'] as $kind) {
57            foreach ($type->names as $name) {
58                if (!$this->matchesKind($name, $kind)) {
59                    continue;
60                }
61
62                try {
63                    return $this->coerceNamed($value, $name, $context, $type->describe());
64                } catch (InvalidTypeCoercion $e) {
65                    $lastFailure = $e->reason();
66                }
67            }
68        }
69
70        $reason = 'no union arm accepted the value';
71
72        if ($lastFailure !== null) {
 
73            $reason .= '; last failure: ' . $lastFailure;
74        }
75
76        $this->fail($value, $context, $type->describe(), $reason);
 
76        $this->fail($value, $context, $type->describe(), $reason);
77    }
46    private function coerceUnion(mixed $value, TypeMetadata $type, HydrationContext $context): mixed
47    {
48        foreach ($type->names as $name) {
 
48        foreach ($type->names as $name) {
 
48        foreach ($type->names as $name) {
49            if ($this->satisfies($value, $name)) {
50                return $value;
51            }
52        }
53
54        $lastFailure = null;
55
56        foreach (['enum', 'immutable', 'mutable', 'int', 'float', 'bool', 'string'] as $kind) {
 
56        foreach (['enum', 'immutable', 'mutable', 'int', 'float', 'bool', 'string'] as $kind) {
 
57            foreach ($type->names as $name) {
 
57            foreach ($type->names as $name) {
 
56        foreach (['enum', 'immutable', 'mutable', 'int', 'float', 'bool', 'string'] as $kind) {
57            foreach ($type->names as $name) {
 
56        foreach (['enum', 'immutable', 'mutable', 'int', 'float', 'bool', 'string'] as $kind) {
 
56        foreach (['enum', 'immutable', 'mutable', 'int', 'float', 'bool', 'string'] as $kind) {
57            foreach ($type->names as $name) {
58                if (!$this->matchesKind($name, $kind)) {
59                    continue;
60                }
61
62                try {
63                    return $this->coerceNamed($value, $name, $context, $type->describe());
64                } catch (InvalidTypeCoercion $e) {
65                    $lastFailure = $e->reason();
66                }
67            }
68        }
69
70        $reason = 'no union arm accepted the value';
71
72        if ($lastFailure !== null) {
 
76        $this->fail($value, $context, $type->describe(), $reason);
77    }
46    private function coerceUnion(mixed $value, TypeMetadata $type, HydrationContext $context): mixed
47    {
48        foreach ($type->names as $name) {
 
48        foreach ($type->names as $name) {
 
48        foreach ($type->names as $name) {
49            if ($this->satisfies($value, $name)) {
50                return $value;
51            }
52        }
53
54        $lastFailure = null;
55
56        foreach (['enum', 'immutable', 'mutable', 'int', 'float', 'bool', 'string'] as $kind) {
 
56        foreach (['enum', 'immutable', 'mutable', 'int', 'float', 'bool', 'string'] as $kind) {
 
57            foreach ($type->names as $name) {
 
56        foreach (['enum', 'immutable', 'mutable', 'int', 'float', 'bool', 'string'] as $kind) {
57            foreach ($type->names as $name) {
 
56        foreach (['enum', 'immutable', 'mutable', 'int', 'float', 'bool', 'string'] as $kind) {
 
56        foreach (['enum', 'immutable', 'mutable', 'int', 'float', 'bool', 'string'] as $kind) {
57            foreach ($type->names as $name) {
58                if (!$this->matchesKind($name, $kind)) {
59                    continue;
60                }
61
62                try {
63                    return $this->coerceNamed($value, $name, $context, $type->describe());
64                } catch (InvalidTypeCoercion $e) {
65                    $lastFailure = $e->reason();
66                }
67            }
68        }
69
70        $reason = 'no union arm accepted the value';
71
72        if ($lastFailure !== null) {
 
73            $reason .= '; last failure: ' . $lastFailure;
74        }
75
76        $this->fail($value, $context, $type->describe(), $reason);
 
76        $this->fail($value, $context, $type->describe(), $reason);
77    }
46    private function coerceUnion(mixed $value, TypeMetadata $type, HydrationContext $context): mixed
47    {
48        foreach ($type->names as $name) {
 
48        foreach ($type->names as $name) {
 
48        foreach ($type->names as $name) {
49            if ($this->satisfies($value, $name)) {
50                return $value;
51            }
52        }
53
54        $lastFailure = null;
55
56        foreach (['enum', 'immutable', 'mutable', 'int', 'float', 'bool', 'string'] as $kind) {
 
56        foreach (['enum', 'immutable', 'mutable', 'int', 'float', 'bool', 'string'] as $kind) {
 
57            foreach ($type->names as $name) {
 
56        foreach (['enum', 'immutable', 'mutable', 'int', 'float', 'bool', 'string'] as $kind) {
57            foreach ($type->names as $name) {
 
56        foreach (['enum', 'immutable', 'mutable', 'int', 'float', 'bool', 'string'] as $kind) {
 
56        foreach (['enum', 'immutable', 'mutable', 'int', 'float', 'bool', 'string'] as $kind) {
57            foreach ($type->names as $name) {
58                if (!$this->matchesKind($name, $kind)) {
59                    continue;
60                }
61
62                try {
63                    return $this->coerceNamed($value, $name, $context, $type->describe());
64                } catch (InvalidTypeCoercion $e) {
65                    $lastFailure = $e->reason();
66                }
67            }
68        }
69
70        $reason = 'no union arm accepted the value';
71
72        if ($lastFailure !== null) {
 
76        $this->fail($value, $context, $type->describe(), $reason);
77    }
46    private function coerceUnion(mixed $value, TypeMetadata $type, HydrationContext $context): mixed
47    {
48        foreach ($type->names as $name) {
 
48        foreach ($type->names as $name) {
 
48        foreach ($type->names as $name) {
49            if ($this->satisfies($value, $name)) {
50                return $value;
51            }
52        }
53
54        $lastFailure = null;
55
56        foreach (['enum', 'immutable', 'mutable', 'int', 'float', 'bool', 'string'] as $kind) {
 
56        foreach (['enum', 'immutable', 'mutable', 'int', 'float', 'bool', 'string'] as $kind) {
 
56        foreach (['enum', 'immutable', 'mutable', 'int', 'float', 'bool', 'string'] as $kind) {
57            foreach ($type->names as $name) {
58                if (!$this->matchesKind($name, $kind)) {
59                    continue;
60                }
61
62                try {
63                    return $this->coerceNamed($value, $name, $context, $type->describe());
64                } catch (InvalidTypeCoercion $e) {
65                    $lastFailure = $e->reason();
66                }
67            }
68        }
69
70        $reason = 'no union arm accepted the value';
71
72        if ($lastFailure !== null) {
 
73            $reason .= '; last failure: ' . $lastFailure;
74        }
75
76        $this->fail($value, $context, $type->describe(), $reason);
 
76        $this->fail($value, $context, $type->describe(), $reason);
77    }
46    private function coerceUnion(mixed $value, TypeMetadata $type, HydrationContext $context): mixed
47    {
48        foreach ($type->names as $name) {
 
48        foreach ($type->names as $name) {
 
48        foreach ($type->names as $name) {
49            if ($this->satisfies($value, $name)) {
50                return $value;
51            }
52        }
53
54        $lastFailure = null;
55
56        foreach (['enum', 'immutable', 'mutable', 'int', 'float', 'bool', 'string'] as $kind) {
 
56        foreach (['enum', 'immutable', 'mutable', 'int', 'float', 'bool', 'string'] as $kind) {
 
56        foreach (['enum', 'immutable', 'mutable', 'int', 'float', 'bool', 'string'] as $kind) {
57            foreach ($type->names as $name) {
58                if (!$this->matchesKind($name, $kind)) {
59                    continue;
60                }
61
62                try {
63                    return $this->coerceNamed($value, $name, $context, $type->describe());
64                } catch (InvalidTypeCoercion $e) {
65                    $lastFailure = $e->reason();
66                }
67            }
68        }
69
70        $reason = 'no union arm accepted the value';
71
72        if ($lastFailure !== null) {
 
76        $this->fail($value, $context, $type->describe(), $reason);
77    }
46    private function coerceUnion(mixed $value, TypeMetadata $type, HydrationContext $context): mixed
47    {
48        foreach ($type->names as $name) {
 
48        foreach ($type->names as $name) {
 
48        foreach ($type->names as $name) {
49            if ($this->satisfies($value, $name)) {
50                return $value;
51            }
52        }
53
54        $lastFailure = null;
55
56        foreach (['enum', 'immutable', 'mutable', 'int', 'float', 'bool', 'string'] as $kind) {
 
56        foreach (['enum', 'immutable', 'mutable', 'int', 'float', 'bool', 'string'] as $kind) {
57            foreach ($type->names as $name) {
58                if (!$this->matchesKind($name, $kind)) {
59                    continue;
60                }
61
62                try {
63                    return $this->coerceNamed($value, $name, $context, $type->describe());
64                } catch (InvalidTypeCoercion $e) {
65                    $lastFailure = $e->reason();
66                }
67            }
68        }
69
70        $reason = 'no union arm accepted the value';
71
72        if ($lastFailure !== null) {
 
73            $reason .= '; last failure: ' . $lastFailure;
74        }
75
76        $this->fail($value, $context, $type->describe(), $reason);
 
76        $this->fail($value, $context, $type->describe(), $reason);
77    }
46    private function coerceUnion(mixed $value, TypeMetadata $type, HydrationContext $context): mixed
47    {
48        foreach ($type->names as $name) {
 
48        foreach ($type->names as $name) {
 
48        foreach ($type->names as $name) {
49            if ($this->satisfies($value, $name)) {
50                return $value;
51            }
52        }
53
54        $lastFailure = null;
55
56        foreach (['enum', 'immutable', 'mutable', 'int', 'float', 'bool', 'string'] as $kind) {
 
56        foreach (['enum', 'immutable', 'mutable', 'int', 'float', 'bool', 'string'] as $kind) {
57            foreach ($type->names as $name) {
58                if (!$this->matchesKind($name, $kind)) {
59                    continue;
60                }
61
62                try {
63                    return $this->coerceNamed($value, $name, $context, $type->describe());
64                } catch (InvalidTypeCoercion $e) {
65                    $lastFailure = $e->reason();
66                }
67            }
68        }
69
70        $reason = 'no union arm accepted the value';
71
72        if ($lastFailure !== null) {
 
76        $this->fail($value, $context, $type->describe(), $reason);
77    }
46    private function coerceUnion(mixed $value, TypeMetadata $type, HydrationContext $context): mixed
47    {
48        foreach ($type->names as $name) {
 
48        foreach ($type->names as $name) {
49            if ($this->satisfies($value, $name)) {
50                return $value;
51            }
52        }
53
54        $lastFailure = null;
55
56        foreach (['enum', 'immutable', 'mutable', 'int', 'float', 'bool', 'string'] as $kind) {
 
56        foreach (['enum', 'immutable', 'mutable', 'int', 'float', 'bool', 'string'] as $kind) {
 
57            foreach ($type->names as $name) {
 
57            foreach ($type->names as $name) {
 
58                if (!$this->matchesKind($name, $kind)) {
 
59                    continue;
 
57            foreach ($type->names as $name) {
 
56        foreach (['enum', 'immutable', 'mutable', 'int', 'float', 'bool', 'string'] as $kind) {
57            foreach ($type->names as $name) {
 
56        foreach (['enum', 'immutable', 'mutable', 'int', 'float', 'bool', 'string'] as $kind) {
 
56        foreach (['enum', 'immutable', 'mutable', 'int', 'float', 'bool', 'string'] as $kind) {
57            foreach ($type->names as $name) {
58                if (!$this->matchesKind($name, $kind)) {
59                    continue;
60                }
61
62                try {
63                    return $this->coerceNamed($value, $name, $context, $type->describe());
64                } catch (InvalidTypeCoercion $e) {
65                    $lastFailure = $e->reason();
66                }
67            }
68        }
69
70        $reason = 'no union arm accepted the value';
71
72        if ($lastFailure !== null) {
 
73            $reason .= '; last failure: ' . $lastFailure;
74        }
75
76        $this->fail($value, $context, $type->describe(), $reason);
 
76        $this->fail($value, $context, $type->describe(), $reason);
77    }
46    private function coerceUnion(mixed $value, TypeMetadata $type, HydrationContext $context): mixed
47    {
48        foreach ($type->names as $name) {
 
48        foreach ($type->names as $name) {
49            if ($this->satisfies($value, $name)) {
50                return $value;
51            }
52        }
53
54        $lastFailure = null;
55
56        foreach (['enum', 'immutable', 'mutable', 'int', 'float', 'bool', 'string'] as $kind) {
 
56        foreach (['enum', 'immutable', 'mutable', 'int', 'float', 'bool', 'string'] as $kind) {
 
57            foreach ($type->names as $name) {
 
57            foreach ($type->names as $name) {
 
58                if (!$this->matchesKind($name, $kind)) {
 
59                    continue;
 
57            foreach ($type->names as $name) {
 
56        foreach (['enum', 'immutable', 'mutable', 'int', 'float', 'bool', 'string'] as $kind) {
57            foreach ($type->names as $name) {
 
56        foreach (['enum', 'immutable', 'mutable', 'int', 'float', 'bool', 'string'] as $kind) {
 
56        foreach (['enum', 'immutable', 'mutable', 'int', 'float', 'bool', 'string'] as $kind) {
57            foreach ($type->names as $name) {
58                if (!$this->matchesKind($name, $kind)) {
59                    continue;
60                }
61
62                try {
63                    return $this->coerceNamed($value, $name, $context, $type->describe());
64                } catch (InvalidTypeCoercion $e) {
65                    $lastFailure = $e->reason();
66                }
67            }
68        }
69
70        $reason = 'no union arm accepted the value';
71
72        if ($lastFailure !== null) {
 
76        $this->fail($value, $context, $type->describe(), $reason);
77    }
46    private function coerceUnion(mixed $value, TypeMetadata $type, HydrationContext $context): mixed
47    {
48        foreach ($type->names as $name) {
 
48        foreach ($type->names as $name) {
49            if ($this->satisfies($value, $name)) {
50                return $value;
51            }
52        }
53
54        $lastFailure = null;
55
56        foreach (['enum', 'immutable', 'mutable', 'int', 'float', 'bool', 'string'] as $kind) {
 
56        foreach (['enum', 'immutable', 'mutable', 'int', 'float', 'bool', 'string'] as $kind) {
 
57            foreach ($type->names as $name) {
 
57            foreach ($type->names as $name) {
 
58                if (!$this->matchesKind($name, $kind)) {
 
62                try {
63                    return $this->coerceNamed($value, $name, $context, $type->describe());
46    private function coerceUnion(mixed $value, TypeMetadata $type, HydrationContext $context): mixed
47    {
48        foreach ($type->names as $name) {
 
48        foreach ($type->names as $name) {
49            if ($this->satisfies($value, $name)) {
50                return $value;
51            }
52        }
53
54        $lastFailure = null;
55
56        foreach (['enum', 'immutable', 'mutable', 'int', 'float', 'bool', 'string'] as $kind) {
 
56        foreach (['enum', 'immutable', 'mutable', 'int', 'float', 'bool', 'string'] as $kind) {
 
57            foreach ($type->names as $name) {
 
57            foreach ($type->names as $name) {
 
56        foreach (['enum', 'immutable', 'mutable', 'int', 'float', 'bool', 'string'] as $kind) {
57            foreach ($type->names as $name) {
 
56        foreach (['enum', 'immutable', 'mutable', 'int', 'float', 'bool', 'string'] as $kind) {
 
56        foreach (['enum', 'immutable', 'mutable', 'int', 'float', 'bool', 'string'] as $kind) {
57            foreach ($type->names as $name) {
58                if (!$this->matchesKind($name, $kind)) {
59                    continue;
60                }
61
62                try {
63                    return $this->coerceNamed($value, $name, $context, $type->describe());
64                } catch (InvalidTypeCoercion $e) {
65                    $lastFailure = $e->reason();
66                }
67            }
68        }
69
70        $reason = 'no union arm accepted the value';
71
72        if ($lastFailure !== null) {
 
73            $reason .= '; last failure: ' . $lastFailure;
74        }
75
76        $this->fail($value, $context, $type->describe(), $reason);
 
76        $this->fail($value, $context, $type->describe(), $reason);
77    }
46    private function coerceUnion(mixed $value, TypeMetadata $type, HydrationContext $context): mixed
47    {
48        foreach ($type->names as $name) {
 
48        foreach ($type->names as $name) {
49            if ($this->satisfies($value, $name)) {
50                return $value;
51            }
52        }
53
54        $lastFailure = null;
55
56        foreach (['enum', 'immutable', 'mutable', 'int', 'float', 'bool', 'string'] as $kind) {
 
56        foreach (['enum', 'immutable', 'mutable', 'int', 'float', 'bool', 'string'] as $kind) {
 
57            foreach ($type->names as $name) {
 
57            foreach ($type->names as $name) {
 
56        foreach (['enum', 'immutable', 'mutable', 'int', 'float', 'bool', 'string'] as $kind) {
57            foreach ($type->names as $name) {
 
56        foreach (['enum', 'immutable', 'mutable', 'int', 'float', 'bool', 'string'] as $kind) {
 
56        foreach (['enum', 'immutable', 'mutable', 'int', 'float', 'bool', 'string'] as $kind) {
57            foreach ($type->names as $name) {
58                if (!$this->matchesKind($name, $kind)) {
59                    continue;
60                }
61
62                try {
63                    return $this->coerceNamed($value, $name, $context, $type->describe());
64                } catch (InvalidTypeCoercion $e) {
65                    $lastFailure = $e->reason();
66                }
67            }
68        }
69
70        $reason = 'no union arm accepted the value';
71
72        if ($lastFailure !== null) {
 
76        $this->fail($value, $context, $type->describe(), $reason);
77    }
46    private function coerceUnion(mixed $value, TypeMetadata $type, HydrationContext $context): mixed
47    {
48        foreach ($type->names as $name) {
 
48        foreach ($type->names as $name) {
49            if ($this->satisfies($value, $name)) {
50                return $value;
51            }
52        }
53
54        $lastFailure = null;
55
56        foreach (['enum', 'immutable', 'mutable', 'int', 'float', 'bool', 'string'] as $kind) {
 
56        foreach (['enum', 'immutable', 'mutable', 'int', 'float', 'bool', 'string'] as $kind) {
 
57            foreach ($type->names as $name) {
 
56        foreach (['enum', 'immutable', 'mutable', 'int', 'float', 'bool', 'string'] as $kind) {
57            foreach ($type->names as $name) {
 
56        foreach (['enum', 'immutable', 'mutable', 'int', 'float', 'bool', 'string'] as $kind) {
 
56        foreach (['enum', 'immutable', 'mutable', 'int', 'float', 'bool', 'string'] as $kind) {
57            foreach ($type->names as $name) {
58                if (!$this->matchesKind($name, $kind)) {
59                    continue;
60                }
61
62                try {
63                    return $this->coerceNamed($value, $name, $context, $type->describe());
64                } catch (InvalidTypeCoercion $e) {
65                    $lastFailure = $e->reason();
66                }
67            }
68        }
69
70        $reason = 'no union arm accepted the value';
71
72        if ($lastFailure !== null) {
 
73            $reason .= '; last failure: ' . $lastFailure;
74        }
75
76        $this->fail($value, $context, $type->describe(), $reason);
 
76        $this->fail($value, $context, $type->describe(), $reason);
77    }
46    private function coerceUnion(mixed $value, TypeMetadata $type, HydrationContext $context): mixed
47    {
48        foreach ($type->names as $name) {
 
48        foreach ($type->names as $name) {
49            if ($this->satisfies($value, $name)) {
50                return $value;
51            }
52        }
53
54        $lastFailure = null;
55
56        foreach (['enum', 'immutable', 'mutable', 'int', 'float', 'bool', 'string'] as $kind) {
 
56        foreach (['enum', 'immutable', 'mutable', 'int', 'float', 'bool', 'string'] as $kind) {
 
57            foreach ($type->names as $name) {
 
56        foreach (['enum', 'immutable', 'mutable', 'int', 'float', 'bool', 'string'] as $kind) {
57            foreach ($type->names as $name) {
 
56        foreach (['enum', 'immutable', 'mutable', 'int', 'float', 'bool', 'string'] as $kind) {
 
56        foreach (['enum', 'immutable', 'mutable', 'int', 'float', 'bool', 'string'] as $kind) {
57            foreach ($type->names as $name) {
58                if (!$this->matchesKind($name, $kind)) {
59                    continue;
60                }
61
62                try {
63                    return $this->coerceNamed($value, $name, $context, $type->describe());
64                } catch (InvalidTypeCoercion $e) {
65                    $lastFailure = $e->reason();
66                }
67            }
68        }
69
70        $reason = 'no union arm accepted the value';
71
72        if ($lastFailure !== null) {
 
76        $this->fail($value, $context, $type->describe(), $reason);
77    }
46    private function coerceUnion(mixed $value, TypeMetadata $type, HydrationContext $context): mixed
47    {
48        foreach ($type->names as $name) {
 
48        foreach ($type->names as $name) {
49            if ($this->satisfies($value, $name)) {
50                return $value;
51            }
52        }
53
54        $lastFailure = null;
55
56        foreach (['enum', 'immutable', 'mutable', 'int', 'float', 'bool', 'string'] as $kind) {
 
56        foreach (['enum', 'immutable', 'mutable', 'int', 'float', 'bool', 'string'] as $kind) {
 
56        foreach (['enum', 'immutable', 'mutable', 'int', 'float', 'bool', 'string'] as $kind) {
57            foreach ($type->names as $name) {
58                if (!$this->matchesKind($name, $kind)) {
59                    continue;
60                }
61
62                try {
63                    return $this->coerceNamed($value, $name, $context, $type->describe());
64                } catch (InvalidTypeCoercion $e) {
65                    $lastFailure = $e->reason();
66                }
67            }
68        }
69
70        $reason = 'no union arm accepted the value';
71
72        if ($lastFailure !== null) {
 
73            $reason .= '; last failure: ' . $lastFailure;
74        }
75
76        $this->fail($value, $context, $type->describe(), $reason);
 
76        $this->fail($value, $context, $type->describe(), $reason);
77    }
46    private function coerceUnion(mixed $value, TypeMetadata $type, HydrationContext $context): mixed
47    {
48        foreach ($type->names as $name) {
 
48        foreach ($type->names as $name) {
49            if ($this->satisfies($value, $name)) {
50                return $value;
51            }
52        }
53
54        $lastFailure = null;
55
56        foreach (['enum', 'immutable', 'mutable', 'int', 'float', 'bool', 'string'] as $kind) {
 
56        foreach (['enum', 'immutable', 'mutable', 'int', 'float', 'bool', 'string'] as $kind) {
 
56        foreach (['enum', 'immutable', 'mutable', 'int', 'float', 'bool', 'string'] as $kind) {
57            foreach ($type->names as $name) {
58                if (!$this->matchesKind($name, $kind)) {
59                    continue;
60                }
61
62                try {
63                    return $this->coerceNamed($value, $name, $context, $type->describe());
64                } catch (InvalidTypeCoercion $e) {
65                    $lastFailure = $e->reason();
66                }
67            }
68        }
69
70        $reason = 'no union arm accepted the value';
71
72        if ($lastFailure !== null) {
 
76        $this->fail($value, $context, $type->describe(), $reason);
77    }
46    private function coerceUnion(mixed $value, TypeMetadata $type, HydrationContext $context): mixed
47    {
48        foreach ($type->names as $name) {
 
48        foreach ($type->names as $name) {
49            if ($this->satisfies($value, $name)) {
50                return $value;
51            }
52        }
53
54        $lastFailure = null;
55
56        foreach (['enum', 'immutable', 'mutable', 'int', 'float', 'bool', 'string'] as $kind) {
 
56        foreach (['enum', 'immutable', 'mutable', 'int', 'float', 'bool', 'string'] as $kind) {
57            foreach ($type->names as $name) {
58                if (!$this->matchesKind($name, $kind)) {
59                    continue;
60                }
61
62                try {
63                    return $this->coerceNamed($value, $name, $context, $type->describe());
64                } catch (InvalidTypeCoercion $e) {
65                    $lastFailure = $e->reason();
66                }
67            }
68        }
69
70        $reason = 'no union arm accepted the value';
71
72        if ($lastFailure !== null) {
 
73            $reason .= '; last failure: ' . $lastFailure;
74        }
75
76        $this->fail($value, $context, $type->describe(), $reason);
 
76        $this->fail($value, $context, $type->describe(), $reason);
77    }
46    private function coerceUnion(mixed $value, TypeMetadata $type, HydrationContext $context): mixed
47    {
48        foreach ($type->names as $name) {
 
48        foreach ($type->names as $name) {
49            if ($this->satisfies($value, $name)) {
50                return $value;
51            }
52        }
53
54        $lastFailure = null;
55
56        foreach (['enum', 'immutable', 'mutable', 'int', 'float', 'bool', 'string'] as $kind) {
 
56        foreach (['enum', 'immutable', 'mutable', 'int', 'float', 'bool', 'string'] as $kind) {
57            foreach ($type->names as $name) {
58                if (!$this->matchesKind($name, $kind)) {
59                    continue;
60                }
61
62                try {
63                    return $this->coerceNamed($value, $name, $context, $type->describe());
64                } catch (InvalidTypeCoercion $e) {
65                    $lastFailure = $e->reason();
66                }
67            }
68        }
69
70        $reason = 'no union arm accepted the value';
71
72        if ($lastFailure !== null) {
 
76        $this->fail($value, $context, $type->describe(), $reason);
77    }
64                } catch (InvalidTypeCoercion $e) {
 
57            foreach ($type->names as $name) {
58                if (!$this->matchesKind($name, $kind)) {
59                    continue;
60                }
61
62                try {
63                    return $this->coerceNamed($value, $name, $context, $type->describe());
64                } catch (InvalidTypeCoercion $e) {
65                    $lastFailure = $e->reason();
 
57            foreach ($type->names as $name) {
 
58                if (!$this->matchesKind($name, $kind)) {
 
59                    continue;
 
57            foreach ($type->names as $name) {
 
56        foreach (['enum', 'immutable', 'mutable', 'int', 'float', 'bool', 'string'] as $kind) {
57            foreach ($type->names as $name) {
 
56        foreach (['enum', 'immutable', 'mutable', 'int', 'float', 'bool', 'string'] as $kind) {
 
57            foreach ($type->names as $name) {
 
57            foreach ($type->names as $name) {
64                } catch (InvalidTypeCoercion $e) {
 
57            foreach ($type->names as $name) {
58                if (!$this->matchesKind($name, $kind)) {
59                    continue;
60                }
61
62                try {
63                    return $this->coerceNamed($value, $name, $context, $type->describe());
64                } catch (InvalidTypeCoercion $e) {
65                    $lastFailure = $e->reason();
 
57            foreach ($type->names as $name) {
 
58                if (!$this->matchesKind($name, $kind)) {
 
59                    continue;
 
57            foreach ($type->names as $name) {
 
56        foreach (['enum', 'immutable', 'mutable', 'int', 'float', 'bool', 'string'] as $kind) {
57            foreach ($type->names as $name) {
 
56        foreach (['enum', 'immutable', 'mutable', 'int', 'float', 'bool', 'string'] as $kind) {
 
57            foreach ($type->names as $name) {
 
56        foreach (['enum', 'immutable', 'mutable', 'int', 'float', 'bool', 'string'] as $kind) {
57            foreach ($type->names as $name) {
64                } catch (InvalidTypeCoercion $e) {
 
57            foreach ($type->names as $name) {
58                if (!$this->matchesKind($name, $kind)) {
59                    continue;
60                }
61
62                try {
63                    return $this->coerceNamed($value, $name, $context, $type->describe());
64                } catch (InvalidTypeCoercion $e) {
65                    $lastFailure = $e->reason();
 
57            foreach ($type->names as $name) {
 
58                if (!$this->matchesKind($name, $kind)) {
 
59                    continue;
 
57            foreach ($type->names as $name) {
 
56        foreach (['enum', 'immutable', 'mutable', 'int', 'float', 'bool', 'string'] as $kind) {
57            foreach ($type->names as $name) {
 
56        foreach (['enum', 'immutable', 'mutable', 'int', 'float', 'bool', 'string'] as $kind) {
 
56        foreach (['enum', 'immutable', 'mutable', 'int', 'float', 'bool', 'string'] as $kind) {
57            foreach ($type->names as $name) {
58                if (!$this->matchesKind($name, $kind)) {
59                    continue;
60                }
61
62                try {
63                    return $this->coerceNamed($value, $name, $context, $type->describe());
64                } catch (InvalidTypeCoercion $e) {
65                    $lastFailure = $e->reason();
66                }
67            }
68        }
69
70        $reason = 'no union arm accepted the value';
71
72        if ($lastFailure !== null) {
 
73            $reason .= '; last failure: ' . $lastFailure;
74        }
75
76        $this->fail($value, $context, $type->describe(), $reason);
 
76        $this->fail($value, $context, $type->describe(), $reason);
77    }
64                } catch (InvalidTypeCoercion $e) {
 
57            foreach ($type->names as $name) {
58                if (!$this->matchesKind($name, $kind)) {
59                    continue;
60                }
61
62                try {
63                    return $this->coerceNamed($value, $name, $context, $type->describe());
64                } catch (InvalidTypeCoercion $e) {
65                    $lastFailure = $e->reason();
 
57            foreach ($type->names as $name) {
 
58                if (!$this->matchesKind($name, $kind)) {
 
59                    continue;
 
57            foreach ($type->names as $name) {
 
56        foreach (['enum', 'immutable', 'mutable', 'int', 'float', 'bool', 'string'] as $kind) {
57            foreach ($type->names as $name) {
 
56        foreach (['enum', 'immutable', 'mutable', 'int', 'float', 'bool', 'string'] as $kind) {
 
56        foreach (['enum', 'immutable', 'mutable', 'int', 'float', 'bool', 'string'] as $kind) {
57            foreach ($type->names as $name) {
58                if (!$this->matchesKind($name, $kind)) {
59                    continue;
60                }
61
62                try {
63                    return $this->coerceNamed($value, $name, $context, $type->describe());
64                } catch (InvalidTypeCoercion $e) {
65                    $lastFailure = $e->reason();
66                }
67            }
68        }
69
70        $reason = 'no union arm accepted the value';
71
72        if ($lastFailure !== null) {
 
76        $this->fail($value, $context, $type->describe(), $reason);
77    }
64                } catch (InvalidTypeCoercion $e) {
 
57            foreach ($type->names as $name) {
58                if (!$this->matchesKind($name, $kind)) {
59                    continue;
60                }
61
62                try {
63                    return $this->coerceNamed($value, $name, $context, $type->describe());
64                } catch (InvalidTypeCoercion $e) {
65                    $lastFailure = $e->reason();
 
57            foreach ($type->names as $name) {
 
58                if (!$this->matchesKind($name, $kind)) {
 
62                try {
63                    return $this->coerceNamed($value, $name, $context, $type->describe());
64                } catch (InvalidTypeCoercion $e) {
 
57            foreach ($type->names as $name) {
58                if (!$this->matchesKind($name, $kind)) {
59                    continue;
60                }
61
62                try {
63                    return $this->coerceNamed($value, $name, $context, $type->describe());
64                } catch (InvalidTypeCoercion $e) {
65                    $lastFailure = $e->reason();
 
57            foreach ($type->names as $name) {
 
56        foreach (['enum', 'immutable', 'mutable', 'int', 'float', 'bool', 'string'] as $kind) {
57            foreach ($type->names as $name) {
 
56        foreach (['enum', 'immutable', 'mutable', 'int', 'float', 'bool', 'string'] as $kind) {
 
57            foreach ($type->names as $name) {
 
57            foreach ($type->names as $name) {
 
58                if (!$this->matchesKind($name, $kind)) {
 
59                    continue;
 
57            foreach ($type->names as $name) {
64                } catch (InvalidTypeCoercion $e) {
 
57            foreach ($type->names as $name) {
58                if (!$this->matchesKind($name, $kind)) {
59                    continue;
60                }
61
62                try {
63                    return $this->coerceNamed($value, $name, $context, $type->describe());
64                } catch (InvalidTypeCoercion $e) {
65                    $lastFailure = $e->reason();
 
57            foreach ($type->names as $name) {
 
56        foreach (['enum', 'immutable', 'mutable', 'int', 'float', 'bool', 'string'] as $kind) {
57            foreach ($type->names as $name) {
 
56        foreach (['enum', 'immutable', 'mutable', 'int', 'float', 'bool', 'string'] as $kind) {
 
57            foreach ($type->names as $name) {
 
57            foreach ($type->names as $name) {
 
58                if (!$this->matchesKind($name, $kind)) {
 
62                try {
63                    return $this->coerceNamed($value, $name, $context, $type->describe());
64                } catch (InvalidTypeCoercion $e) {
 
57            foreach ($type->names as $name) {
58                if (!$this->matchesKind($name, $kind)) {
59                    continue;
60                }
61
62                try {
63                    return $this->coerceNamed($value, $name, $context, $type->describe());
64                } catch (InvalidTypeCoercion $e) {
65                    $lastFailure = $e->reason();
 
57            foreach ($type->names as $name) {
 
56        foreach (['enum', 'immutable', 'mutable', 'int', 'float', 'bool', 'string'] as $kind) {
57            foreach ($type->names as $name) {
 
56        foreach (['enum', 'immutable', 'mutable', 'int', 'float', 'bool', 'string'] as $kind) {
 
57            foreach ($type->names as $name) {
 
56        foreach (['enum', 'immutable', 'mutable', 'int', 'float', 'bool', 'string'] as $kind) {
57            foreach ($type->names as $name) {
64                } catch (InvalidTypeCoercion $e) {
 
57            foreach ($type->names as $name) {
58                if (!$this->matchesKind($name, $kind)) {
59                    continue;
60                }
61
62                try {
63                    return $this->coerceNamed($value, $name, $context, $type->describe());
64                } catch (InvalidTypeCoercion $e) {
65                    $lastFailure = $e->reason();
 
57            foreach ($type->names as $name) {
 
56        foreach (['enum', 'immutable', 'mutable', 'int', 'float', 'bool', 'string'] as $kind) {
57            foreach ($type->names as $name) {
 
56        foreach (['enum', 'immutable', 'mutable', 'int', 'float', 'bool', 'string'] as $kind) {
 
56        foreach (['enum', 'immutable', 'mutable', 'int', 'float', 'bool', 'string'] as $kind) {
57            foreach ($type->names as $name) {
58                if (!$this->matchesKind($name, $kind)) {
59                    continue;
60                }
61
62                try {
63                    return $this->coerceNamed($value, $name, $context, $type->describe());
64                } catch (InvalidTypeCoercion $e) {
65                    $lastFailure = $e->reason();
66                }
67            }
68        }
69
70        $reason = 'no union arm accepted the value';
71
72        if ($lastFailure !== null) {
 
73            $reason .= '; last failure: ' . $lastFailure;
74        }
75
76        $this->fail($value, $context, $type->describe(), $reason);
 
76        $this->fail($value, $context, $type->describe(), $reason);
77    }
64                } catch (InvalidTypeCoercion $e) {
 
57            foreach ($type->names as $name) {
58                if (!$this->matchesKind($name, $kind)) {
59                    continue;
60                }
61
62                try {
63                    return $this->coerceNamed($value, $name, $context, $type->describe());
64                } catch (InvalidTypeCoercion $e) {
65                    $lastFailure = $e->reason();
 
57            foreach ($type->names as $name) {
 
56        foreach (['enum', 'immutable', 'mutable', 'int', 'float', 'bool', 'string'] as $kind) {
57            foreach ($type->names as $name) {
 
56        foreach (['enum', 'immutable', 'mutable', 'int', 'float', 'bool', 'string'] as $kind) {
 
56        foreach (['enum', 'immutable', 'mutable', 'int', 'float', 'bool', 'string'] as $kind) {
57            foreach ($type->names as $name) {
58                if (!$this->matchesKind($name, $kind)) {
59                    continue;
60                }
61
62                try {
63                    return $this->coerceNamed($value, $name, $context, $type->describe());
64                } catch (InvalidTypeCoercion $e) {
65                    $lastFailure = $e->reason();
66                }
67            }
68        }
69
70        $reason = 'no union arm accepted the value';
71
72        if ($lastFailure !== null) {
 
76        $this->fail($value, $context, $type->describe(), $reason);
77    }
TypeCoercer->fail
358        mixed $value,
359        HydrationContext $context,
360        string $description,
361        string $reason,
362    ): never {
363        throw InvalidTypeCoercion::forContext($context, $value, $description, $reason);
364    }
TypeCoercer->intFromString
330    private function intFromString(string $value): ?int
331    {
332        if (!preg_match('/^-?\d+$/', $value)) {
 
332        if (!preg_match('/^-?\d+$/', $value)) {
 
332        if (!preg_match('/^-?\d+$/', $value)) {
 
333            return null;
330    private function intFromString(string $value): ?int
331    {
332        if (!preg_match('/^-?\d+$/', $value)) {
 
332        if (!preg_match('/^-?\d+$/', $value)) {
 
332        if (!preg_match('/^-?\d+$/', $value)) {
 
336        if (!$this->integerStringFits($value)) {
 
337            return null;
330    private function intFromString(string $value): ?int
331    {
332        if (!preg_match('/^-?\d+$/', $value)) {
 
332        if (!preg_match('/^-?\d+$/', $value)) {
 
332        if (!preg_match('/^-?\d+$/', $value)) {
 
336        if (!$this->integerStringFits($value)) {
 
340        return (int) $value;
341    }
330    private function intFromString(string $value): ?int
331    {
332        if (!preg_match('/^-?\d+$/', $value)) {
 
332        if (!preg_match('/^-?\d+$/', $value)) {
 
332        if (!preg_match('/^-?\d+$/', $value)) {
 
333            return null;
330    private function intFromString(string $value): ?int
331    {
332        if (!preg_match('/^-?\d+$/', $value)) {
 
332        if (!preg_match('/^-?\d+$/', $value)) {
 
332        if (!preg_match('/^-?\d+$/', $value)) {
 
336        if (!$this->integerStringFits($value)) {
 
337            return null;
330    private function intFromString(string $value): ?int
331    {
332        if (!preg_match('/^-?\d+$/', $value)) {
 
332        if (!preg_match('/^-?\d+$/', $value)) {
 
332        if (!preg_match('/^-?\d+$/', $value)) {
 
336        if (!$this->integerStringFits($value)) {
 
340        return (int) $value;
341    }
TypeCoercer->integerStringFits
343    private function integerStringFits(string $value): bool
344    {
345        $negative = str_starts_with($value, '-');
 
345        $negative = str_starts_with($value, '-');
 
345        $negative = str_starts_with($value, '-');
346        $digits = $negative ? substr($value, 1) : $value;
 
346        $digits = $negative ? substr($value, 1) : $value;
 
346        $digits = $negative ? substr($value, 1) : $value;
 
346        $digits = $negative ? substr($value, 1) : $value;
 
346        $digits = $negative ? substr($value, 1) : $value;
347        $digits = ltrim($digits, '0');
348        $limit = $negative ? substr((string) PHP_INT_MIN, 1) : (string) PHP_INT_MAX;
 
348        $limit = $negative ? substr((string) PHP_INT_MIN, 1) : (string) PHP_INT_MAX;
 
348        $limit = $negative ? substr((string) PHP_INT_MIN, 1) : (string) PHP_INT_MAX;
 
348        $limit = $negative ? substr((string) PHP_INT_MIN, 1) : (string) PHP_INT_MAX;
 
348        $limit = $negative ? substr((string) PHP_INT_MIN, 1) : (string) PHP_INT_MAX;
349
350        return (
351            strlen($digits) < strlen($limit)
 
352                || strlen($digits) === strlen($limit)
 
353                && strcmp($digits, $limit) <= 0
 
353                && strcmp($digits, $limit) <= 0
 
353                && strcmp($digits, $limit) <= 0
354        );
355    }
343    private function integerStringFits(string $value): bool
344    {
345        $negative = str_starts_with($value, '-');
 
345        $negative = str_starts_with($value, '-');
 
345        $negative = str_starts_with($value, '-');
346        $digits = $negative ? substr($value, 1) : $value;
 
346        $digits = $negative ? substr($value, 1) : $value;
 
346        $digits = $negative ? substr($value, 1) : $value;
 
346        $digits = $negative ? substr($value, 1) : $value;
 
346        $digits = $negative ? substr($value, 1) : $value;
347        $digits = ltrim($digits, '0');
348        $limit = $negative ? substr((string) PHP_INT_MIN, 1) : (string) PHP_INT_MAX;
 
348        $limit = $negative ? substr((string) PHP_INT_MIN, 1) : (string) PHP_INT_MAX;
 
348        $limit = $negative ? substr((string) PHP_INT_MIN, 1) : (string) PHP_INT_MAX;
 
348        $limit = $negative ? substr((string) PHP_INT_MIN, 1) : (string) PHP_INT_MAX;
 
348        $limit = $negative ? substr((string) PHP_INT_MIN, 1) : (string) PHP_INT_MAX;
349
350        return (
351            strlen($digits) < strlen($limit)
 
352                || strlen($digits) === strlen($limit)
 
353                && strcmp($digits, $limit) <= 0
 
353                && strcmp($digits, $limit) <= 0
354        );
355    }
343    private function integerStringFits(string $value): bool
344    {
345        $negative = str_starts_with($value, '-');
 
345        $negative = str_starts_with($value, '-');
 
345        $negative = str_starts_with($value, '-');
346        $digits = $negative ? substr($value, 1) : $value;
 
346        $digits = $negative ? substr($value, 1) : $value;
 
346        $digits = $negative ? substr($value, 1) : $value;
 
346        $digits = $negative ? substr($value, 1) : $value;
 
346        $digits = $negative ? substr($value, 1) : $value;
347        $digits = ltrim($digits, '0');
348        $limit = $negative ? substr((string) PHP_INT_MIN, 1) : (string) PHP_INT_MAX;
 
348        $limit = $negative ? substr((string) PHP_INT_MIN, 1) : (string) PHP_INT_MAX;
 
348        $limit = $negative ? substr((string) PHP_INT_MIN, 1) : (string) PHP_INT_MAX;
 
348        $limit = $negative ? substr((string) PHP_INT_MIN, 1) : (string) PHP_INT_MAX;
 
348        $limit = $negative ? substr((string) PHP_INT_MIN, 1) : (string) PHP_INT_MAX;
349
350        return (
351            strlen($digits) < strlen($limit)
 
353                && strcmp($digits, $limit) <= 0
354        );
355    }
343    private function integerStringFits(string $value): bool
344    {
345        $negative = str_starts_with($value, '-');
 
345        $negative = str_starts_with($value, '-');
 
345        $negative = str_starts_with($value, '-');
346        $digits = $negative ? substr($value, 1) : $value;
 
346        $digits = $negative ? substr($value, 1) : $value;
 
346        $digits = $negative ? substr($value, 1) : $value;
 
346        $digits = $negative ? substr($value, 1) : $value;
 
346        $digits = $negative ? substr($value, 1) : $value;
347        $digits = ltrim($digits, '0');
348        $limit = $negative ? substr((string) PHP_INT_MIN, 1) : (string) PHP_INT_MAX;
 
348        $limit = $negative ? substr((string) PHP_INT_MIN, 1) : (string) PHP_INT_MAX;
 
348        $limit = $negative ? substr((string) PHP_INT_MIN, 1) : (string) PHP_INT_MAX;
 
348        $limit = $negative ? substr((string) PHP_INT_MIN, 1) : (string) PHP_INT_MAX;
 
348        $limit = $negative ? substr((string) PHP_INT_MIN, 1) : (string) PHP_INT_MAX;
349
350        return (
351            strlen($digits) < strlen($limit)
 
352                || strlen($digits) === strlen($limit)
 
353                && strcmp($digits, $limit) <= 0
 
353                && strcmp($digits, $limit) <= 0
 
353                && strcmp($digits, $limit) <= 0
354        );
355    }
343    private function integerStringFits(string $value): bool
344    {
345        $negative = str_starts_with($value, '-');
 
345        $negative = str_starts_with($value, '-');
 
345        $negative = str_starts_with($value, '-');
346        $digits = $negative ? substr($value, 1) : $value;
 
346        $digits = $negative ? substr($value, 1) : $value;
 
346        $digits = $negative ? substr($value, 1) : $value;
 
346        $digits = $negative ? substr($value, 1) : $value;
 
346        $digits = $negative ? substr($value, 1) : $value;
347        $digits = ltrim($digits, '0');
348        $limit = $negative ? substr((string) PHP_INT_MIN, 1) : (string) PHP_INT_MAX;
 
348        $limit = $negative ? substr((string) PHP_INT_MIN, 1) : (string) PHP_INT_MAX;
 
348        $limit = $negative ? substr((string) PHP_INT_MIN, 1) : (string) PHP_INT_MAX;
 
348        $limit = $negative ? substr((string) PHP_INT_MIN, 1) : (string) PHP_INT_MAX;
 
348        $limit = $negative ? substr((string) PHP_INT_MIN, 1) : (string) PHP_INT_MAX;
349
350        return (
351            strlen($digits) < strlen($limit)
 
352                || strlen($digits) === strlen($limit)
 
353                && strcmp($digits, $limit) <= 0
 
353                && strcmp($digits, $limit) <= 0
354        );
355    }
343    private function integerStringFits(string $value): bool
344    {
345        $negative = str_starts_with($value, '-');
 
345        $negative = str_starts_with($value, '-');
 
345        $negative = str_starts_with($value, '-');
346        $digits = $negative ? substr($value, 1) : $value;
 
346        $digits = $negative ? substr($value, 1) : $value;
 
346        $digits = $negative ? substr($value, 1) : $value;
 
346        $digits = $negative ? substr($value, 1) : $value;
 
346        $digits = $negative ? substr($value, 1) : $value;
347        $digits = ltrim($digits, '0');
348        $limit = $negative ? substr((string) PHP_INT_MIN, 1) : (string) PHP_INT_MAX;
 
348        $limit = $negative ? substr((string) PHP_INT_MIN, 1) : (string) PHP_INT_MAX;
 
348        $limit = $negative ? substr((string) PHP_INT_MIN, 1) : (string) PHP_INT_MAX;
 
348        $limit = $negative ? substr((string) PHP_INT_MIN, 1) : (string) PHP_INT_MAX;
 
348        $limit = $negative ? substr((string) PHP_INT_MIN, 1) : (string) PHP_INT_MAX;
349
350        return (
351            strlen($digits) < strlen($limit)
 
353                && strcmp($digits, $limit) <= 0
354        );
355    }
343    private function integerStringFits(string $value): bool
344    {
345        $negative = str_starts_with($value, '-');
 
345        $negative = str_starts_with($value, '-');
 
345        $negative = str_starts_with($value, '-');
346        $digits = $negative ? substr($value, 1) : $value;
 
346        $digits = $negative ? substr($value, 1) : $value;
 
346        $digits = $negative ? substr($value, 1) : $value;
 
346        $digits = $negative ? substr($value, 1) : $value;
 
346        $digits = $negative ? substr($value, 1) : $value;
347        $digits = ltrim($digits, '0');
348        $limit = $negative ? substr((string) PHP_INT_MIN, 1) : (string) PHP_INT_MAX;
 
348        $limit = $negative ? substr((string) PHP_INT_MIN, 1) : (string) PHP_INT_MAX;
 
348        $limit = $negative ? substr((string) PHP_INT_MIN, 1) : (string) PHP_INT_MAX;
349
350        return (
351            strlen($digits) < strlen($limit)
 
352                || strlen($digits) === strlen($limit)
 
353                && strcmp($digits, $limit) <= 0
 
353                && strcmp($digits, $limit) <= 0
 
353                && strcmp($digits, $limit) <= 0
354        );
355    }
343    private function integerStringFits(string $value): bool
344    {
345        $negative = str_starts_with($value, '-');
 
345        $negative = str_starts_with($value, '-');
 
345        $negative = str_starts_with($value, '-');
346        $digits = $negative ? substr($value, 1) : $value;
 
346        $digits = $negative ? substr($value, 1) : $value;
 
346        $digits = $negative ? substr($value, 1) : $value;
 
346        $digits = $negative ? substr($value, 1) : $value;
 
346        $digits = $negative ? substr($value, 1) : $value;
347        $digits = ltrim($digits, '0');
348        $limit = $negative ? substr((string) PHP_INT_MIN, 1) : (string) PHP_INT_MAX;
 
348        $limit = $negative ? substr((string) PHP_INT_MIN, 1) : (string) PHP_INT_MAX;
 
348        $limit = $negative ? substr((string) PHP_INT_MIN, 1) : (string) PHP_INT_MAX;
349
350        return (
351            strlen($digits) < strlen($limit)
 
352                || strlen($digits) === strlen($limit)
 
353                && strcmp($digits, $limit) <= 0
 
353                && strcmp($digits, $limit) <= 0
354        );
355    }
343    private function integerStringFits(string $value): bool
344    {
345        $negative = str_starts_with($value, '-');
 
345        $negative = str_starts_with($value, '-');
 
345        $negative = str_starts_with($value, '-');
346        $digits = $negative ? substr($value, 1) : $value;
 
346        $digits = $negative ? substr($value, 1) : $value;
 
346        $digits = $negative ? substr($value, 1) : $value;
 
346        $digits = $negative ? substr($value, 1) : $value;
 
346        $digits = $negative ? substr($value, 1) : $value;
347        $digits = ltrim($digits, '0');
348        $limit = $negative ? substr((string) PHP_INT_MIN, 1) : (string) PHP_INT_MAX;
 
348        $limit = $negative ? substr((string) PHP_INT_MIN, 1) : (string) PHP_INT_MAX;
 
348        $limit = $negative ? substr((string) PHP_INT_MIN, 1) : (string) PHP_INT_MAX;
349
350        return (
351            strlen($digits) < strlen($limit)
 
353                && strcmp($digits, $limit) <= 0
354        );
355    }
343    private function integerStringFits(string $value): bool
344    {
345        $negative = str_starts_with($value, '-');
 
345        $negative = str_starts_with($value, '-');
 
345        $negative = str_starts_with($value, '-');
346        $digits = $negative ? substr($value, 1) : $value;
 
346        $digits = $negative ? substr($value, 1) : $value;
 
346        $digits = $negative ? substr($value, 1) : $value;
 
346        $digits = $negative ? substr($value, 1) : $value;
 
346        $digits = $negative ? substr($value, 1) : $value;
347        $digits = ltrim($digits, '0');
348        $limit = $negative ? substr((string) PHP_INT_MIN, 1) : (string) PHP_INT_MAX;
 
348        $limit = $negative ? substr((string) PHP_INT_MIN, 1) : (string) PHP_INT_MAX;
 
348        $limit = $negative ? substr((string) PHP_INT_MIN, 1) : (string) PHP_INT_MAX;
 
348        $limit = $negative ? substr((string) PHP_INT_MIN, 1) : (string) PHP_INT_MAX;
 
348        $limit = $negative ? substr((string) PHP_INT_MIN, 1) : (string) PHP_INT_MAX;
349
350        return (
351            strlen($digits) < strlen($limit)
 
352                || strlen($digits) === strlen($limit)
 
353                && strcmp($digits, $limit) <= 0
 
353                && strcmp($digits, $limit) <= 0
 
353                && strcmp($digits, $limit) <= 0
354        );
355    }
343    private function integerStringFits(string $value): bool
344    {
345        $negative = str_starts_with($value, '-');
 
345        $negative = str_starts_with($value, '-');
 
345        $negative = str_starts_with($value, '-');
346        $digits = $negative ? substr($value, 1) : $value;
 
346        $digits = $negative ? substr($value, 1) : $value;
 
346        $digits = $negative ? substr($value, 1) : $value;
 
346        $digits = $negative ? substr($value, 1) : $value;
 
346        $digits = $negative ? substr($value, 1) : $value;
347        $digits = ltrim($digits, '0');
348        $limit = $negative ? substr((string) PHP_INT_MIN, 1) : (string) PHP_INT_MAX;
 
348        $limit = $negative ? substr((string) PHP_INT_MIN, 1) : (string) PHP_INT_MAX;
 
348        $limit = $negative ? substr((string) PHP_INT_MIN, 1) : (string) PHP_INT_MAX;
 
348        $limit = $negative ? substr((string) PHP_INT_MIN, 1) : (string) PHP_INT_MAX;
 
348        $limit = $negative ? substr((string) PHP_INT_MIN, 1) : (string) PHP_INT_MAX;
349
350        return (
351            strlen($digits) < strlen($limit)
 
352                || strlen($digits) === strlen($limit)
 
353                && strcmp($digits, $limit) <= 0
 
353                && strcmp($digits, $limit) <= 0
354        );
355    }
343    private function integerStringFits(string $value): bool
344    {
345        $negative = str_starts_with($value, '-');
 
345        $negative = str_starts_with($value, '-');
 
345        $negative = str_starts_with($value, '-');
346        $digits = $negative ? substr($value, 1) : $value;
 
346        $digits = $negative ? substr($value, 1) : $value;
 
346        $digits = $negative ? substr($value, 1) : $value;
 
346        $digits = $negative ? substr($value, 1) : $value;
 
346        $digits = $negative ? substr($value, 1) : $value;
347        $digits = ltrim($digits, '0');
348        $limit = $negative ? substr((string) PHP_INT_MIN, 1) : (string) PHP_INT_MAX;
 
348        $limit = $negative ? substr((string) PHP_INT_MIN, 1) : (string) PHP_INT_MAX;
 
348        $limit = $negative ? substr((string) PHP_INT_MIN, 1) : (string) PHP_INT_MAX;
 
348        $limit = $negative ? substr((string) PHP_INT_MIN, 1) : (string) PHP_INT_MAX;
 
348        $limit = $negative ? substr((string) PHP_INT_MIN, 1) : (string) PHP_INT_MAX;
349
350        return (
351            strlen($digits) < strlen($limit)
 
353                && strcmp($digits, $limit) <= 0
354        );
355    }
343    private function integerStringFits(string $value): bool
344    {
345        $negative = str_starts_with($value, '-');
 
345        $negative = str_starts_with($value, '-');
 
345        $negative = str_starts_with($value, '-');
346        $digits = $negative ? substr($value, 1) : $value;
 
346        $digits = $negative ? substr($value, 1) : $value;
 
346        $digits = $negative ? substr($value, 1) : $value;
 
346        $digits = $negative ? substr($value, 1) : $value;
 
346        $digits = $negative ? substr($value, 1) : $value;
347        $digits = ltrim($digits, '0');
348        $limit = $negative ? substr((string) PHP_INT_MIN, 1) : (string) PHP_INT_MAX;
 
348        $limit = $negative ? substr((string) PHP_INT_MIN, 1) : (string) PHP_INT_MAX;
 
348        $limit = $negative ? substr((string) PHP_INT_MIN, 1) : (string) PHP_INT_MAX;
 
348        $limit = $negative ? substr((string) PHP_INT_MIN, 1) : (string) PHP_INT_MAX;
 
348        $limit = $negative ? substr((string) PHP_INT_MIN, 1) : (string) PHP_INT_MAX;
349
350        return (
351            strlen($digits) < strlen($limit)
 
352                || strlen($digits) === strlen($limit)
 
353                && strcmp($digits, $limit) <= 0
 
353                && strcmp($digits, $limit) <= 0
 
353                && strcmp($digits, $limit) <= 0
354        );
355    }
343    private function integerStringFits(string $value): bool
344    {
345        $negative = str_starts_with($value, '-');
 
345        $negative = str_starts_with($value, '-');
 
345        $negative = str_starts_with($value, '-');
346        $digits = $negative ? substr($value, 1) : $value;
 
346        $digits = $negative ? substr($value, 1) : $value;
 
346        $digits = $negative ? substr($value, 1) : $value;
 
346        $digits = $negative ? substr($value, 1) : $value;
 
346        $digits = $negative ? substr($value, 1) : $value;
347        $digits = ltrim($digits, '0');
348        $limit = $negative ? substr((string) PHP_INT_MIN, 1) : (string) PHP_INT_MAX;
 
348        $limit = $negative ? substr((string) PHP_INT_MIN, 1) : (string) PHP_INT_MAX;
 
348        $limit = $negative ? substr((string) PHP_INT_MIN, 1) : (string) PHP_INT_MAX;
 
348        $limit = $negative ? substr((string) PHP_INT_MIN, 1) : (string) PHP_INT_MAX;
 
348        $limit = $negative ? substr((string) PHP_INT_MIN, 1) : (string) PHP_INT_MAX;
349
350        return (
351            strlen($digits) < strlen($limit)
 
352                || strlen($digits) === strlen($limit)
 
353                && strcmp($digits, $limit) <= 0
 
353                && strcmp($digits, $limit) <= 0
354        );
355    }
343    private function integerStringFits(string $value): bool
344    {
345        $negative = str_starts_with($value, '-');
 
345        $negative = str_starts_with($value, '-');
 
345        $negative = str_starts_with($value, '-');
346        $digits = $negative ? substr($value, 1) : $value;
 
346        $digits = $negative ? substr($value, 1) : $value;
 
346        $digits = $negative ? substr($value, 1) : $value;
 
346        $digits = $negative ? substr($value, 1) : $value;
 
346        $digits = $negative ? substr($value, 1) : $value;
347        $digits = ltrim($digits, '0');
348        $limit = $negative ? substr((string) PHP_INT_MIN, 1) : (string) PHP_INT_MAX;
 
348        $limit = $negative ? substr((string) PHP_INT_MIN, 1) : (string) PHP_INT_MAX;
 
348        $limit = $negative ? substr((string) PHP_INT_MIN, 1) : (string) PHP_INT_MAX;
 
348        $limit = $negative ? substr((string) PHP_INT_MIN, 1) : (string) PHP_INT_MAX;
 
348        $limit = $negative ? substr((string) PHP_INT_MIN, 1) : (string) PHP_INT_MAX;
349
350        return (
351            strlen($digits) < strlen($limit)
 
353                && strcmp($digits, $limit) <= 0
354        );
355    }
343    private function integerStringFits(string $value): bool
344    {
345        $negative = str_starts_with($value, '-');
 
345        $negative = str_starts_with($value, '-');
 
345        $negative = str_starts_with($value, '-');
346        $digits = $negative ? substr($value, 1) : $value;
 
346        $digits = $negative ? substr($value, 1) : $value;
 
346        $digits = $negative ? substr($value, 1) : $value;
 
346        $digits = $negative ? substr($value, 1) : $value;
 
346        $digits = $negative ? substr($value, 1) : $value;
347        $digits = ltrim($digits, '0');
348        $limit = $negative ? substr((string) PHP_INT_MIN, 1) : (string) PHP_INT_MAX;
 
348        $limit = $negative ? substr((string) PHP_INT_MIN, 1) : (string) PHP_INT_MAX;
 
348        $limit = $negative ? substr((string) PHP_INT_MIN, 1) : (string) PHP_INT_MAX;
349
350        return (
351            strlen($digits) < strlen($limit)
 
352                || strlen($digits) === strlen($limit)
 
353                && strcmp($digits, $limit) <= 0
 
353                && strcmp($digits, $limit) <= 0
 
353                && strcmp($digits, $limit) <= 0
354        );
355    }
343    private function integerStringFits(string $value): bool
344    {
345        $negative = str_starts_with($value, '-');
 
345        $negative = str_starts_with($value, '-');
 
345        $negative = str_starts_with($value, '-');
346        $digits = $negative ? substr($value, 1) : $value;
 
346        $digits = $negative ? substr($value, 1) : $value;
 
346        $digits = $negative ? substr($value, 1) : $value;
 
346        $digits = $negative ? substr($value, 1) : $value;
 
346        $digits = $negative ? substr($value, 1) : $value;
347        $digits = ltrim($digits, '0');
348        $limit = $negative ? substr((string) PHP_INT_MIN, 1) : (string) PHP_INT_MAX;
 
348        $limit = $negative ? substr((string) PHP_INT_MIN, 1) : (string) PHP_INT_MAX;
 
348        $limit = $negative ? substr((string) PHP_INT_MIN, 1) : (string) PHP_INT_MAX;
349
350        return (
351            strlen($digits) < strlen($limit)
 
352                || strlen($digits) === strlen($limit)
 
353                && strcmp($digits, $limit) <= 0
 
353                && strcmp($digits, $limit) <= 0
354        );
355    }
343    private function integerStringFits(string $value): bool
344    {
345        $negative = str_starts_with($value, '-');
 
345        $negative = str_starts_with($value, '-');
 
345        $negative = str_starts_with($value, '-');
346        $digits = $negative ? substr($value, 1) : $value;
 
346        $digits = $negative ? substr($value, 1) : $value;
 
346        $digits = $negative ? substr($value, 1) : $value;
 
346        $digits = $negative ? substr($value, 1) : $value;
 
346        $digits = $negative ? substr($value, 1) : $value;
347        $digits = ltrim($digits, '0');
348        $limit = $negative ? substr((string) PHP_INT_MIN, 1) : (string) PHP_INT_MAX;
 
348        $limit = $negative ? substr((string) PHP_INT_MIN, 1) : (string) PHP_INT_MAX;
 
348        $limit = $negative ? substr((string) PHP_INT_MIN, 1) : (string) PHP_INT_MAX;
349
350        return (
351            strlen($digits) < strlen($limit)
 
353                && strcmp($digits, $limit) <= 0
354        );
355    }
343    private function integerStringFits(string $value): bool
344    {
345        $negative = str_starts_with($value, '-');
 
345        $negative = str_starts_with($value, '-');
 
345        $negative = str_starts_with($value, '-');
346        $digits = $negative ? substr($value, 1) : $value;
 
346        $digits = $negative ? substr($value, 1) : $value;
 
346        $digits = $negative ? substr($value, 1) : $value;
347        $digits = ltrim($digits, '0');
348        $limit = $negative ? substr((string) PHP_INT_MIN, 1) : (string) PHP_INT_MAX;
 
348        $limit = $negative ? substr((string) PHP_INT_MIN, 1) : (string) PHP_INT_MAX;
 
348        $limit = $negative ? substr((string) PHP_INT_MIN, 1) : (string) PHP_INT_MAX;
 
348        $limit = $negative ? substr((string) PHP_INT_MIN, 1) : (string) PHP_INT_MAX;
 
348        $limit = $negative ? substr((string) PHP_INT_MIN, 1) : (string) PHP_INT_MAX;
349
350        return (
351            strlen($digits) < strlen($limit)
 
352                || strlen($digits) === strlen($limit)
 
353                && strcmp($digits, $limit) <= 0
 
353                && strcmp($digits, $limit) <= 0
 
353                && strcmp($digits, $limit) <= 0
354        );
355    }
343    private function integerStringFits(string $value): bool
344    {
345        $negative = str_starts_with($value, '-');
 
345        $negative = str_starts_with($value, '-');
 
345        $negative = str_starts_with($value, '-');
346        $digits = $negative ? substr($value, 1) : $value;
 
346        $digits = $negative ? substr($value, 1) : $value;
 
346        $digits = $negative ? substr($value, 1) : $value;
347        $digits = ltrim($digits, '0');
348        $limit = $negative ? substr((string) PHP_INT_MIN, 1) : (string) PHP_INT_MAX;
 
348        $limit = $negative ? substr((string) PHP_INT_MIN, 1) : (string) PHP_INT_MAX;
 
348        $limit = $negative ? substr((string) PHP_INT_MIN, 1) : (string) PHP_INT_MAX;
 
348        $limit = $negative ? substr((string) PHP_INT_MIN, 1) : (string) PHP_INT_MAX;
 
348        $limit = $negative ? substr((string) PHP_INT_MIN, 1) : (string) PHP_INT_MAX;
349
350        return (
351            strlen($digits) < strlen($limit)
 
352                || strlen($digits) === strlen($limit)
 
353                && strcmp($digits, $limit) <= 0
 
353                && strcmp($digits, $limit) <= 0
354        );
355    }
343    private function integerStringFits(string $value): bool
344    {
345        $negative = str_starts_with($value, '-');
 
345        $negative = str_starts_with($value, '-');
 
345        $negative = str_starts_with($value, '-');
346        $digits = $negative ? substr($value, 1) : $value;
 
346        $digits = $negative ? substr($value, 1) : $value;
 
346        $digits = $negative ? substr($value, 1) : $value;
347        $digits = ltrim($digits, '0');
348        $limit = $negative ? substr((string) PHP_INT_MIN, 1) : (string) PHP_INT_MAX;
 
348        $limit = $negative ? substr((string) PHP_INT_MIN, 1) : (string) PHP_INT_MAX;
 
348        $limit = $negative ? substr((string) PHP_INT_MIN, 1) : (string) PHP_INT_MAX;
 
348        $limit = $negative ? substr((string) PHP_INT_MIN, 1) : (string) PHP_INT_MAX;
 
348        $limit = $negative ? substr((string) PHP_INT_MIN, 1) : (string) PHP_INT_MAX;
349
350        return (
351            strlen($digits) < strlen($limit)
 
353                && strcmp($digits, $limit) <= 0
354        );
355    }
343    private function integerStringFits(string $value): bool
344    {
345        $negative = str_starts_with($value, '-');
 
345        $negative = str_starts_with($value, '-');
 
345        $negative = str_starts_with($value, '-');
346        $digits = $negative ? substr($value, 1) : $value;
 
346        $digits = $negative ? substr($value, 1) : $value;
 
346        $digits = $negative ? substr($value, 1) : $value;
347        $digits = ltrim($digits, '0');
348        $limit = $negative ? substr((string) PHP_INT_MIN, 1) : (string) PHP_INT_MAX;
 
348        $limit = $negative ? substr((string) PHP_INT_MIN, 1) : (string) PHP_INT_MAX;
 
348        $limit = $negative ? substr((string) PHP_INT_MIN, 1) : (string) PHP_INT_MAX;
 
348        $limit = $negative ? substr((string) PHP_INT_MIN, 1) : (string) PHP_INT_MAX;
 
348        $limit = $negative ? substr((string) PHP_INT_MIN, 1) : (string) PHP_INT_MAX;
349
350        return (
351            strlen($digits) < strlen($limit)
 
352                || strlen($digits) === strlen($limit)
 
353                && strcmp($digits, $limit) <= 0
 
353                && strcmp($digits, $limit) <= 0
 
353                && strcmp($digits, $limit) <= 0
354        );
355    }
343    private function integerStringFits(string $value): bool
344    {
345        $negative = str_starts_with($value, '-');
 
345        $negative = str_starts_with($value, '-');
 
345        $negative = str_starts_with($value, '-');
346        $digits = $negative ? substr($value, 1) : $value;
 
346        $digits = $negative ? substr($value, 1) : $value;
 
346        $digits = $negative ? substr($value, 1) : $value;
347        $digits = ltrim($digits, '0');
348        $limit = $negative ? substr((string) PHP_INT_MIN, 1) : (string) PHP_INT_MAX;
 
348        $limit = $negative ? substr((string) PHP_INT_MIN, 1) : (string) PHP_INT_MAX;
 
348        $limit = $negative ? substr((string) PHP_INT_MIN, 1) : (string) PHP_INT_MAX;
 
348        $limit = $negative ? substr((string) PHP_INT_MIN, 1) : (string) PHP_INT_MAX;
 
348        $limit = $negative ? substr((string) PHP_INT_MIN, 1) : (string) PHP_INT_MAX;
349
350        return (
351            strlen($digits) < strlen($limit)
 
352                || strlen($digits) === strlen($limit)
 
353                && strcmp($digits, $limit) <= 0
 
353                && strcmp($digits, $limit) <= 0
354        );
355    }
343    private function integerStringFits(string $value): bool
344    {
345        $negative = str_starts_with($value, '-');
 
345        $negative = str_starts_with($value, '-');
 
345        $negative = str_starts_with($value, '-');
346        $digits = $negative ? substr($value, 1) : $value;
 
346        $digits = $negative ? substr($value, 1) : $value;
 
346        $digits = $negative ? substr($value, 1) : $value;
347        $digits = ltrim($digits, '0');
348        $limit = $negative ? substr((string) PHP_INT_MIN, 1) : (string) PHP_INT_MAX;
 
348        $limit = $negative ? substr((string) PHP_INT_MIN, 1) : (string) PHP_INT_MAX;
 
348        $limit = $negative ? substr((string) PHP_INT_MIN, 1) : (string) PHP_INT_MAX;
 
348        $limit = $negative ? substr((string) PHP_INT_MIN, 1) : (string) PHP_INT_MAX;
 
348        $limit = $negative ? substr((string) PHP_INT_MIN, 1) : (string) PHP_INT_MAX;
349
350        return (
351            strlen($digits) < strlen($limit)
 
353                && strcmp($digits, $limit) <= 0
354        );
355    }
343    private function integerStringFits(string $value): bool
344    {
345        $negative = str_starts_with($value, '-');
 
345        $negative = str_starts_with($value, '-');
 
345        $negative = str_starts_with($value, '-');
346        $digits = $negative ? substr($value, 1) : $value;
 
346        $digits = $negative ? substr($value, 1) : $value;
 
346        $digits = $negative ? substr($value, 1) : $value;
347        $digits = ltrim($digits, '0');
348        $limit = $negative ? substr((string) PHP_INT_MIN, 1) : (string) PHP_INT_MAX;
 
348        $limit = $negative ? substr((string) PHP_INT_MIN, 1) : (string) PHP_INT_MAX;
 
348        $limit = $negative ? substr((string) PHP_INT_MIN, 1) : (string) PHP_INT_MAX;
349
350        return (
351            strlen($digits) < strlen($limit)
 
352                || strlen($digits) === strlen($limit)
 
353                && strcmp($digits, $limit) <= 0
 
353                && strcmp($digits, $limit) <= 0
 
353                && strcmp($digits, $limit) <= 0
354        );
355    }
343    private function integerStringFits(string $value): bool
344    {
345        $negative = str_starts_with($value, '-');
 
345        $negative = str_starts_with($value, '-');
 
345        $negative = str_starts_with($value, '-');
346        $digits = $negative ? substr($value, 1) : $value;
 
346        $digits = $negative ? substr($value, 1) : $value;
 
346        $digits = $negative ? substr($value, 1) : $value;
347        $digits = ltrim($digits, '0');
348        $limit = $negative ? substr((string) PHP_INT_MIN, 1) : (string) PHP_INT_MAX;
 
348        $limit = $negative ? substr((string) PHP_INT_MIN, 1) : (string) PHP_INT_MAX;
 
348        $limit = $negative ? substr((string) PHP_INT_MIN, 1) : (string) PHP_INT_MAX;
349
350        return (
351            strlen($digits) < strlen($limit)
 
352                || strlen($digits) === strlen($limit)
 
353                && strcmp($digits, $limit) <= 0
 
353                && strcmp($digits, $limit) <= 0
354        );
355    }
343    private function integerStringFits(string $value): bool
344    {
345        $negative = str_starts_with($value, '-');
 
345        $negative = str_starts_with($value, '-');
 
345        $negative = str_starts_with($value, '-');
346        $digits = $negative ? substr($value, 1) : $value;
 
346        $digits = $negative ? substr($value, 1) : $value;
 
346        $digits = $negative ? substr($value, 1) : $value;
347        $digits = ltrim($digits, '0');
348        $limit = $negative ? substr((string) PHP_INT_MIN, 1) : (string) PHP_INT_MAX;
 
348        $limit = $negative ? substr((string) PHP_INT_MIN, 1) : (string) PHP_INT_MAX;
 
348        $limit = $negative ? substr((string) PHP_INT_MIN, 1) : (string) PHP_INT_MAX;
349
350        return (
351            strlen($digits) < strlen($limit)
 
353                && strcmp($digits, $limit) <= 0
354        );
355    }
343    private function integerStringFits(string $value): bool
344    {
345        $negative = str_starts_with($value, '-');
 
345        $negative = str_starts_with($value, '-');
 
345        $negative = str_starts_with($value, '-');
346        $digits = $negative ? substr($value, 1) : $value;
 
346        $digits = $negative ? substr($value, 1) : $value;
 
346        $digits = $negative ? substr($value, 1) : $value;
 
346        $digits = $negative ? substr($value, 1) : $value;
 
346        $digits = $negative ? substr($value, 1) : $value;
347        $digits = ltrim($digits, '0');
348        $limit = $negative ? substr((string) PHP_INT_MIN, 1) : (string) PHP_INT_MAX;
 
348        $limit = $negative ? substr((string) PHP_INT_MIN, 1) : (string) PHP_INT_MAX;
 
348        $limit = $negative ? substr((string) PHP_INT_MIN, 1) : (string) PHP_INT_MAX;
 
348        $limit = $negative ? substr((string) PHP_INT_MIN, 1) : (string) PHP_INT_MAX;
 
348        $limit = $negative ? substr((string) PHP_INT_MIN, 1) : (string) PHP_INT_MAX;
349
350        return (
351            strlen($digits) < strlen($limit)
 
352                || strlen($digits) === strlen($limit)
 
353                && strcmp($digits, $limit) <= 0
 
353                && strcmp($digits, $limit) <= 0
 
353                && strcmp($digits, $limit) <= 0
354        );
355    }
343    private function integerStringFits(string $value): bool
344    {
345        $negative = str_starts_with($value, '-');
 
345        $negative = str_starts_with($value, '-');
 
345        $negative = str_starts_with($value, '-');
346        $digits = $negative ? substr($value, 1) : $value;
 
346        $digits = $negative ? substr($value, 1) : $value;
 
346        $digits = $negative ? substr($value, 1) : $value;
 
346        $digits = $negative ? substr($value, 1) : $value;
 
346        $digits = $negative ? substr($value, 1) : $value;
347        $digits = ltrim($digits, '0');
348        $limit = $negative ? substr((string) PHP_INT_MIN, 1) : (string) PHP_INT_MAX;
 
348        $limit = $negative ? substr((string) PHP_INT_MIN, 1) : (string) PHP_INT_MAX;
 
348        $limit = $negative ? substr((string) PHP_INT_MIN, 1) : (string) PHP_INT_MAX;
 
348        $limit = $negative ? substr((string) PHP_INT_MIN, 1) : (string) PHP_INT_MAX;
 
348        $limit = $negative ? substr((string) PHP_INT_MIN, 1) : (string) PHP_INT_MAX;
349
350        return (
351            strlen($digits) < strlen($limit)
 
352                || strlen($digits) === strlen($limit)
 
353                && strcmp($digits, $limit) <= 0
 
353                && strcmp($digits, $limit) <= 0
354        );
355    }
343    private function integerStringFits(string $value): bool
344    {
345        $negative = str_starts_with($value, '-');
 
345        $negative = str_starts_with($value, '-');
 
345        $negative = str_starts_with($value, '-');
346        $digits = $negative ? substr($value, 1) : $value;
 
346        $digits = $negative ? substr($value, 1) : $value;
 
346        $digits = $negative ? substr($value, 1) : $value;
 
346        $digits = $negative ? substr($value, 1) : $value;
 
346        $digits = $negative ? substr($value, 1) : $value;
347        $digits = ltrim($digits, '0');
348        $limit = $negative ? substr((string) PHP_INT_MIN, 1) : (string) PHP_INT_MAX;
 
348        $limit = $negative ? substr((string) PHP_INT_MIN, 1) : (string) PHP_INT_MAX;
 
348        $limit = $negative ? substr((string) PHP_INT_MIN, 1) : (string) PHP_INT_MAX;
 
348        $limit = $negative ? substr((string) PHP_INT_MIN, 1) : (string) PHP_INT_MAX;
 
348        $limit = $negative ? substr((string) PHP_INT_MIN, 1) : (string) PHP_INT_MAX;
349
350        return (
351            strlen($digits) < strlen($limit)
 
353                && strcmp($digits, $limit) <= 0
354        );
355    }
343    private function integerStringFits(string $value): bool
344    {
345        $negative = str_starts_with($value, '-');
 
345        $negative = str_starts_with($value, '-');
 
345        $negative = str_starts_with($value, '-');
346        $digits = $negative ? substr($value, 1) : $value;
 
346        $digits = $negative ? substr($value, 1) : $value;
 
346        $digits = $negative ? substr($value, 1) : $value;
 
346        $digits = $negative ? substr($value, 1) : $value;
 
346        $digits = $negative ? substr($value, 1) : $value;
347        $digits = ltrim($digits, '0');
348        $limit = $negative ? substr((string) PHP_INT_MIN, 1) : (string) PHP_INT_MAX;
 
348        $limit = $negative ? substr((string) PHP_INT_MIN, 1) : (string) PHP_INT_MAX;
 
348        $limit = $negative ? substr((string) PHP_INT_MIN, 1) : (string) PHP_INT_MAX;
 
348        $limit = $negative ? substr((string) PHP_INT_MIN, 1) : (string) PHP_INT_MAX;
 
348        $limit = $negative ? substr((string) PHP_INT_MIN, 1) : (string) PHP_INT_MAX;
349
350        return (
351            strlen($digits) < strlen($limit)
 
352                || strlen($digits) === strlen($limit)
 
353                && strcmp($digits, $limit) <= 0
 
353                && strcmp($digits, $limit) <= 0
 
353                && strcmp($digits, $limit) <= 0
354        );
355    }
343    private function integerStringFits(string $value): bool
344    {
345        $negative = str_starts_with($value, '-');
 
345        $negative = str_starts_with($value, '-');
 
345        $negative = str_starts_with($value, '-');
346        $digits = $negative ? substr($value, 1) : $value;
 
346        $digits = $negative ? substr($value, 1) : $value;
 
346        $digits = $negative ? substr($value, 1) : $value;
 
346        $digits = $negative ? substr($value, 1) : $value;
 
346        $digits = $negative ? substr($value, 1) : $value;
347        $digits = ltrim($digits, '0');
348        $limit = $negative ? substr((string) PHP_INT_MIN, 1) : (string) PHP_INT_MAX;
 
348        $limit = $negative ? substr((string) PHP_INT_MIN, 1) : (string) PHP_INT_MAX;
 
348        $limit = $negative ? substr((string) PHP_INT_MIN, 1) : (string) PHP_INT_MAX;
 
348        $limit = $negative ? substr((string) PHP_INT_MIN, 1) : (string) PHP_INT_MAX;
 
348        $limit = $negative ? substr((string) PHP_INT_MIN, 1) : (string) PHP_INT_MAX;
349
350        return (
351            strlen($digits) < strlen($limit)
 
352                || strlen($digits) === strlen($limit)
 
353                && strcmp($digits, $limit) <= 0
 
353                && strcmp($digits, $limit) <= 0
354        );
355    }
343    private function integerStringFits(string $value): bool
344    {
345        $negative = str_starts_with($value, '-');
 
345        $negative = str_starts_with($value, '-');
 
345        $negative = str_starts_with($value, '-');
346        $digits = $negative ? substr($value, 1) : $value;
 
346        $digits = $negative ? substr($value, 1) : $value;
 
346        $digits = $negative ? substr($value, 1) : $value;
 
346        $digits = $negative ? substr($value, 1) : $value;
 
346        $digits = $negative ? substr($value, 1) : $value;
347        $digits = ltrim($digits, '0');
348        $limit = $negative ? substr((string) PHP_INT_MIN, 1) : (string) PHP_INT_MAX;
 
348        $limit = $negative ? substr((string) PHP_INT_MIN, 1) : (string) PHP_INT_MAX;
 
348        $limit = $negative ? substr((string) PHP_INT_MIN, 1) : (string) PHP_INT_MAX;
 
348        $limit = $negative ? substr((string) PHP_INT_MIN, 1) : (string) PHP_INT_MAX;
 
348        $limit = $negative ? substr((string) PHP_INT_MIN, 1) : (string) PHP_INT_MAX;
349
350        return (
351            strlen($digits) < strlen($limit)
 
353                && strcmp($digits, $limit) <= 0
354        );
355    }
343    private function integerStringFits(string $value): bool
344    {
345        $negative = str_starts_with($value, '-');
 
345        $negative = str_starts_with($value, '-');
 
345        $negative = str_starts_with($value, '-');
346        $digits = $negative ? substr($value, 1) : $value;
 
346        $digits = $negative ? substr($value, 1) : $value;
 
346        $digits = $negative ? substr($value, 1) : $value;
 
346        $digits = $negative ? substr($value, 1) : $value;
 
346        $digits = $negative ? substr($value, 1) : $value;
347        $digits = ltrim($digits, '0');
348        $limit = $negative ? substr((string) PHP_INT_MIN, 1) : (string) PHP_INT_MAX;
 
348        $limit = $negative ? substr((string) PHP_INT_MIN, 1) : (string) PHP_INT_MAX;
 
348        $limit = $negative ? substr((string) PHP_INT_MIN, 1) : (string) PHP_INT_MAX;
349
350        return (
351            strlen($digits) < strlen($limit)
 
352                || strlen($digits) === strlen($limit)
 
353                && strcmp($digits, $limit) <= 0
 
353                && strcmp($digits, $limit) <= 0
 
353                && strcmp($digits, $limit) <= 0
354        );
355    }
343    private function integerStringFits(string $value): bool
344    {
345        $negative = str_starts_with($value, '-');
 
345        $negative = str_starts_with($value, '-');
 
345        $negative = str_starts_with($value, '-');
346        $digits = $negative ? substr($value, 1) : $value;
 
346        $digits = $negative ? substr($value, 1) : $value;
 
346        $digits = $negative ? substr($value, 1) : $value;
 
346        $digits = $negative ? substr($value, 1) : $value;
 
346        $digits = $negative ? substr($value, 1) : $value;
347        $digits = ltrim($digits, '0');
348        $limit = $negative ? substr((string) PHP_INT_MIN, 1) : (string) PHP_INT_MAX;
 
348        $limit = $negative ? substr((string) PHP_INT_MIN, 1) : (string) PHP_INT_MAX;
 
348        $limit = $negative ? substr((string) PHP_INT_MIN, 1) : (string) PHP_INT_MAX;
349
350        return (
351            strlen($digits) < strlen($limit)
 
352                || strlen($digits) === strlen($limit)
 
353                && strcmp($digits, $limit) <= 0
 
353                && strcmp($digits, $limit) <= 0
354        );
355    }
343    private function integerStringFits(string $value): bool
344    {
345        $negative = str_starts_with($value, '-');
 
345        $negative = str_starts_with($value, '-');
 
345        $negative = str_starts_with($value, '-');
346        $digits = $negative ? substr($value, 1) : $value;
 
346        $digits = $negative ? substr($value, 1) : $value;
 
346        $digits = $negative ? substr($value, 1) : $value;
 
346        $digits = $negative ? substr($value, 1) : $value;
 
346        $digits = $negative ? substr($value, 1) : $value;
347        $digits = ltrim($digits, '0');
348        $limit = $negative ? substr((string) PHP_INT_MIN, 1) : (string) PHP_INT_MAX;
 
348        $limit = $negative ? substr((string) PHP_INT_MIN, 1) : (string) PHP_INT_MAX;
 
348        $limit = $negative ? substr((string) PHP_INT_MIN, 1) : (string) PHP_INT_MAX;
349
350        return (
351            strlen($digits) < strlen($limit)
 
353                && strcmp($digits, $limit) <= 0
354        );
355    }
343    private function integerStringFits(string $value): bool
344    {
345        $negative = str_starts_with($value, '-');
 
345        $negative = str_starts_with($value, '-');
 
345        $negative = str_starts_with($value, '-');
346        $digits = $negative ? substr($value, 1) : $value;
 
346        $digits = $negative ? substr($value, 1) : $value;
 
346        $digits = $negative ? substr($value, 1) : $value;
 
346        $digits = $negative ? substr($value, 1) : $value;
 
346        $digits = $negative ? substr($value, 1) : $value;
347        $digits = ltrim($digits, '0');
348        $limit = $negative ? substr((string) PHP_INT_MIN, 1) : (string) PHP_INT_MAX;
 
348        $limit = $negative ? substr((string) PHP_INT_MIN, 1) : (string) PHP_INT_MAX;
 
348        $limit = $negative ? substr((string) PHP_INT_MIN, 1) : (string) PHP_INT_MAX;
 
348        $limit = $negative ? substr((string) PHP_INT_MIN, 1) : (string) PHP_INT_MAX;
 
348        $limit = $negative ? substr((string) PHP_INT_MIN, 1) : (string) PHP_INT_MAX;
349
350        return (
351            strlen($digits) < strlen($limit)
 
352                || strlen($digits) === strlen($limit)
 
353                && strcmp($digits, $limit) <= 0
 
353                && strcmp($digits, $limit) <= 0
 
353                && strcmp($digits, $limit) <= 0
354        );
355    }
343    private function integerStringFits(string $value): bool
344    {
345        $negative = str_starts_with($value, '-');
 
345        $negative = str_starts_with($value, '-');
 
345        $negative = str_starts_with($value, '-');
346        $digits = $negative ? substr($value, 1) : $value;
 
346        $digits = $negative ? substr($value, 1) : $value;
 
346        $digits = $negative ? substr($value, 1) : $value;
 
346        $digits = $negative ? substr($value, 1) : $value;
 
346        $digits = $negative ? substr($value, 1) : $value;
347        $digits = ltrim($digits, '0');
348        $limit = $negative ? substr((string) PHP_INT_MIN, 1) : (string) PHP_INT_MAX;
 
348        $limit = $negative ? substr((string) PHP_INT_MIN, 1) : (string) PHP_INT_MAX;
 
348        $limit = $negative ? substr((string) PHP_INT_MIN, 1) : (string) PHP_INT_MAX;
 
348        $limit = $negative ? substr((string) PHP_INT_MIN, 1) : (string) PHP_INT_MAX;
 
348        $limit = $negative ? substr((string) PHP_INT_MIN, 1) : (string) PHP_INT_MAX;
349
350        return (
351            strlen($digits) < strlen($limit)
 
352                || strlen($digits) === strlen($limit)
 
353                && strcmp($digits, $limit) <= 0
 
353                && strcmp($digits, $limit) <= 0
354        );
355    }
343    private function integerStringFits(string $value): bool
344    {
345        $negative = str_starts_with($value, '-');
 
345        $negative = str_starts_with($value, '-');
 
345        $negative = str_starts_with($value, '-');
346        $digits = $negative ? substr($value, 1) : $value;
 
346        $digits = $negative ? substr($value, 1) : $value;
 
346        $digits = $negative ? substr($value, 1) : $value;
 
346        $digits = $negative ? substr($value, 1) : $value;
 
346        $digits = $negative ? substr($value, 1) : $value;
347        $digits = ltrim($digits, '0');
348        $limit = $negative ? substr((string) PHP_INT_MIN, 1) : (string) PHP_INT_MAX;
 
348        $limit = $negative ? substr((string) PHP_INT_MIN, 1) : (string) PHP_INT_MAX;
 
348        $limit = $negative ? substr((string) PHP_INT_MIN, 1) : (string) PHP_INT_MAX;
 
348        $limit = $negative ? substr((string) PHP_INT_MIN, 1) : (string) PHP_INT_MAX;
 
348        $limit = $negative ? substr((string) PHP_INT_MIN, 1) : (string) PHP_INT_MAX;
349
350        return (
351            strlen($digits) < strlen($limit)
 
353                && strcmp($digits, $limit) <= 0
354        );
355    }
343    private function integerStringFits(string $value): bool
344    {
345        $negative = str_starts_with($value, '-');
 
345        $negative = str_starts_with($value, '-');
 
345        $negative = str_starts_with($value, '-');
346        $digits = $negative ? substr($value, 1) : $value;
 
346        $digits = $negative ? substr($value, 1) : $value;
 
346        $digits = $negative ? substr($value, 1) : $value;
 
346        $digits = $negative ? substr($value, 1) : $value;
 
346        $digits = $negative ? substr($value, 1) : $value;
347        $digits = ltrim($digits, '0');
348        $limit = $negative ? substr((string) PHP_INT_MIN, 1) : (string) PHP_INT_MAX;
 
348        $limit = $negative ? substr((string) PHP_INT_MIN, 1) : (string) PHP_INT_MAX;
 
348        $limit = $negative ? substr((string) PHP_INT_MIN, 1) : (string) PHP_INT_MAX;
 
348        $limit = $negative ? substr((string) PHP_INT_MIN, 1) : (string) PHP_INT_MAX;
 
348        $limit = $negative ? substr((string) PHP_INT_MIN, 1) : (string) PHP_INT_MAX;
349
350        return (
351            strlen($digits) < strlen($limit)
 
352                || strlen($digits) === strlen($limit)
 
353                && strcmp($digits, $limit) <= 0
 
353                && strcmp($digits, $limit) <= 0
 
353                && strcmp($digits, $limit) <= 0
354        );
355    }
343    private function integerStringFits(string $value): bool
344    {
345        $negative = str_starts_with($value, '-');
 
345        $negative = str_starts_with($value, '-');
 
345        $negative = str_starts_with($value, '-');
346        $digits = $negative ? substr($value, 1) : $value;
 
346        $digits = $negative ? substr($value, 1) : $value;
 
346        $digits = $negative ? substr($value, 1) : $value;
 
346        $digits = $negative ? substr($value, 1) : $value;
 
346        $digits = $negative ? substr($value, 1) : $value;
347        $digits = ltrim($digits, '0');
348        $limit = $negative ? substr((string) PHP_INT_MIN, 1) : (string) PHP_INT_MAX;
 
348        $limit = $negative ? substr((string) PHP_INT_MIN, 1) : (string) PHP_INT_MAX;
 
348        $limit = $negative ? substr((string) PHP_INT_MIN, 1) : (string) PHP_INT_MAX;
 
348        $limit = $negative ? substr((string) PHP_INT_MIN, 1) : (string) PHP_INT_MAX;
 
348        $limit = $negative ? substr((string) PHP_INT_MIN, 1) : (string) PHP_INT_MAX;
349
350        return (
351            strlen($digits) < strlen($limit)
 
352                || strlen($digits) === strlen($limit)
 
353                && strcmp($digits, $limit) <= 0
 
353                && strcmp($digits, $limit) <= 0
354        );
355    }
343    private function integerStringFits(string $value): bool
344    {
345        $negative = str_starts_with($value, '-');
 
345        $negative = str_starts_with($value, '-');
 
345        $negative = str_starts_with($value, '-');
346        $digits = $negative ? substr($value, 1) : $value;
 
346        $digits = $negative ? substr($value, 1) : $value;
 
346        $digits = $negative ? substr($value, 1) : $value;
 
346        $digits = $negative ? substr($value, 1) : $value;
 
346        $digits = $negative ? substr($value, 1) : $value;
347        $digits = ltrim($digits, '0');
348        $limit = $negative ? substr((string) PHP_INT_MIN, 1) : (string) PHP_INT_MAX;
 
348        $limit = $negative ? substr((string) PHP_INT_MIN, 1) : (string) PHP_INT_MAX;
 
348        $limit = $negative ? substr((string) PHP_INT_MIN, 1) : (string) PHP_INT_MAX;
 
348        $limit = $negative ? substr((string) PHP_INT_MIN, 1) : (string) PHP_INT_MAX;
 
348        $limit = $negative ? substr((string) PHP_INT_MIN, 1) : (string) PHP_INT_MAX;
349
350        return (
351            strlen($digits) < strlen($limit)
 
353                && strcmp($digits, $limit) <= 0
354        );
355    }
343    private function integerStringFits(string $value): bool
344    {
345        $negative = str_starts_with($value, '-');
 
345        $negative = str_starts_with($value, '-');
 
345        $negative = str_starts_with($value, '-');
346        $digits = $negative ? substr($value, 1) : $value;
 
346        $digits = $negative ? substr($value, 1) : $value;
 
346        $digits = $negative ? substr($value, 1) : $value;
 
346        $digits = $negative ? substr($value, 1) : $value;
 
346        $digits = $negative ? substr($value, 1) : $value;
347        $digits = ltrim($digits, '0');
348        $limit = $negative ? substr((string) PHP_INT_MIN, 1) : (string) PHP_INT_MAX;
 
348        $limit = $negative ? substr((string) PHP_INT_MIN, 1) : (string) PHP_INT_MAX;
 
348        $limit = $negative ? substr((string) PHP_INT_MIN, 1) : (string) PHP_INT_MAX;
349
350        return (
351            strlen($digits) < strlen($limit)
 
352                || strlen($digits) === strlen($limit)
 
353                && strcmp($digits, $limit) <= 0
 
353                && strcmp($digits, $limit) <= 0
 
353                && strcmp($digits, $limit) <= 0
354        );
355    }
343    private function integerStringFits(string $value): bool
344    {
345        $negative = str_starts_with($value, '-');
 
345        $negative = str_starts_with($value, '-');
 
345        $negative = str_starts_with($value, '-');
346        $digits = $negative ? substr($value, 1) : $value;
 
346        $digits = $negative ? substr($value, 1) : $value;
 
346        $digits = $negative ? substr($value, 1) : $value;
 
346        $digits = $negative ? substr($value, 1) : $value;
 
346        $digits = $negative ? substr($value, 1) : $value;
347        $digits = ltrim($digits, '0');
348        $limit = $negative ? substr((string) PHP_INT_MIN, 1) : (string) PHP_INT_MAX;
 
348        $limit = $negative ? substr((string) PHP_INT_MIN, 1) : (string) PHP_INT_MAX;
 
348        $limit = $negative ? substr((string) PHP_INT_MIN, 1) : (string) PHP_INT_MAX;
349
350        return (
351            strlen($digits) < strlen($limit)
 
352                || strlen($digits) === strlen($limit)
 
353                && strcmp($digits, $limit) <= 0
 
353                && strcmp($digits, $limit) <= 0
354        );
355    }
343    private function integerStringFits(string $value): bool
344    {
345        $negative = str_starts_with($value, '-');
 
345        $negative = str_starts_with($value, '-');
 
345        $negative = str_starts_with($value, '-');
346        $digits = $negative ? substr($value, 1) : $value;
 
346        $digits = $negative ? substr($value, 1) : $value;
 
346        $digits = $negative ? substr($value, 1) : $value;
 
346        $digits = $negative ? substr($value, 1) : $value;
 
346        $digits = $negative ? substr($value, 1) : $value;
347        $digits = ltrim($digits, '0');
348        $limit = $negative ? substr((string) PHP_INT_MIN, 1) : (string) PHP_INT_MAX;
 
348        $limit = $negative ? substr((string) PHP_INT_MIN, 1) : (string) PHP_INT_MAX;
 
348        $limit = $negative ? substr((string) PHP_INT_MIN, 1) : (string) PHP_INT_MAX;
349
350        return (
351            strlen($digits) < strlen($limit)
 
353                && strcmp($digits, $limit) <= 0
354        );
355    }
343    private function integerStringFits(string $value): bool
344    {
345        $negative = str_starts_with($value, '-');
 
345        $negative = str_starts_with($value, '-');
 
345        $negative = str_starts_with($value, '-');
346        $digits = $negative ? substr($value, 1) : $value;
 
346        $digits = $negative ? substr($value, 1) : $value;
 
346        $digits = $negative ? substr($value, 1) : $value;
347        $digits = ltrim($digits, '0');
348        $limit = $negative ? substr((string) PHP_INT_MIN, 1) : (string) PHP_INT_MAX;
 
348        $limit = $negative ? substr((string) PHP_INT_MIN, 1) : (string) PHP_INT_MAX;
 
348        $limit = $negative ? substr((string) PHP_INT_MIN, 1) : (string) PHP_INT_MAX;
 
348        $limit = $negative ? substr((string) PHP_INT_MIN, 1) : (string) PHP_INT_MAX;
 
348        $limit = $negative ? substr((string) PHP_INT_MIN, 1) : (string) PHP_INT_MAX;
349
350        return (
351            strlen($digits) < strlen($limit)
 
352                || strlen($digits) === strlen($limit)
 
353                && strcmp($digits, $limit) <= 0
 
353                && strcmp($digits, $limit) <= 0
 
353                && strcmp($digits, $limit) <= 0
354        );
355    }
343    private function integerStringFits(string $value): bool
344    {
345        $negative = str_starts_with($value, '-');
 
345        $negative = str_starts_with($value, '-');
 
345        $negative = str_starts_with($value, '-');
346        $digits = $negative ? substr($value, 1) : $value;
 
346        $digits = $negative ? substr($value, 1) : $value;
 
346        $digits = $negative ? substr($value, 1) : $value;
347        $digits = ltrim($digits, '0');
348        $limit = $negative ? substr((string) PHP_INT_MIN, 1) : (string) PHP_INT_MAX;
 
348        $limit = $negative ? substr((string) PHP_INT_MIN, 1) : (string) PHP_INT_MAX;
 
348        $limit = $negative ? substr((string) PHP_INT_MIN, 1) : (string) PHP_INT_MAX;
 
348        $limit = $negative ? substr((string) PHP_INT_MIN, 1) : (string) PHP_INT_MAX;
 
348        $limit = $negative ? substr((string) PHP_INT_MIN, 1) : (string) PHP_INT_MAX;
349
350        return (
351            strlen($digits) < strlen($limit)
 
352                || strlen($digits) === strlen($limit)
 
353                && strcmp($digits, $limit) <= 0
 
353                && strcmp($digits, $limit) <= 0
354        );
355    }
343    private function integerStringFits(string $value): bool
344    {
345        $negative = str_starts_with($value, '-');
 
345        $negative = str_starts_with($value, '-');
 
345        $negative = str_starts_with($value, '-');
346        $digits = $negative ? substr($value, 1) : $value;
 
346        $digits = $negative ? substr($value, 1) : $value;
 
346        $digits = $negative ? substr($value, 1) : $value;
347        $digits = ltrim($digits, '0');
348        $limit = $negative ? substr((string) PHP_INT_MIN, 1) : (string) PHP_INT_MAX;
 
348        $limit = $negative ? substr((string) PHP_INT_MIN, 1) : (string) PHP_INT_MAX;
 
348        $limit = $negative ? substr((string) PHP_INT_MIN, 1) : (string) PHP_INT_MAX;
 
348        $limit = $negative ? substr((string) PHP_INT_MIN, 1) : (string) PHP_INT_MAX;
 
348        $limit = $negative ? substr((string) PHP_INT_MIN, 1) : (string) PHP_INT_MAX;
349
350        return (
351            strlen($digits) < strlen($limit)
 
353                && strcmp($digits, $limit) <= 0
354        );
355    }
343    private function integerStringFits(string $value): bool
344    {
345        $negative = str_starts_with($value, '-');
 
345        $negative = str_starts_with($value, '-');
 
345        $negative = str_starts_with($value, '-');
346        $digits = $negative ? substr($value, 1) : $value;
 
346        $digits = $negative ? substr($value, 1) : $value;
 
346        $digits = $negative ? substr($value, 1) : $value;
347        $digits = ltrim($digits, '0');
348        $limit = $negative ? substr((string) PHP_INT_MIN, 1) : (string) PHP_INT_MAX;
 
348        $limit = $negative ? substr((string) PHP_INT_MIN, 1) : (string) PHP_INT_MAX;
 
348        $limit = $negative ? substr((string) PHP_INT_MIN, 1) : (string) PHP_INT_MAX;
 
348        $limit = $negative ? substr((string) PHP_INT_MIN, 1) : (string) PHP_INT_MAX;
 
348        $limit = $negative ? substr((string) PHP_INT_MIN, 1) : (string) PHP_INT_MAX;
349
350        return (
351            strlen($digits) < strlen($limit)
 
352                || strlen($digits) === strlen($limit)
 
353                && strcmp($digits, $limit) <= 0
 
353                && strcmp($digits, $limit) <= 0
 
353                && strcmp($digits, $limit) <= 0
354        );
355    }
343    private function integerStringFits(string $value): bool
344    {
345        $negative = str_starts_with($value, '-');
 
345        $negative = str_starts_with($value, '-');
 
345        $negative = str_starts_with($value, '-');
346        $digits = $negative ? substr($value, 1) : $value;
 
346        $digits = $negative ? substr($value, 1) : $value;
 
346        $digits = $negative ? substr($value, 1) : $value;
347        $digits = ltrim($digits, '0');
348        $limit = $negative ? substr((string) PHP_INT_MIN, 1) : (string) PHP_INT_MAX;
 
348        $limit = $negative ? substr((string) PHP_INT_MIN, 1) : (string) PHP_INT_MAX;
 
348        $limit = $negative ? substr((string) PHP_INT_MIN, 1) : (string) PHP_INT_MAX;
 
348        $limit = $negative ? substr((string) PHP_INT_MIN, 1) : (string) PHP_INT_MAX;
 
348        $limit = $negative ? substr((string) PHP_INT_MIN, 1) : (string) PHP_INT_MAX;
349
350        return (
351            strlen($digits) < strlen($limit)
 
352                || strlen($digits) === strlen($limit)
 
353                && strcmp($digits, $limit) <= 0
 
353                && strcmp($digits, $limit) <= 0
354        );
355    }
343    private function integerStringFits(string $value): bool
344    {
345        $negative = str_starts_with($value, '-');
 
345        $negative = str_starts_with($value, '-');
 
345        $negative = str_starts_with($value, '-');
346        $digits = $negative ? substr($value, 1) : $value;
 
346        $digits = $negative ? substr($value, 1) : $value;
 
346        $digits = $negative ? substr($value, 1) : $value;
347        $digits = ltrim($digits, '0');
348        $limit = $negative ? substr((string) PHP_INT_MIN, 1) : (string) PHP_INT_MAX;
 
348        $limit = $negative ? substr((string) PHP_INT_MIN, 1) : (string) PHP_INT_MAX;
 
348        $limit = $negative ? substr((string) PHP_INT_MIN, 1) : (string) PHP_INT_MAX;
 
348        $limit = $negative ? substr((string) PHP_INT_MIN, 1) : (string) PHP_INT_MAX;
 
348        $limit = $negative ? substr((string) PHP_INT_MIN, 1) : (string) PHP_INT_MAX;
349
350        return (
351            strlen($digits) < strlen($limit)
 
353                && strcmp($digits, $limit) <= 0
354        );
355    }
343    private function integerStringFits(string $value): bool
344    {
345        $negative = str_starts_with($value, '-');
 
345        $negative = str_starts_with($value, '-');
 
345        $negative = str_starts_with($value, '-');
346        $digits = $negative ? substr($value, 1) : $value;
 
346        $digits = $negative ? substr($value, 1) : $value;
 
346        $digits = $negative ? substr($value, 1) : $value;
347        $digits = ltrim($digits, '0');
348        $limit = $negative ? substr((string) PHP_INT_MIN, 1) : (string) PHP_INT_MAX;
 
348        $limit = $negative ? substr((string) PHP_INT_MIN, 1) : (string) PHP_INT_MAX;
 
348        $limit = $negative ? substr((string) PHP_INT_MIN, 1) : (string) PHP_INT_MAX;
349
350        return (
351            strlen($digits) < strlen($limit)
 
352                || strlen($digits) === strlen($limit)
 
353                && strcmp($digits, $limit) <= 0
 
353                && strcmp($digits, $limit) <= 0
 
353                && strcmp($digits, $limit) <= 0
354        );
355    }
343    private function integerStringFits(string $value): bool
344    {
345        $negative = str_starts_with($value, '-');
 
345        $negative = str_starts_with($value, '-');
 
345        $negative = str_starts_with($value, '-');
346        $digits = $negative ? substr($value, 1) : $value;
 
346        $digits = $negative ? substr($value, 1) : $value;
 
346        $digits = $negative ? substr($value, 1) : $value;
347        $digits = ltrim($digits, '0');
348        $limit = $negative ? substr((string) PHP_INT_MIN, 1) : (string) PHP_INT_MAX;
 
348        $limit = $negative ? substr((string) PHP_INT_MIN, 1) : (string) PHP_INT_MAX;
 
348        $limit = $negative ? substr((string) PHP_INT_MIN, 1) : (string) PHP_INT_MAX;
349
350        return (
351            strlen($digits) < strlen($limit)
 
352                || strlen($digits) === strlen($limit)
 
353                && strcmp($digits, $limit) <= 0
 
353                && strcmp($digits, $limit) <= 0
354        );
355    }
343    private function integerStringFits(string $value): bool
344    {
345        $negative = str_starts_with($value, '-');
 
345        $negative = str_starts_with($value, '-');
 
345        $negative = str_starts_with($value, '-');
346        $digits = $negative ? substr($value, 1) : $value;
 
346        $digits = $negative ? substr($value, 1) : $value;
 
346        $digits = $negative ? substr($value, 1) : $value;
347        $digits = ltrim($digits, '0');
348        $limit = $negative ? substr((string) PHP_INT_MIN, 1) : (string) PHP_INT_MAX;
 
348        $limit = $negative ? substr((string) PHP_INT_MIN, 1) : (string) PHP_INT_MAX;
 
348        $limit = $negative ? substr((string) PHP_INT_MIN, 1) : (string) PHP_INT_MAX;
349
350        return (
351            strlen($digits) < strlen($limit)
 
353                && strcmp($digits, $limit) <= 0
354        );
355    }
TypeCoercer->matchesKind
321    private function matchesKind(NamedTypeMetadata $name, string $kind): bool
322    {
323        return match ($kind) {
 
324            'enum' => $name->enum !== null,
 
326            default => $name->scalar === $kind,
327        };
328    }
321    private function matchesKind(NamedTypeMetadata $name, string $kind): bool
322    {
323        return match ($kind) {
 
325            'immutable', 'mutable' => $name->date === $kind,
 
326            default => $name->scalar === $kind,
327        };
328    }
321    private function matchesKind(NamedTypeMetadata $name, string $kind): bool
322    {
323        return match ($kind) {
 
325            'immutable', 'mutable' => $name->date === $kind,
 
326            default => $name->scalar === $kind,
327        };
328    }
321    private function matchesKind(NamedTypeMetadata $name, string $kind): bool
322    {
323        return match ($kind) {
 
326            default => $name->scalar === $kind,
 
326            default => $name->scalar === $kind,
327        };
328    }
TypeCoercer->satisfies
293    private function satisfies(mixed $value, NamedTypeMetadata $name): bool
294    {
295        return match ($name->scalar) {
 
296            'int' => is_int($value),
 
300            default => $this->satisfiesSpecial($value, $name),
301        };
302    }
293    private function satisfies(mixed $value, NamedTypeMetadata $name): bool
294    {
295        return match ($name->scalar) {
 
297            'float' => is_float($value) && is_finite($value),
 
297            'float' => is_float($value) && is_finite($value),
 
297            'float' => is_float($value) && is_finite($value),
 
300            default => $this->satisfiesSpecial($value, $name),
301        };
302    }
293    private function satisfies(mixed $value, NamedTypeMetadata $name): bool
294    {
295        return match ($name->scalar) {
 
297            'float' => is_float($value) && is_finite($value),
 
297            'float' => is_float($value) && is_finite($value),
 
300            default => $this->satisfiesSpecial($value, $name),
301        };
302    }
293    private function satisfies(mixed $value, NamedTypeMetadata $name): bool
294    {
295        return match ($name->scalar) {
 
298            'bool' => is_bool($value),
 
300            default => $this->satisfiesSpecial($value, $name),
301        };
302    }
293    private function satisfies(mixed $value, NamedTypeMetadata $name): bool
294    {
295        return match ($name->scalar) {
 
299            'string' => is_string($value),
 
300            default => $this->satisfiesSpecial($value, $name),
301        };
302    }
293    private function satisfies(mixed $value, NamedTypeMetadata $name): bool
294    {
295        return match ($name->scalar) {
 
300            default => $this->satisfiesSpecial($value, $name),
 
300            default => $this->satisfiesSpecial($value, $name),
301        };
302    }
TypeCoercer->satisfiesSpecial
304    private function satisfiesSpecial(mixed $value, NamedTypeMetadata $name): bool
305    {
306        if ($name->date === 'immutable') {
 
307            return $value instanceof DateTimeImmutable;
304    private function satisfiesSpecial(mixed $value, NamedTypeMetadata $name): bool
305    {
306        if ($name->date === 'immutable') {
 
310        if ($name->date === 'mutable') {
 
311            return $value instanceof DateTime;
304    private function satisfiesSpecial(mixed $value, NamedTypeMetadata $name): bool
305    {
306        if ($name->date === 'immutable') {
 
310        if ($name->date === 'mutable') {
 
314        if ($name->enum !== null) {
 
315            return $value instanceof $name->enum;
304    private function satisfiesSpecial(mixed $value, NamedTypeMetadata $name): bool
305    {
306        if ($name->date === 'immutable') {
 
310        if ($name->date === 'mutable') {
 
314        if ($name->enum !== null) {
 
318        return false;
319    }