create-prisma-php-app 3.3.11 → 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.
@@ -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
- $response = [
592
- 'success' => false,
593
- 'error' => 'Callback not provided',
594
- 'response' => null,
595
- ];
622
+ $data = self::getRequestData();
596
623
 
597
- if (!empty($_FILES)) {
598
- $data = $_POST;
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
- if (empty($data['callback'])) {
625
- echo json_encode($response, JSON_UNESCAPED_UNICODE);
626
- exit;
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
- echo json_encode(['success' => false, 'error' => 'Missing session key or secret'], JSON_UNESCAPED_UNICODE);
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
- echo json_encode(['success' => false, 'error' => 'Invalid session key'], JSON_UNESCAPED_UNICODE);
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
- echo json_encode(['success' => false, 'error' => 'Bad key length'], JSON_UNESCAPED_UNICODE);
644
- exit;
668
+ throw new RuntimeException('Bad key length');
645
669
  }
646
670
 
647
- $parts = explode(':', $data['callback'], 2);
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
- echo json_encode(['success' => false, 'error' => 'Malformed callback payload'], JSON_UNESCAPED_UNICODE);
650
- exit;
684
+ throw new RuntimeException('Malformed callback payload');
651
685
  }
652
- [$iv_b64, $ct_b64] = $parts;
653
- $iv = base64_decode($iv_b64, true);
654
- $ct = base64_decode($ct_b64, true);
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
- echo json_encode(['success' => false, 'error' => 'Invalid callback payload'], JSON_UNESCAPED_UNICODE);
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
- echo json_encode(['success' => false, 'error' => 'Decryption failed'], JSON_UNESCAPED_UNICODE);
662
- exit;
697
+ throw new RuntimeException('Decryption failed');
663
698
  }
664
699
 
665
- $callbackName = preg_replace('/[^a-zA-Z0-9_:\->]/', '', $plain);
666
- if ($callbackName === '' || $callbackName[0] === '_') {
667
- echo json_encode(['success' => false, 'error' => 'Invalid callback'], JSON_UNESCAPED_UNICODE);
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
- if (
672
- isset($data['callback']) &&
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
- $args = self::convertToArrayObject($data);
683
- if (strpos($callbackName, '->') !== false || strpos($callbackName, '::') !== false) {
684
- $out = self::dispatchMethod($callbackName, $args);
685
- } else {
686
- $out = self::dispatchFunction($callbackName, $args);
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
- if ($out !== null) {
690
- echo json_encode($out, JSON_UNESCAPED_UNICODE);
691
- }
692
- exit;
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)