create-prisma-php-app 3.0.1 → 3.0.3
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.
|
@@ -17,10 +17,19 @@ use LibXMLError;
|
|
|
17
17
|
use DOMXPath;
|
|
18
18
|
use ReflectionClass;
|
|
19
19
|
use ReflectionProperty;
|
|
20
|
+
use ReflectionType;
|
|
21
|
+
use ReflectionNamedType;
|
|
20
22
|
|
|
21
23
|
class TemplateCompiler
|
|
22
24
|
{
|
|
23
25
|
protected const BINDING_REGEX = '/\{\{\s*((?:(?!\{\{|\}\})[\s\S])*?)\s*\}\}/uS';
|
|
26
|
+
private const LITERAL_TEXT_TAGS = [
|
|
27
|
+
'code' => true,
|
|
28
|
+
'pre' => true,
|
|
29
|
+
'samp' => true,
|
|
30
|
+
'kbd' => true,
|
|
31
|
+
'var' => true,
|
|
32
|
+
];
|
|
24
33
|
|
|
25
34
|
protected static array $classMappings = [];
|
|
26
35
|
protected static array $selfClosingTags = [
|
|
@@ -141,23 +150,22 @@ class TemplateCompiler
|
|
|
141
150
|
|
|
142
151
|
public static function convertToXml(string $templateContent): DOMDocument
|
|
143
152
|
{
|
|
144
|
-
$
|
|
145
|
-
$
|
|
153
|
+
$content = self::protectInlineScripts($templateContent);
|
|
154
|
+
$content = self::normalizeNamedEntities($content);
|
|
146
155
|
|
|
147
|
-
$
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
)
|
|
151
|
-
);
|
|
156
|
+
$content = self::escapeAmpersands($content);
|
|
157
|
+
$content = self::escapeAttributeAngles($content);
|
|
158
|
+
$content = self::escapeMustacheAngles($content);
|
|
152
159
|
|
|
153
|
-
$
|
|
154
|
-
libxml_use_internal_errors(true);
|
|
160
|
+
$xml = "<root>{$content}</root>";
|
|
155
161
|
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
162
|
+
$dom = new DOMDocument('1.0', 'UTF-8');
|
|
163
|
+
libxml_use_internal_errors(true);
|
|
164
|
+
if (!$dom->loadXML($xml, LIBXML_NOERROR | LIBXML_NOWARNING | LIBXML_NONET)) {
|
|
165
|
+
throw new RuntimeException(
|
|
166
|
+
'XML Parsing Failed: ' . implode('; ', self::getXmlErrors())
|
|
167
|
+
);
|
|
159
168
|
}
|
|
160
|
-
|
|
161
169
|
libxml_clear_errors();
|
|
162
170
|
libxml_use_internal_errors(false);
|
|
163
171
|
return $dom;
|
|
@@ -309,6 +317,15 @@ class TemplateCompiler
|
|
|
309
317
|
|
|
310
318
|
private static function processTextNode(DOMText $node): string
|
|
311
319
|
{
|
|
320
|
+
$parent = strtolower($node->parentNode?->nodeName ?? '');
|
|
321
|
+
if (isset(self::LITERAL_TEXT_TAGS[$parent])) {
|
|
322
|
+
return htmlspecialchars(
|
|
323
|
+
$node->textContent,
|
|
324
|
+
ENT_NOQUOTES | ENT_SUBSTITUTE,
|
|
325
|
+
'UTF-8'
|
|
326
|
+
);
|
|
327
|
+
}
|
|
328
|
+
|
|
312
329
|
return preg_replace_callback(
|
|
313
330
|
self::BINDING_REGEX,
|
|
314
331
|
fn($m) => self::processBindingExpression(trim($m[1])),
|
|
@@ -528,9 +545,12 @@ class TemplateCompiler
|
|
|
528
545
|
|
|
529
546
|
foreach (self::$publicProperties[$className] as $prop) {
|
|
530
547
|
$name = $prop->getName();
|
|
531
|
-
|
|
532
|
-
|
|
548
|
+
|
|
549
|
+
if (!array_key_exists($name, $attributes)) {
|
|
550
|
+
continue;
|
|
533
551
|
}
|
|
552
|
+
$value = self::coerce($attributes[$name], $prop->getType());
|
|
553
|
+
$prop->setValue($inst, $value);
|
|
534
554
|
}
|
|
535
555
|
|
|
536
556
|
if ($ctor) {
|
|
@@ -540,6 +560,52 @@ class TemplateCompiler
|
|
|
540
560
|
return $inst;
|
|
541
561
|
}
|
|
542
562
|
|
|
563
|
+
private static function coerce(mixed $value, ?ReflectionType $type): mixed
|
|
564
|
+
{
|
|
565
|
+
if (!$type instanceof ReflectionNamedType || $type->isBuiltin() === false) {
|
|
566
|
+
return $value;
|
|
567
|
+
}
|
|
568
|
+
|
|
569
|
+
$name = $type->getName();
|
|
570
|
+
if ($value === '' && $name === 'bool') return true;
|
|
571
|
+
|
|
572
|
+
return match ($name) {
|
|
573
|
+
'bool' => filter_var($value, FILTER_VALIDATE_BOOLEAN, FILTER_NULL_ON_FAILURE) ?? false,
|
|
574
|
+
'int' => (int) $value,
|
|
575
|
+
'float' => (float) $value,
|
|
576
|
+
'array' => self::toArray($value),
|
|
577
|
+
|
|
578
|
+
default => $value,
|
|
579
|
+
};
|
|
580
|
+
}
|
|
581
|
+
|
|
582
|
+
private static function toArray(mixed $v): array
|
|
583
|
+
{
|
|
584
|
+
if (is_array($v)) {
|
|
585
|
+
return $v;
|
|
586
|
+
}
|
|
587
|
+
if (is_string($v)) {
|
|
588
|
+
$decoded = json_decode($v, true);
|
|
589
|
+
if (is_array($decoded)) {
|
|
590
|
+
return $decoded;
|
|
591
|
+
}
|
|
592
|
+
if (str_contains($v, ',')) {
|
|
593
|
+
return array_map('trim', explode(',', $v));
|
|
594
|
+
}
|
|
595
|
+
return [$decoded ?? self::coerceScalarString($v)];
|
|
596
|
+
}
|
|
597
|
+
return [$v];
|
|
598
|
+
}
|
|
599
|
+
|
|
600
|
+
private static function coerceScalarString(string $s): mixed
|
|
601
|
+
{
|
|
602
|
+
return match (strtolower($s)) {
|
|
603
|
+
'true' => true,
|
|
604
|
+
'false' => false,
|
|
605
|
+
default => $s,
|
|
606
|
+
};
|
|
607
|
+
}
|
|
608
|
+
|
|
543
609
|
protected static function initializeClassMappings(): void
|
|
544
610
|
{
|
|
545
611
|
foreach (PrismaPHPSettings::$classLogFiles as $tag => $cls) {
|