create-prisma-php-app 4.0.0-alpha.50 → 4.0.0-alpha.52

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.
@@ -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) === true
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) === true) {
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
- }
@@ -1,181 +0,0 @@
1
- <?php
2
-
3
- declare(strict_types=1);
4
-
5
- namespace Lib;
6
-
7
- use Exception;
8
-
9
- class BSPathRewrite
10
- {
11
- public string $pattern;
12
- public string $replacement;
13
-
14
- public function __construct(array $data)
15
- {
16
- $this->pattern = $data['pattern'] ?? '';
17
- $this->replacement = $data['replacement'] ?? '';
18
- }
19
- }
20
-
21
- class PrismaSettings
22
- {
23
- public string $projectName;
24
- public string $projectRootPath;
25
- public string $phpEnvironment;
26
- public string $phpRootPathExe;
27
- public string $phpGenerateClassPath;
28
- public string $bsTarget;
29
- public BSPathRewrite $bsPathRewrite;
30
- public bool $backendOnly;
31
- public bool $swaggerDocs;
32
- public bool $tailwindcss;
33
- public bool $websocket;
34
- public bool $prisma;
35
- public bool $docker;
36
- public string $version;
37
- public array $excludeFiles;
38
-
39
- public function __construct(array $data)
40
- {
41
- $this->projectName = $data['projectName'] ?? '';
42
- $this->projectRootPath = $data['projectRootPath'] ?? '';
43
- $this->phpEnvironment = $data['phpEnvironment'] ?? '';
44
- $this->phpRootPathExe = $data['phpRootPathExe'] ?? '';
45
- $this->bsTarget = $data['bsTarget'] ?? '';
46
- $this->bsPathRewrite = new BSPathRewrite($data['bsPathRewrite'] ?? []);
47
- $this->backendOnly = $data['backendOnly'] ?? false;
48
- $this->swaggerDocs = $data['swaggerDocs'] ?? false;
49
- $this->tailwindcss = $data['tailwindcss'] ?? false;
50
- $this->websocket = $data['websocket'] ?? false;
51
- $this->prisma = $data['prisma'] ?? false;
52
- $this->docker = $data['docker'] ?? false;
53
- $this->version = $data['version'] ?? '';
54
- $this->excludeFiles = $data['excludeFiles'] ?? [];
55
- }
56
- }
57
-
58
- class PrismaPHPSettings
59
- {
60
- /**
61
- * The settings from the prisma-php.json file.
62
- *
63
- * @var PrismaSettings
64
- */
65
- public static PrismaSettings $option;
66
-
67
- /**
68
- * The list of route files from the files-list.json file.
69
- *
70
- * @var array
71
- */
72
- public static array $routeFiles = [];
73
-
74
- /**
75
- * The list of class log files.
76
- *
77
- * @var array
78
- */
79
- public static array $classLogFiles = [];
80
-
81
- /**
82
- * The list of include files.
83
- *
84
- * @var array
85
- */
86
- public static array $includeFiles = [];
87
-
88
- /**
89
- * The local storage key for the app state.
90
- *
91
- * @var string
92
- */
93
- public static string $localStoreKey;
94
-
95
- public static function init(): void
96
- {
97
- self::$option = self::getPrismaSettings();
98
- self::$routeFiles = self::getRoutesFileList();
99
- self::$classLogFiles = self::getClassesLogFiles();
100
- self::$includeFiles = self::getIncludeFiles();
101
- self::$localStoreKey = self::getLocalStorageKey();
102
- }
103
-
104
- /**
105
- * Get Prisma settings from the JSON file.
106
- *
107
- * @return PrismaSettings
108
- * @throws Exception if the JSON file cannot be decoded.
109
- */
110
- private static function getPrismaSettings(): PrismaSettings
111
- {
112
- $prismaPHPSettingsJson = DOCUMENT_PATH . '/prisma-php.json';
113
-
114
- if (!file_exists($prismaPHPSettingsJson)) {
115
- throw new Exception("Settings file not found: $prismaPHPSettingsJson");
116
- }
117
-
118
- $jsonContent = file_get_contents($prismaPHPSettingsJson);
119
- $decodedJson = json_decode($jsonContent, true);
120
-
121
- if (json_last_error() !== JSON_ERROR_NONE) {
122
- throw new Exception("Failed to decode JSON: " . json_last_error_msg());
123
- }
124
-
125
- return new PrismaSettings($decodedJson);
126
- }
127
-
128
- private static function getRoutesFileList(): array
129
- {
130
- $jsonFileName = SETTINGS_PATH . '/files-list.json';
131
- if (!file_exists($jsonFileName)) {
132
- return [];
133
- }
134
-
135
- $jsonContent = file_get_contents($jsonFileName);
136
- if ($jsonContent === false || empty(trim($jsonContent))) {
137
- return [];
138
- }
139
-
140
- $routeFiles = json_decode($jsonContent, true);
141
- return is_array($routeFiles) ? $routeFiles : [];
142
- }
143
-
144
- private static function getClassesLogFiles(): array
145
- {
146
- $jsonFileName = SETTINGS_PATH . '/class-imports.json';
147
- if (!file_exists($jsonFileName)) {
148
- return [];
149
- }
150
-
151
- $jsonContent = file_get_contents($jsonFileName);
152
- if ($jsonContent === false || empty(trim($jsonContent))) {
153
- return [];
154
- }
155
-
156
- $classLogFiles = json_decode($jsonContent, true);
157
- return is_array($classLogFiles) ? $classLogFiles : [];
158
- }
159
-
160
- private static function getIncludeFiles(): array
161
- {
162
- $jsonFileName = SETTINGS_PATH . "/request-data.json";
163
- if (!file_exists($jsonFileName)) {
164
- return [];
165
- }
166
-
167
- $jsonContent = file_get_contents($jsonFileName);
168
- if ($jsonContent === false || empty(trim($jsonContent))) {
169
- return [];
170
- }
171
-
172
- $includeFiles = json_decode($jsonContent, true);
173
- return is_array($includeFiles) ? $includeFiles : [];
174
- }
175
-
176
- private static function getLocalStorageKey(): string
177
- {
178
- $localStorageKey = $_ENV['LOCALSTORE_KEY'] ?? 'pphp_local_store_59e13';
179
- return strtolower(preg_replace('/\s+/', '_', trim($localStorageKey)));
180
- }
181
- }