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

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 (59) hide show
  1. package/dist/.htaccess +54 -41
  2. package/dist/bootstrap.php +143 -98
  3. package/dist/index.js +264 -99
  4. package/dist/settings/auto-swagger-docs.ts +196 -95
  5. package/dist/settings/bs-config.ts +56 -58
  6. package/dist/settings/files-list.json +1 -1
  7. package/dist/settings/restart-mcp.ts +58 -0
  8. package/dist/settings/restart-websocket.ts +51 -45
  9. package/dist/settings/utils.ts +240 -0
  10. package/dist/src/Lib/AI/ChatGPTClient.php +147 -0
  11. package/dist/src/Lib/Auth/Auth.php +544 -0
  12. package/dist/src/Lib/Auth/AuthConfig.php +89 -0
  13. package/dist/src/Lib/CacheHandler.php +121 -0
  14. package/dist/src/Lib/ErrorHandler.php +322 -0
  15. package/dist/src/Lib/FileManager/UploadFile.php +383 -0
  16. package/dist/src/Lib/Headers/Boom.php +192 -0
  17. package/dist/src/Lib/IncludeTracker.php +59 -0
  18. package/dist/src/Lib/MCP/WeatherTools.php +104 -0
  19. package/dist/src/Lib/MCP/mcp-server.php +80 -0
  20. package/dist/src/Lib/MainLayout.php +230 -0
  21. package/dist/src/Lib/Middleware/AuthMiddleware.php +154 -0
  22. package/dist/src/Lib/Middleware/CorsMiddleware.php +145 -0
  23. package/dist/src/Lib/PHPMailer/Mailer.php +169 -0
  24. package/dist/src/Lib/PHPX/Exceptions/ComponentValidationException.php +49 -0
  25. package/dist/src/Lib/PHPX/Fragment.php +32 -0
  26. package/dist/src/Lib/PHPX/IPHPX.php +22 -0
  27. package/dist/src/Lib/PHPX/PHPX.php +287 -0
  28. package/dist/src/Lib/PHPX/TemplateCompiler.php +641 -0
  29. package/dist/src/Lib/PHPX/TwMerge.php +346 -0
  30. package/dist/src/Lib/PHPX/TypeCoercer.php +490 -0
  31. package/dist/src/Lib/PartialRenderer.php +40 -0
  32. package/dist/src/Lib/PrismaPHPSettings.php +181 -0
  33. package/dist/src/Lib/Request.php +479 -0
  34. package/dist/src/Lib/Security/RateLimiter.php +33 -0
  35. package/dist/src/Lib/Set.php +102 -0
  36. package/dist/src/Lib/StateManager.php +127 -0
  37. package/dist/src/Lib/Validator.php +752 -0
  38. package/dist/src/{Websocket → Lib/Websocket}/ConnectionManager.php +1 -1
  39. package/dist/src/Lib/Websocket/websocket-server.php +118 -0
  40. package/dist/src/app/error.php +1 -1
  41. package/dist/src/app/index.php +24 -5
  42. package/dist/src/app/js/index.js +1 -1
  43. package/dist/src/app/layout.php +2 -2
  44. package/package.json +1 -1
  45. package/dist/settings/restart-websocket.bat +0 -28
  46. package/dist/src/app/assets/images/prisma-php-black.svg +0 -6
  47. package/dist/websocket-server.php +0 -22
  48. package/vendor/autoload.php +0 -25
  49. package/vendor/composer/ClassLoader.php +0 -579
  50. package/vendor/composer/InstalledVersions.php +0 -359
  51. package/vendor/composer/LICENSE +0 -21
  52. package/vendor/composer/autoload_classmap.php +0 -10
  53. package/vendor/composer/autoload_namespaces.php +0 -9
  54. package/vendor/composer/autoload_psr4.php +0 -10
  55. package/vendor/composer/autoload_real.php +0 -38
  56. package/vendor/composer/autoload_static.php +0 -25
  57. package/vendor/composer/installed.json +0 -825
  58. package/vendor/composer/installed.php +0 -132
  59. package/vendor/composer/platform_check.php +0 -26
