create-prisma-php-app 1.21.500 → 1.22.500
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 +186 -153
- package/dist/index.js +43 -10
- package/dist/settings/bs-config.cjs +1 -0
- package/dist/settings/start-dev.js +3 -3
- package/dist/src/Lib/Headers/Boom.php +39 -0
- package/dist/src/app/layout.php +1 -1
- package/dist/tailwind.config.js +3 -6
- package/dist/tsconfig.json +9 -9
- package/package.json +1 -1
package/dist/bootstrap.php
CHANGED
|
@@ -25,8 +25,17 @@ function determineContentToInclude()
|
|
|
25
25
|
$baseDir = APP_PATH;
|
|
26
26
|
$includePath = '';
|
|
27
27
|
$layoutsToInclude = [];
|
|
28
|
-
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* ============ Middleware Management ============
|
|
31
|
+
* AuthMiddleware is invoked to handle authentication logic for the current route ($uri).
|
|
32
|
+
* ================================================
|
|
33
|
+
*/
|
|
29
34
|
AuthMiddleware::handle($uri);
|
|
35
|
+
/**
|
|
36
|
+
* ============ End of Middleware Management ======
|
|
37
|
+
* ================================================
|
|
38
|
+
*/
|
|
30
39
|
|
|
31
40
|
$isDirectAccessToPrivateRoute = preg_match('/\/_/', $uri);
|
|
32
41
|
if ($isDirectAccessToPrivateRoute) {
|
|
@@ -107,31 +116,23 @@ function getFilePrecedence()
|
|
|
107
116
|
global $_filesListRoutes;
|
|
108
117
|
|
|
109
118
|
// Normalize the file paths for consistent comparison
|
|
110
|
-
$_filesListRoutes
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
return preg_match('/^\.\/src\/app\/route\.php$/', $route);
|
|
117
|
-
});
|
|
118
|
-
|
|
119
|
-
// If route.php is found, return just the file name
|
|
120
|
-
if (!empty($routeFile)) {
|
|
121
|
-
return '/route.php';
|
|
119
|
+
foreach ($_filesListRoutes as $route) {
|
|
120
|
+
$normalizedRoute = str_replace('\\', '/', $route);
|
|
121
|
+
// First, check for route.php
|
|
122
|
+
if (preg_match('/^\.\/src\/app\/route\.php$/', $normalizedRoute)) {
|
|
123
|
+
return '/route.php';
|
|
124
|
+
}
|
|
122
125
|
}
|
|
123
126
|
|
|
124
|
-
// If route.php is
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
if (!empty($indexFile)) {
|
|
131
|
-
return '/index.php';
|
|
127
|
+
// If no route.php is found, check for index.php
|
|
128
|
+
foreach ($_filesListRoutes as $route) {
|
|
129
|
+
$normalizedRoute = str_replace('\\', '/', $route);
|
|
130
|
+
if (preg_match('/^\.\/src\/app\/index\.php$/', $normalizedRoute)) {
|
|
131
|
+
return '/index.php';
|
|
132
|
+
}
|
|
132
133
|
}
|
|
133
134
|
|
|
134
|
-
// If neither file is found, return null
|
|
135
|
+
// If neither file is found, return null
|
|
135
136
|
return null;
|
|
136
137
|
}
|
|
137
138
|
|
|
@@ -145,42 +146,19 @@ function uriExtractor(string $scriptUrl): string
|
|
|
145
146
|
}
|
|
146
147
|
|
|
147
148
|
$escapedIdentifier = preg_quote($projectName, '/');
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
if (!empty($matches[1])) {
|
|
151
|
-
$leftTrim = ltrim($matches[1], '/');
|
|
152
|
-
$rightTrim = rtrim($leftTrim, '/');
|
|
153
|
-
return "$rightTrim";
|
|
154
|
-
}
|
|
149
|
+
if (preg_match("/(?:.*$escapedIdentifier)(\/.*)$/", $scriptUrl, $matches) && !empty($matches[1])) {
|
|
150
|
+
return rtrim(ltrim($matches[1], '/'), '/');
|
|
155
151
|
}
|
|
156
152
|
|
|
157
153
|
return "/";
|
|
158
154
|
}
|
|
159
155
|
|
|
160
|
-
function
|
|
156
|
+
function getFilesListRoutes()
|
|
161
157
|
{
|
|
162
|
-
|
|
163
|
-
$
|
|
164
|
-
|
|
165
|
-
if (is_dir($directory)) {
|
|
166
|
-
$filesList = [];
|
|
167
|
-
|
|
168
|
-
$iterator = new RecursiveIteratorIterator(new RecursiveDirectoryIterator($directory));
|
|
169
|
-
|
|
170
|
-
foreach ($iterator as $file) {
|
|
171
|
-
if ($file->isFile()) {
|
|
172
|
-
$filesList[] = $file->getPathname();
|
|
173
|
-
}
|
|
174
|
-
}
|
|
175
|
-
|
|
176
|
-
$jsonData = json_encode($filesList, JSON_PRETTY_PRINT);
|
|
177
|
-
$jsonFileName = SETTINGS_PATH . '/files-list.json';
|
|
178
|
-
file_put_contents($jsonFileName, $jsonData);
|
|
158
|
+
$jsonFileName = SETTINGS_PATH . '/files-list.json';
|
|
159
|
+
$routeFiles = file_exists($jsonFileName) ? json_decode(file_get_contents($jsonFileName), true) : [];
|
|
179
160
|
|
|
180
|
-
|
|
181
|
-
$_filesListRoutes = json_decode(file_get_contents($jsonFileName), true);
|
|
182
|
-
}
|
|
183
|
-
}
|
|
161
|
+
return $routeFiles;
|
|
184
162
|
}
|
|
185
163
|
|
|
186
164
|
function findGroupFolder($uri): string
|
|
@@ -213,14 +191,21 @@ function dynamicRoute($uri)
|
|
|
213
191
|
foreach ($_filesListRoutes as $route) {
|
|
214
192
|
$normalizedRoute = trim(str_replace('\\', '/', $route), '.');
|
|
215
193
|
$routeSegments = explode('/', ltrim($normalizedRoute, '/'));
|
|
194
|
+
|
|
195
|
+
$filteredRouteSegments = array_values(array_filter($routeSegments, function ($segment) {
|
|
196
|
+
return !preg_match('/\(.+\)/', $segment); // Skip segments with parentheses (groups)
|
|
197
|
+
}));
|
|
198
|
+
|
|
216
199
|
$singleDynamic = preg_match_all('/\[[^\]]+\]/', $normalizedRoute, $matches) === 1 && !strpos($normalizedRoute, '[...');
|
|
217
200
|
if ($singleDynamic) {
|
|
218
|
-
$segmentMatch = singleDynamicRoute($uriSegments, $
|
|
219
|
-
|
|
201
|
+
$segmentMatch = singleDynamicRoute($uriSegments, $filteredRouteSegments);
|
|
202
|
+
$index = array_search($segmentMatch, $filteredRouteSegments);
|
|
203
|
+
if ($index !== false && isset($uriSegments[$index])) {
|
|
220
204
|
$trimSegmentMatch = trim($segmentMatch, '[]');
|
|
221
|
-
$dynamicRouteParams = new \ArrayObject([$trimSegmentMatch => $uriSegments[
|
|
222
|
-
|
|
223
|
-
$dynamicRouteUri =
|
|
205
|
+
$dynamicRouteParams = new \ArrayObject([$trimSegmentMatch => $uriSegments[$index]], \ArrayObject::ARRAY_AS_PROPS);
|
|
206
|
+
$dynamicRouteUri = str_replace($segmentMatch, $uriSegments[$index], $normalizedRoute);
|
|
207
|
+
$dynamicRouteUri = preg_replace('/\(.+\)/', '', $dynamicRouteUri);
|
|
208
|
+
$dynamicRouteUri = preg_replace('/\/+/', '/', $dynamicRouteUri);
|
|
224
209
|
$dynamicRouteUriDirname = dirname($dynamicRouteUri);
|
|
225
210
|
$dynamicRouteUriDirname = rtrim($dynamicRouteUriDirname, '/');
|
|
226
211
|
|
|
@@ -235,30 +220,41 @@ function dynamicRoute($uri)
|
|
|
235
220
|
}
|
|
236
221
|
}
|
|
237
222
|
} elseif (strpos($normalizedRoute, '[...') !== false) {
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
223
|
+
// Clean and normalize the route
|
|
224
|
+
$cleanedNormalizedRoute = preg_replace('/\(.+\)/', '', $normalizedRoute); // Remove any public/private segment
|
|
225
|
+
$cleanedNormalizedRoute = preg_replace('/\/+/', '/', $cleanedNormalizedRoute); // Replace multiple slashes
|
|
226
|
+
$dynamicSegmentRoute = preg_replace('/\[\.\.\..*?\].*/', '', $cleanedNormalizedRoute); // Remove the dynamic segment
|
|
227
|
+
|
|
228
|
+
// Check if the normalized URI starts with the cleaned route
|
|
229
|
+
if (strpos("/src/app/$normalizedUri", $dynamicSegmentRoute) === 0) {
|
|
230
|
+
$trimmedUri = str_replace($dynamicSegmentRoute, '', "/src/app/$normalizedUri");
|
|
231
|
+
$uriParts = explode('/', trim($trimmedUri, '/'));
|
|
232
|
+
|
|
233
|
+
// Extract the dynamic segment content
|
|
234
|
+
if (preg_match('/\[\.\.\.(.*?)\]/', $normalizedRoute, $matches)) {
|
|
235
|
+
$dynamicParam = $matches[1];
|
|
236
|
+
$dynamicRouteParams = new \ArrayObject([$dynamicParam => $uriParts], \ArrayObject::ARRAY_AS_PROPS);
|
|
248
237
|
}
|
|
238
|
+
|
|
239
|
+
// Check for 'route.php'
|
|
249
240
|
if (strpos($normalizedRoute, 'route.php') !== false) {
|
|
250
241
|
$uriMatch = $normalizedRoute;
|
|
251
242
|
break;
|
|
252
|
-
}
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
// Handle matching routes ending with 'index.php'
|
|
246
|
+
if (strpos($normalizedRoute, 'index.php') !== false) {
|
|
247
|
+
$segmentMatch = "[...$dynamicParam]";
|
|
248
|
+
$index = array_search($segmentMatch, $filteredRouteSegments);
|
|
249
|
+
|
|
250
|
+
if ($index !== false && isset($uriSegments[$index])) {
|
|
251
|
+
// Generate the dynamic URI
|
|
252
|
+
$dynamicRouteUri = str_replace($segmentMatch, implode('/', $uriParts), $cleanedNormalizedRoute);
|
|
253
|
+
$dynamicRouteUriDirname = rtrim(dirname($dynamicRouteUri), '/');
|
|
258
254
|
|
|
259
|
-
$expectedUri =
|
|
260
|
-
$expectedUri = rtrim($expectedUri, '/');
|
|
255
|
+
$expectedUri = rtrim("/src/app/$normalizedUri", '/');
|
|
261
256
|
|
|
257
|
+
// Compare the expected and dynamic URIs
|
|
262
258
|
if ($expectedUri === $dynamicRouteUriDirname) {
|
|
263
259
|
$uriMatch = $normalizedRoute;
|
|
264
260
|
break;
|
|
@@ -280,6 +276,7 @@ function isGroupIdentifier($segment): bool
|
|
|
280
276
|
function matchGroupFolder($constructedPath): ?string
|
|
281
277
|
{
|
|
282
278
|
global $_filesListRoutes;
|
|
279
|
+
|
|
283
280
|
$bestMatch = null;
|
|
284
281
|
$normalizedConstructedPath = ltrim(str_replace('\\', '/', $constructedPath), './');
|
|
285
282
|
|
|
@@ -330,6 +327,10 @@ function singleDynamicRoute($uriSegments, $routeSegments)
|
|
|
330
327
|
|
|
331
328
|
function checkForDuplicateRoutes()
|
|
332
329
|
{
|
|
330
|
+
if ($_ENV['APP_ENV'] !== 'development') {
|
|
331
|
+
return;
|
|
332
|
+
}
|
|
333
|
+
|
|
333
334
|
global $_filesListRoutes;
|
|
334
335
|
$normalizedRoutesMap = [];
|
|
335
336
|
foreach ($_filesListRoutes as $route) {
|
|
@@ -363,72 +364,27 @@ function checkForDuplicateRoutes()
|
|
|
363
364
|
}
|
|
364
365
|
}
|
|
365
366
|
|
|
366
|
-
function setupErrorHandling(&$content)
|
|
367
|
-
{
|
|
368
|
-
set_error_handler(function ($severity, $message, $file, $line) use (&$content) {
|
|
369
|
-
$content .= "<div class='error'>Error: {$severity} - {$message} in {$file} on line {$line}</div>";
|
|
370
|
-
});
|
|
371
|
-
|
|
372
|
-
set_exception_handler(function ($exception) use (&$content) {
|
|
373
|
-
$content .= "<div class='error'>Exception: " . htmlspecialchars($exception->getMessage(), ENT_QUOTES, 'UTF-8') . "</div>";
|
|
374
|
-
});
|
|
375
|
-
|
|
376
|
-
register_shutdown_function(function () use (&$content) {
|
|
377
|
-
$error = error_get_last();
|
|
378
|
-
if ($error !== null && in_array($error['type'], [E_ERROR, E_PARSE, E_CORE_ERROR, E_COMPILE_ERROR, E_RECOVERABLE_ERROR])) {
|
|
379
|
-
$formattedError = "<div class='error'>Fatal Error: " . htmlspecialchars($error['message'], ENT_QUOTES, 'UTF-8') .
|
|
380
|
-
" in " . htmlspecialchars($error['file'], ENT_QUOTES, 'UTF-8') .
|
|
381
|
-
" on line " . $error['line'] . "</div>";
|
|
382
|
-
$content .= $formattedError;
|
|
383
|
-
modifyOutputLayoutForError($content);
|
|
384
|
-
}
|
|
385
|
-
});
|
|
386
|
-
}
|
|
387
|
-
|
|
388
367
|
|
|
389
368
|
function containsChildContent($filePath)
|
|
390
369
|
{
|
|
391
370
|
$fileContent = file_get_contents($filePath);
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
)
|
|
398
|
-
return true;
|
|
399
|
-
} else {
|
|
400
|
-
return false;
|
|
401
|
-
}
|
|
371
|
+
|
|
372
|
+
// Regular expression to match different ways of echoing $childContent
|
|
373
|
+
$pattern = '/\<\?=\s*\$childContent\s*;?\s*\?>|echo\s*\$childContent\s*;?/';
|
|
374
|
+
|
|
375
|
+
// Return true if $childContent variables are found, false otherwise
|
|
376
|
+
return preg_match($pattern, $fileContent) === 1;
|
|
402
377
|
}
|
|
403
378
|
|
|
404
379
|
function containsContent($filePath)
|
|
405
380
|
{
|
|
406
381
|
$fileContent = file_get_contents($filePath);
|
|
407
|
-
if (
|
|
408
|
-
(strpos($fileContent, 'echo $content') === false &&
|
|
409
|
-
strpos($fileContent, 'echo $content;') === false) &&
|
|
410
|
-
(strpos($fileContent, '<?= $content ?>') === false) &&
|
|
411
|
-
(strpos($fileContent, '<?= $content; ?>') === false)
|
|
412
|
-
) {
|
|
413
|
-
return true;
|
|
414
|
-
} else {
|
|
415
|
-
return false;
|
|
416
|
-
}
|
|
417
|
-
}
|
|
418
|
-
|
|
419
|
-
function modifyOutputLayoutForError($contentToAdd)
|
|
420
|
-
{
|
|
421
|
-
if ($_ENV['SHOW_ERRORS'] === "false") exit;
|
|
422
|
-
|
|
423
|
-
$layoutContent = file_get_contents(APP_PATH . '/layout.php');
|
|
424
|
-
if ($layoutContent !== false) {
|
|
425
|
-
$newBodyContent = "<body class=\"fatal-error\">$contentToAdd</body>";
|
|
426
382
|
|
|
427
|
-
|
|
383
|
+
// Improved regular expression to match different ways of echoing $content
|
|
384
|
+
$pattern = '/\<\?=\s*\$content\s*;?\s*\?>|echo\s*\$content\s*;?/';
|
|
428
385
|
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
}
|
|
386
|
+
// Return true if content variables are found, false otherwise
|
|
387
|
+
return preg_match($pattern, $fileContent) === 1;
|
|
432
388
|
}
|
|
433
389
|
|
|
434
390
|
function convertToArrayObject($data)
|
|
@@ -579,17 +535,7 @@ function getLoadingsFiles()
|
|
|
579
535
|
return '';
|
|
580
536
|
}
|
|
581
537
|
|
|
582
|
-
|
|
583
|
-
{
|
|
584
|
-
$token = getBearerToken();
|
|
585
|
-
if ($token) {
|
|
586
|
-
$auth = Auth::getInstance();
|
|
587
|
-
$verifyToken = $auth->verifyToken($token);
|
|
588
|
-
if ($verifyToken) {
|
|
589
|
-
$auth->authenticate($verifyToken);
|
|
590
|
-
}
|
|
591
|
-
}
|
|
592
|
-
}
|
|
538
|
+
|
|
593
539
|
|
|
594
540
|
function getPrismaSettings(): \ArrayObject
|
|
595
541
|
{
|
|
@@ -607,15 +553,101 @@ function getPrismaSettings(): \ArrayObject
|
|
|
607
553
|
}
|
|
608
554
|
}
|
|
609
555
|
|
|
610
|
-
|
|
556
|
+
function modifyOutputLayoutForError($contentToAdd)
|
|
557
|
+
{
|
|
558
|
+
global
|
|
559
|
+
$baseUrl,
|
|
560
|
+
$metadata,
|
|
561
|
+
$content,
|
|
562
|
+
$childContent,
|
|
563
|
+
$uri,
|
|
564
|
+
$pathname,
|
|
565
|
+
$dynamicRouteParams,
|
|
566
|
+
$params,
|
|
567
|
+
$referer,
|
|
568
|
+
$mainLayoutHead,
|
|
569
|
+
$mainLayoutFooter;
|
|
570
|
+
|
|
571
|
+
$errorFile = APP_PATH . '/error.php';
|
|
572
|
+
$errorFileExists = file_exists($errorFile);
|
|
573
|
+
|
|
574
|
+
if ($_ENV['SHOW_ERRORS'] === "false") {
|
|
575
|
+
if ($errorFileExists) {
|
|
576
|
+
$contentToAdd = "<div class='error'>An error occurred</div>";
|
|
577
|
+
} else {
|
|
578
|
+
exit; // Exit if SHOW_ERRORS is false and no error file exists
|
|
579
|
+
}
|
|
580
|
+
}
|
|
581
|
+
|
|
582
|
+
if ($errorFileExists) {
|
|
583
|
+
|
|
584
|
+
$errorContent = $contentToAdd;
|
|
585
|
+
|
|
586
|
+
$layoutFile = APP_PATH . '/layout.php';
|
|
587
|
+
if (file_exists($layoutFile)) {
|
|
588
|
+
|
|
589
|
+
ob_start();
|
|
590
|
+
include_once $errorFile;
|
|
591
|
+
$content = ob_get_clean();
|
|
592
|
+
include $layoutFile;
|
|
593
|
+
} else {
|
|
594
|
+
echo $errorContent;
|
|
595
|
+
}
|
|
596
|
+
}
|
|
597
|
+
exit;
|
|
598
|
+
}
|
|
599
|
+
|
|
600
|
+
set_error_handler(function ($severity, $message, $file, $line) {
|
|
601
|
+
if (!(error_reporting() & $severity)) {
|
|
602
|
+
// This error code is not included in error_reporting
|
|
603
|
+
return;
|
|
604
|
+
}
|
|
605
|
+
|
|
606
|
+
// Capture the specific severity types, including warnings (E_WARNING)
|
|
607
|
+
$errorContent = "<div class='error'>Error: {$severity} - {$message} in {$file} on line {$line}</div>";
|
|
608
|
+
|
|
609
|
+
// If needed, log it or output immediately based on severity
|
|
610
|
+
if ($severity === E_WARNING || $severity === E_NOTICE) {
|
|
611
|
+
modifyOutputLayoutForError($errorContent);
|
|
612
|
+
}
|
|
613
|
+
});
|
|
614
|
+
|
|
615
|
+
set_exception_handler(function ($exception) {
|
|
616
|
+
$errorContent = "<div class='error'>Exception: " . htmlspecialchars($exception->getMessage(), ENT_QUOTES, 'UTF-8') . "</div>";
|
|
617
|
+
modifyOutputLayoutForError($errorContent);
|
|
618
|
+
});
|
|
619
|
+
|
|
620
|
+
register_shutdown_function(function () {
|
|
621
|
+
$error = error_get_last();
|
|
622
|
+
if ($error !== null && in_array($error['type'], [E_ERROR, E_PARSE, E_CORE_ERROR, E_COMPILE_ERROR, E_RECOVERABLE_ERROR])) {
|
|
623
|
+
$formattedError = "<div class='error'>Fatal Error: " . htmlspecialchars($error['message'], ENT_QUOTES, 'UTF-8') .
|
|
624
|
+
" in " . htmlspecialchars($error['file'], ENT_QUOTES, 'UTF-8') .
|
|
625
|
+
" on line " . $error['line'] . "</div>";
|
|
626
|
+
$errorContent = $formattedError;
|
|
627
|
+
modifyOutputLayoutForError($errorContent);
|
|
628
|
+
}
|
|
629
|
+
});
|
|
630
|
+
|
|
611
631
|
require_once SETTINGS_PATH . '/public-functions.php';
|
|
612
632
|
require_once SETTINGS_PATH . '/request-methods.php';
|
|
613
633
|
$_metadataFile = APP_PATH . '/metadata.php';
|
|
614
634
|
$_metadataArray = file_exists($_metadataFile) ? require_once $_metadataFile : [];
|
|
615
|
-
$_filesListRoutes =
|
|
635
|
+
$_filesListRoutes = getFilesListRoutes();
|
|
616
636
|
$_prismaPHPSettings = getPrismaSettings();
|
|
617
637
|
$_fileToInclude = '';
|
|
618
638
|
|
|
639
|
+
function authenticateUserToken()
|
|
640
|
+
{
|
|
641
|
+
$token = getBearerToken();
|
|
642
|
+
if ($token) {
|
|
643
|
+
$auth = Auth::getInstance();
|
|
644
|
+
$verifyToken = $auth->verifyToken($token);
|
|
645
|
+
if ($verifyToken) {
|
|
646
|
+
$auth->authenticate($verifyToken);
|
|
647
|
+
}
|
|
648
|
+
}
|
|
649
|
+
}
|
|
650
|
+
|
|
619
651
|
/**
|
|
620
652
|
* @var array $metadata Metadata information
|
|
621
653
|
*/
|
|
@@ -656,7 +688,10 @@ try {
|
|
|
656
688
|
$_layoutsToInclude = $_determineContentToInclude['layouts'] ?? [];
|
|
657
689
|
$uri = $_determineContentToInclude['uri'] ?? '';
|
|
658
690
|
$pathname = $uri ? "/" . $uri : "/";
|
|
659
|
-
$_fileToInclude =
|
|
691
|
+
$_fileToInclude = null;
|
|
692
|
+
if (is_file($_contentToInclude)) {
|
|
693
|
+
$_fileToInclude = basename($_contentToInclude); // returns the file name
|
|
694
|
+
}
|
|
660
695
|
authenticateUserToken();
|
|
661
696
|
|
|
662
697
|
if (empty($_contentToInclude)) {
|
|
@@ -710,11 +745,11 @@ try {
|
|
|
710
745
|
|
|
711
746
|
$_isContentIncluded = false;
|
|
712
747
|
$_isChildContentIncluded = false;
|
|
713
|
-
|
|
748
|
+
$_isContentVariableIncluded = containsContent($_parentLayoutPath);
|
|
749
|
+
if (!$_isContentVariableIncluded) {
|
|
714
750
|
$_isContentIncluded = true;
|
|
715
751
|
}
|
|
716
752
|
|
|
717
|
-
ob_start();
|
|
718
753
|
if (!empty($_contentToInclude)) {
|
|
719
754
|
if (!$_isParentLayout) {
|
|
720
755
|
ob_start();
|
|
@@ -726,7 +761,8 @@ try {
|
|
|
726
761
|
continue;
|
|
727
762
|
}
|
|
728
763
|
|
|
729
|
-
|
|
764
|
+
$_isChildContentVariableIncluded = containsChildContent($layoutPath);
|
|
765
|
+
if (!$_isChildContentVariableIncluded) {
|
|
730
766
|
$_isChildContentIncluded = true;
|
|
731
767
|
}
|
|
732
768
|
|
|
@@ -759,18 +795,15 @@ try {
|
|
|
759
795
|
}
|
|
760
796
|
} else {
|
|
761
797
|
if ($_isContentIncluded) {
|
|
762
|
-
|
|
763
|
-
modifyOutputLayoutForError($content);
|
|
798
|
+
echo "<div class='error'>The parent layout file does not contain <?php echo \$content; ?> Or <?= \$content ?><br>" . "<strong>$_parentLayoutPath</strong></div>";
|
|
764
799
|
} else {
|
|
765
|
-
|
|
766
|
-
modifyOutputLayoutForError($content);
|
|
800
|
+
echo "<div class='error'>The layout file does not contain <?php echo \$childContent; ?> or <?= \$childContent ?><br><strong>$layoutPath</strong></div>";
|
|
767
801
|
}
|
|
768
802
|
}
|
|
769
803
|
} catch (Throwable $e) {
|
|
770
|
-
$content = ob_get_clean();
|
|
771
804
|
$errorDetails = "Unhandled Exception: " . htmlspecialchars($e->getMessage(), ENT_QUOTES, 'UTF-8');
|
|
772
805
|
$errorDetails .= "<br>File: " . htmlspecialchars($e->getFile(), ENT_QUOTES, 'UTF-8');
|
|
773
806
|
$errorDetails .= "<br>Line: " . htmlspecialchars($e->getLine(), ENT_QUOTES, 'UTF-8');
|
|
774
|
-
$
|
|
775
|
-
modifyOutputLayoutForError($
|
|
807
|
+
$errorDetails = "<div class='error'>$errorDetails</div>";
|
|
808
|
+
modifyOutputLayoutForError($errorDetails);
|
|
776
809
|
}
|
package/dist/index.js
CHANGED
|
@@ -67,10 +67,31 @@ function configureBrowserSyncCommand(baseDir) {
|
|
|
67
67
|
// TypeScript content to write
|
|
68
68
|
const bsConfigTsContent = `const { createProxyMiddleware } = require("http-proxy-middleware");
|
|
69
69
|
const fs = require("fs");
|
|
70
|
+
const chokidar = require("chokidar");
|
|
71
|
+
const { execSync } = require("child_process");
|
|
70
72
|
|
|
71
73
|
const jsonData = fs.readFileSync("prisma-php.json", "utf8");
|
|
72
74
|
const config = JSON.parse(jsonData);
|
|
73
75
|
|
|
76
|
+
// Watch for file changes (create, delete, save)
|
|
77
|
+
const watcher = chokidar.watch("src/**/*", {
|
|
78
|
+
ignored: /(^|[\/\\])\../, // Ignore dotfiles
|
|
79
|
+
persistent: true,
|
|
80
|
+
usePolling: true,
|
|
81
|
+
interval: 1000,
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
watcher
|
|
85
|
+
.on("add", async (path) => {
|
|
86
|
+
execSync("node settings/files-list-config.js");
|
|
87
|
+
})
|
|
88
|
+
.on("change", async (path) => {
|
|
89
|
+
execSync("node settings/files-list-config.js");
|
|
90
|
+
})
|
|
91
|
+
.on("unlink", async (path) => {
|
|
92
|
+
execSync("node settings/files-list-config.js");
|
|
93
|
+
});
|
|
94
|
+
|
|
74
95
|
module.exports = {
|
|
75
96
|
proxy: "http://localhost:3000",
|
|
76
97
|
middleware: [
|
|
@@ -91,6 +112,10 @@ module.exports = {
|
|
|
91
112
|
open: false,
|
|
92
113
|
ghostMode: false,
|
|
93
114
|
codeSync: true, // Disable synchronization of code changes across clients
|
|
115
|
+
watchOptions: {
|
|
116
|
+
usePolling: true,
|
|
117
|
+
interval: 1000,
|
|
118
|
+
},
|
|
94
119
|
};`;
|
|
95
120
|
// Determine the path and write the bs-config.js
|
|
96
121
|
const bsConfigPath = path.join(baseDir, "settings", "bs-config.cjs");
|
|
@@ -149,12 +174,11 @@ async function updatePackageJson(baseDir, answer) {
|
|
|
149
174
|
let updatedScripts = Object.assign({}, packageJson.scripts);
|
|
150
175
|
// Conditionally add "browser-sync" command
|
|
151
176
|
updatedScripts.browserSync = browserSyncCommand;
|
|
152
|
-
updatedScripts.
|
|
177
|
+
updatedScripts.npmRunAll =
|
|
153
178
|
answersToInclude.length > 0
|
|
154
179
|
? `npm-run-all -p ${answersToInclude.join(" ")}`
|
|
155
180
|
: 'echo "No additional scripts to run"';
|
|
156
|
-
updatedScripts.
|
|
157
|
-
updatedScripts.dev = `npm run startDev`;
|
|
181
|
+
updatedScripts.dev = `node settings/start-dev.js`;
|
|
158
182
|
// Finally, assign the updated scripts back to packageJson
|
|
159
183
|
packageJson.scripts = updatedScripts;
|
|
160
184
|
packageJson.type = "module";
|
|
@@ -285,11 +309,7 @@ function copyRecursiveSync(src, dest, answer) {
|
|
|
285
309
|
return;
|
|
286
310
|
}
|
|
287
311
|
if (!answer.backendOnly && dest.includes("route.php")) return;
|
|
288
|
-
if (
|
|
289
|
-
answer.backendOnly &&
|
|
290
|
-
!answer.swaggerDocs &&
|
|
291
|
-
(dest.includes("start-dev.js") || dest.includes("swagger-setup.js"))
|
|
292
|
-
) {
|
|
312
|
+
if (!answer.swaggerDocs && dest.includes("swagger-setup.js")) {
|
|
293
313
|
return;
|
|
294
314
|
}
|
|
295
315
|
fs.copyFileSync(src, dest, 0);
|
|
@@ -308,7 +328,7 @@ function createOrUpdateTailwindConfig(baseDir) {
|
|
|
308
328
|
const filePath = path.join(baseDir, "tailwind.config.js");
|
|
309
329
|
if (checkExcludeFiles(filePath)) return;
|
|
310
330
|
const newContent = [
|
|
311
|
-
"./src
|
|
331
|
+
"./src/**/*.{html,js,php}",
|
|
312
332
|
// Add more paths as needed
|
|
313
333
|
];
|
|
314
334
|
let configData = fs.readFileSync(filePath, "utf8");
|
|
@@ -488,7 +508,10 @@ SHOW_ERRORS=true
|
|
|
488
508
|
# CHATGPT_API_KEY=sk-your-api-key
|
|
489
509
|
|
|
490
510
|
# APP TIMEZONE - Set your application timezone - Default is "UTC"
|
|
491
|
-
APP_TIMEZONE="UTC"
|
|
511
|
+
APP_TIMEZONE="UTC"
|
|
512
|
+
|
|
513
|
+
# APP ENV - Set your application environment - Default is "development" - Change this in production to "production"
|
|
514
|
+
APP_ENV=development`;
|
|
492
515
|
if (answer.prisma) {
|
|
493
516
|
const prismaEnvContent = `# Environment variables declared in this file are automatically made available to Prisma.
|
|
494
517
|
# See the documentation for more detail: https://pris.ly/d/prisma-schema#accessing-environment-variables-from-the-schema
|
|
@@ -1019,6 +1042,16 @@ async function main() {
|
|
|
1019
1042
|
fs.rmSync(swaggerDocsFolder, { recursive: true, force: true }); // Use fs.rmSync instead of fs.rmdirSync
|
|
1020
1043
|
console.log(`swagger-docs was deleted successfully.`);
|
|
1021
1044
|
}
|
|
1045
|
+
const swaggerFiles = ["swagger-setup.js"];
|
|
1046
|
+
swaggerFiles.forEach((file) => {
|
|
1047
|
+
const filePath = path.join(projectPath, "settings", file);
|
|
1048
|
+
if (fs.existsSync(filePath)) {
|
|
1049
|
+
fs.unlinkSync(filePath); // Delete each file if it exists
|
|
1050
|
+
console.log(`${file} was deleted successfully.`);
|
|
1051
|
+
} else {
|
|
1052
|
+
console.log(`${file} does not exist.`);
|
|
1053
|
+
}
|
|
1054
|
+
});
|
|
1022
1055
|
updateUninstallDependencies.push("swagger-jsdoc");
|
|
1023
1056
|
}
|
|
1024
1057
|
if (!updateAnswer.tailwindcss) {
|
|
@@ -148,10 +148,10 @@ async function startDev() {
|
|
|
148
148
|
console.error(`Failed to start browserSync process: ${err.message}`);
|
|
149
149
|
});
|
|
150
150
|
|
|
151
|
-
// Function to start
|
|
151
|
+
// Function to start npmRunAll process
|
|
152
152
|
function startDevProcess() {
|
|
153
|
-
console.log("Starting
|
|
154
|
-
devProcess = spawn("npm", ["run", "
|
|
153
|
+
console.log("Starting npmRunAll...");
|
|
154
|
+
devProcess = spawn("npm", ["run", "npmRunAll"], { shell: true });
|
|
155
155
|
|
|
156
156
|
devProcess.stdout.on("data", (data) => {
|
|
157
157
|
process.stdout.write(data);
|
|
@@ -72,6 +72,19 @@ class Boom
|
|
|
72
72
|
return new self(401, $message, $details);
|
|
73
73
|
}
|
|
74
74
|
|
|
75
|
+
/**
|
|
76
|
+
* Factory method for 402 Payment Required.
|
|
77
|
+
*
|
|
78
|
+
* @param string $message Error message.
|
|
79
|
+
* @param array $details Additional error details.
|
|
80
|
+
*
|
|
81
|
+
* @return self
|
|
82
|
+
*/
|
|
83
|
+
public static function paymentRequired(string $message = 'Payment Required', array $details = []): self
|
|
84
|
+
{
|
|
85
|
+
return new self(402, $message, $details);
|
|
86
|
+
}
|
|
87
|
+
|
|
75
88
|
/**
|
|
76
89
|
* Factory method for 403 Forbidden.
|
|
77
90
|
*
|
|
@@ -98,6 +111,32 @@ class Boom
|
|
|
98
111
|
return new self(404, $message, $details);
|
|
99
112
|
}
|
|
100
113
|
|
|
114
|
+
/**
|
|
115
|
+
* Factory method for 405 Method Not Allowed.
|
|
116
|
+
*
|
|
117
|
+
* @param string $message Error message.
|
|
118
|
+
* @param array $details Additional error details.
|
|
119
|
+
*
|
|
120
|
+
* @return self
|
|
121
|
+
*/
|
|
122
|
+
public static function methodNotAllowed(string $message = 'Method Not Allowed', array $details = []): self
|
|
123
|
+
{
|
|
124
|
+
return new self(405, $message, $details);
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
/**
|
|
128
|
+
* Factory method for 406 Not Acceptable.
|
|
129
|
+
*
|
|
130
|
+
* @param string $message Error message.
|
|
131
|
+
* @param array $details Additional error details.
|
|
132
|
+
*
|
|
133
|
+
* @return self
|
|
134
|
+
*/
|
|
135
|
+
public static function notAcceptable(string $message = 'Not Acceptable', array $details = []): self
|
|
136
|
+
{
|
|
137
|
+
return new self(406, $message, $details);
|
|
138
|
+
}
|
|
139
|
+
|
|
101
140
|
/**
|
|
102
141
|
* Factory method for 500 Internal Server Error.
|
|
103
142
|
*
|
package/dist/src/app/layout.php
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
7
7
|
<meta name="pp-description" content="<?= htmlspecialchars($metadata['description']); ?>">
|
|
8
8
|
<title><?= htmlspecialchars($metadata['title']); ?></title>
|
|
9
|
-
<link rel="icon" href="<?= $baseUrl;
|
|
9
|
+
<link rel="icon" href="<?= $baseUrl; ?>/favicon.ico" type="image/x-icon">
|
|
10
10
|
</head>
|
|
11
11
|
|
|
12
12
|
<body>
|
package/dist/tailwind.config.js
CHANGED
package/dist/tsconfig.json
CHANGED
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
// "disableReferencedProjectLoad": true, /* Reduce the number of projects loaded automatically by TypeScript. */
|
|
12
12
|
|
|
13
13
|
/* Language and Environment */
|
|
14
|
-
"target": "
|
|
14
|
+
"target": "ESNext" /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */,
|
|
15
15
|
// "lib": [], /* Specify a set of bundled library declaration files that describe the target runtime environment. */
|
|
16
16
|
// "jsx": "preserve", /* Specify what JSX code is generated. */
|
|
17
17
|
// "experimentalDecorators": true, /* Enable experimental support for legacy experimental decorators. */
|
|
@@ -25,7 +25,7 @@
|
|
|
25
25
|
// "moduleDetection": "auto", /* Control what method is used to detect module-format JS files. */
|
|
26
26
|
|
|
27
27
|
/* Modules */
|
|
28
|
-
"module": "
|
|
28
|
+
"module": "NodeNext" /* Specify what module code is generated. */,
|
|
29
29
|
// "rootDir": "./", /* Specify the root folder within your source files. */
|
|
30
30
|
// "moduleResolution": "node10", /* Specify how TypeScript looks up a file from a given module specifier. */
|
|
31
31
|
// "baseUrl": "./", /* Specify the base directory to resolve non-relative module names. */
|
|
@@ -39,7 +39,7 @@
|
|
|
39
39
|
// "resolvePackageJsonExports": true, /* Use the package.json 'exports' field when resolving package imports. */
|
|
40
40
|
// "resolvePackageJsonImports": true, /* Use the package.json 'imports' field when resolving imports. */
|
|
41
41
|
// "customConditions": [], /* Conditions to set in addition to the resolver-specific defaults when resolving imports. */
|
|
42
|
-
|
|
42
|
+
"resolveJsonModule": true /* Enable importing .json files. */,
|
|
43
43
|
// "allowArbitraryExtensions": true, /* Enable importing files with any extension, provided a declaration file is present. */
|
|
44
44
|
// "noResolve": true, /* Disallow 'import's, 'require's or '<reference>'s from expanding the number of files TypeScript should add to a project. */
|
|
45
45
|
|
|
@@ -77,12 +77,12 @@
|
|
|
77
77
|
// "isolatedModules": true, /* Ensure that each file can be safely transpiled without relying on other imports. */
|
|
78
78
|
// "verbatimModuleSyntax": true, /* Do not transform or elide any imports or exports not marked as type-only, ensuring they are written in the output file's format based on the 'module' setting. */
|
|
79
79
|
// "allowSyntheticDefaultImports": true, /* Allow 'import x from y' when a module doesn't have a default export. */
|
|
80
|
-
"esModuleInterop": true
|
|
80
|
+
"esModuleInterop": true /* Emit additional JavaScript to ease support for importing CommonJS modules. This enables 'allowSyntheticDefaultImports' for type compatibility. */,
|
|
81
81
|
// "preserveSymlinks": true, /* Disable resolving symlinks to their realpath. This correlates to the same flag in node. */
|
|
82
|
-
"forceConsistentCasingInFileNames": true
|
|
82
|
+
"forceConsistentCasingInFileNames": true /* Ensure that casing is correct in imports. */,
|
|
83
83
|
|
|
84
84
|
/* Type Checking */
|
|
85
|
-
"strict": true
|
|
85
|
+
"strict": true /* Enable all strict type-checking options. */,
|
|
86
86
|
// "noImplicitAny": true, /* Enable error reporting for expressions and declarations with an implied 'any' type. */
|
|
87
87
|
// "strictNullChecks": true, /* When type checking, take into account 'null' and 'undefined'. */
|
|
88
88
|
// "strictFunctionTypes": true, /* When assigning functions, check to ensure parameters and the return values are subtype-compatible. */
|
|
@@ -104,8 +104,8 @@
|
|
|
104
104
|
|
|
105
105
|
/* Completeness */
|
|
106
106
|
// "skipDefaultLibCheck": true, /* Skip type checking .d.ts files that are included with TypeScript. */
|
|
107
|
-
"skipLibCheck": true
|
|
107
|
+
"skipLibCheck": true /* Skip type checking all .d.ts files. */
|
|
108
108
|
},
|
|
109
|
-
"include": ["src/**/*.ts"],
|
|
110
|
-
"exclude": ["node_modules"]
|
|
109
|
+
"include": ["src/**/*.ts", "settings/**/*.ts"],
|
|
110
|
+
"exclude": ["node_modules", "vendor"]
|
|
111
111
|
}
|