create-prisma-php-app 3.0.4 → 3.1.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.
- package/dist/bootstrap.php +35 -13
- package/dist/src/Lib/ErrorHandler.php +194 -0
- package/dist/src/Lib/IncludeTracker.php +2 -41
- package/dist/src/Lib/PHPX/Exceptions/ComponentValidationException.php +49 -0
- package/dist/src/Lib/PHPX/PHPX.php +6 -11
- package/dist/src/Lib/PHPX/TemplateCompiler.php +61 -99
- package/dist/src/Lib/PrismaPHPSettings.php +0 -19
- package/dist/src/app/error.php +1 -1
- package/dist/src/app/js/index.js +1 -1
- package/package.json +1 -1
- package/dist/settings/html-events.json +0 -118
|
@@ -14,11 +14,11 @@ use DOMText;
|
|
|
14
14
|
use RuntimeException;
|
|
15
15
|
use Bootstrap;
|
|
16
16
|
use LibXMLError;
|
|
17
|
-
use DOMXPath;
|
|
18
17
|
use ReflectionClass;
|
|
19
18
|
use ReflectionProperty;
|
|
20
19
|
use ReflectionType;
|
|
21
20
|
use ReflectionNamedType;
|
|
21
|
+
use Lib\PHPX\Exceptions\ComponentValidationException;
|
|
22
22
|
|
|
23
23
|
class TemplateCompiler
|
|
24
24
|
{
|
|
@@ -30,6 +30,11 @@ class TemplateCompiler
|
|
|
30
30
|
'kbd' => true,
|
|
31
31
|
'var' => true,
|
|
32
32
|
];
|
|
33
|
+
private const SYSTEM_PROPS = [
|
|
34
|
+
'children' => true,
|
|
35
|
+
'key' => true,
|
|
36
|
+
'ref' => true,
|
|
37
|
+
];
|
|
33
38
|
|
|
34
39
|
protected static array $classMappings = [];
|
|
35
40
|
protected static array $selfClosingTags = [
|
|
@@ -56,6 +61,7 @@ class TemplateCompiler
|
|
|
56
61
|
private static array $reflections = [];
|
|
57
62
|
private static array $constructors = [];
|
|
58
63
|
private static array $publicProperties = [];
|
|
64
|
+
private static array $allowedProps = [];
|
|
59
65
|
|
|
60
66
|
public static function compile(string $templateContent): string
|
|
61
67
|
{
|
|
@@ -276,8 +282,8 @@ class TemplateCompiler
|
|
|
276
282
|
$node->setAttribute('type', 'text/php');
|
|
277
283
|
}
|
|
278
284
|
|
|
279
|
-
if ($node->hasAttribute('pp-
|
|
280
|
-
self::$sectionStack[] = $node->getAttribute('pp-
|
|
285
|
+
if ($node->hasAttribute('pp-component')) {
|
|
286
|
+
self::$sectionStack[] = $node->getAttribute('pp-component');
|
|
281
287
|
$pushed = true;
|
|
282
288
|
}
|
|
283
289
|
|
|
@@ -361,7 +367,6 @@ class TemplateCompiler
|
|
|
361
367
|
string $componentName,
|
|
362
368
|
array $incomingProps
|
|
363
369
|
): string {
|
|
364
|
-
$incomingProps = self::sanitizeIncomingProps($incomingProps);
|
|
365
370
|
$mapping = self::selectComponentMapping($componentName);
|
|
366
371
|
$instance = self::initializeComponentInstance($mapping, $incomingProps);
|
|
367
372
|
|
|
@@ -370,7 +375,7 @@ class TemplateCompiler
|
|
|
370
375
|
$childHtml .= self::processNode($c);
|
|
371
376
|
}
|
|
372
377
|
|
|
373
|
-
$instance->children =
|
|
378
|
+
$instance->children = $childHtml;
|
|
374
379
|
|
|
375
380
|
$baseId = 's' . base_convert(sprintf('%u', crc32($mapping['className'])), 10, 36);
|
|
376
381
|
$idx = self::$componentInstanceCounts[$baseId] ?? 0;
|
|
@@ -379,55 +384,10 @@ class TemplateCompiler
|
|
|
379
384
|
|
|
380
385
|
$html = $instance->render();
|
|
381
386
|
$fragDom = self::convertToXml($html);
|
|
382
|
-
$xpath = new DOMXPath($fragDom);
|
|
383
|
-
|
|
384
|
-
/** @var DOMElement $el */
|
|
385
|
-
foreach ($xpath->query('//*') as $el) {
|
|
386
|
-
|
|
387
|
-
$tag = $el->tagName;
|
|
388
|
-
if (ctype_upper($tag[0]) || isset(self::$classMappings[$tag])) {
|
|
389
|
-
continue;
|
|
390
|
-
}
|
|
391
|
-
|
|
392
|
-
$originalEvents = [];
|
|
393
|
-
$componentEvents = [];
|
|
394
|
-
|
|
395
|
-
foreach (iterator_to_array($el->attributes) as $attr) {
|
|
396
|
-
$name = $attr->name;
|
|
397
|
-
$value = $attr->value;
|
|
398
|
-
|
|
399
|
-
if (str_starts_with($name, 'pp-original-')) {
|
|
400
|
-
$origName = substr($name, strlen('pp-original-'));
|
|
401
|
-
$originalEvents[$origName] = $value;
|
|
402
|
-
} elseif (str_starts_with($name, 'on')) {
|
|
403
|
-
$event = substr($name, 2);
|
|
404
|
-
if ($value !== '' && in_array($event, PrismaPHPSettings::$htmlEvents, true)) {
|
|
405
|
-
$componentEvents[$name] = $value;
|
|
406
|
-
}
|
|
407
|
-
}
|
|
408
|
-
}
|
|
409
|
-
|
|
410
|
-
foreach (array_keys($originalEvents) as $k) $el->removeAttribute("pp-original-{$k}");
|
|
411
|
-
foreach (array_keys($componentEvents) as $k) $el->removeAttribute($k);
|
|
412
|
-
|
|
413
|
-
foreach ($componentEvents as $evAttr => $compValue) {
|
|
414
|
-
$el->setAttribute("data-pp-child-{$evAttr}", $compValue);
|
|
415
|
-
|
|
416
|
-
if (isset($originalEvents[$evAttr])) {
|
|
417
|
-
$el->setAttribute("data-pp-parent-{$evAttr}", $originalEvents[$evAttr]);
|
|
418
|
-
unset($originalEvents[$evAttr]);
|
|
419
|
-
}
|
|
420
|
-
}
|
|
421
|
-
|
|
422
|
-
foreach ($originalEvents as $name => $value) {
|
|
423
|
-
$el->setAttribute($name, $value);
|
|
424
|
-
}
|
|
425
|
-
}
|
|
426
|
-
|
|
427
387
|
$root = $fragDom->documentElement;
|
|
428
388
|
foreach ($root->childNodes as $c) {
|
|
429
389
|
if ($c instanceof DOMElement) {
|
|
430
|
-
$c->setAttribute('pp-
|
|
390
|
+
$c->setAttribute('pp-component', $sectionId);
|
|
431
391
|
break;
|
|
432
392
|
}
|
|
433
393
|
}
|
|
@@ -444,53 +404,6 @@ class TemplateCompiler
|
|
|
444
404
|
return $htmlOut;
|
|
445
405
|
}
|
|
446
406
|
|
|
447
|
-
protected static function sanitizeIncomingProps(array $props): array
|
|
448
|
-
{
|
|
449
|
-
foreach ($props as $key => $val) {
|
|
450
|
-
if (str_starts_with($key, 'on')) {
|
|
451
|
-
$event = substr($key, 2);
|
|
452
|
-
if (in_array($event, PrismaPHPSettings::$htmlEvents, true) && trim((string)$val) !== '') {
|
|
453
|
-
$props["pp-original-on{$event}"] = (string)$val;
|
|
454
|
-
unset($props[$key]);
|
|
455
|
-
}
|
|
456
|
-
}
|
|
457
|
-
}
|
|
458
|
-
|
|
459
|
-
return $props;
|
|
460
|
-
}
|
|
461
|
-
|
|
462
|
-
protected static function sanitizeEventAttributes(string $html): string
|
|
463
|
-
{
|
|
464
|
-
$fragDom = self::convertToXml($html, false);
|
|
465
|
-
$xpath = new DOMXPath($fragDom);
|
|
466
|
-
|
|
467
|
-
/** @var DOMElement $el */
|
|
468
|
-
foreach ($xpath->query('//*') as $el) {
|
|
469
|
-
foreach (iterator_to_array($el->attributes) as $attr) {
|
|
470
|
-
$name = strtolower($attr->name);
|
|
471
|
-
|
|
472
|
-
if (!str_starts_with($name, 'on')) {
|
|
473
|
-
continue;
|
|
474
|
-
}
|
|
475
|
-
|
|
476
|
-
$event = substr($name, 2);
|
|
477
|
-
$value = trim($attr->value);
|
|
478
|
-
|
|
479
|
-
if ($value !== '' && in_array($event, PrismaPHPSettings::$htmlEvents, true)) {
|
|
480
|
-
$el->setAttribute("pp-original-on{$event}", $value);
|
|
481
|
-
}
|
|
482
|
-
|
|
483
|
-
$el->removeAttribute($name);
|
|
484
|
-
}
|
|
485
|
-
}
|
|
486
|
-
|
|
487
|
-
$body = $fragDom->getElementsByTagName('body')[0] ?? null;
|
|
488
|
-
|
|
489
|
-
return $body instanceof DOMElement
|
|
490
|
-
? self::innerXml($body)
|
|
491
|
-
: self::innerXml($fragDom);
|
|
492
|
-
}
|
|
493
|
-
|
|
494
407
|
private static function selectComponentMapping(string $componentName): array
|
|
495
408
|
{
|
|
496
409
|
if (!isset(self::$classMappings[$componentName])) {
|
|
@@ -529,16 +442,20 @@ class TemplateCompiler
|
|
|
529
442
|
throw new RuntimeException("Class {$className} not found");
|
|
530
443
|
}
|
|
531
444
|
|
|
445
|
+
self::cacheClassReflection($className);
|
|
446
|
+
|
|
532
447
|
if (!isset(self::$reflections[$className])) {
|
|
533
448
|
$rc = new ReflectionClass($className);
|
|
534
449
|
self::$reflections[$className] = $rc;
|
|
535
450
|
self::$constructors[$className] = $rc->getConstructor();
|
|
536
451
|
self::$publicProperties[$className] = array_filter(
|
|
537
452
|
$rc->getProperties(ReflectionProperty::IS_PUBLIC),
|
|
538
|
-
fn(ReflectionProperty $p) =>
|
|
453
|
+
fn(ReflectionProperty $p) => !$p->isStatic()
|
|
539
454
|
);
|
|
540
455
|
}
|
|
541
456
|
|
|
457
|
+
self::validateComponentProps($className, $attributes);
|
|
458
|
+
|
|
542
459
|
$ref = self::$reflections[$className];
|
|
543
460
|
$ctor = self::$constructors[$className];
|
|
544
461
|
$inst = $ref->newInstanceWithoutConstructor();
|
|
@@ -560,6 +477,51 @@ class TemplateCompiler
|
|
|
560
477
|
return $inst;
|
|
561
478
|
}
|
|
562
479
|
|
|
480
|
+
private static function cacheClassReflection(string $className): void
|
|
481
|
+
{
|
|
482
|
+
if (isset(self::$reflections[$className])) {
|
|
483
|
+
return;
|
|
484
|
+
}
|
|
485
|
+
|
|
486
|
+
$rc = new ReflectionClass($className);
|
|
487
|
+
self::$reflections[$className] = $rc;
|
|
488
|
+
self::$constructors[$className] = $rc->getConstructor();
|
|
489
|
+
|
|
490
|
+
$publicProps = array_filter(
|
|
491
|
+
$rc->getProperties(ReflectionProperty::IS_PUBLIC),
|
|
492
|
+
fn(ReflectionProperty $p) => !$p->isStatic()
|
|
493
|
+
);
|
|
494
|
+
self::$publicProperties[$className] = $publicProps;
|
|
495
|
+
|
|
496
|
+
$allowed = self::SYSTEM_PROPS;
|
|
497
|
+
foreach ($publicProps as $prop) {
|
|
498
|
+
$allowed[$prop->getName()] = true;
|
|
499
|
+
}
|
|
500
|
+
self::$allowedProps[$className] = $allowed;
|
|
501
|
+
}
|
|
502
|
+
|
|
503
|
+
private static function validateComponentProps(string $className, array $attributes): void
|
|
504
|
+
{
|
|
505
|
+
foreach (self::$publicProperties[$className] as $prop) {
|
|
506
|
+
$name = $prop->getName();
|
|
507
|
+
$type = $prop->getType();
|
|
508
|
+
|
|
509
|
+
if (
|
|
510
|
+
$type instanceof ReflectionNamedType && $type->isBuiltin()
|
|
511
|
+
&& ! $type->allowsNull()
|
|
512
|
+
&& ! array_key_exists($name, $attributes)
|
|
513
|
+
) {
|
|
514
|
+
throw new ComponentValidationException(
|
|
515
|
+
$name,
|
|
516
|
+
$className,
|
|
517
|
+
array_map(fn($p) => $p->getName(), self::$publicProperties[$className])
|
|
518
|
+
);
|
|
519
|
+
}
|
|
520
|
+
}
|
|
521
|
+
|
|
522
|
+
return;
|
|
523
|
+
}
|
|
524
|
+
|
|
563
525
|
private static function coerce(mixed $value, ?ReflectionType $type): mixed
|
|
564
526
|
{
|
|
565
527
|
if (!$type instanceof ReflectionNamedType || $type->isBuiltin() === false) {
|
|
@@ -92,13 +92,6 @@ class PrismaPHPSettings
|
|
|
92
92
|
*/
|
|
93
93
|
public static string $localStoreKey;
|
|
94
94
|
|
|
95
|
-
/**
|
|
96
|
-
* The HTML events loaded from the html-events.json file.
|
|
97
|
-
*
|
|
98
|
-
* @var array
|
|
99
|
-
*/
|
|
100
|
-
public static array $htmlEvents = [];
|
|
101
|
-
|
|
102
95
|
public static function init(): void
|
|
103
96
|
{
|
|
104
97
|
self::$option = self::getPrismaSettings();
|
|
@@ -106,7 +99,6 @@ class PrismaPHPSettings
|
|
|
106
99
|
self::$classLogFiles = self::getClassesLogFiles();
|
|
107
100
|
self::$includeFiles = self::getIncludeFiles();
|
|
108
101
|
self::$localStoreKey = self::getLocalStorageKey();
|
|
109
|
-
self::$htmlEvents = self::getHtmlEvents();
|
|
110
102
|
}
|
|
111
103
|
|
|
112
104
|
/**
|
|
@@ -186,15 +178,4 @@ class PrismaPHPSettings
|
|
|
186
178
|
$localStorageKey = $_ENV['LOCALSTORE_KEY'] ?? 'pphp_local_store_59e13';
|
|
187
179
|
return strtolower(preg_replace('/\s+/', '_', trim($localStorageKey)));
|
|
188
180
|
}
|
|
189
|
-
|
|
190
|
-
private static function getHtmlEvents(): array
|
|
191
|
-
{
|
|
192
|
-
$path = SETTINGS_PATH . '/html-events.json';
|
|
193
|
-
if (!file_exists($path)) {
|
|
194
|
-
return [];
|
|
195
|
-
}
|
|
196
|
-
|
|
197
|
-
$events = json_decode(file_get_contents($path), true);
|
|
198
|
-
return is_array($events) ? $events : [];
|
|
199
|
-
}
|
|
200
181
|
}
|
package/dist/src/app/error.php
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
<?php use Lib\ErrorHandler; ?>
|
|
2
2
|
|
|
3
|
-
<div class="flex items-center justify-center
|
|
3
|
+
<div class="flex items-center justify-center">
|
|
4
4
|
<div class="text-center max-w-md">
|
|
5
5
|
<h1 class="text-6xl font-bold text-red-500">Oops!</h1>
|
|
6
6
|
<p class="text-xl mt-4"><?= ErrorHandler::$content ?></p>
|