create-prisma-php-app 3.1.10 → 4.0.0-alpha.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (34) hide show
  1. package/dist/bootstrap.php +10 -10
  2. package/dist/index.js +12 -8
  3. package/dist/settings/restart-websocket.bat +1 -1
  4. package/dist/settings/restart-websocket.ts +2 -9
  5. package/dist/src/{Lib/Websocket → Websocket}/ConnectionManager.php +1 -1
  6. package/dist/src/app/error.php +1 -1
  7. package/dist/src/app/index.php +1 -1
  8. package/dist/src/app/js/index.js +1 -1
  9. package/dist/src/app/layout.php +2 -2
  10. package/dist/{src/Lib/Websocket/websocket-server.php → websocket-server.php} +2 -7
  11. package/package.json +1 -1
  12. package/dist/src/Lib/AI/ChatGPTClient.php +0 -147
  13. package/dist/src/Lib/Auth/Auth.php +0 -544
  14. package/dist/src/Lib/Auth/AuthConfig.php +0 -89
  15. package/dist/src/Lib/CacheHandler.php +0 -121
  16. package/dist/src/Lib/ErrorHandler.php +0 -322
  17. package/dist/src/Lib/FileManager/UploadFile.php +0 -383
  18. package/dist/src/Lib/Headers/Boom.php +0 -208
  19. package/dist/src/Lib/IncludeTracker.php +0 -59
  20. package/dist/src/Lib/MainLayout.php +0 -215
  21. package/dist/src/Lib/Middleware/AuthMiddleware.php +0 -154
  22. package/dist/src/Lib/PHPMailer/Mailer.php +0 -169
  23. package/dist/src/Lib/PHPX/Exceptions/ComponentValidationException.php +0 -49
  24. package/dist/src/Lib/PHPX/IPHPX.php +0 -22
  25. package/dist/src/Lib/PHPX/PHPX.php +0 -173
  26. package/dist/src/Lib/PHPX/TemplateCompiler.php +0 -571
  27. package/dist/src/Lib/PHPX/TwMerge.php +0 -195
  28. package/dist/src/Lib/PHPX/TypeCoercer.php +0 -490
  29. package/dist/src/Lib/PartialRenderer.php +0 -40
  30. package/dist/src/Lib/PrismaPHPSettings.php +0 -181
  31. package/dist/src/Lib/Request.php +0 -476
  32. package/dist/src/Lib/Set.php +0 -102
  33. package/dist/src/Lib/StateManager.php +0 -127
  34. package/dist/src/Lib/Validator.php +0 -738
