create-prisma-php-app 3.1.11 → 4.0.0-alpha.2

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,571 +0,0 @@
1
- <?php
2
-
3
- declare(strict_types=1);
4
-
5
- namespace Lib\PHPX;
6
-
7
- use Lib\PrismaPHPSettings;
8
- use Lib\MainLayout;
9
- use DOMDocument;
10
- use DOMElement;
11
- use DOMComment;
12
- use DOMNode;
13
- use DOMText;
14
- use RuntimeException;
15
- use Bootstrap;
16
- use LibXMLError;
17
- use ReflectionClass;
18
- use ReflectionProperty;
19
- use ReflectionType;
20
- use ReflectionNamedType;
21
- use Lib\PHPX\TypeCoercer;
22
- use Lib\PHPX\Exceptions\ComponentValidationException;
23
-
24
- class TemplateCompiler
25
- {
26
- protected const BINDING_REGEX = '/\{\{\s*((?:(?!\{\{|\}\})[\s\S])*?)\s*\}\}/uS';
27
- private const LITERAL_TEXT_TAGS = [
28
- 'code' => true,
29
- 'pre' => true,
30
- 'samp' => true,
31
- 'kbd' => true,
32
- 'var' => true,
33
- ];
34
- private const SYSTEM_PROPS = [
35
- 'children' => true,
36
- 'key' => true,
37
- 'ref' => true,
38
- ];
39
-
40
- protected static array $classMappings = [];
41
- protected static array $selfClosingTags = [
42
- 'area',
43
- 'base',
44
- 'br',
45
- 'col',
46
- 'command',
47
- 'embed',
48
- 'hr',
49
- 'img',
50
- 'input',
51
- 'keygen',
52
- 'link',
53
- 'meta',
54
- 'param',
55
- 'source',
56
- 'track',
57
- 'wbr'
58
- ];
59
- private static array $sectionStack = [];
60
- private static int $compileDepth = 0;
61
- private static array $componentInstanceCounts = [];
62
- private static array $reflections = [];
63
- private static array $constructors = [];
64
- private static array $publicProperties = [];
65
- private static array $allowedProps = [];
66
-
67
- public static function compile(string $templateContent): string
68
- {
69
- if (self::$compileDepth === 0) {
70
- self::$componentInstanceCounts = [];
71
- }
72
- self::$compileDepth++;
73
-
74
- if (empty(self::$classMappings)) {
75
- self::initializeClassMappings();
76
- }
77
-
78
- $dom = self::convertToXml($templateContent);
79
- $root = $dom->documentElement;
80
-
81
- $output = [];
82
- foreach ($root->childNodes as $child) {
83
- $output[] = self::processNode($child);
84
- }
85
-
86
- self::$compileDepth--;
87
- return implode('', $output);
88
- }
89
-
90
- public static function injectDynamicContent(string $htmlContent): string
91
- {
92
- $headOpenPattern = '/(<head\b[^>]*>)/i';
93
-
94
- $htmlContent = preg_replace(
95
- $headOpenPattern,
96
- '$1' . MainLayout::outputMetadata(),
97
- $htmlContent,
98
- 1
99
- );
100
-
101
- $headClosePattern = '/(<\/head\s*>)/i';
102
- $headScripts = MainLayout::outputHeadScripts();
103
- $htmlContent = preg_replace(
104
- $headClosePattern,
105
- $headScripts . '$1',
106
- $htmlContent,
107
- 1
108
- );
109
-
110
- if (!isset($_SERVER['HTTP_X_PPHP_NAVIGATION'])) {
111
- $htmlContent = preg_replace(
112
- '/<body([^>]*)>/i',
113
- '<body$1 hidden>',
114
- $htmlContent,
115
- 1
116
- );
117
- }
118
-
119
- $bodyClosePattern = '/(<\/body\s*>)/i';
120
-
121
- $htmlContent = preg_replace(
122
- $bodyClosePattern,
123
- MainLayout::outputFooterScripts() . '$1',
124
- $htmlContent,
125
- 1
126
- );
127
-
128
- return $htmlContent;
129
- }
130
-
131
- private static function escapeAmpersands(string $content): string
132
- {
133
- return preg_replace(
134
- '/&(?![a-zA-Z][A-Za-z0-9]*;|#[0-9]+;|#x[0-9A-Fa-f]+;)/',
135
- '&amp;',
136
- $content
137
- );
138
- }
139
-
140
- private static function escapeAttributeAngles(string $html): string
141
- {
142
- return preg_replace_callback(
143
- '/(\s[\w:-]+=)([\'"])(.*?)\2/s',
144
- fn($m) => $m[1] . $m[2] . str_replace(['<', '>'], ['&lt;', '&gt;'], $m[3]) . $m[2],
145
- $html
146
- );
147
- }
148
-
149
- private static function escapeMustacheAngles(string $content): string
150
- {
151
- return preg_replace_callback(
152
- '/\{\{[\s\S]*?\}\}/u',
153
- fn($m) => str_replace(['<', '>'], ['&lt;', '&gt;'], $m[0]),
154
- $content
155
- );
156
- }
157
-
158
- public static function convertToXml(string $templateContent): DOMDocument
159
- {
160
- $content = self::protectInlineScripts($templateContent);
161
- $content = self::normalizeNamedEntities($content);
162
-
163
- $content = self::escapeAmpersands($content);
164
- $content = self::escapeAttributeAngles($content);
165
- $content = self::escapeMustacheAngles($content);
166
-
167
- $xml = "<root>{$content}</root>";
168
-
169
- $dom = new DOMDocument('1.0', 'UTF-8');
170
- libxml_use_internal_errors(true);
171
- if (!$dom->loadXML($xml, LIBXML_NOERROR | LIBXML_NOWARNING | LIBXML_NONET)) {
172
- throw new RuntimeException(
173
- 'XML Parsing Failed: ' . implode('; ', self::getXmlErrors())
174
- );
175
- }
176
- libxml_clear_errors();
177
- libxml_use_internal_errors(false);
178
- return $dom;
179
- }
180
-
181
- private static function normalizeNamedEntities(string $html): string
182
- {
183
- return preg_replace_callback(
184
- '/&([a-zA-Z][a-zA-Z0-9]+);/',
185
- static function (array $m): string {
186
- $decoded = html_entity_decode($m[0], ENT_HTML5, 'UTF-8');
187
-
188
- if ($decoded === $m[0]) {
189
- return $m[0];
190
- }
191
-
192
- if (function_exists('mb_ord')) {
193
- return '&#' . mb_ord($decoded, 'UTF-8') . ';';
194
- }
195
-
196
- $code = unpack('N', mb_convert_encoding($decoded, 'UCS-4BE', 'UTF-8'))[1];
197
- return '&#' . $code . ';';
198
- },
199
- $html
200
- );
201
- }
202
-
203
- private static function protectInlineScripts(string $html): string
204
- {
205
- return preg_replace_callback(
206
- '#<script\b([^>]*?)>(.*?)</script>#is',
207
- static function ($m) {
208
- if (preg_match('/\bsrc\s*=/i', $m[1])) {
209
- return $m[0];
210
- }
211
-
212
- if (strpos($m[2], '<![CDATA[') !== false) {
213
- return $m[0];
214
- }
215
-
216
- if (preg_match('/\btype\s*=\s*(["\']?)(?!text\/|application\/javascript|module)/i', $m[1])) {
217
- return $m[0];
218
- }
219
-
220
- $code = str_replace(']]>', ']]]]><![CDATA[>', $m[2]);
221
-
222
- return "<script{$m[1]}><![CDATA[\n{$code}\n]]></script>";
223
- },
224
- $html
225
- );
226
- }
227
-
228
- public static function innerXml(DOMNode $node): string
229
- {
230
- if ($node instanceof DOMDocument) {
231
- $node = $node->documentElement;
232
- }
233
-
234
- /** @var DOMDocument $doc */
235
- $doc = $node->ownerDocument;
236
-
237
- $html = '';
238
- foreach ($node->childNodes as $child) {
239
- $html .= $doc->saveXML($child);
240
- }
241
- return $html;
242
- }
243
-
244
- protected static function getXmlErrors(): array
245
- {
246
- $errors = libxml_get_errors();
247
- libxml_clear_errors();
248
- return array_map(fn($e) => self::formatLibxmlError($e), $errors);
249
- }
250
-
251
- protected static function formatLibxmlError(LibXMLError $error): string
252
- {
253
- $type = match ($error->level) {
254
- LIBXML_ERR_WARNING => 'Warning',
255
- LIBXML_ERR_ERROR => 'Error',
256
- LIBXML_ERR_FATAL => 'Fatal',
257
- default => 'Unknown',
258
- };
259
- return sprintf(
260
- "[%s] Line %d, Col %d: %s",
261
- $type,
262
- $error->line,
263
- $error->column,
264
- trim($error->message)
265
- );
266
- }
267
-
268
- protected static function processNode(DOMNode $node): string
269
- {
270
- if ($node instanceof DOMText) {
271
- return self::processTextNode($node);
272
- }
273
-
274
- if ($node instanceof DOMElement) {
275
- $pushed = false;
276
- $tag = strtolower($node->nodeName);
277
-
278
- if (
279
- $tag === 'script' &&
280
- !$node->hasAttribute('src') &&
281
- !$node->hasAttribute('type')
282
- ) {
283
- $node->setAttribute('type', 'text/php');
284
- }
285
-
286
- if ($node->hasAttribute('pp-component')) {
287
- self::$sectionStack[] = $node->getAttribute('pp-component');
288
- $pushed = true;
289
- }
290
-
291
- self::processAttributes($node);
292
-
293
- if (isset(self::$classMappings[$node->nodeName])) {
294
- $html = self::renderComponent(
295
- $node,
296
- $node->nodeName,
297
- self::getNodeAttributes($node)
298
- );
299
- if ($pushed) {
300
- array_pop(self::$sectionStack);
301
- }
302
- return $html;
303
- }
304
-
305
- $children = '';
306
- foreach ($node->childNodes as $c) {
307
- $children .= self::processNode($c);
308
- }
309
- $attrs = self::getNodeAttributes($node) + ['children' => $children];
310
- $out = self::renderAsHtml($node->nodeName, $attrs);
311
-
312
- if ($pushed) {
313
- array_pop(self::$sectionStack);
314
- }
315
- return $out;
316
- }
317
-
318
- if ($node instanceof DOMComment) {
319
- return "<!--{$node->textContent}-->";
320
- }
321
-
322
- return $node->textContent;
323
- }
324
-
325
- private static function processTextNode(DOMText $node): string
326
- {
327
- $parent = strtolower($node->parentNode?->nodeName ?? '');
328
- if (isset(self::LITERAL_TEXT_TAGS[$parent])) {
329
- return htmlspecialchars(
330
- $node->textContent,
331
- ENT_NOQUOTES | ENT_SUBSTITUTE,
332
- 'UTF-8'
333
- );
334
- }
335
-
336
- return preg_replace_callback(
337
- self::BINDING_REGEX,
338
- fn($m) => self::processBindingExpression(trim($m[1])),
339
- $node->textContent
340
- );
341
- }
342
-
343
- private static function processAttributes(DOMElement $node): void
344
- {
345
- foreach ($node->attributes as $a) {
346
- if (!preg_match(self::BINDING_REGEX, $a->value, $m)) {
347
- continue;
348
- }
349
-
350
- $rawExpr = trim($m[1]);
351
- $node->setAttribute("pp-bind-{$a->name}", $rawExpr);
352
- }
353
- }
354
-
355
- private static function processBindingExpression(string $expr): string
356
- {
357
- $escaped = htmlspecialchars($expr, ENT_QUOTES, 'UTF-8');
358
-
359
- if (preg_match('/^[\w.]+$/u', $expr)) {
360
- return "<span pp-bind=\"{$escaped}\"></span>";
361
- }
362
-
363
- return "<span pp-bind-expr=\"{$escaped}\"></span>";
364
- }
365
-
366
- protected static function renderComponent(
367
- DOMElement $node,
368
- string $componentName,
369
- array $incomingProps
370
- ): string {
371
- $mapping = self::selectComponentMapping($componentName);
372
- $instance = self::initializeComponentInstance($mapping, $incomingProps);
373
-
374
- $childHtml = '';
375
- foreach ($node->childNodes as $c) {
376
- $childHtml .= self::processNode($c);
377
- }
378
-
379
- $instance->children = $childHtml;
380
-
381
- $baseId = 's' . base_convert(sprintf('%u', crc32($mapping['className'])), 10, 36);
382
- $idx = self::$componentInstanceCounts[$baseId] ?? 0;
383
- self::$componentInstanceCounts[$baseId] = $idx + 1;
384
- $sectionId = $idx === 0 ? $baseId : "{$baseId}{$idx}";
385
-
386
- $html = $instance->render();
387
- $fragDom = self::convertToXml($html);
388
- $root = $fragDom->documentElement;
389
- foreach ($root->childNodes as $c) {
390
- if ($c instanceof DOMElement) {
391
- $c->setAttribute('pp-component', $sectionId);
392
- break;
393
- }
394
- }
395
-
396
- $htmlOut = self::innerXml($fragDom);
397
- if (
398
- str_contains($htmlOut, '{{') ||
399
- self::hasComponentTag($htmlOut) ||
400
- stripos($htmlOut, '<script') !== false
401
- ) {
402
- $htmlOut = self::compile($htmlOut);
403
- }
404
-
405
- return $htmlOut;
406
- }
407
-
408
- private static function selectComponentMapping(string $componentName): array
409
- {
410
- if (!isset(self::$classMappings[$componentName])) {
411
- throw new RuntimeException("Component {$componentName} not registered");
412
- }
413
- $mappings = self::$classMappings[$componentName];
414
-
415
- $srcNorm = str_replace('\\', '/', SRC_PATH) . '/';
416
- $relImp = str_replace($srcNorm, '', str_replace('\\', '/', Bootstrap::$contentToInclude));
417
-
418
- if (isset($mappings[0]) && is_array($mappings[0])) {
419
- foreach ($mappings as $entry) {
420
- $imp = isset($entry['importer'])
421
- ? str_replace('\\', '/', $entry['importer'])
422
- : '';
423
- if (str_replace($srcNorm, '', $imp) === $relImp) {
424
- return $entry;
425
- }
426
- }
427
- return $mappings[0];
428
- }
429
- return $mappings;
430
- }
431
-
432
- protected static function initializeComponentInstance(array $mapping, array $attributes)
433
- {
434
- if (!isset($mapping['className'], $mapping['filePath'])) {
435
- throw new RuntimeException("Invalid mapping");
436
- }
437
-
438
- $className = $mapping['className'];
439
- $filePath = $mapping['filePath'];
440
-
441
- require_once str_replace('\\', '/', SRC_PATH . '/' . $filePath);
442
- if (!class_exists($className)) {
443
- throw new RuntimeException("Class {$className} not found");
444
- }
445
-
446
- self::cacheClassReflection($className);
447
-
448
- if (!isset(self::$reflections[$className])) {
449
- $rc = new ReflectionClass($className);
450
- self::$reflections[$className] = $rc;
451
- self::$constructors[$className] = $rc->getConstructor();
452
- self::$publicProperties[$className] = array_filter(
453
- $rc->getProperties(ReflectionProperty::IS_PUBLIC),
454
- fn(ReflectionProperty $p) => !$p->isStatic()
455
- );
456
- }
457
-
458
- self::validateComponentProps($className, $attributes);
459
-
460
- $ref = self::$reflections[$className];
461
- $ctor = self::$constructors[$className];
462
- $inst = $ref->newInstanceWithoutConstructor();
463
-
464
- foreach (self::$publicProperties[$className] as $prop) {
465
- $name = $prop->getName();
466
-
467
- if (!array_key_exists($name, $attributes)) {
468
- continue;
469
- }
470
- $value = self::coerce($attributes[$name], $prop->getType());
471
- $prop->setValue($inst, $value);
472
- }
473
-
474
- if ($ctor) {
475
- $ctor->invoke($inst, $attributes);
476
- }
477
-
478
- return $inst;
479
- }
480
-
481
- private static function cacheClassReflection(string $className): void
482
- {
483
- if (isset(self::$reflections[$className])) {
484
- return;
485
- }
486
-
487
- $rc = new ReflectionClass($className);
488
- self::$reflections[$className] = $rc;
489
- self::$constructors[$className] = $rc->getConstructor();
490
-
491
- $publicProps = array_filter(
492
- $rc->getProperties(ReflectionProperty::IS_PUBLIC),
493
- fn(ReflectionProperty $p) => !$p->isStatic()
494
- );
495
- self::$publicProperties[$className] = $publicProps;
496
-
497
- $allowed = self::SYSTEM_PROPS;
498
- foreach ($publicProps as $prop) {
499
- $allowed[$prop->getName()] = true;
500
- }
501
- self::$allowedProps[$className] = $allowed;
502
- }
503
-
504
- private static function validateComponentProps(string $className, array $attributes): void
505
- {
506
- foreach (self::$publicProperties[$className] as $prop) {
507
- $name = $prop->getName();
508
- $type = $prop->getType();
509
-
510
- if (
511
- $type instanceof ReflectionNamedType && $type->isBuiltin()
512
- && ! $type->allowsNull()
513
- && ! array_key_exists($name, $attributes)
514
- ) {
515
- throw new ComponentValidationException(
516
- $name,
517
- $className,
518
- array_map(fn($p) => $p->getName(), self::$publicProperties[$className])
519
- );
520
- }
521
- }
522
-
523
- return;
524
- }
525
-
526
- private static function coerce(mixed $value, ?ReflectionType $type): mixed
527
- {
528
- return TypeCoercer::coerce($value, $type);
529
- }
530
-
531
- protected static function initializeClassMappings(): void
532
- {
533
- foreach (PrismaPHPSettings::$classLogFiles as $tag => $cls) {
534
- self::$classMappings[$tag] = $cls;
535
- }
536
- }
537
-
538
- protected static function hasComponentTag(string $html): bool
539
- {
540
- return preg_match('/<\/*[A-Z][\w-]*/u', $html) === 1;
541
- }
542
-
543
- private static function getNodeAttributes(DOMElement $node): array
544
- {
545
- $out = [];
546
- foreach ($node->attributes as $a) {
547
- $out[$a->name] = $a->value;
548
- }
549
- return $out;
550
- }
551
-
552
- private static function renderAsHtml(string $tag, array $attrs): string
553
- {
554
- $pairs = [];
555
- foreach ($attrs as $k => $v) {
556
- if ($k === 'children') {
557
- continue;
558
- }
559
- $pairs[] = sprintf(
560
- '%s="%s"',
561
- $k,
562
- htmlspecialchars($v, ENT_QUOTES | ENT_SUBSTITUTE, 'UTF-8')
563
- );
564
- }
565
- $attrStr = $pairs ? ' ' . implode(' ', $pairs) : '';
566
-
567
- return in_array(strtolower($tag), self::$selfClosingTags, true)
568
- ? "<{$tag}{$attrStr} />"
569
- : "<{$tag}{$attrStr}>{$attrs['children']}</{$tag}>";
570
- }
571
- }