create-prisma-php-app 3.3.10 → 3.3.12
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
CHANGED
|
@@ -84,6 +84,13 @@ final class Bootstrap extends RuntimeException
|
|
|
84
84
|
// Set a function call key as a cookie
|
|
85
85
|
self::functionCallNameEncrypt();
|
|
86
86
|
|
|
87
|
+
self::$secondRequestC69CD = Request::$data['secondRequestC69CD'] ?? false;
|
|
88
|
+
|
|
89
|
+
// Check if the request is for a local store callback
|
|
90
|
+
if (Request::$isWire && !self::$secondRequestC69CD) {
|
|
91
|
+
self::isLocalStoreCallback();
|
|
92
|
+
}
|
|
93
|
+
|
|
87
94
|
$contentInfo = self::determineContentToInclude();
|
|
88
95
|
self::$contentToInclude = $contentInfo['path'] ?? '';
|
|
89
96
|
self::$layoutsToInclude = $contentInfo['layouts'] ?? [];
|
|
@@ -114,7 +121,6 @@ final class Bootstrap extends RuntimeException
|
|
|
114
121
|
self::$isContentIncluded = true;
|
|
115
122
|
}
|
|
116
123
|
|
|
117
|
-
self::$secondRequestC69CD = Request::$data['secondRequestC69CD'] ?? false;
|
|
118
124
|
self::$isPartialRequest =
|
|
119
125
|
!empty(Request::$data['pphpSync71163'])
|
|
120
126
|
&& !empty(Request::$data['selectors'])
|
|
@@ -128,6 +134,31 @@ final class Bootstrap extends RuntimeException
|
|
|
128
134
|
ErrorHandler::checkFatalError();
|
|
129
135
|
}
|
|
130
136
|
|
|
137
|
+
private static function isLocalStoreCallback(): void
|
|
138
|
+
{
|
|
139
|
+
$data = self::getRequestData();
|
|
140
|
+
|
|
141
|
+
if (empty($data['callback'])) {
|
|
142
|
+
self::jsonExit(['success' => false, 'error' => 'Callback not provided', 'response' => null]);
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
try {
|
|
146
|
+
$aesKey = self::getAesKeyFromJwt();
|
|
147
|
+
} catch (RuntimeException $e) {
|
|
148
|
+
self::jsonExit(['success' => false, 'error' => $e->getMessage()]);
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
try {
|
|
152
|
+
$callbackName = self::decryptCallback($data['callback'], $aesKey);
|
|
153
|
+
} catch (RuntimeException $e) {
|
|
154
|
+
self::jsonExit(['success' => false, 'error' => $e->getMessage()]);
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
if ($callbackName === PrismaPHPSettings::$localStoreKey) {
|
|
158
|
+
self::jsonExit(['success' => true, 'response' => 'localStorage updated']);
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
|
|
131
162
|
private static function functionCallNameEncrypt(): void
|
|
132
163
|
{
|
|
133
164
|
$hmacSecret = $_ENV['FUNCTION_CALL_SECRET'] ?? '';
|
|
@@ -588,108 +619,117 @@ final class Bootstrap extends RuntimeException
|
|
|
588
619
|
|
|
589
620
|
public static function wireCallback(): void
|
|
590
621
|
{
|
|
591
|
-
$
|
|
592
|
-
'success' => false,
|
|
593
|
-
'error' => 'Callback not provided',
|
|
594
|
-
'response' => null,
|
|
595
|
-
];
|
|
622
|
+
$data = self::getRequestData();
|
|
596
623
|
|
|
597
|
-
if (
|
|
598
|
-
|
|
599
|
-
foreach ($_FILES as $key => $file) {
|
|
600
|
-
if (is_array($file['name'])) {
|
|
601
|
-
$files = [];
|
|
602
|
-
foreach ($file['name'] as $i => $name) {
|
|
603
|
-
$files[] = [
|
|
604
|
-
'name' => $name,
|
|
605
|
-
'type' => $file['type'][$i],
|
|
606
|
-
'tmp_name' => $file['tmp_name'][$i],
|
|
607
|
-
'error' => $file['error'][$i],
|
|
608
|
-
'size' => $file['size'][$i],
|
|
609
|
-
];
|
|
610
|
-
}
|
|
611
|
-
$data[$key] = $files;
|
|
612
|
-
} else {
|
|
613
|
-
$data[$key] = $file;
|
|
614
|
-
}
|
|
615
|
-
}
|
|
616
|
-
} else {
|
|
617
|
-
$raw = file_get_contents('php://input');
|
|
618
|
-
$data = json_decode($raw, true);
|
|
619
|
-
if (json_last_error() !== JSON_ERROR_NONE) {
|
|
620
|
-
$data = $_POST;
|
|
621
|
-
}
|
|
624
|
+
if (empty($data['callback'])) {
|
|
625
|
+
self::jsonExit(['success' => false, 'error' => 'Callback not provided', 'response' => null]);
|
|
622
626
|
}
|
|
623
627
|
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
628
|
+
try {
|
|
629
|
+
$aesKey = self::getAesKeyFromJwt();
|
|
630
|
+
} catch (RuntimeException $e) {
|
|
631
|
+
self::jsonExit(['success' => false, 'error' => $e->getMessage()]);
|
|
632
|
+
}
|
|
633
|
+
|
|
634
|
+
try {
|
|
635
|
+
$callbackName = self::decryptCallback($data['callback'], $aesKey);
|
|
636
|
+
} catch (RuntimeException $e) {
|
|
637
|
+
self::jsonExit(['success' => false, 'error' => $e->getMessage()]);
|
|
627
638
|
}
|
|
628
639
|
|
|
640
|
+
$args = self::convertToArrayObject($data);
|
|
641
|
+
$out = str_contains($callbackName, '->') || str_contains($callbackName, '::')
|
|
642
|
+
? self::dispatchMethod($callbackName, $args)
|
|
643
|
+
: self::dispatchFunction($callbackName, $args);
|
|
644
|
+
|
|
645
|
+
if ($out !== null) {
|
|
646
|
+
self::jsonExit($out);
|
|
647
|
+
}
|
|
648
|
+
exit;
|
|
649
|
+
}
|
|
650
|
+
|
|
651
|
+
private static function getAesKeyFromJwt(): string
|
|
652
|
+
{
|
|
629
653
|
$token = $_COOKIE['pphp_function_call_jwt'] ?? null;
|
|
630
654
|
$jwtSecret = $_ENV['FUNCTION_CALL_SECRET'] ?? null;
|
|
655
|
+
|
|
631
656
|
if (!$token || !$jwtSecret) {
|
|
632
|
-
|
|
633
|
-
exit;
|
|
657
|
+
throw new RuntimeException('Missing session key or secret');
|
|
634
658
|
}
|
|
659
|
+
|
|
635
660
|
try {
|
|
636
661
|
$decoded = JWT::decode($token, new Key($jwtSecret, 'HS256'));
|
|
637
662
|
} catch (Throwable) {
|
|
638
|
-
|
|
639
|
-
exit;
|
|
663
|
+
throw new RuntimeException('Invalid session key');
|
|
640
664
|
}
|
|
665
|
+
|
|
641
666
|
$aesKey = base64_decode($decoded->k, true);
|
|
642
667
|
if ($aesKey === false || strlen($aesKey) !== 32) {
|
|
643
|
-
|
|
644
|
-
exit;
|
|
668
|
+
throw new RuntimeException('Bad key length');
|
|
645
669
|
}
|
|
646
670
|
|
|
647
|
-
|
|
671
|
+
return $aesKey;
|
|
672
|
+
}
|
|
673
|
+
|
|
674
|
+
private static function jsonExit(array $payload): void
|
|
675
|
+
{
|
|
676
|
+
echo json_encode($payload, JSON_UNESCAPED_UNICODE);
|
|
677
|
+
exit;
|
|
678
|
+
}
|
|
679
|
+
|
|
680
|
+
private static function decryptCallback(string $encrypted, string $aesKey): string
|
|
681
|
+
{
|
|
682
|
+
$parts = explode(':', $encrypted, 2);
|
|
648
683
|
if (count($parts) !== 2) {
|
|
649
|
-
|
|
650
|
-
exit;
|
|
684
|
+
throw new RuntimeException('Malformed callback payload');
|
|
651
685
|
}
|
|
652
|
-
[$
|
|
653
|
-
|
|
654
|
-
$
|
|
686
|
+
[$ivB64, $ctB64] = $parts;
|
|
687
|
+
|
|
688
|
+
$iv = base64_decode($ivB64, true);
|
|
689
|
+
$ct = base64_decode($ctB64, true);
|
|
690
|
+
|
|
655
691
|
if ($iv === false || strlen($iv) !== 16 || $ct === false) {
|
|
656
|
-
|
|
657
|
-
exit;
|
|
692
|
+
throw new RuntimeException('Invalid callback payload');
|
|
658
693
|
}
|
|
694
|
+
|
|
659
695
|
$plain = openssl_decrypt($ct, 'AES-256-CBC', $aesKey, OPENSSL_RAW_DATA, $iv);
|
|
660
696
|
if ($plain === false) {
|
|
661
|
-
|
|
662
|
-
exit;
|
|
697
|
+
throw new RuntimeException('Decryption failed');
|
|
663
698
|
}
|
|
664
699
|
|
|
665
|
-
$
|
|
666
|
-
if ($
|
|
667
|
-
|
|
668
|
-
exit;
|
|
700
|
+
$callback = preg_replace('/[^a-zA-Z0-9_:\->]/', '', $plain);
|
|
701
|
+
if ($callback === '' || $callback[0] === '_') {
|
|
702
|
+
throw new RuntimeException('Invalid callback');
|
|
669
703
|
}
|
|
670
704
|
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
$callbackName === PrismaPHPSettings::$localStoreKey
|
|
674
|
-
) {
|
|
675
|
-
echo json_encode([
|
|
676
|
-
'success' => true,
|
|
677
|
-
'response' => 'localStorage updated',
|
|
678
|
-
], JSON_UNESCAPED_UNICODE);
|
|
679
|
-
exit;
|
|
680
|
-
}
|
|
705
|
+
return $callback;
|
|
706
|
+
}
|
|
681
707
|
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
$
|
|
708
|
+
private static function getRequestData(): array
|
|
709
|
+
{
|
|
710
|
+
if (!empty($_FILES)) {
|
|
711
|
+
$data = $_POST;
|
|
712
|
+
foreach ($_FILES as $key => $file) {
|
|
713
|
+
$data[$key] = is_array($file['name'])
|
|
714
|
+
? array_map(
|
|
715
|
+
fn($i) => [
|
|
716
|
+
'name' => $file['name'][$i],
|
|
717
|
+
'type' => $file['type'][$i],
|
|
718
|
+
'tmp_name' => $file['tmp_name'][$i],
|
|
719
|
+
'error' => $file['error'][$i],
|
|
720
|
+
'size' => $file['size'][$i],
|
|
721
|
+
],
|
|
722
|
+
array_keys($file['name'])
|
|
723
|
+
)
|
|
724
|
+
: $file;
|
|
725
|
+
}
|
|
726
|
+
return $data;
|
|
687
727
|
}
|
|
688
728
|
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
729
|
+
$raw = file_get_contents('php://input');
|
|
730
|
+
$json = json_decode($raw, true);
|
|
731
|
+
|
|
732
|
+
return (json_last_error() === JSON_ERROR_NONE) ? $json : $_POST;
|
|
693
733
|
}
|
|
694
734
|
|
|
695
735
|
private static function dispatchFunction(string $fn, mixed $args)
|
|
@@ -408,23 +408,23 @@ class TemplateCompiler
|
|
|
408
408
|
string $componentName,
|
|
409
409
|
array $incomingProps
|
|
410
410
|
): string {
|
|
411
|
-
$mapping
|
|
412
|
-
$instance
|
|
411
|
+
$mapping = self::selectComponentMapping($componentName);
|
|
412
|
+
$instance = self::initializeComponentInstance($mapping, $incomingProps);
|
|
413
413
|
|
|
414
414
|
$childHtml = '';
|
|
415
415
|
foreach ($node->childNodes as $c) {
|
|
416
416
|
$childHtml .= self::processNode($c);
|
|
417
417
|
}
|
|
418
418
|
|
|
419
|
-
$instance->children = $childHtml;
|
|
419
|
+
$instance->children = trim($childHtml);
|
|
420
420
|
|
|
421
|
-
$baseId
|
|
422
|
-
$idx
|
|
421
|
+
$baseId = 's' . base_convert(sprintf('%u', crc32($mapping['className'])), 10, 36);
|
|
422
|
+
$idx = self::$componentInstanceCounts[$baseId] ?? 0;
|
|
423
423
|
self::$componentInstanceCounts[$baseId] = $idx + 1;
|
|
424
424
|
$sectionId = $idx === 0 ? $baseId : "{$baseId}{$idx}";
|
|
425
425
|
|
|
426
|
-
$html
|
|
427
|
-
$fragDom
|
|
426
|
+
$html = $instance->render();
|
|
427
|
+
$fragDom = self::convertToXml($html);
|
|
428
428
|
$root = $fragDom->documentElement;
|
|
429
429
|
foreach ($root->childNodes as $c) {
|
|
430
430
|
if ($c instanceof DOMElement) {
|