@@ -1,195 +0,0 @@
1
- <?php
2
-
3
- declare(strict_types=1);
4
-
5
- namespace Lib\PHPX;
6
-
7
- class TwMerge
8
- {
9
- private static $classGroupPatterns = [
10
- // **General Padding classes**
11
- "p" => "/^p-/",
12
- // **Specific Padding classes**
13
- "pt" => "/^pt-/",
14
- "pr" => "/^pr-/",
15
- "pb" => "/^pb-/",
16
- "pl" => "/^pl-/",
17
- "px" => "/^px-/",
18
- "py" => "/^py-/",
19
- // **Margin classes**
20
- "m" => "/^m-/",
21
- "mt" => "/^mt-/",
22
- "mr" => "/^mr-/",
23
- "mb" => "/^mb-/",
24
- "ml" => "/^ml-/",
25
- "mx" => "/^mx-/",
26
- "my" => "/^my-/",
27
- // **Background color classes**
28
- "bg" => "/^bg-/",
29
- // **Text size classes**
30
- "text-size" => '/^text-(xs|sm|base|lg|xl|[2-9]xl)$/',
31
- // **Text alignment classes**
32
- "text-alignment" => '/^text-(left|center|right|justify)$/',
33
- // **Text color classes**
34
- "text-color" => '/^text-(?!xs$|sm$|base$|lg$|xl$|[2-9]xl$).+$/',
35
- // **Text transform classes**
36
- "text-transform" => '/^text-(uppercase|lowercase|capitalize|normal-case)$/',
37
- // **Text decoration classes**
38
- "text-decoration" => '/^text-(underline|line-through|no-underline)$/',
39
- // **Border width classes**
40
- "border-width" => '/^border(-[0-9]+)?$/',
41
- // **Border color classes**
42
- "border-color" => "/^border-(?![0-9])/",
43
- // **Border radius classes**
44
- "rounded" => '/^rounded(-.*)?$/',
45
- // **Font weight classes**
46
- "font" => '/^font-(thin|extralight|light|normal|medium|semibold|bold|extrabold|black)$/',
47
- // **Hover background color classes**
48
- "hover:bg" => "/^hover:bg-/",
49
- // **Hover text color classes**
50
- "hover:text" => "/^hover:text-/",
51
- // **Transition classes**
52
- "transition" => '/^transition(-[a-z]+)?$/',
53
- // **Opacity classes**
54
- "opacity" => '/^opacity(-[0-9]+)?$/',
55
- // **Flexbox alignment classes**
56
- "justify" => "/^justify-(start|end|center|between|around|evenly)$/",
57
- // **Flexbox alignment classes**
58
- "items" => "/^items-(start|end|center|baseline|stretch)$/",
59
- // **Width classes**
60
- "w" => "/^w-(full|[0-9]+|\\[.+\\])$/",
61
- // **Max-width classes**
62
- "max-w" => '/^max-w-(full|[0-9]+|\\[.+\\]|[a-zA-Z]+)$/',
63
- // **Other utility classes can be added here**
64
- ];
65
-
66
- private static $conflictGroups = [
67
- // **Padding conflict groups**
68
- "p" => ["p", "px", "py", "pt", "pr", "pb", "pl"],
69
- "px" => ["px", "pl", "pr"],
70
- "py" => ["py", "pt", "pb"],
71
- "pt" => ["pt"],
72
- "pr" => ["pr"],
73
- "pb" => ["pb"],
74
- "pl" => ["pl"],
75
- // **Margin conflict groups**
76
- "m" => ["m", "mx", "my", "mt", "mr", "mb", "ml"],
77
- "mx" => ["mx", "ml", "mr"],
78
- "my" => ["my", "mt", "mb"],
79
- "mt" => ["mt"],
80
- "mr" => ["mr"],
81
- "mb" => ["mb"],
82
- "ml" => ["ml"],
83
- // **Border width conflict group**
84
- "border-width" => ["border-width"],
85
- // **Border color conflict group**
86
- "border-color" => ["border-color"],
87
- // **Text size conflict group**
88
- "text-size" => ["text-size"],
89
- // **Text color conflict group**
90
- "text-color" => ["text-color"],
91
- // **Text alignment conflict group**
92
- "text-alignment" => ["text-alignment"],
93
- // **Text transform conflict group**
94
- "text-transform" => ["text-transform"],
95
- // **Text decoration conflict group**
96
- "text-decoration" => ["text-decoration"],
97
- // **Opacity conflict group**
98
- "opacity" => ["opacity"],
99
- // **Flexbox alignment conflict groups**
100
- "justify" => ["justify"],
101
- // **Flexbox alignment conflict group**
102
- "items" => ["items"],
103
- // **Width conflict group**
104
- "w" => ["w"],
105
- // **Max-width conflict group**
106
- "max-w" => ["max-w"],
107
- // **Add other conflict groups as needed**
108
- ];
109
-
110
- /**
111
- * Merges multiple CSS class strings or arrays of CSS class strings into a single, optimized CSS class string.
112
- *
113
- * @param string|array ...$classes The CSS classes to be merged.
114
- * @return string A single CSS class string with duplicates and conflicts resolved.
115
- */
116
- public static function mergeClasses(string|array ...$classes): string
117
- {
118
- $classArray = [];
119
-
120
- foreach ($classes as $class) {
121
- // Handle arrays by flattening them into strings.
122
- $classList = is_array($class) ? $class : [$class];
123
- foreach ($classList as $item) {
124
- if (!empty(trim($item))) {
125
- // Split the classes by any whitespace characters.
126
- $splitClasses = preg_split("/\s+/", $item);
127
- foreach ($splitClasses as $individualClass) {
128
- $classKey = self::getClassGroup($individualClass);
129
-
130
- // If the class is non-responsive (no colon), remove any responsive variants for the same base.
131
- if (strpos($classKey, ':') === false) {
132
- $baseGroup = $classKey;
133
- foreach ($classArray as $existingKey => $existingClass) {
134
-
135
- if (
136
- is_string($existingKey) // make sure we have a string
137
- && $existingKey !== $baseGroup
138
- && substr($existingKey, -strlen($baseGroup)) === $baseGroup
139
- ) {
140
- unset($classArray[$existingKey]);
141
- }
142
- }
143
- }
144
-
145
- // Remove conflicting classes based on the conflict groups.
146
- $conflictingKeys = self::getConflictingKeys($classKey);
147
- foreach ($conflictingKeys as $key) {
148
- unset($classArray[$key]);
149
- }
150
-
151
- // Update the array, prioritizing the last occurrence.
152
- $classArray[$classKey] = $individualClass;
153
- }
154
- }
155
- }
156
- }
157
-
158
- // Combine the final classes into a single string.
159
- return implode(" ", array_values($classArray));
160
- }
161
-
162
- private static function getClassGroup($class)
163
- {
164
- // Match optional prefixes (responsive and variants).
165
- $pattern = '/^((?:[a-z-]+:)*)(.+)$/';
166
- if (preg_match($pattern, $class, $matches)) {
167
- $prefixes = $matches[1];
168
- $utilityClass = $matches[2];
169
-
170
- // Match the utilityClass against patterns.
171
- foreach (self::$classGroupPatterns as $groupKey => $regex) {
172
- if (preg_match($regex, $utilityClass)) {
173
- return $prefixes . $groupKey;
174
- }
175
- }
176
- // If no match, use the full class.
177
- return $prefixes . $utilityClass;
178
- }
179
- // For classes without a recognizable prefix, return the class itself.
180
- return $class;
181
- }
182
-
183
- private static function getConflictingKeys($classKey)
184
- {
185
- // Remove any responsive or variant prefixes.
186
- $baseClassKey = preg_replace("/^(?:[a-z-]+:)+/", "", $classKey);
187
- if (isset(self::$conflictGroups[$baseClassKey])) {
188
- $prefix = preg_replace("/" . preg_quote($baseClassKey, "/") . '$/', "", $classKey);
189
- return array_map(function ($conflict) use ($prefix) {
190
- return $prefix . $conflict;
191
- }, self::$conflictGroups[$baseClassKey]);
192
- }
193
- return [$classKey];
194
- }
195
- }
@@ -1,490 +0,0 @@
1
- <?php
2
-
3
- declare(strict_types=1);
4
-
5
- namespace Lib\PHPX;
6
-
7
- use Lib\Validator;
8
- use ReflectionType;
9
- use ReflectionNamedType;
10
- use ReflectionUnionType;
11
- use ReflectionIntersectionType;
12
- use DateTime;
13
- use DateTimeImmutable;
14
- use DateTimeInterface;
15
- use Brick\Math\BigDecimal;
16
- use Brick\Math\BigInteger;
17
-
18
- class TypeCoercer
19
- {
20
- private static array $typeCache = [];
21
- private static array $phpTypeMap = [
22
- 'boolean' => 'bool',
23
- 'integer' => 'int',
24
- 'double' => 'float',
25
- 'string' => 'string',
26
- 'array' => 'array',
27
- 'object' => 'object',
28
- 'resource' => 'resource',
29
- 'NULL' => 'null',
30
- ];
31
-
32
- public static function coerce(mixed $value, ?ReflectionType $type, array $validationRules = []): mixed
33
- {
34
- if ($type === null) {
35
- return $value;
36
- }
37
- $typeKey = self::getTypeKey($type);
38
- if (!isset(self::$typeCache[$typeKey])) {
39
- self::$typeCache[$typeKey] = self::analyzeType($type);
40
- }
41
- $typeInfo = self::$typeCache[$typeKey];
42
- return self::coerceWithTypeInfo($value, $typeInfo, $validationRules);
43
- }
44
-
45
- private static function coerceWithTypeInfo(mixed $value, array $typeInfo, array $validationRules = []): mixed
46
- {
47
- if ($value === null && $typeInfo['allowsNull']) {
48
- return null;
49
- }
50
- if ($typeInfo['isUnion']) {
51
- return self::coerceUnionTypeSmart($value, $typeInfo['types'], $validationRules);
52
- }
53
- if (count($typeInfo['types']) === 1) {
54
- $currentType = self::getNormalizedType($value);
55
- $targetType = $typeInfo['types'][0]['name'];
56
- if ($currentType === $targetType) {
57
- return $value;
58
- }
59
- return self::coerceSingleType($value, $typeInfo['types'][0], $validationRules);
60
- }
61
- return $value;
62
- }
63
-
64
- private static function analyzeType(ReflectionType $type): array
65
- {
66
- $info = [
67
- 'isUnion' => false,
68
- 'isIntersection' => false,
69
- 'allowsNull' => false,
70
- 'types' => [],
71
- ];
72
- if ($type instanceof ReflectionUnionType) {
73
- $info['isUnion'] = true;
74
- foreach ($type->getTypes() as $unionType) {
75
- if ($unionType->getName() === 'null') {
76
- $info['allowsNull'] = true;
77
- } else {
78
- $info['types'][] = [
79
- 'name' => $unionType->getName(),
80
- 'isBuiltin' => $unionType->isBuiltin(),
81
- 'allowsNull' => $unionType->allowsNull(),
82
- ];
83
- }
84
- }
85
- } elseif ($type instanceof ReflectionNamedType) {
86
- $info['allowsNull'] = $type->allowsNull();
87
- if ($type->getName() !== 'null') {
88
- $info['types'][] = [
89
- 'name' => $type->getName(),
90
- 'isBuiltin' => $type->isBuiltin(),
91
- 'allowsNull' => $type->allowsNull(),
92
- ];
93
- }
94
- } elseif ($type instanceof ReflectionIntersectionType) {
95
- $info['isIntersection'] = true;
96
- foreach ($type->getTypes() as $intersectionType) {
97
- if ($intersectionType instanceof ReflectionNamedType) {
98
- $info['types'][] = [
99
- 'name' => $intersectionType->getName(),
100
- 'isBuiltin' => $intersectionType->isBuiltin(),
101
- 'allowsNull' => $intersectionType->allowsNull(),
102
- ];
103
- } else {
104
- $info['types'][] = [
105
- 'name' => (string) $intersectionType,
106
- 'isBuiltin' => false,
107
- 'allowsNull' => false,
108
- ];
109
- }
110
- }
111
- }
112
- return $info;
113
- }
114
-
115
- private static function coerceUnionTypeSmart(mixed $value, array $types, array $validationRules = []): mixed
116
- {
117
- $typeNames = array_column($types, 'name');
118
- return match (true) {
119
- self::hasTypes($typeNames, ['string', 'bool']) =>
120
- self::coerceStringBoolUnion($value, $types, $validationRules),
121
- self::hasTypes($typeNames, ['int', 'string']) =>
122
- self::coerceIntStringUnion($value, $types, $validationRules),
123
- self::hasTypes($typeNames, ['float', 'string']) =>
124
- self::coerceFloatStringUnion($value, $types, $validationRules),
125
- self::hasTypes($typeNames, ['array', 'string']) =>
126
- self::coerceArrayStringUnion($value, $types, $validationRules),
127
- self::hasDateTimeTypes($typeNames) =>
128
- self::coerceDateTimeUnion($value, $types, $validationRules),
129
- default => self::coerceUnionTypePhpCompliant($value, $types, $validationRules)
130
- };
131
- }
132
-
133
- private static function coerceStringBoolUnion(mixed $value, array $types, array $validationRules = []): mixed
134
- {
135
- if (is_bool($value)) {
136
- return $value;
137
- }
138
- if (is_string($value) && self::isBooleanLike($value)) {
139
- return self::coerceBool($value, $validationRules);
140
- }
141
- return self::coerceString($value, $validationRules);
142
- }
143
-
144
- private static function coerceIntStringUnion(mixed $value, array $types, array $validationRules = []): mixed
145
- {
146
- if (is_int($value)) {
147
- return $value;
148
- }
149
- if (self::isIntegerLike($value)) {
150
- return self::coerceInt($value, $validationRules);
151
- }
152
- return self::coerceString($value, $validationRules);
153
- }
154
-
155
- private static function coerceFloatStringUnion(mixed $value, array $types, array $validationRules = []): mixed
156
- {
157
- if (is_float($value)) {
158
- return $value;
159
- }
160
- if (is_string($value) && is_numeric($value)) {
161
- return self::coerceFloat($value, $validationRules);
162
- }
163
- return self::coerceString($value, $validationRules);
164
- }
165
-
166
- private static function coerceArrayStringUnion(mixed $value, array $types, array $validationRules = []): mixed
167
- {
168
- if (is_array($value)) {
169
- return $value;
170
- }
171
- if (is_string($value) && self::isArrayLike($value)) {
172
- return self::coerceArray($value, $validationRules);
173
- }
174
- return self::coerceString($value, $validationRules);
175
- }
176
-
177
- private static function coerceDateTimeUnion(mixed $value, array $types, array $validationRules = []): mixed
178
- {
179
- if ($value instanceof DateTimeInterface) {
180
- return $value;
181
- }
182
- if (is_string($value) || is_numeric($value)) {
183
- foreach ($types as $typeInfo) {
184
- if (in_array($typeInfo['name'], ['DateTime', 'DateTimeImmutable', 'DateTimeInterface'])) {
185
- $coerced = self::coerceCustomType($value, $typeInfo['name'], $validationRules);
186
- if ($coerced !== $value) {
187
- return $coerced;
188
- }
189
- }
190
- }
191
- }
192
- return self::coerceString($value, $validationRules);
193
- }
194
-
195
- private static function coerceUnionTypePhpCompliant(mixed $value, array $types, array $validationRules = []): mixed
196
- {
197
- foreach ($types as $typeInfo) {
198
- $coerced = self::coerceSingleType($value, $typeInfo, $validationRules);
199
- if (self::isValidCoercion($value, $coerced, $typeInfo['name'])) {
200
- return $coerced;
201
- }
202
- }
203
- return $value;
204
- }
205
-
206
- private static function hasTypes(array $typeNames, array $requiredTypes): bool
207
- {
208
- foreach ($requiredTypes as $required) {
209
- if (!in_array($required, $typeNames, true)) {
210
- return false;
211
- }
212
- }
213
- return true;
214
- }
215
-
216
- private static function hasDateTimeTypes(array $typeNames): bool
217
- {
218
- $dateTimeTypes = ['DateTime', 'DateTimeImmutable', 'DateTimeInterface'];
219
- return !empty(array_intersect($typeNames, $dateTimeTypes));
220
- }
221
-
222
- private static function isBooleanLike(mixed $value): bool
223
- {
224
- if (!is_string($value)) {
225
- return false;
226
- }
227
- return in_array(strtolower(trim($value)), [
228
- 'true',
229
- 'false',
230
- '1',
231
- '0',
232
- 'yes',
233
- 'no',
234
- 'on',
235
- 'off',
236
- 'checked',
237
- ''
238
- ], true);
239
- }
240
-
241
- private static function isIntegerLike(mixed $value): bool
242
- {
243
- return is_string($value) && is_numeric($value) && (string)(int)$value === trim($value);
244
- }
245
-
246
- private static function isArrayLike(string $value): bool
247
- {
248
- return Validator::json($value) ||
249
- str_contains($value, ',') ||
250
- str_contains($value, '[') ||
251
- str_contains($value, '{');
252
- }
253
-
254
- private static function getNormalizedType(mixed $value): string
255
- {
256
- $type = gettype($value);
257
- return self::$phpTypeMap[$type] ?? $type;
258
- }
259
-
260
- private static function coerceSingleType(mixed $value, array $typeInfo, array $validationRules = []): mixed
261
- {
262
- if (!$typeInfo['isBuiltin']) {
263
- return self::coerceCustomType($value, $typeInfo['name'], $validationRules);
264
- }
265
- return match ($typeInfo['name']) {
266
- 'bool' => self::coerceBool($value, $validationRules),
267
- 'int' => self::coerceInt($value, $validationRules),
268
- 'float' => self::coerceFloat($value, $validationRules),
269
- 'string' => self::coerceString($value, $validationRules),
270
- 'array' => self::coerceArray($value, $validationRules),
271
- 'object' => self::coerceObject($value, $validationRules),
272
- 'mixed' => $value,
273
- default => $value,
274
- };
275
- }
276
-
277
- private static function coerceBool(mixed $value, array $rules = []): mixed
278
- {
279
- $validated = Validator::boolean($value);
280
- if ($validated !== null) {
281
- return $validated;
282
- }
283
- if (is_string($value)) {
284
- return match (strtolower(trim($value))) {
285
- 'true', '1', 'yes', 'on', 'checked' => true,
286
- 'false', '0', 'no', 'off', '' => false,
287
- default => filter_var($value, FILTER_VALIDATE_BOOLEAN, FILTER_NULL_ON_FAILURE) ?? false,
288
- };
289
- }
290
- return (bool) $value;
291
- }
292
-
293
- private static function coerceInt(mixed $value, array $rules = []): mixed
294
- {
295
- $validated = Validator::int($value);
296
- return $validated ?? (int) $value;
297
- }
298
-
299
- private static function coerceFloat(mixed $value, array $rules = []): mixed
300
- {
301
- $validated = Validator::float($value);
302
- return $validated ?? (float) $value;
303
- }
304
-
305
- private static function coerceString(mixed $value, array $rules = []): mixed
306
- {
307
- $escapeHtml = $rules['escapeHtml'] ?? true;
308
- if (isset($rules['type'])) {
309
- return match ($rules['type']) {
310
- 'email' => Validator::email($value) ?? Validator::string($value, $escapeHtml),
311
- 'url' => Validator::url($value) ?? Validator::string($value, $escapeHtml),
312
- 'uuid' => Validator::uuid($value) ?? Validator::string($value, $escapeHtml),
313
- 'ulid' => Validator::ulid($value) ?? Validator::string($value, $escapeHtml),
314
- 'cuid' => Validator::cuid($value) ?? Validator::string($value, $escapeHtml),
315
- 'cuid2' => Validator::cuid2($value) ?? Validator::string($value, $escapeHtml),
316
- 'ip' => Validator::ip($value) ?? Validator::string($value, $escapeHtml),
317
- 'html' => Validator::html(Validator::string($value, false)),
318
- 'emojis' => Validator::emojis(Validator::string($value, $escapeHtml)),
319
- default => Validator::string($value, $escapeHtml),
320
- };
321
- }
322
- return Validator::string($value, $escapeHtml);
323
- }
324
-
325
- private static function coerceArray(mixed $value, array $rules = []): array
326
- {
327
- if (is_array($value)) {
328
- return $value;
329
- }
330
- if (is_string($value)) {
331
- if (Validator::json($value)) {
332
- $decoded = json_decode($value, true);
333
- if (is_array($decoded)) {
334
- return $decoded;
335
- }
336
- }
337
- if (str_contains($value, ',')) {
338
- return array_map('trim', explode(',', $value));
339
- }
340
- return [$value];
341
- }
342
- return (array) $value;
343
- }
344
-
345
- private static function coerceObject(mixed $value, array $rules = []): object
346
- {
347
- if (is_object($value)) {
348
- return $value;
349
- }
350
- if (is_array($value)) {
351
- return (object) $value;
352
- }
353
- if (is_string($value) && Validator::json($value)) {
354
- $decoded = json_decode($value);
355
- if (is_object($decoded)) {
356
- return $decoded;
357
- }
358
- }
359
- return (object) $value;
360
- }
361
-
362
- private static function coerceCustomType(mixed $value, string $typeName, array $rules = []): mixed
363
- {
364
- return match ($typeName) {
365
- 'DateTime' => self::coerceDateTime($value, $rules),
366
- 'DateTimeImmutable' => self::coerceDateTimeImmutable($value, $rules),
367
- 'DateTimeInterface' => self::coerceDateTimeInterface($value, $rules),
368
- 'BigDecimal' => self::coerceBigDecimal($value, $rules),
369
- 'BigInteger' => self::coerceBigInteger($value, $rules),
370
- default => $value,
371
- };
372
- }
373
-
374
- private static function coerceDateTime(mixed $value, array $rules = []): mixed
375
- {
376
- if ($value instanceof DateTime) {
377
- return $value;
378
- }
379
- if ($value instanceof DateTimeImmutable) {
380
- return DateTime::createFromImmutable($value);
381
- }
382
- $format = $rules['format'] ?? null;
383
- try {
384
- if ($format) {
385
- return DateTime::createFromFormat($format, (string)$value) ?: $value;
386
- } else {
387
- return new DateTime((string)$value);
388
- }
389
- } catch (\Exception) {
390
- return $value;
391
- }
392
- }
393
-
394
- private static function coerceDateTimeImmutable(mixed $value, array $rules = []): mixed
395
- {
396
- if ($value instanceof DateTimeImmutable) {
397
- return $value;
398
- }
399
- if ($value instanceof DateTime) {
400
- return DateTimeImmutable::createFromMutable($value);
401
- }
402
- $format = $rules['format'] ?? null;
403
- try {
404
- if ($format) {
405
- return DateTimeImmutable::createFromFormat($format, (string)$value) ?: $value;
406
- } else {
407
- return new DateTimeImmutable((string)$value);
408
- }
409
- } catch (\Exception) {
410
- return $value;
411
- }
412
- }
413
-
414
- private static function coerceDateTimeInterface(mixed $value, array $rules = []): mixed
415
- {
416
- if ($value instanceof DateTimeInterface) {
417
- return $value;
418
- }
419
- return self::coerceDateTimeImmutable($value, $rules);
420
- }
421
-
422
- private static function coerceBigDecimal(mixed $value, array $rules = []): mixed
423
- {
424
- $scale = $rules['scale'] ?? 30;
425
- return Validator::decimal($value, $scale) ?? $value;
426
- }
427
-
428
- private static function coerceBigInteger(mixed $value, array $rules = []): mixed
429
- {
430
- return Validator::bigInt($value) ?? $value;
431
- }
432
-
433
- private static function isValidCoercion(mixed $original, mixed $coerced, string $typeName): bool
434
- {
435
- if (gettype($original) === gettype($coerced)) {
436
- return match ($typeName) {
437
- 'string' => true,
438
- 'array' => $original !== $coerced,
439
- default => $original === $coerced,
440
- };
441
- }
442
- return match ($typeName) {
443
- 'bool' => is_bool($coerced),
444
- 'int' => is_int($coerced),
445
- 'float' => is_float($coerced),
446
- 'string' => is_string($coerced),
447
- 'array' => is_array($coerced),
448
- 'object' => is_object($coerced),
449
- 'DateTime' => $coerced instanceof DateTime,
450
- 'DateTimeImmutable' => $coerced instanceof DateTimeImmutable,
451
- 'DateTimeInterface' => $coerced instanceof DateTimeInterface,
452
- 'BigDecimal' => $coerced instanceof BigDecimal,
453
- 'BigInteger' => $coerced instanceof BigInteger,
454
- default => true,
455
- };
456
- }
457
-
458
- private static function getTypeKey(ReflectionType $type): string
459
- {
460
- if ($type instanceof ReflectionUnionType) {
461
- $types = array_map(fn($t) => $t->getName(), $type->getTypes());
462
- sort($types);
463
- return 'union:' . implode('|', $types);
464
- }
465
- if ($type instanceof ReflectionNamedType) {
466
- return 'named:' . $type->getName() . ($type->allowsNull() ? '|null' : '');
467
- }
468
- if ($type instanceof ReflectionIntersectionType) {
469
- $types = array_map(function ($t) {
470
- return $t instanceof ReflectionNamedType ? $t->getName() : (string) $t;
471
- }, $type->getTypes());
472
- sort($types);
473
- return 'intersection:' . implode('&', $types);
474
- }
475
- return 'unknown:' . get_class($type);
476
- }
477
-
478
- public static function clearCache(): void
479
- {
480
- self::$typeCache = [];
481
- }
482
-
483
- public static function getCacheStats(): array
484
- {
485
- return [
486
- 'type_cache_size' => count(self::$typeCache),
487
- 'cached_types' => array_keys(self::$typeCache),
488
- ];
489
- }
490
- }