@@ -0,0 +1,121 @@
1
+ <?php
2
+
3
+ declare(strict_types=1);
4
+
5
+ namespace Lib;
6
+
7
+ use Lib\PrismaPHPSettings;
8
+
9
+ class CacheHandler
10
+ {
11
+ public static bool $isCacheable = true; // Enable or disable caching by route
12
+ public static int $ttl = 0; // Time to live in seconds (0 = no action taken)
13
+
14
+ private static string $cacheDir = DOCUMENT_PATH . '/caches';
15
+ private static bool $cacheDirChecked = false;
16
+
17
+ private static function ensureCacheDirectoryExists(): void
18
+ {
19
+ if (self::$cacheDirChecked) {
20
+ return;
21
+ }
22
+
23
+ if (!is_dir(self::$cacheDir) && !mkdir(self::$cacheDir, 0777, true) && !is_dir(self::$cacheDir)) {
24
+ die("Error: Unable to create cache directory at: " . self::$cacheDir);
25
+ }
26
+
27
+ self::$cacheDirChecked = true;
28
+ }
29
+
30
+ public static function getCacheFilePath(string $uri): string
31
+ {
32
+ $requestFilesData = PrismaPHPSettings::$includeFiles;
33
+ $fileName = $requestFilesData[$uri]['fileName'] ?? '';
34
+ $isCacheable = $requestFilesData[$uri]['isCacheable'] ?? self::$isCacheable;
35
+
36
+ if (!$isCacheable || $fileName === '') {
37
+ return '';
38
+ }
39
+
40
+ return self::$cacheDir . '/' . $fileName . '.html';
41
+ }
42
+
43
+ private static function isExpired(string $cacheFile, int $ttlSeconds = 600): bool
44
+ {
45
+ if (!file_exists($cacheFile)) {
46
+ return true;
47
+ }
48
+ $fileAge = time() - filemtime($cacheFile);
49
+ return $fileAge > $ttlSeconds;
50
+ }
51
+
52
+ /**
53
+ * Serve cache if available, not expired, and route is marked cacheable.
54
+ * We look up a route-specific TTL if defined, otherwise use a fallback.
55
+ */
56
+ public static function serveCache(string $uri, int $defaultTtl = 600): void
57
+ {
58
+ if ($uri === '') {
59
+ return;
60
+ }
61
+
62
+ $requestFilesData = PrismaPHPSettings::$includeFiles;
63
+
64
+ // Get the route-specific TTL if set, or default to 0
65
+ $routeTtl = $requestFilesData[$uri]['cacheTtl'] ?? 0;
66
+
67
+ // If the route has a TTL greater than 0, use that.
68
+ // Otherwise (0 or not defined), use the default.
69
+ $ttlSeconds = ($routeTtl > 0) ? $routeTtl : $defaultTtl;
70
+
71
+ $cacheFile = self::getCacheFilePath($uri);
72
+
73
+ if ($cacheFile === '' || !file_exists($cacheFile) || self::isExpired($cacheFile, $ttlSeconds)) {
74
+ return;
75
+ }
76
+
77
+ echo "<!-- Cached copy generated at: " . date('Y-m-d H:i:s', filemtime($cacheFile)) . " -->\n";
78
+ readfile($cacheFile);
79
+ exit;
80
+ }
81
+
82
+ public static function saveCache(string $uri, string $content, bool $useLock = true): void
83
+ {
84
+ if ($uri === '') {
85
+ return;
86
+ }
87
+ self::ensureCacheDirectoryExists();
88
+
89
+ $cacheFile = self::getCacheFilePath($uri);
90
+ if ($cacheFile === '') {
91
+ return;
92
+ }
93
+
94
+ $flags = $useLock ? LOCK_EX : 0;
95
+ $written = @file_put_contents($cacheFile, $content, $flags);
96
+
97
+ if ($written === false) {
98
+ die("Error: Failed to write cache file: $cacheFile");
99
+ }
100
+ }
101
+
102
+ public static function resetCache(?string $uri = null): void
103
+ {
104
+ self::ensureCacheDirectoryExists();
105
+
106
+ if ($uri !== null) {
107
+ $cacheFile = self::getCacheFilePath($uri);
108
+ if ($cacheFile !== '' && file_exists($cacheFile)) {
109
+ unlink($cacheFile);
110
+ }
111
+ return;
112
+ }
113
+
114
+ $files = glob(self::$cacheDir . '/*.html') ?: [];
115
+ foreach ($files as $file) {
116
+ if (is_file($file)) {
117
+ unlink($file);
118
+ }
119
+ }
120
+ }
121
+ }
@@ -0,0 +1,322 @@
1
+ <?php
2
+
3
+ declare(strict_types=1);
4
+
5
+ namespace Lib;
6
+
7
+ use Bootstrap;
8
+ use Lib\MainLayout;
9
+ use Throwable;
10
+ use Lib\PHPX\Exceptions\ComponentValidationException;
11
+
12
+ class ErrorHandler
13
+ {
14
+ public static string $content = '';
15
+
16
+ public static function registerHandlers(): void
17
+ {
18
+ self::registerExceptionHandler();
19
+ self::registerShutdownFunction();
20
+ self::registerErrorHandler();
21
+ }
22
+
23
+ private static function registerExceptionHandler(): void
24
+ {
25
+ set_exception_handler(function ($exception) {
26
+ $errorContent = Bootstrap::isAjaxOrXFileRequestOrRouteFile()
27
+ ? "Exception: " . $exception->getMessage()
28
+ : "<div class='error'>Exception: " . htmlspecialchars($exception->getMessage(), ENT_QUOTES, 'UTF-8') . "</div>";
29
+
30
+ self::modifyOutputLayoutForError($errorContent);
31
+ });
32
+ }
33
+
34
+ private static function registerShutdownFunction(): void
35
+ {
36
+ register_shutdown_function(function () {
37
+ $error = error_get_last();
38
+ if (
39
+ $error !== null &&
40
+ in_array($error['type'], [E_ERROR, E_PARSE, E_CORE_ERROR, E_COMPILE_ERROR, E_RECOVERABLE_ERROR], true)
41
+ ) {
42
+ $errorContent = Bootstrap::isAjaxOrXFileRequestOrRouteFile()
43
+ ? "Fatal Error: " . $error['message'] . " in " . $error['file'] . " on line " . $error['line']
44
+ : "<div class='error'>Fatal Error: " . htmlspecialchars($error['message'], ENT_QUOTES, 'UTF-8') .
45
+ " in " . htmlspecialchars($error['file'], ENT_QUOTES, 'UTF-8') .
46
+ " on line " . $error['line'] . "</div>";
47
+
48
+ self::modifyOutputLayoutForError($errorContent);
49
+ }
50
+ });
51
+ }
52
+
53
+ private static function registerErrorHandler(): void
54
+ {
55
+ set_error_handler(function ($severity, $message, $file, $line) {
56
+ if (!(error_reporting() & $severity)) {
57
+ return;
58
+ }
59
+ $errorContent = Bootstrap::isAjaxOrXFileRequestOrRouteFile()
60
+ ? "Error: {$severity} - {$message} in {$file} on line {$line}"
61
+ : "<div class='error'>Error: {$message} in {$file} on line {$line}</div>";
62
+
63
+ if ($severity === E_WARNING || $severity === E_NOTICE) {
64
+ self::modifyOutputLayoutForError($errorContent);
65
+ }
66
+ });
67
+ }
68
+
69
+ public static function checkFatalError(): void
70
+ {
71
+ $error = error_get_last();
72
+ if (
73
+ $error !== null &&
74
+ in_array($error['type'], [E_ERROR, E_PARSE, E_CORE_ERROR, E_COMPILE_ERROR, E_RECOVERABLE_ERROR], true)
75
+ ) {
76
+ $errorContent = Bootstrap::isAjaxOrXFileRequestOrRouteFile()
77
+ ? "Fatal Error: " . $error['message'] . " in " . $error['file'] . " on line " . $error['line']
78
+ : "<div class='error'>Fatal Error: " . htmlspecialchars($error['message'], ENT_QUOTES, 'UTF-8') .
79
+ " in " . htmlspecialchars($error['file'], ENT_QUOTES, 'UTF-8') .
80
+ " on line " . $error['line'] . "</div>";
81
+
82
+ self::modifyOutputLayoutForError($errorContent);
83
+ }
84
+ }
85
+
86
+ public static function modifyOutputLayoutForError($contentToAdd): void
87
+ {
88
+ $errorFile = APP_PATH . '/error.php';
89
+ $errorFileExists = file_exists($errorFile);
90
+
91
+ if ($_ENV['SHOW_ERRORS'] === "false") {
92
+ if ($errorFileExists) {
93
+ $contentToAdd = Bootstrap::isAjaxOrXFileRequestOrRouteFile() ? "An error occurred" : "<div class='error'>An error occurred</div>";
94
+ } else {
95
+ exit;
96
+ }
97
+ }
98
+
99
+ if ($errorFileExists) {
100
+ if (ob_get_level()) {
101
+ ob_end_clean();
102
+ }
103
+ self::$content = $contentToAdd;
104
+ if (Bootstrap::isAjaxOrXFileRequestOrRouteFile()) {
105
+ header('Content-Type: application/json');
106
+ echo json_encode(['success' => false, 'error' => self::$content]);
107
+ http_response_code(403);
108
+ } else {
109
+ $layoutFile = APP_PATH . '/layout.php';
110
+ if (file_exists($layoutFile)) {
111
+ ob_start();
112
+ require_once $errorFile;
113
+ MainLayout::$children = ob_get_clean();
114
+ require $layoutFile;
115
+ } else {
116
+ echo self::$content;
117
+ }
118
+ }
119
+ } else {
120
+ if (Bootstrap::isAjaxOrXFileRequestOrRouteFile()) {
121
+ header('Content-Type: application/json');
122
+ echo json_encode(['success' => false, 'error' => $contentToAdd]);
123
+ http_response_code(403);
124
+ } else {
125
+ echo $contentToAdd;
126
+ }
127
+ }
128
+ exit;
129
+ }
130
+
131
+ public static function formatExceptionForDisplay(Throwable $exception): string
132
+ {
133
+ // Handle specific exception types
134
+ if ($exception instanceof ComponentValidationException) {
135
+ return self::formatComponentValidationError($exception);
136
+ }
137
+
138
+ // Handle template compilation errors specifically
139
+ if (strpos($exception->getMessage(), 'Invalid prop') !== false) {
140
+ return self::formatTemplateCompilerError($exception);
141
+ }
142
+
143
+ // Generic exception formatting
144
+ return self::formatGenericException($exception);
145
+ }
146
+
147
+ private static function formatComponentValidationError(ComponentValidationException $exception): string
148
+ {
149
+ $message = htmlspecialchars($exception->getMessage(), ENT_QUOTES, 'UTF-8');
150
+ $file = htmlspecialchars($exception->getFile(), ENT_QUOTES, 'UTF-8');
151
+ $line = $exception->getLine();
152
+
153
+ // Get the details from the ComponentValidationException
154
+ $propName = method_exists($exception, 'getPropName') ? $exception->getPropName() : 'unknown';
155
+ $componentName = method_exists($exception, 'getComponentName') ? $exception->getComponentName() : 'unknown';
156
+ $availableProps = method_exists($exception, 'getAvailableProps') ? $exception->getAvailableProps() : [];
157
+
158
+ $availablePropsString = !empty($availableProps) ? implode(', ', $availableProps) : 'none defined';
159
+
160
+ return <<<HTML
161
+ <div class="error-container max-w-4xl mx-auto mt-8 bg-red-50 border border-red-200 rounded-lg shadow-lg">
162
+ <div class="bg-red-100 px-6 py-4 border-b border-red-200">
163
+ <h2 class="text-xl font-bold text-red-800 flex items-center">
164
+ <svg class="w-6 h-6 mr-2" fill="currentColor" viewBox="0 0 20 20">
165
+ <path fill-rule="evenodd" d="M18 10a8 8 0 11-16 0 8 8 0 0116 0zm-7 4a1 1 0 11-2 0 1 1 0 012 0zm-1-9a1 1 0 00-1 1v4a1 1 0 102 0V6a1 1 0 00-1-1z" clip-rule="evenodd"></path>
166
+ </svg>
167
+ Component Validation Error
168
+ </h2>
169
+ </div>
170
+
171
+ <div class="p-6">
172
+ <div class="bg-white border border-red-200 rounded-lg p-4 mb-4">
173
+ <div class="mb-3">
174
+ <span class="inline-block bg-red-100 text-red-800 px-3 py-1 rounded-full text-sm font-medium">
175
+ Component: {$componentName}
176
+ </span>
177
+ <span class="inline-block bg-red-100 text-red-800 px-3 py-1 rounded-full text-sm font-medium ml-2">
178
+ Invalid Prop: {$propName}
179
+ </span>
180
+ </div>
181
+ <pre class="text-sm text-red-800 whitespace-pre-wrap font-mono">{$message}</pre>
182
+ </div>
183
+
184
+ <div class="text-sm text-gray-600 mb-4">
185
+ <strong>File:</strong> <code class="bg-gray-100 px-2 py-1 rounded text-xs">{$file}</code><br />
186
+ <strong>Line:</strong> <span class="bg-gray-100 px-2 py-1 rounded text-xs">{$line}</span>
187
+ </div>
188
+
189
+ <div class="bg-blue-50 border border-blue-200 rounded-lg p-4 mb-4">
190
+ <h4 class="font-medium text-blue-800 mb-2">💡 Available Props:</h4>
191
+ <p class="text-blue-700 text-sm">
192
+ <code class="bg-blue-100 px-2 py-1 rounded text-xs">{$availablePropsString}</code>
193
+ </p>
194
+ </div>
195
+
196
+ <div class="bg-green-50 border border-green-200 rounded-lg p-4 mb-4">
197
+ <h4 class="font-medium text-green-800 mb-2">🔧 Quick Fixes:</h4>
198
+ <ul class="text-green-700 text-sm space-y-1">
199
+ <li>• Remove the '<code>{$propName}</code>' prop from your template</li>
200
+ <li>• Add '<code>public \${$propName};</code>' to your <code>{$componentName}</code> component class</li>
201
+ <li>• Use data attributes: '<code>data-{$propName}</code>' instead</li>
202
+ </ul>
203
+ </div>
204
+
205
+ <details class="mt-4">
206
+ <summary class="cursor-pointer text-red-600 font-medium hover:text-red-800 select-none">
207
+ Show Stack Trace
208
+ </summary>
209
+ <div class="mt-3 bg-gray-50 border border-gray-200 rounded p-4">
210
+ <pre class="text-xs text-gray-700 overflow-auto whitespace-pre-wrap max-h-96">{$exception->getTraceAsString()}</pre>
211
+ </div>
212
+ </details>
213
+ </div>
214
+ </div>
215
+ HTML;
216
+ }
217
+
218
+ private static function formatTemplateCompilerError(Throwable $exception): string
219
+ {
220
+ $message = htmlspecialchars($exception->getMessage(), ENT_QUOTES, 'UTF-8');
221
+ $file = htmlspecialchars($exception->getFile(), ENT_QUOTES, 'UTF-8');
222
+ $line = $exception->getLine();
223
+
224
+ // Extract the component validation error details
225
+ if (preg_match("/Invalid prop '([^']+)' passed to component '([^']+)'/", $exception->getMessage(), $matches)) {
226
+ $invalidProp = $matches[1];
227
+ $componentName = $matches[2];
228
+
229
+ return <<<HTML
230
+ <div class="error-container max-w-4xl mx-auto mt-8 bg-red-50 border border-red-200 rounded-lg shadow-lg">
231
+ <div class="bg-red-100 px-6 py-4 border-b border-red-200">
232
+ <h2 class="text-xl font-bold text-red-800 flex items-center">
233
+ <svg class="w-6 h-6 mr-2" fill="currentColor" viewBox="0 0 20 20">
234
+ <path fill-rule="evenodd" d="M18 10a8 8 0 11-16 0 8 8 0 0116 0zm-7 4a1 1 0 11-2 0 1 1 0 012 0zm-1-9a1 1 0 00-1 1v4a1 1 0 102 0V6a1 1 0 00-1-1z" clip-rule="evenodd"></path>
235
+ </svg>
236
+ Template Compilation Error
237
+ </h2>
238
+ </div>
239
+
240
+ <div class="p-6">
241
+ <div class="bg-white border border-red-200 rounded-lg p-4 mb-4">
242
+ <div class="mb-3">
243
+ <span class="inline-block bg-red-100 text-red-800 px-3 py-1 rounded-full text-sm font-medium">
244
+ Component: {$componentName}
245
+ </span>
246
+ <span class="inline-block bg-red-100 text-red-800 px-3 py-1 rounded-full text-sm font-medium ml-2">
247
+ Invalid Prop: {$invalidProp}
248
+ </span>
249
+ </div>
250
+ <p class="text-red-800 font-medium">{$message}</p>
251
+ </div>
252
+
253
+ <div class="text-sm text-gray-600 mb-4">
254
+ <strong>File:</strong> {$file}<br />
255
+ <strong>Line:</strong> {$line}
256
+ </div>
257
+
258
+ <div class="bg-blue-50 border border-blue-200 rounded-lg p-4 mb-4">
259
+ <h4 class="font-medium text-blue-800 mb-2">💡 Quick Fix:</h4>
260
+ <p class="text-blue-700 text-sm">
261
+ Either remove the '<code>{$invalidProp}</code>' prop from your template, or add it as a public property to your <code>{$componentName}</code> component class.
262
+ </p>
263
+ </div>
264
+
265
+ <details class="mt-4">
266
+ <summary class="cursor-pointer text-red-600 font-medium hover:text-red-800 select-none">
267
+ Show Stack Trace
268
+ </summary>
269
+ <div class="mt-3 bg-gray-50 border border-gray-200 rounded p-4">
270
+ <pre class="text-xs text-gray-700 overflow-auto whitespace-pre-wrap">{$exception->getTraceAsString()}</pre>
271
+ </div>
272
+ </details>
273
+ </div>
274
+ </div>
275
+ HTML;
276
+ }
277
+
278
+ // Fallback to generic formatting
279
+ return self::formatGenericException($exception);
280
+ }
281
+
282
+ private static function formatGenericException(Throwable $exception): string
283
+ {
284
+ $type = htmlspecialchars(get_class($exception), ENT_QUOTES, 'UTF-8');
285
+ $message = htmlspecialchars($exception->getMessage(), ENT_QUOTES, 'UTF-8');
286
+ $file = htmlspecialchars($exception->getFile(), ENT_QUOTES, 'UTF-8');
287
+ $line = $exception->getLine();
288
+
289
+ return <<<HTML
290
+ <div class="error-container max-w-4xl mx-auto mt-8 bg-red-50 border border-red-200 rounded-lg shadow-lg">
291
+ <div class="bg-red-100 px-6 py-4 border-b border-red-200">
292
+ <h2 class="text-xl font-bold text-red-800 flex items-center">
293
+ <svg class="w-6 h-6 mr-2" fill="currentColor" viewBox="0 0 20 20">
294
+ <path fill-rule="evenodd" d="M18 10a8 8 0 11-16 0 8 8 0 0116 0zm-7 4a1 1 0 11-2 0 1 1 0 012 0zm-1-9a1 1 0 00-1 1v4a1 1 0 102 0V6a1 1 0 00-1-1z" clip-rule="evenodd"></path>
295
+ </svg>
296
+ {$type}
297
+ </h2>
298
+ </div>
299
+
300
+ <div class="p-6">
301
+ <div class="bg-white border border-red-200 rounded-lg p-4 mb-4">
302
+ <p class="text-red-800 font-medium break-words">{$message}</p>
303
+ </div>
304
+
305
+ <div class="text-sm text-gray-600 mb-4">
306
+ <strong>File:</strong> <code class="bg-gray-100 px-2 py-1 rounded text-xs">{$file}</code><br />
307
+ <strong>Line:</strong> <span class="bg-gray-100 px-2 py-1 rounded text-xs">{$line}</span>
308
+ </div>
309
+
310
+ <details class="mt-4">
311
+ <summary class="cursor-pointer text-red-600 font-medium hover:text-red-800 select-none">
312
+ Show Stack Trace
313
+ </summary>
314
+ <div class="mt-3 bg-gray-50 border border-gray-200 rounded p-4">
315
+ <pre class="text-xs text-gray-700 overflow-auto whitespace-pre-wrap max-h-96">{$exception->getTraceAsString()}</pre>
316
+ </div>
317
+ </details>
318
+ </div>
319
+ </div>
320
+ HTML;
321
+ }
322
+ }