create-prisma-php-app 1.28.8 → 2.0.0-alpha.1
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/composer.json +8 -6
- package/dist/bootstrap.php +652 -682
- package/dist/index.js +50 -29
- package/dist/postcss.config.js +1 -3
- package/dist/prisma/seed.ts +2 -2
- package/dist/prisma-php.js +2 -42
- package/dist/settings/prisma-sdk.ts +4 -0
- package/dist/settings/project-name.ts +26 -2
- package/dist/src/Lib/AI/ChatGPTClient.php +32 -103
- package/dist/src/Lib/CacheHandler.php +121 -0
- package/dist/src/Lib/ErrorHandler.php +123 -0
- package/dist/src/Lib/MainLayout.php +23 -22
- package/dist/src/Lib/PHPX/TemplateCompiler.php +47 -24
- package/dist/src/Lib/PHPX/TwMerge.php +7 -1
- package/dist/src/Lib/Prisma/Classes/PPHPUtility.php +234 -105
- package/dist/src/Lib/Prisma/Model/IModel.php +15 -11
- package/dist/src/Lib/PrismaPHPSettings.php +44 -9
- package/dist/src/Lib/Request.php +1 -1
- package/dist/src/Lib/Validator.php +35 -7
- package/dist/src/app/css/tailwind.css +1 -3
- package/dist/src/app/index.php +2 -2
- package/dist/src/app/js/index.js +12 -1
- package/dist/src/app/layout.php +4 -7
- package/dist/src/app/not-found.php +1 -1
- package/package.json +1 -1
- package/tsconfig.json +5 -6
- package/dist/prisma-client-php/index.enc +0 -1
- package/dist/prisma-client-php/index.js +0 -19
- package/dist/prisma-client-php/key.enc +0 -1
- package/dist/tailwind.config.js +0 -8
package/dist/bootstrap.php
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
declare(strict_types=1);
|
|
4
4
|
|
|
5
|
-
if (session_status()
|
|
5
|
+
if (session_status() === PHP_SESSION_NONE) {
|
|
6
6
|
session_start();
|
|
7
7
|
}
|
|
8
8
|
|
|
@@ -17,793 +17,781 @@ use Lib\Middleware\AuthMiddleware;
|
|
|
17
17
|
use Lib\Auth\Auth;
|
|
18
18
|
use Lib\MainLayout;
|
|
19
19
|
use Lib\PHPX\TemplateCompiler;
|
|
20
|
+
use Lib\CacheHandler;
|
|
21
|
+
use Lib\ErrorHandler;
|
|
20
22
|
|
|
21
|
-
|
|
22
|
-
|
|
23
|
+
final class Bootstrap
|
|
24
|
+
{
|
|
25
|
+
public static string $contentToInclude = '';
|
|
26
|
+
public static array $layoutsToInclude = [];
|
|
27
|
+
public static string $requestFilePath = '';
|
|
28
|
+
public static string $parentLayoutPath = '';
|
|
29
|
+
public static bool $isParentLayout = false;
|
|
30
|
+
public static bool $isContentIncluded = false;
|
|
31
|
+
public static bool $isChildContentIncluded = false;
|
|
32
|
+
public static bool $isContentVariableIncluded = false;
|
|
33
|
+
public static bool $secondRequestC69CD = false;
|
|
34
|
+
public static array $requestFilesData = [];
|
|
35
|
+
|
|
36
|
+
private static array $fileExistCache = [];
|
|
37
|
+
private static array $regexCache = [];
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Main entry point to run the entire routing and rendering logic.
|
|
41
|
+
*/
|
|
42
|
+
public static function run(): void
|
|
43
|
+
{
|
|
44
|
+
// Load environment variables
|
|
45
|
+
Dotenv::createImmutable(DOCUMENT_PATH)->load();
|
|
23
46
|
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
StateManager::init();
|
|
47
|
+
// Set timezone
|
|
48
|
+
date_default_timezone_set($_ENV['APP_TIMEZONE'] ?? 'UTC');
|
|
27
49
|
|
|
28
|
-
|
|
50
|
+
// Initialize essential classes
|
|
51
|
+
PrismaPHPSettings::init();
|
|
52
|
+
Request::init();
|
|
53
|
+
StateManager::init();
|
|
29
54
|
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
* AuthMiddleware is invoked to handle authentication logic for the current route ($pathname).
|
|
60
|
-
* ================================================
|
|
61
|
-
*/
|
|
62
|
-
AuthMiddleware::handle($pathname);
|
|
63
|
-
/**
|
|
64
|
-
* ============ End of Middleware Management ======
|
|
65
|
-
* ================================================
|
|
66
|
-
*/
|
|
55
|
+
// Register custom handlers (exception, shutdown, error)
|
|
56
|
+
ErrorHandler::registerHandlers();
|
|
57
|
+
|
|
58
|
+
// Set a local store key as a cookie (before any output)
|
|
59
|
+
setcookie("pphp_local_store_key", PrismaPHPSettings::$localStoreKey, time() + 3600, "/", "", false, false);
|
|
60
|
+
|
|
61
|
+
$contentInfo = self::determineContentToInclude();
|
|
62
|
+
self::$contentToInclude = $contentInfo['path'] ?? '';
|
|
63
|
+
self::$layoutsToInclude = $contentInfo['layouts'] ?? [];
|
|
64
|
+
|
|
65
|
+
Request::$pathname = $contentInfo['pathname'] ? '/' . $contentInfo['pathname'] : '/';
|
|
66
|
+
Request::$uri = $contentInfo['uri'] ? $contentInfo['uri'] : '/';
|
|
67
|
+
|
|
68
|
+
if (is_file(self::$contentToInclude)) {
|
|
69
|
+
Request::$fileToInclude = basename(self::$contentToInclude);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
if (self::fileExistsCached(self::$contentToInclude)) {
|
|
73
|
+
Request::$fileToInclude = basename(self::$contentToInclude);
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
self::checkForDuplicateRoutes();
|
|
77
|
+
self::authenticateUserToken();
|
|
78
|
+
|
|
79
|
+
self::$requestFilePath = APP_PATH . Request::$pathname;
|
|
80
|
+
self::$parentLayoutPath = APP_PATH . '/layout.php';
|
|
81
|
+
|
|
82
|
+
self::$isParentLayout = !empty(self::$layoutsToInclude)
|
|
83
|
+
&& strpos(self::$layoutsToInclude[0], 'src/app/layout.php') !== false;
|
|
67
84
|
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
$serverFetchSite = $_SERVER['HTTP_SEC_FETCH_SITE'] ?? '';
|
|
72
|
-
if (isset($serverFetchSite) && $serverFetchSite === 'same-origin') {
|
|
73
|
-
$sameSiteFetch = true;
|
|
85
|
+
self::$isContentVariableIncluded = self::containsChildren(self::$parentLayoutPath);
|
|
86
|
+
if (!self::$isContentVariableIncluded) {
|
|
87
|
+
self::$isContentIncluded = true;
|
|
74
88
|
}
|
|
75
89
|
|
|
76
|
-
|
|
77
|
-
|
|
90
|
+
self::$secondRequestC69CD = Request::$data['secondRequestC69CD'] ?? false;
|
|
91
|
+
self::$requestFilesData = PrismaPHPSettings::$includeFiles;
|
|
92
|
+
|
|
93
|
+
// Detect any fatal error that might have occurred before hitting this point
|
|
94
|
+
ErrorHandler::checkFatalError();
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
private static function fileExistsCached(string $path): bool
|
|
98
|
+
{
|
|
99
|
+
if (!isset(self::$fileExistCache[$path])) {
|
|
100
|
+
self::$fileExistCache[$path] = file_exists($path);
|
|
78
101
|
}
|
|
102
|
+
return self::$fileExistCache[$path];
|
|
79
103
|
}
|
|
80
104
|
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
105
|
+
private static function pregMatchCached(string $pattern, string $subject): bool
|
|
106
|
+
{
|
|
107
|
+
$cacheKey = md5($pattern . $subject);
|
|
108
|
+
if (!isset(self::$regexCache[$cacheKey])) {
|
|
109
|
+
self::$regexCache[$cacheKey] = preg_match($pattern, $subject) === 1;
|
|
110
|
+
}
|
|
111
|
+
return self::$regexCache[$cacheKey];
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
private static function determineContentToInclude(): array
|
|
115
|
+
{
|
|
116
|
+
$requestUri = $_SERVER['REQUEST_URI'];
|
|
117
|
+
$requestUri = empty($_SERVER['SCRIPT_URL']) ? trim(self::uriExtractor($requestUri)) : trim($requestUri);
|
|
118
|
+
|
|
119
|
+
// Without query params
|
|
120
|
+
$scriptUrl = explode('?', $requestUri, 2)[0];
|
|
121
|
+
$pathname = $_SERVER['SCRIPT_URL'] ?? $scriptUrl;
|
|
122
|
+
$pathname = trim($pathname, '/');
|
|
123
|
+
$baseDir = APP_PATH;
|
|
124
|
+
$includePath = '';
|
|
125
|
+
$layoutsToInclude = [];
|
|
126
|
+
|
|
127
|
+
/**
|
|
128
|
+
* ============ Middleware Management ============
|
|
129
|
+
* AuthMiddleware is invoked to handle authentication logic for the current route ($pathname).
|
|
130
|
+
* ================================================
|
|
131
|
+
*/
|
|
132
|
+
AuthMiddleware::handle($pathname);
|
|
133
|
+
/**
|
|
134
|
+
* ============ End of Middleware Management ======
|
|
135
|
+
* ================================================
|
|
136
|
+
*/
|
|
137
|
+
|
|
138
|
+
// e.g., avoid direct access to _private files
|
|
139
|
+
$isDirectAccessToPrivateRoute = preg_match('/_/', $pathname);
|
|
140
|
+
if ($isDirectAccessToPrivateRoute) {
|
|
141
|
+
$sameSiteFetch = false;
|
|
142
|
+
$serverFetchSite = $_SERVER['HTTP_SEC_FETCH_SITE'] ?? '';
|
|
143
|
+
if (isset($serverFetchSite) && $serverFetchSite === 'same-origin') {
|
|
144
|
+
$sameSiteFetch = true;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
if (!$sameSiteFetch) {
|
|
148
|
+
return [
|
|
149
|
+
'path' => $includePath,
|
|
150
|
+
'layouts' => $layoutsToInclude,
|
|
151
|
+
'pathname' => $pathname,
|
|
152
|
+
'uri' => $requestUri
|
|
153
|
+
];
|
|
87
154
|
}
|
|
88
155
|
}
|
|
89
156
|
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
157
|
+
// Find matching route
|
|
158
|
+
if ($pathname) {
|
|
159
|
+
$groupFolder = self::findGroupFolder($pathname);
|
|
160
|
+
if ($groupFolder) {
|
|
161
|
+
$path = __DIR__ . $groupFolder;
|
|
162
|
+
if (self::fileExistsCached($path)) {
|
|
95
163
|
$includePath = $path;
|
|
96
164
|
}
|
|
97
165
|
}
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
$currentPath = $baseDir;
|
|
101
|
-
$getGroupFolder = getGroupFolder($groupFolder);
|
|
102
|
-
$modifiedPathname = $pathname;
|
|
103
|
-
if (!empty($getGroupFolder)) {
|
|
104
|
-
$modifiedPathname = trim($getGroupFolder, "/src/app/");
|
|
105
|
-
}
|
|
106
166
|
|
|
107
|
-
|
|
108
|
-
|
|
167
|
+
if (empty($includePath)) {
|
|
168
|
+
$dynamicRoute = self::dynamicRoute($pathname);
|
|
169
|
+
if ($dynamicRoute) {
|
|
170
|
+
$path = __DIR__ . $dynamicRoute;
|
|
171
|
+
if (self::fileExistsCached($path)) {
|
|
172
|
+
$includePath = $path;
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
}
|
|
109
176
|
|
|
110
|
-
|
|
111
|
-
$
|
|
112
|
-
|
|
113
|
-
|
|
177
|
+
// Check for layout hierarchy
|
|
178
|
+
$currentPath = $baseDir;
|
|
179
|
+
$getGroupFolder = self::getGroupFolder($groupFolder);
|
|
180
|
+
$modifiedPathname = $pathname;
|
|
181
|
+
if (!empty($getGroupFolder)) {
|
|
182
|
+
$modifiedPathname = trim($getGroupFolder, "/src/app/");
|
|
114
183
|
}
|
|
115
|
-
}
|
|
116
184
|
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
185
|
+
foreach (explode('/', $modifiedPathname) as $segment) {
|
|
186
|
+
if (empty($segment)) {
|
|
187
|
+
continue;
|
|
188
|
+
}
|
|
121
189
|
|
|
122
|
-
|
|
190
|
+
$currentPath .= '/' . $segment;
|
|
191
|
+
$potentialLayoutPath = $currentPath . '/layout.php';
|
|
192
|
+
if (self::fileExistsCached($potentialLayoutPath) && !in_array($potentialLayoutPath, $layoutsToInclude, true)) {
|
|
193
|
+
$layoutsToInclude[] = $potentialLayoutPath;
|
|
194
|
+
}
|
|
195
|
+
}
|
|
123
196
|
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
197
|
+
// If it was a dynamic route, we also check for any relevant layout
|
|
198
|
+
if (isset($dynamicRoute) && !empty($dynamicRoute)) {
|
|
199
|
+
$currentDynamicPath = $baseDir;
|
|
200
|
+
foreach (explode('/', $dynamicRoute) as $segment) {
|
|
201
|
+
if (empty($segment)) {
|
|
202
|
+
continue;
|
|
203
|
+
}
|
|
204
|
+
if ($segment === 'src' || $segment === 'app') {
|
|
205
|
+
continue;
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
$currentDynamicPath .= '/' . $segment;
|
|
209
|
+
$potentialDynamicRoute = $currentDynamicPath . '/layout.php';
|
|
210
|
+
if (self::fileExistsCached($potentialDynamicRoute) && !in_array($potentialDynamicRoute, $layoutsToInclude, true)) {
|
|
211
|
+
$layoutsToInclude[] = $potentialDynamicRoute;
|
|
212
|
+
}
|
|
128
213
|
}
|
|
129
214
|
}
|
|
130
|
-
}
|
|
131
215
|
|
|
132
|
-
|
|
133
|
-
$layoutsToInclude
|
|
216
|
+
// If still no layout, fallback to the app-level layout.php
|
|
217
|
+
if (empty($layoutsToInclude)) {
|
|
218
|
+
$layoutsToInclude[] = $baseDir . '/layout.php';
|
|
219
|
+
}
|
|
220
|
+
} else {
|
|
221
|
+
// If path is empty, we’re basically at "/"
|
|
222
|
+
$includePath = $baseDir . self::getFilePrecedence();
|
|
134
223
|
}
|
|
135
|
-
} else {
|
|
136
|
-
$includePath = $baseDir . getFilePrecedence();
|
|
137
|
-
}
|
|
138
224
|
|
|
139
|
-
|
|
140
|
-
|
|
225
|
+
return [
|
|
226
|
+
'path' => $includePath,
|
|
227
|
+
'layouts' => $layoutsToInclude,
|
|
228
|
+
'pathname' => $pathname,
|
|
229
|
+
'uri' => $requestUri
|
|
230
|
+
];
|
|
231
|
+
}
|
|
141
232
|
|
|
142
|
-
function getFilePrecedence()
|
|
143
|
-
{
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
233
|
+
private static function getFilePrecedence(): ?string
|
|
234
|
+
{
|
|
235
|
+
foreach (PrismaPHPSettings::$routeFiles as $route) {
|
|
236
|
+
if (pathinfo($route, PATHINFO_EXTENSION) !== 'php') {
|
|
237
|
+
continue;
|
|
238
|
+
}
|
|
239
|
+
if (preg_match('/^\.\/src\/app\/route\.php$/', $route)) {
|
|
240
|
+
return '/route.php';
|
|
241
|
+
}
|
|
242
|
+
if (preg_match('/^\.\/src\/app\/index\.php$/', $route)) {
|
|
243
|
+
return '/index.php';
|
|
244
|
+
}
|
|
148
245
|
}
|
|
246
|
+
return null;
|
|
247
|
+
}
|
|
149
248
|
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
249
|
+
private static function uriExtractor(string $scriptUrl): string
|
|
250
|
+
{
|
|
251
|
+
$projectName = PrismaPHPSettings::$option->projectName ?? '';
|
|
252
|
+
if (empty($projectName)) {
|
|
253
|
+
return "/";
|
|
153
254
|
}
|
|
154
255
|
|
|
155
|
-
|
|
156
|
-
if (preg_match(
|
|
157
|
-
return '/
|
|
256
|
+
$escapedIdentifier = preg_quote($projectName, '/');
|
|
257
|
+
if (preg_match("/(?:.*$escapedIdentifier)(\/.*)$/", $scriptUrl, $matches) && !empty($matches[1])) {
|
|
258
|
+
return rtrim(ltrim($matches[1], '/'), '/');
|
|
158
259
|
}
|
|
159
|
-
}
|
|
160
|
-
|
|
161
|
-
// If neither file is found, return null
|
|
162
|
-
return null;
|
|
163
|
-
}
|
|
164
260
|
|
|
165
|
-
function uriExtractor(string $scriptUrl): string
|
|
166
|
-
{
|
|
167
|
-
$projectName = PrismaPHPSettings::$option->projectName ?? '';
|
|
168
|
-
if (empty($projectName)) {
|
|
169
261
|
return "/";
|
|
170
262
|
}
|
|
171
263
|
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
return "/";
|
|
178
|
-
}
|
|
179
|
-
|
|
180
|
-
function findGroupFolder($pathname): string
|
|
181
|
-
{
|
|
182
|
-
$pathnameSegments = explode('/', $pathname);
|
|
183
|
-
foreach ($pathnameSegments as $segment) {
|
|
184
|
-
if (!empty($segment)) {
|
|
185
|
-
if (isGroupIdentifier($segment)) {
|
|
264
|
+
private static function findGroupFolder(string $pathname): string
|
|
265
|
+
{
|
|
266
|
+
$pathnameSegments = explode('/', $pathname);
|
|
267
|
+
foreach ($pathnameSegments as $segment) {
|
|
268
|
+
if (!empty($segment) && self::pregMatchCached('/^\(.*\)$/', $segment)) {
|
|
186
269
|
return $segment;
|
|
187
270
|
}
|
|
188
271
|
}
|
|
189
|
-
}
|
|
190
272
|
|
|
191
|
-
|
|
192
|
-
if ($matchedGroupFolder) {
|
|
193
|
-
return $matchedGroupFolder;
|
|
194
|
-
} else {
|
|
195
|
-
return '';
|
|
273
|
+
return self::matchGroupFolder($pathname) ?: '';
|
|
196
274
|
}
|
|
197
|
-
}
|
|
198
275
|
|
|
199
|
-
function dynamicRoute($pathname)
|
|
200
|
-
{
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
276
|
+
private static function dynamicRoute($pathname)
|
|
277
|
+
{
|
|
278
|
+
$pathnameMatch = null;
|
|
279
|
+
$normalizedPathname = ltrim(str_replace('\\', '/', $pathname), './');
|
|
280
|
+
$normalizedPathnameEdited = "src/app/$normalizedPathname";
|
|
281
|
+
$pathnameSegments = explode('/', $normalizedPathnameEdited);
|
|
205
282
|
|
|
206
|
-
|
|
207
|
-
|
|
283
|
+
foreach (PrismaPHPSettings::$routeFiles as $route) {
|
|
284
|
+
$normalizedRoute = trim(str_replace('\\', '/', $route), '.');
|
|
208
285
|
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
}
|
|
286
|
+
if (pathinfo($normalizedRoute, PATHINFO_EXTENSION) !== 'php') {
|
|
287
|
+
continue;
|
|
288
|
+
}
|
|
213
289
|
|
|
214
|
-
|
|
290
|
+
$routeSegments = explode('/', ltrim($normalizedRoute, '/'));
|
|
215
291
|
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
292
|
+
$filteredRouteSegments = array_values(array_filter($routeSegments, function ($segment) {
|
|
293
|
+
return !preg_match('/\(.+\)/', $segment);
|
|
294
|
+
}));
|
|
219
295
|
|
|
220
|
-
|
|
296
|
+
$singleDynamic = (preg_match_all('/\[[^\]]+\]/', $normalizedRoute, $matches) === 1)
|
|
297
|
+
&& strpos($normalizedRoute, '[...') === false;
|
|
298
|
+
$routeCount = count($filteredRouteSegments);
|
|
299
|
+
if (in_array(end($filteredRouteSegments), ['index.php', 'route.php'])) {
|
|
300
|
+
$expectedSegmentCount = $routeCount - 1;
|
|
301
|
+
} else {
|
|
302
|
+
$expectedSegmentCount = $routeCount;
|
|
303
|
+
}
|
|
221
304
|
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
305
|
+
if ($singleDynamic) {
|
|
306
|
+
if (count($pathnameSegments) !== $expectedSegmentCount) {
|
|
307
|
+
continue;
|
|
308
|
+
}
|
|
225
309
|
|
|
226
|
-
|
|
227
|
-
$
|
|
228
|
-
Request::$dynamicParams = new \ArrayObject([$trimSegmentMatch => $pathnameSegments[$index]], \ArrayObject::ARRAY_AS_PROPS);
|
|
310
|
+
$segmentMatch = self::singleDynamicRoute($pathnameSegments, $filteredRouteSegments);
|
|
311
|
+
$index = array_search($segmentMatch, $filteredRouteSegments);
|
|
229
312
|
|
|
230
|
-
$
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
313
|
+
if ($index !== false && isset($pathnameSegments[$index])) {
|
|
314
|
+
$trimSegmentMatch = trim($segmentMatch, '[]');
|
|
315
|
+
Request::$dynamicParams = new ArrayObject(
|
|
316
|
+
[$trimSegmentMatch => $pathnameSegments[$index]],
|
|
317
|
+
ArrayObject::ARRAY_AS_PROPS
|
|
318
|
+
);
|
|
234
319
|
|
|
235
|
-
|
|
320
|
+
$dynamicRoutePathname = str_replace($segmentMatch, $pathnameSegments[$index], $normalizedRoute);
|
|
321
|
+
$dynamicRoutePathname = preg_replace('/\(.+\)/', '', $dynamicRoutePathname);
|
|
322
|
+
$dynamicRoutePathname = preg_replace('/\/+/', '/', $dynamicRoutePathname);
|
|
323
|
+
$dynamicRoutePathnameDirname = rtrim(dirname($dynamicRoutePathname), '/');
|
|
236
324
|
|
|
237
|
-
|
|
238
|
-
|
|
325
|
+
$expectedPathname = rtrim('/src/app/' . $normalizedPathname, '/');
|
|
326
|
+
|
|
327
|
+
if ((strpos($normalizedRoute, 'route.php') !== false || strpos($normalizedRoute, 'index.php') !== false)
|
|
328
|
+
&& $expectedPathname === $dynamicRoutePathnameDirname
|
|
329
|
+
) {
|
|
239
330
|
$pathnameMatch = $normalizedRoute;
|
|
240
331
|
break;
|
|
241
332
|
}
|
|
242
333
|
}
|
|
243
|
-
}
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
$cleanedNormalizedRoute = preg_replace('/\(.+\)/', '', $normalizedRoute);
|
|
247
|
-
$cleanedNormalizedRoute = preg_replace('/\/+/', '/', $cleanedNormalizedRoute);
|
|
248
|
-
$dynamicSegmentRoute = preg_replace('/\[\.\.\..*?\].*/', '', $cleanedNormalizedRoute);
|
|
249
|
-
|
|
250
|
-
// Check if the normalized pathname starts with the cleaned route
|
|
251
|
-
if (strpos("/src/app/$normalizedPathname", $dynamicSegmentRoute) === 0) {
|
|
252
|
-
$trimmedPathname = str_replace($dynamicSegmentRoute, '', "/src/app/$normalizedPathname");
|
|
253
|
-
$pathnameParts = explode('/', trim($trimmedPathname, '/'));
|
|
254
|
-
|
|
255
|
-
// Extract the dynamic segment content
|
|
256
|
-
if (preg_match('/\[\.\.\.(.*?)\]/', $normalizedRoute, $matches)) {
|
|
257
|
-
$dynamicParam = $matches[1];
|
|
258
|
-
Request::$dynamicParams = new \ArrayObject([$dynamicParam => $pathnameParts], \ArrayObject::ARRAY_AS_PROPS);
|
|
334
|
+
} elseif (strpos($normalizedRoute, '[...') !== false) {
|
|
335
|
+
if (count($pathnameSegments) <= $expectedSegmentCount) {
|
|
336
|
+
continue;
|
|
259
337
|
}
|
|
260
338
|
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
339
|
+
$cleanedNormalizedRoute = preg_replace('/\(.+\)/', '', $normalizedRoute);
|
|
340
|
+
$cleanedNormalizedRoute = preg_replace('/\/+/', '/', $cleanedNormalizedRoute);
|
|
341
|
+
$dynamicSegmentRoute = preg_replace('/\[\.\.\..*?\].*/', '', $cleanedNormalizedRoute);
|
|
342
|
+
|
|
343
|
+
if (strpos("/src/app/$normalizedPathname", $dynamicSegmentRoute) === 0) {
|
|
344
|
+
$trimmedPathname = str_replace($dynamicSegmentRoute, '', "/src/app/$normalizedPathname");
|
|
345
|
+
$pathnameParts = explode('/', trim($trimmedPathname, '/'));
|
|
346
|
+
|
|
347
|
+
if (preg_match('/\[\.\.\.(.*?)\]/', $normalizedRoute, $matches)) {
|
|
348
|
+
$dynamicParam = $matches[1];
|
|
349
|
+
Request::$dynamicParams = new ArrayObject(
|
|
350
|
+
[$dynamicParam => $pathnameParts],
|
|
351
|
+
ArrayObject::ARRAY_AS_PROPS
|
|
352
|
+
);
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
if (strpos($normalizedRoute, 'route.php') !== false) {
|
|
356
|
+
$pathnameMatch = $normalizedRoute;
|
|
357
|
+
break;
|
|
358
|
+
}
|
|
266
359
|
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
$index = array_search($segmentMatch, $filteredRouteSegments);
|
|
360
|
+
if (strpos($normalizedRoute, 'index.php') !== false) {
|
|
361
|
+
$segmentMatch = "[...$dynamicParam]";
|
|
362
|
+
$index = array_search($segmentMatch, $filteredRouteSegments);
|
|
271
363
|
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
$dynamicRoutePathnameDirname = rtrim(dirname($dynamicRoutePathname), '/');
|
|
364
|
+
if ($index !== false && isset($pathnameSegments[$index])) {
|
|
365
|
+
$dynamicRoutePathname = str_replace($segmentMatch, implode('/', $pathnameParts), $cleanedNormalizedRoute);
|
|
366
|
+
$dynamicRoutePathnameDirname = rtrim(dirname($dynamicRoutePathname), '/');
|
|
276
367
|
|
|
277
|
-
|
|
368
|
+
$expectedPathname = rtrim("/src/app/$normalizedPathname", '/');
|
|
278
369
|
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
370
|
+
if ($expectedPathname === $dynamicRoutePathnameDirname) {
|
|
371
|
+
$pathnameMatch = $normalizedRoute;
|
|
372
|
+
break;
|
|
373
|
+
}
|
|
283
374
|
}
|
|
284
375
|
}
|
|
285
376
|
}
|
|
286
377
|
}
|
|
287
378
|
}
|
|
288
|
-
}
|
|
289
|
-
|
|
290
|
-
return $pathnameMatch;
|
|
291
|
-
}
|
|
292
379
|
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
return (bool)preg_match('/^\(.*\)$/', $segment);
|
|
296
|
-
}
|
|
297
|
-
|
|
298
|
-
function matchGroupFolder($constructedPath): ?string
|
|
299
|
-
{
|
|
300
|
-
$bestMatch = null;
|
|
301
|
-
$normalizedConstructedPath = ltrim(str_replace('\\', '/', $constructedPath), './');
|
|
380
|
+
return $pathnameMatch;
|
|
381
|
+
}
|
|
302
382
|
|
|
303
|
-
$
|
|
304
|
-
|
|
383
|
+
private static function matchGroupFolder(string $constructedPath): ?string
|
|
384
|
+
{
|
|
385
|
+
$bestMatch = null;
|
|
386
|
+
$normalizedConstructedPath = ltrim(str_replace('\\', '/', $constructedPath), './');
|
|
387
|
+
$routeFile = "/src/app/$normalizedConstructedPath/route.php";
|
|
388
|
+
$indexFile = "/src/app/$normalizedConstructedPath/index.php";
|
|
305
389
|
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
390
|
+
foreach (PrismaPHPSettings::$routeFiles as $route) {
|
|
391
|
+
if (pathinfo($route, PATHINFO_EXTENSION) !== 'php') {
|
|
392
|
+
continue;
|
|
393
|
+
}
|
|
394
|
+
$normalizedRoute = trim(str_replace('\\', '/', $route), '.');
|
|
395
|
+
$cleanedRoute = preg_replace('/\/\([^)]+\)/', '', $normalizedRoute);
|
|
396
|
+
|
|
397
|
+
if ($cleanedRoute === $routeFile) {
|
|
398
|
+
$bestMatch = $normalizedRoute;
|
|
399
|
+
break;
|
|
400
|
+
} elseif ($cleanedRoute === $indexFile && !$bestMatch) {
|
|
401
|
+
$bestMatch = $normalizedRoute;
|
|
402
|
+
}
|
|
309
403
|
}
|
|
310
404
|
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
$cleanedRoute = preg_replace('/\/\([^)]+\)/', '', $normalizedRoute);
|
|
314
|
-
if ($cleanedRoute === $routeFile) {
|
|
315
|
-
$bestMatch = $normalizedRoute;
|
|
316
|
-
break;
|
|
317
|
-
} elseif ($cleanedRoute === $indexFile && !$bestMatch) {
|
|
318
|
-
$bestMatch = $normalizedRoute;
|
|
319
|
-
}
|
|
405
|
+
return $bestMatch;
|
|
320
406
|
}
|
|
321
407
|
|
|
322
|
-
|
|
323
|
-
|
|
408
|
+
private static function getGroupFolder($pathname): string
|
|
409
|
+
{
|
|
410
|
+
$lastSlashPos = strrpos($pathname, '/');
|
|
411
|
+
if ($lastSlashPos === false) {
|
|
412
|
+
return "";
|
|
413
|
+
}
|
|
324
414
|
|
|
325
|
-
|
|
326
|
-
{
|
|
327
|
-
|
|
415
|
+
$pathWithoutFile = substr($pathname, 0, $lastSlashPos);
|
|
416
|
+
if (preg_match('/\(([^)]+)\)[^()]*$/', $pathWithoutFile, $matches)) {
|
|
417
|
+
return $pathWithoutFile;
|
|
418
|
+
}
|
|
328
419
|
|
|
329
|
-
if ($lastSlashPos === false) {
|
|
330
420
|
return "";
|
|
331
421
|
}
|
|
332
422
|
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
{
|
|
344
|
-
$segmentMatch = "";
|
|
345
|
-
foreach ($routeSegments as $index => $segment) {
|
|
346
|
-
if (preg_match('/^\[[^\]]+\]$/', $segment)) {
|
|
347
|
-
return "{$segment}";
|
|
348
|
-
} else {
|
|
349
|
-
if ($segment !== $pathnameSegments[$index]) {
|
|
350
|
-
return $segmentMatch;
|
|
423
|
+
private static function singleDynamicRoute($pathnameSegments, $routeSegments)
|
|
424
|
+
{
|
|
425
|
+
$segmentMatch = "";
|
|
426
|
+
foreach ($routeSegments as $index => $segment) {
|
|
427
|
+
if (preg_match('/^\[[^\]]+\]$/', $segment)) {
|
|
428
|
+
return $segment;
|
|
429
|
+
} else {
|
|
430
|
+
if (!isset($pathnameSegments[$index]) || $segment !== $pathnameSegments[$index]) {
|
|
431
|
+
return $segmentMatch;
|
|
432
|
+
}
|
|
351
433
|
}
|
|
352
434
|
}
|
|
435
|
+
return $segmentMatch;
|
|
353
436
|
}
|
|
354
|
-
return $segmentMatch;
|
|
355
|
-
}
|
|
356
437
|
|
|
357
|
-
function checkForDuplicateRoutes()
|
|
358
|
-
{
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
foreach (PrismaPHPSettings::$routeFiles as $route) {
|
|
363
|
-
if (pathinfo($route, PATHINFO_EXTENSION) !== 'php') {
|
|
364
|
-
continue;
|
|
438
|
+
private static function checkForDuplicateRoutes(): void
|
|
439
|
+
{
|
|
440
|
+
// Skip checks in production
|
|
441
|
+
if (isset($_ENV['APP_ENV']) && $_ENV['APP_ENV'] === 'production') {
|
|
442
|
+
return;
|
|
365
443
|
}
|
|
366
444
|
|
|
367
|
-
$
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
$normalizedRoutesMap[$routeNormalized][] = $route;
|
|
373
|
-
}
|
|
445
|
+
$normalizedRoutesMap = [];
|
|
446
|
+
foreach (PrismaPHPSettings::$routeFiles as $route) {
|
|
447
|
+
if (pathinfo($route, PATHINFO_EXTENSION) !== 'php') {
|
|
448
|
+
continue;
|
|
449
|
+
}
|
|
374
450
|
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
451
|
+
$routeWithoutGroups = preg_replace('/\(.*?\)/', '', $route);
|
|
452
|
+
$routeTrimmed = ltrim($routeWithoutGroups, '.\\/');
|
|
453
|
+
$routeTrimmed = preg_replace('#/{2,}#', '/', $routeTrimmed);
|
|
454
|
+
$routeTrimmed = preg_replace('#\\\\{2,}#', '\\', $routeTrimmed);
|
|
455
|
+
$routeNormalized = str_replace(['\\', '/'], DIRECTORY_SEPARATOR, $routeTrimmed);
|
|
379
456
|
|
|
380
|
-
|
|
381
|
-
|
|
457
|
+
$normalizedRoutesMap[$routeNormalized][] = $route;
|
|
458
|
+
}
|
|
382
459
|
|
|
383
|
-
|
|
460
|
+
$errorMessages = [];
|
|
461
|
+
foreach ($normalizedRoutesMap as $normalizedRoute => $originalRoutes) {
|
|
462
|
+
$basename = basename($normalizedRoute);
|
|
463
|
+
if ($basename === 'layout.php') {
|
|
464
|
+
continue;
|
|
465
|
+
}
|
|
384
466
|
|
|
385
|
-
|
|
386
|
-
$
|
|
467
|
+
if (
|
|
468
|
+
count($originalRoutes) > 1 &&
|
|
469
|
+
strpos($normalizedRoute, DIRECTORY_SEPARATOR) !== false
|
|
470
|
+
) {
|
|
471
|
+
if ($basename !== 'route.php' && $basename !== 'index.php') {
|
|
472
|
+
continue;
|
|
473
|
+
}
|
|
474
|
+
$errorMessages[] = "Duplicate route found after normalization: " . $normalizedRoute;
|
|
475
|
+
foreach ($originalRoutes as $originalRoute) {
|
|
476
|
+
$errorMessages[] = "- Grouped original route: " . $originalRoute;
|
|
477
|
+
}
|
|
387
478
|
}
|
|
388
479
|
}
|
|
389
|
-
}
|
|
390
480
|
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
481
|
+
if (!empty($errorMessages)) {
|
|
482
|
+
$errorMessageString = self::isAjaxOrXFileRequestOrRouteFile()
|
|
483
|
+
? implode("\n", $errorMessages)
|
|
484
|
+
: implode("<br>", $errorMessages);
|
|
485
|
+
|
|
486
|
+
ErrorHandler::modifyOutputLayoutForError($errorMessageString);
|
|
396
487
|
}
|
|
397
|
-
modifyOutputLayoutForError($errorMessageString);
|
|
398
488
|
}
|
|
399
|
-
}
|
|
400
|
-
|
|
401
|
-
function containsChildLayoutChildren($filePath)
|
|
402
|
-
{
|
|
403
|
-
$fileContent = file_get_contents($filePath);
|
|
404
489
|
|
|
405
|
-
|
|
406
|
-
|
|
490
|
+
public static function containsChildLayoutChildren($filePath): bool
|
|
491
|
+
{
|
|
492
|
+
if (!self::fileExistsCached($filePath)) {
|
|
493
|
+
return false;
|
|
494
|
+
}
|
|
407
495
|
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
496
|
+
$fileContent = @file_get_contents($filePath);
|
|
497
|
+
if ($fileContent === false) {
|
|
498
|
+
return false;
|
|
499
|
+
}
|
|
411
500
|
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
501
|
+
// Check usage of MainLayout::$childLayoutChildren
|
|
502
|
+
$pattern = '/\<\?=\s*MainLayout::\$childLayoutChildren\s*;?\s*\?>|echo\s*MainLayout::\$childLayoutChildren\s*;?/';
|
|
503
|
+
return (bool) preg_match($pattern, $fileContent);
|
|
504
|
+
}
|
|
415
505
|
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
}
|
|
506
|
+
private static function containsChildren($filePath): bool
|
|
507
|
+
{
|
|
508
|
+
if (!self::fileExistsCached($filePath)) {
|
|
509
|
+
return false;
|
|
510
|
+
}
|
|
421
511
|
|
|
422
|
-
|
|
423
|
-
{
|
|
424
|
-
|
|
425
|
-
$arrayObject = new \ArrayObject([], \ArrayObject::ARRAY_AS_PROPS);
|
|
426
|
-
foreach ($data as $key => $value) {
|
|
427
|
-
$arrayObject[$key] = convertToArrayObject($value);
|
|
512
|
+
$fileContent = @file_get_contents($filePath);
|
|
513
|
+
if ($fileContent === false) {
|
|
514
|
+
return false;
|
|
428
515
|
}
|
|
429
|
-
return $arrayObject;
|
|
430
|
-
}
|
|
431
|
-
return $data;
|
|
432
|
-
}
|
|
433
516
|
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
$response = [
|
|
439
|
-
'success' => false,
|
|
440
|
-
'error' => 'Callback not provided',
|
|
441
|
-
'data' => null
|
|
442
|
-
];
|
|
517
|
+
// Check usage of MainLayout::$children
|
|
518
|
+
$pattern = '/\<\?=\s*MainLayout::\$children\s*;?\s*\?>|echo\s*MainLayout::\$children\s*;?/';
|
|
519
|
+
return (bool) preg_match($pattern, $fileContent);
|
|
520
|
+
}
|
|
443
521
|
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
$hasFile = isset($_FILES['file']) && !empty($_FILES['file']['name'][0]);
|
|
449
|
-
|
|
450
|
-
// Process form data
|
|
451
|
-
if ($hasFile) {
|
|
452
|
-
// Handle file upload, including multiple files
|
|
453
|
-
$data = $_POST; // Form data will be available in $_POST
|
|
454
|
-
|
|
455
|
-
if (is_array($_FILES['file']['name'])) {
|
|
456
|
-
// Multiple files uploaded
|
|
457
|
-
$files = [];
|
|
458
|
-
foreach ($_FILES['file']['name'] as $index => $name) {
|
|
459
|
-
$files[] = [
|
|
460
|
-
'name' => $name,
|
|
461
|
-
'type' => $_FILES['file']['type'][$index],
|
|
462
|
-
'tmp_name' => $_FILES['file']['tmp_name'][$index],
|
|
463
|
-
'error' => $_FILES['file']['error'][$index],
|
|
464
|
-
'size' => $_FILES['file']['size'][$index],
|
|
465
|
-
];
|
|
466
|
-
}
|
|
467
|
-
$data['files'] = $files;
|
|
468
|
-
} else {
|
|
469
|
-
// Single file uploaded
|
|
470
|
-
$data['file'] = $_FILES['file']; // Attach single file information to data
|
|
471
|
-
}
|
|
472
|
-
} else {
|
|
473
|
-
// Handle non-file form data (likely JSON)
|
|
474
|
-
$input = file_get_contents('php://input');
|
|
475
|
-
$data = json_decode($input, true);
|
|
522
|
+
private static function convertToArrayObject($data)
|
|
523
|
+
{
|
|
524
|
+
return is_array($data) ? (object) $data : $data;
|
|
525
|
+
}
|
|
476
526
|
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
527
|
+
/**
|
|
528
|
+
* Used specifically for wire (AJAX) calls.
|
|
529
|
+
* Ends execution with JSON response.
|
|
530
|
+
*/
|
|
531
|
+
public static function wireCallback()
|
|
532
|
+
{
|
|
533
|
+
try {
|
|
534
|
+
// Initialize response
|
|
535
|
+
$response = [
|
|
536
|
+
'success' => false,
|
|
537
|
+
'error' => 'Callback not provided',
|
|
538
|
+
'data' => null
|
|
539
|
+
];
|
|
482
540
|
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
// Sanitize and create a dynamic function name
|
|
486
|
-
$callbackName = preg_replace('/[^a-zA-Z0-9_]/', '', $data['callback']); // Sanitize the callback name
|
|
541
|
+
$callbackResponse = null;
|
|
542
|
+
$data = [];
|
|
487
543
|
|
|
488
|
-
// Check if the
|
|
489
|
-
|
|
490
|
-
$dataObject = convertToArrayObject($data);
|
|
544
|
+
// Check if the request includes one or more files
|
|
545
|
+
$hasFile = isset($_FILES['file']) && !empty($_FILES['file']['name'][0]);
|
|
491
546
|
|
|
492
|
-
|
|
493
|
-
|
|
547
|
+
// Process form data
|
|
548
|
+
if ($hasFile) {
|
|
549
|
+
$data = $_POST;
|
|
494
550
|
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
551
|
+
if (is_array($_FILES['file']['name'])) {
|
|
552
|
+
$files = [];
|
|
553
|
+
foreach ($_FILES['file']['name'] as $index => $name) {
|
|
554
|
+
$files[] = [
|
|
555
|
+
'name' => $name,
|
|
556
|
+
'type' => $_FILES['file']['type'][$index],
|
|
557
|
+
'tmp_name' => $_FILES['file']['tmp_name'][$index],
|
|
558
|
+
'error' => $_FILES['file']['error'][$index],
|
|
559
|
+
'size' => $_FILES['file']['size'][$index],
|
|
560
|
+
];
|
|
561
|
+
}
|
|
562
|
+
$data['files'] = $files;
|
|
502
563
|
} else {
|
|
503
|
-
|
|
504
|
-
$response = [
|
|
505
|
-
'success' => true,
|
|
506
|
-
'response' => $callbackResponse
|
|
507
|
-
];
|
|
564
|
+
$data['file'] = $_FILES['file'];
|
|
508
565
|
}
|
|
509
566
|
} else {
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
567
|
+
$input = file_get_contents('php://input');
|
|
568
|
+
$data = json_decode($input, true);
|
|
569
|
+
|
|
570
|
+
if (json_last_error() !== JSON_ERROR_NONE) {
|
|
571
|
+
$data = $_POST;
|
|
572
|
+
}
|
|
573
|
+
}
|
|
574
|
+
|
|
575
|
+
// Validate and call the dynamic function
|
|
576
|
+
if (isset($data['callback'])) {
|
|
577
|
+
$callbackName = preg_replace('/[^a-zA-Z0-9_]/', '', $data['callback']);
|
|
578
|
+
|
|
579
|
+
if (function_exists($callbackName) && is_callable($callbackName)) {
|
|
580
|
+
$dataObject = self::convertToArrayObject($data);
|
|
581
|
+
// Call the function
|
|
582
|
+
$callbackResponse = call_user_func($callbackName, $dataObject);
|
|
583
|
+
|
|
584
|
+
if (is_string($callbackResponse) || is_bool($callbackResponse)) {
|
|
585
|
+
$response = [
|
|
586
|
+
'success' => true,
|
|
587
|
+
'response' => $callbackResponse
|
|
588
|
+
];
|
|
589
|
+
} else {
|
|
590
|
+
$response = [
|
|
591
|
+
'success' => true,
|
|
592
|
+
'response' => $callbackResponse
|
|
593
|
+
];
|
|
594
|
+
}
|
|
515
595
|
} else {
|
|
516
|
-
$
|
|
596
|
+
if ($callbackName === PrismaPHPSettings::$localStoreKey) {
|
|
597
|
+
$response = [
|
|
598
|
+
'success' => true,
|
|
599
|
+
'response' => 'localStorage updated'
|
|
600
|
+
];
|
|
601
|
+
} else {
|
|
602
|
+
$response['error'] = 'Invalid callback';
|
|
603
|
+
}
|
|
517
604
|
}
|
|
605
|
+
} else {
|
|
606
|
+
$response['error'] = 'No callback provided';
|
|
518
607
|
}
|
|
519
|
-
} else {
|
|
520
|
-
$response['error'] = 'No callback provided';
|
|
521
|
-
}
|
|
522
608
|
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
echo json_encode($response);
|
|
526
|
-
} else {
|
|
527
|
-
if (isset($response['error'])) {
|
|
609
|
+
// Output the JSON response only if the callbackResponse is not null
|
|
610
|
+
if ($callbackResponse !== null || isset($response['error'])) {
|
|
528
611
|
echo json_encode($response);
|
|
529
612
|
}
|
|
613
|
+
} catch (Throwable $e) {
|
|
614
|
+
$response = [
|
|
615
|
+
'success' => false,
|
|
616
|
+
'error' => 'Exception occurred',
|
|
617
|
+
'message' => htmlspecialchars($e->getMessage(), ENT_QUOTES, 'UTF-8'),
|
|
618
|
+
'file' => htmlspecialchars($e->getFile(), ENT_QUOTES, 'UTF-8'),
|
|
619
|
+
'line' => (int) $e->getLine()
|
|
620
|
+
];
|
|
621
|
+
|
|
622
|
+
echo json_encode($response, JSON_UNESCAPED_UNICODE);
|
|
530
623
|
}
|
|
531
|
-
} catch (Throwable $e) {
|
|
532
|
-
// Handle any exceptions and prepare an error response
|
|
533
|
-
$response = [
|
|
534
|
-
'success' => false,
|
|
535
|
-
'error' => 'Exception occurred',
|
|
536
|
-
'message' => htmlspecialchars($e->getMessage(), ENT_QUOTES, 'UTF-8'),
|
|
537
|
-
'file' => htmlspecialchars($e->getFile(), ENT_QUOTES, 'UTF-8'),
|
|
538
|
-
'line' => $e->getLine()
|
|
539
|
-
];
|
|
540
624
|
|
|
541
|
-
|
|
542
|
-
echo json_encode($response);
|
|
625
|
+
exit;
|
|
543
626
|
}
|
|
544
627
|
|
|
545
|
-
|
|
546
|
-
|
|
628
|
+
public static function getLoadingsFiles(): string
|
|
629
|
+
{
|
|
630
|
+
// Gather all loading.php files
|
|
631
|
+
$loadingFiles = array_filter(PrismaPHPSettings::$routeFiles, function ($route) {
|
|
632
|
+
$normalizedRoute = str_replace('\\', '/', $route);
|
|
633
|
+
return preg_match('/\/loading\.php$/', $normalizedRoute);
|
|
634
|
+
});
|
|
547
635
|
|
|
548
|
-
function
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
return preg_match('/\/loading\.php$/', $normalizedRoute);
|
|
553
|
-
});
|
|
636
|
+
$haveLoadingFileContent = array_reduce($loadingFiles, function ($carry, $route) {
|
|
637
|
+
$normalizeUri = str_replace('\\', '/', $route);
|
|
638
|
+
$fileUrl = str_replace('./src/app', '', $normalizeUri);
|
|
639
|
+
$route = str_replace(['\\', './'], ['/', ''], $route);
|
|
554
640
|
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
641
|
+
ob_start();
|
|
642
|
+
include($route);
|
|
643
|
+
$loadingContent = ob_get_clean();
|
|
644
|
+
|
|
645
|
+
if ($loadingContent !== false) {
|
|
646
|
+
$url = $fileUrl === '/loading.php'
|
|
647
|
+
? '/'
|
|
648
|
+
: str_replace('/loading.php', '', $fileUrl);
|
|
649
|
+
$carry .= '<div pp-loading-url="' . $url . '">' . $loadingContent . '</div>';
|
|
650
|
+
}
|
|
651
|
+
return $carry;
|
|
652
|
+
}, '');
|
|
563
653
|
|
|
564
|
-
if ($
|
|
565
|
-
|
|
566
|
-
$carry .= '<div pp-loading-url="' . $url . '">' . $loadingContent . '</div>';
|
|
654
|
+
if ($haveLoadingFileContent) {
|
|
655
|
+
return '<div style="display: none;" id="loading-file-1B87E">' . $haveLoadingFileContent . '</div>';
|
|
567
656
|
}
|
|
568
|
-
|
|
569
|
-
return $carry;
|
|
570
|
-
}, '');
|
|
571
|
-
|
|
572
|
-
if ($haveLoadingFileContent) {
|
|
573
|
-
return '<div style="display: none;" id="loading-file-1B87E">' . $haveLoadingFileContent . '</div>';
|
|
657
|
+
return '';
|
|
574
658
|
}
|
|
575
659
|
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
function modifyOutputLayoutForError($contentToAdd)
|
|
580
|
-
{
|
|
581
|
-
$errorFile = APP_PATH . '/error.php';
|
|
582
|
-
$errorFileExists = file_exists($errorFile);
|
|
660
|
+
public static function createUpdateRequestData(): void
|
|
661
|
+
{
|
|
662
|
+
$requestJsonData = SETTINGS_PATH . '/request-data.json';
|
|
583
663
|
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
if (isAjaxOrXFileRequestOrRouteFile()) {
|
|
587
|
-
$contentToAdd = "An error occurred";
|
|
588
|
-
} else {
|
|
589
|
-
$contentToAdd = "<div class='error'>An error occurred</div>";
|
|
590
|
-
}
|
|
664
|
+
if (file_exists($requestJsonData)) {
|
|
665
|
+
$currentData = json_decode(file_get_contents($requestJsonData), true) ?? [];
|
|
591
666
|
} else {
|
|
592
|
-
|
|
667
|
+
$currentData = [];
|
|
593
668
|
}
|
|
594
|
-
}
|
|
595
669
|
|
|
596
|
-
|
|
670
|
+
$includedFiles = get_included_files();
|
|
671
|
+
$srcAppFiles = [];
|
|
672
|
+
foreach ($includedFiles as $filename) {
|
|
673
|
+
if (strpos($filename, DIRECTORY_SEPARATOR . 'src' . DIRECTORY_SEPARATOR . 'app' . DIRECTORY_SEPARATOR) !== false) {
|
|
674
|
+
$srcAppFiles[] = $filename;
|
|
675
|
+
}
|
|
676
|
+
}
|
|
597
677
|
|
|
598
|
-
$
|
|
678
|
+
$currentUrl = urldecode(Request::$uri);
|
|
599
679
|
|
|
600
|
-
if (
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
'error' => $errorContent
|
|
605
|
-
]);
|
|
606
|
-
http_response_code(403);
|
|
607
|
-
} else {
|
|
608
|
-
$layoutFile = APP_PATH . '/layout.php';
|
|
609
|
-
if (file_exists($layoutFile)) {
|
|
680
|
+
if (isset($currentData[$currentUrl])) {
|
|
681
|
+
$currentData[$currentUrl]['includedFiles'] = array_values(array_unique(
|
|
682
|
+
array_merge($currentData[$currentUrl]['includedFiles'], $srcAppFiles)
|
|
683
|
+
));
|
|
610
684
|
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
MainLayout::$children = ob_get_clean();
|
|
614
|
-
require_once $layoutFile;
|
|
615
|
-
} else {
|
|
616
|
-
echo $errorContent;
|
|
685
|
+
if (!Request::$isWire && !self::$secondRequestC69CD) {
|
|
686
|
+
$currentData[$currentUrl]['isCacheable'] = CacheHandler::$isCacheable;
|
|
617
687
|
}
|
|
618
|
-
}
|
|
619
|
-
} else {
|
|
620
|
-
if (isAjaxOrXFileRequestOrRouteFile()) {
|
|
621
|
-
header('Content-Type: application/json');
|
|
622
|
-
echo json_encode([
|
|
623
|
-
'success' => false,
|
|
624
|
-
'error' => $contentToAdd
|
|
625
|
-
]);
|
|
626
|
-
http_response_code(403);
|
|
627
688
|
} else {
|
|
628
|
-
|
|
689
|
+
$currentData[$currentUrl] = [
|
|
690
|
+
'url' => $currentUrl,
|
|
691
|
+
'fileName' => self::convertUrlToFileName($currentUrl),
|
|
692
|
+
'isCacheable' => CacheHandler::$isCacheable,
|
|
693
|
+
'cacheTtl' => CacheHandler::$ttl,
|
|
694
|
+
'includedFiles' => $srcAppFiles,
|
|
695
|
+
];
|
|
629
696
|
}
|
|
630
|
-
}
|
|
631
|
-
exit;
|
|
632
|
-
}
|
|
633
|
-
|
|
634
|
-
function createUpdateRequestData()
|
|
635
|
-
{
|
|
636
|
-
$requestJsonData = SETTINGS_PATH . '/request-data.json';
|
|
637
697
|
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
// Read the current data from the JSON file
|
|
641
|
-
$currentData = json_decode(file_get_contents($requestJsonData), true);
|
|
642
|
-
} else {
|
|
643
|
-
// If the file doesn't exist, initialize an empty array
|
|
644
|
-
$currentData = [];
|
|
645
|
-
}
|
|
698
|
+
$existingData = file_exists($requestJsonData) ? file_get_contents($requestJsonData) : '';
|
|
699
|
+
$newData = json_encode($currentData, JSON_PRETTY_PRINT);
|
|
646
700
|
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
// Filter only the files inside the src/app directory
|
|
651
|
-
$srcAppFiles = [];
|
|
652
|
-
foreach ($includedFiles as $filename) {
|
|
653
|
-
if (strpos($filename, DIRECTORY_SEPARATOR . 'src' . DIRECTORY_SEPARATOR . 'app' . DIRECTORY_SEPARATOR) !== false) {
|
|
654
|
-
$srcAppFiles[] = $filename;
|
|
701
|
+
if ($existingData !== $newData) {
|
|
702
|
+
file_put_contents($requestJsonData, $newData);
|
|
655
703
|
}
|
|
656
704
|
}
|
|
657
705
|
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
// Merge the existing and new included files, removing duplicates
|
|
664
|
-
$currentData[$currentUrl]['includedFiles'] = array_unique(
|
|
665
|
-
array_merge($currentData[$currentUrl]['includedFiles'], $srcAppFiles)
|
|
666
|
-
);
|
|
667
|
-
} else {
|
|
668
|
-
// If the URL doesn't exist, add a new entry
|
|
669
|
-
$currentData[$currentUrl] = [
|
|
670
|
-
'url' => $currentUrl,
|
|
671
|
-
'includedFiles' => $srcAppFiles,
|
|
672
|
-
];
|
|
706
|
+
private static function convertUrlToFileName(string $url): string
|
|
707
|
+
{
|
|
708
|
+
$url = trim($url, '/');
|
|
709
|
+
$fileName = preg_replace('/[^a-zA-Z0-9-_]/', '_', $url);
|
|
710
|
+
return $fileName ?: 'index';
|
|
673
711
|
}
|
|
674
712
|
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
{
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
$auth = Auth::getInstance();
|
|
685
|
-
$verifyToken = $auth->verifyToken($token);
|
|
686
|
-
if ($verifyToken) {
|
|
687
|
-
$auth->signIn($verifyToken);
|
|
713
|
+
private static function authenticateUserToken(): void
|
|
714
|
+
{
|
|
715
|
+
$token = Request::getBearerToken();
|
|
716
|
+
if ($token) {
|
|
717
|
+
$auth = Auth::getInstance();
|
|
718
|
+
$verifyToken = $auth->verifyToken($token);
|
|
719
|
+
if ($verifyToken) {
|
|
720
|
+
$auth->signIn($verifyToken);
|
|
721
|
+
}
|
|
688
722
|
}
|
|
689
723
|
}
|
|
690
|
-
}
|
|
691
|
-
|
|
692
|
-
function isAjaxOrXFileRequestOrRouteFile(): bool
|
|
693
|
-
{
|
|
694
|
-
if (Request::$fileToInclude === 'index.php') {
|
|
695
|
-
return false;
|
|
696
|
-
}
|
|
697
724
|
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
if (isAjaxOrXFileRequestOrRouteFile()) {
|
|
703
|
-
$errorContent = "Exception: " . $exception->getMessage();
|
|
704
|
-
} else {
|
|
705
|
-
$errorContent = "<div class='error'>Exception: " . htmlspecialchars($exception->getMessage(), ENT_QUOTES, 'UTF-8') . "</div>";
|
|
706
|
-
}
|
|
707
|
-
modifyOutputLayoutForError($errorContent);
|
|
708
|
-
});
|
|
709
|
-
|
|
710
|
-
register_shutdown_function(function () {
|
|
711
|
-
$error = error_get_last();
|
|
712
|
-
if ($error !== null && in_array($error['type'], [E_ERROR, E_PARSE, E_CORE_ERROR, E_COMPILE_ERROR, E_RECOVERABLE_ERROR])) {
|
|
713
|
-
|
|
714
|
-
if (isAjaxOrXFileRequestOrRouteFile()) {
|
|
715
|
-
$errorContent = "Fatal Error: " . $error['message'] . " in " . $error['file'] . " on line " . $error['line'];
|
|
716
|
-
} else {
|
|
717
|
-
$errorContent = "<div class='error'>Fatal Error: " . htmlspecialchars($error['message'], ENT_QUOTES, 'UTF-8') .
|
|
718
|
-
" in " . htmlspecialchars($error['file'], ENT_QUOTES, 'UTF-8') .
|
|
719
|
-
" on line " . $error['line'] . "</div>";
|
|
725
|
+
public static function isAjaxOrXFileRequestOrRouteFile(): bool
|
|
726
|
+
{
|
|
727
|
+
if (Request::$fileToInclude === 'index.php') {
|
|
728
|
+
return false;
|
|
720
729
|
}
|
|
721
|
-
modifyOutputLayoutForError($errorContent);
|
|
722
|
-
}
|
|
723
|
-
});
|
|
724
730
|
|
|
725
|
-
|
|
726
|
-
$_determineContentToInclude = determineContentToInclude();
|
|
727
|
-
$_contentToInclude = $_determineContentToInclude['path'] ?? '';
|
|
728
|
-
$_layoutsToInclude = $_determineContentToInclude['layouts'] ?? [];
|
|
729
|
-
Request::$pathname = $_determineContentToInclude['pathname'] ? '/' . $_determineContentToInclude['pathname'] : '/';
|
|
730
|
-
Request::$uri = $_determineContentToInclude['uri'] ? $_determineContentToInclude['uri'] : '/';
|
|
731
|
-
if (is_file($_contentToInclude)) {
|
|
732
|
-
Request::$fileToInclude = basename($_contentToInclude); // returns the file name
|
|
731
|
+
return Request::$isAjax || Request::$isXFileRequest || Request::$fileToInclude === 'route.php';
|
|
733
732
|
}
|
|
733
|
+
}
|
|
734
734
|
|
|
735
|
-
|
|
736
|
-
|
|
735
|
+
// ============================================================================
|
|
736
|
+
// Main Execution
|
|
737
|
+
// ============================================================================
|
|
738
|
+
Bootstrap::run();
|
|
737
739
|
|
|
738
|
-
|
|
740
|
+
try {
|
|
741
|
+
// 1) If there's no content to include:
|
|
742
|
+
if (empty(Bootstrap::$contentToInclude)) {
|
|
739
743
|
if (!Request::$isXFileRequest && PrismaPHPSettings::$option->backendOnly) {
|
|
740
|
-
// Set the header and output a JSON response for permission denied
|
|
741
744
|
header('Content-Type: application/json');
|
|
742
745
|
echo json_encode([
|
|
743
746
|
'success' => false,
|
|
744
747
|
'error' => 'Permission denied'
|
|
745
748
|
]);
|
|
746
|
-
http_response_code(403);
|
|
749
|
+
http_response_code(403);
|
|
747
750
|
exit;
|
|
748
751
|
}
|
|
749
752
|
|
|
750
|
-
|
|
751
|
-
if (is_file(
|
|
752
|
-
if (file_exists(
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
// Include the PHP file without setting the JSON header
|
|
756
|
-
include $_requestFilePath;
|
|
753
|
+
// If the file physically exists on disk and we’re dealing with an X-File request
|
|
754
|
+
if (is_file(Bootstrap::$requestFilePath)) {
|
|
755
|
+
if (file_exists(Bootstrap::$requestFilePath) && Request::$isXFileRequest) {
|
|
756
|
+
if (pathinfo(Bootstrap::$requestFilePath, PATHINFO_EXTENSION) === 'php') {
|
|
757
|
+
include Bootstrap::$requestFilePath;
|
|
757
758
|
} else {
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
header('Content-Type: ' . mime_content_type($_requestFilePath)); // Dynamic content type
|
|
761
|
-
readfile($_requestFilePath);
|
|
759
|
+
header('Content-Type: ' . mime_content_type(Bootstrap::$requestFilePath));
|
|
760
|
+
readfile(Bootstrap::$requestFilePath);
|
|
762
761
|
}
|
|
763
762
|
exit;
|
|
764
763
|
}
|
|
765
764
|
} else if (PrismaPHPSettings::$option->backendOnly) {
|
|
766
|
-
// Set the header and output a JSON response for file not found
|
|
767
765
|
header('Content-Type: application/json');
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
'error' => 'Not found'
|
|
771
|
-
]);
|
|
772
|
-
http_response_code(404); // Set HTTP status code to 404 Not Found
|
|
773
|
-
exit;
|
|
766
|
+
http_response_code(404);
|
|
767
|
+
exit(json_encode(['success' => false, 'error' => 'Not found']));
|
|
774
768
|
}
|
|
775
769
|
}
|
|
776
770
|
|
|
777
|
-
|
|
771
|
+
// 2) If the chosen file is route.php -> output JSON
|
|
772
|
+
if (!empty(Bootstrap::$contentToInclude) && Request::$fileToInclude === 'route.php') {
|
|
778
773
|
header('Content-Type: application/json');
|
|
779
|
-
require_once
|
|
774
|
+
require_once Bootstrap::$contentToInclude;
|
|
780
775
|
exit;
|
|
781
776
|
}
|
|
782
777
|
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
$_isChildContentIncluded = false;
|
|
788
|
-
$_isContentVariableIncluded = containsChildren($_parentLayoutPath);
|
|
789
|
-
if (!$_isContentVariableIncluded) {
|
|
790
|
-
$_isContentIncluded = true;
|
|
791
|
-
}
|
|
792
|
-
|
|
793
|
-
if (!empty($_contentToInclude) && !empty(Request::$fileToInclude)) {
|
|
794
|
-
if (!$_isParentLayout) {
|
|
778
|
+
// 3) If there is some valid content (index.php or something else)
|
|
779
|
+
if (!empty(Bootstrap::$contentToInclude) && !empty(Request::$fileToInclude)) {
|
|
780
|
+
// We only load the content now if we're NOT dealing with the top-level parent layout
|
|
781
|
+
if (!Bootstrap::$isParentLayout) {
|
|
795
782
|
ob_start();
|
|
796
|
-
require_once
|
|
783
|
+
require_once Bootstrap::$contentToInclude;
|
|
797
784
|
MainLayout::$childLayoutChildren = ob_get_clean();
|
|
798
785
|
}
|
|
799
|
-
|
|
800
|
-
|
|
786
|
+
|
|
787
|
+
// Then process all the reversed layouts in the chain
|
|
788
|
+
foreach (array_reverse(Bootstrap::$layoutsToInclude) as $layoutPath) {
|
|
789
|
+
if (Bootstrap::$parentLayoutPath === $layoutPath) {
|
|
801
790
|
continue;
|
|
802
791
|
}
|
|
803
792
|
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
$_isChildContentIncluded = true;
|
|
793
|
+
if (!Bootstrap::containsChildLayoutChildren($layoutPath)) {
|
|
794
|
+
Bootstrap::$isChildContentIncluded = true;
|
|
807
795
|
}
|
|
808
796
|
|
|
809
797
|
ob_start();
|
|
@@ -811,31 +799,36 @@ try {
|
|
|
811
799
|
MainLayout::$childLayoutChildren = ob_get_clean();
|
|
812
800
|
}
|
|
813
801
|
} else {
|
|
802
|
+
// Fallback: we include not-found.php
|
|
814
803
|
ob_start();
|
|
815
804
|
require_once APP_PATH . '/not-found.php';
|
|
816
805
|
MainLayout::$childLayoutChildren = ob_get_clean();
|
|
817
806
|
}
|
|
818
807
|
|
|
819
|
-
|
|
808
|
+
// If the top-level layout is in use
|
|
809
|
+
if (Bootstrap::$isParentLayout && !empty(Bootstrap::$contentToInclude)) {
|
|
820
810
|
ob_start();
|
|
821
|
-
require_once
|
|
811
|
+
require_once Bootstrap::$contentToInclude;
|
|
822
812
|
MainLayout::$childLayoutChildren = ob_get_clean();
|
|
823
813
|
}
|
|
824
814
|
|
|
825
|
-
if (
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
createUpdateRequestData();
|
|
815
|
+
if (!Bootstrap::$isContentIncluded && !Bootstrap::$isChildContentIncluded) {
|
|
816
|
+
// Provide request-data for SSR caching, if needed
|
|
817
|
+
if (!Bootstrap::$secondRequestC69CD) {
|
|
818
|
+
Bootstrap::createUpdateRequestData();
|
|
830
819
|
}
|
|
831
820
|
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
821
|
+
// If there’s caching
|
|
822
|
+
if (isset(Bootstrap::$requestFilesData[Request::$uri])) {
|
|
823
|
+
if ($_ENV['CACHE_ENABLED'] === 'true') {
|
|
824
|
+
CacheHandler::serveCache(Request::$uri, intval($_ENV['CACHE_TTL']));
|
|
825
|
+
}
|
|
826
|
+
}
|
|
837
827
|
|
|
838
|
-
|
|
828
|
+
// For wire calls, re-include the files if needed
|
|
829
|
+
if (Request::$isWire && !Bootstrap::$secondRequestC69CD) {
|
|
830
|
+
if (isset(Bootstrap::$requestFilesData[Request::$uri])) {
|
|
831
|
+
foreach (Bootstrap::$requestFilesData[Request::$uri]['includedFiles'] as $file) {
|
|
839
832
|
if (file_exists($file)) {
|
|
840
833
|
ob_start();
|
|
841
834
|
require_once $file;
|
|
@@ -845,77 +838,54 @@ try {
|
|
|
845
838
|
}
|
|
846
839
|
}
|
|
847
840
|
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
841
|
+
// If it’s a wire request, handle wire callback
|
|
842
|
+
if (Request::$isWire && !Bootstrap::$secondRequestC69CD) {
|
|
843
|
+
ob_end_clean();
|
|
844
|
+
Bootstrap::wireCallback();
|
|
845
|
+
}
|
|
846
|
+
|
|
847
|
+
MainLayout::$children = MainLayout::$childLayoutChildren . Bootstrap::getLoadingsFiles();
|
|
851
848
|
|
|
852
849
|
ob_start();
|
|
853
850
|
require_once APP_PATH . '/layout.php';
|
|
851
|
+
MainLayout::$html = ob_get_clean();
|
|
852
|
+
MainLayout::$html = TemplateCompiler::compile(MainLayout::$html);
|
|
853
|
+
MainLayout::$html = TemplateCompiler::injectDynamicContent(MainLayout::$html);
|
|
854
|
+
MainLayout::$html = "<!DOCTYPE html>\n" . MainLayout::$html;
|
|
854
855
|
|
|
855
|
-
if (Request::$
|
|
856
|
-
|
|
857
|
-
wireCallback();
|
|
858
|
-
} else {
|
|
859
|
-
echo ob_get_clean();
|
|
856
|
+
if (isset(Bootstrap::$requestFilesData[Request::$uri]['fileName']) && $_ENV['CACHE_ENABLED'] === 'true') {
|
|
857
|
+
CacheHandler::saveCache(Request::$uri, MainLayout::$html);
|
|
860
858
|
}
|
|
859
|
+
|
|
860
|
+
echo MainLayout::$html;
|
|
861
861
|
} else {
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
} else {
|
|
866
|
-
$_errorDetails = "<div class='error'>The parent layout file does not contain <?php echo MainLayout::\$children; ?> Or <?= MainLayout::\$children ?><br>" . "<strong>$_parentLayoutPath</strong></div>";
|
|
867
|
-
}
|
|
868
|
-
modifyOutputLayoutForError($_errorDetails);
|
|
869
|
-
} else {
|
|
870
|
-
if (isAjaxOrXFileRequestOrRouteFile()) {
|
|
871
|
-
$_errorDetails = "The layout file does not contain <?php echo MainLayout::\$childLayoutChildren; ?> or <?= MainLayout::\$childLayoutChildren ?><br><strong>$layoutPath</strong>";
|
|
872
|
-
} else {
|
|
873
|
-
$_errorDetails = "<div class='error'>The layout file does not contain <?php echo MainLayout::\$childLayoutChildren; ?> or <?= MainLayout::\$childLayoutChildren ?><br><strong>$layoutPath</strong></div>";
|
|
874
|
-
}
|
|
875
|
-
modifyOutputLayoutForError($_errorDetails);
|
|
876
|
-
}
|
|
877
|
-
}
|
|
878
|
-
} catch (Throwable $e) {
|
|
879
|
-
if (isAjaxOrXFileRequestOrRouteFile()) {
|
|
880
|
-
$_errorDetails = "Unhandled Exception: " . $e->getMessage() . " in " . $e->getFile() . " on line " . $e->getLine();
|
|
881
|
-
} else {
|
|
882
|
-
$_errorDetails = "Unhandled Exception: " . htmlspecialchars($e->getMessage(), ENT_QUOTES, 'UTF-8');
|
|
883
|
-
$_errorDetails .= "<br>File: " . htmlspecialchars($e->getFile(), ENT_QUOTES, 'UTF-8');
|
|
884
|
-
$_errorDetails .= "<br>Line: " . htmlspecialchars((string)$e->getLine(), ENT_QUOTES, 'UTF-8');
|
|
885
|
-
$_errorDetails .= "<br/>TraceAsString: " . htmlspecialchars($e->getTraceAsString(), ENT_QUOTES, 'UTF-8');
|
|
886
|
-
$_errorDetails = "<div class='error'>$_errorDetails</div>";
|
|
887
|
-
}
|
|
888
|
-
modifyOutputLayoutForError($_errorDetails);
|
|
889
|
-
}
|
|
862
|
+
$layoutPath = Bootstrap::$isContentIncluded
|
|
863
|
+
? Bootstrap::$parentLayoutPath
|
|
864
|
+
: (Bootstrap::$layoutsToInclude[0] ?? '');
|
|
890
865
|
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
if ($lastErrorCapture !== null) {
|
|
866
|
+
$message = "The layout file does not contain <?= MainLayout::\$childLayoutChildren ?>\n<strong>$layoutPath</strong>";
|
|
867
|
+
$htmlMessage = "<div class='error'>The layout file does not contain <?= MainLayout::\$childLayoutChildren ?><br><strong>$layoutPath</strong></div>";
|
|
894
868
|
|
|
895
|
-
if (
|
|
896
|
-
$
|
|
897
|
-
|
|
898
|
-
$errorContent = "<div class='error'>Error: " . $lastErrorCapture['message'] . " in " . $lastErrorCapture['file'] . " on line " . $lastErrorCapture['line'] . "</div>";
|
|
869
|
+
if (Bootstrap::$isContentIncluded) {
|
|
870
|
+
$message = "The parent layout file does not contain <?= MainLayout::\$children ?> or <?= MainLayout::\$childLayoutChildren ?><br><strong>$layoutPath</strong>";
|
|
871
|
+
$htmlMessage = "<div class='error'>The parent layout file does not contain <?= MainLayout::\$children ?> or <?= MainLayout::\$childLayoutChildren ?><br><strong>$layoutPath</strong></div>";
|
|
899
872
|
}
|
|
900
|
-
modifyOutputLayoutForError($errorContent);
|
|
901
|
-
}
|
|
902
|
-
})();
|
|
903
873
|
|
|
904
|
-
|
|
905
|
-
if (!(error_reporting() & $severity)) {
|
|
906
|
-
// This error code is not included in error_reporting
|
|
907
|
-
return;
|
|
908
|
-
}
|
|
874
|
+
$errorDetails = Bootstrap::isAjaxOrXFileRequestOrRouteFile() ? $message : $htmlMessage;
|
|
909
875
|
|
|
910
|
-
|
|
911
|
-
if (isAjaxOrXFileRequestOrRouteFile()) {
|
|
912
|
-
$errorContent = "Error: {$severity} - {$message} in {$file} on line {$line}";
|
|
913
|
-
} else {
|
|
914
|
-
$errorContent = "<div class='error'>Error: {$message} in {$file} on line {$line}</div>";
|
|
876
|
+
ErrorHandler::modifyOutputLayoutForError($errorDetails);
|
|
915
877
|
}
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
878
|
+
} catch (Throwable $e) {
|
|
879
|
+
if (Bootstrap::isAjaxOrXFileRequestOrRouteFile()) {
|
|
880
|
+
$errorDetails = "Unhandled Exception: " . $e->getMessage() .
|
|
881
|
+
" in " . $e->getFile() .
|
|
882
|
+
" on line " . $e->getLine();
|
|
883
|
+
} else {
|
|
884
|
+
$errorDetails = "Unhandled Exception: " . htmlspecialchars($e->getMessage(), ENT_QUOTES, 'UTF-8');
|
|
885
|
+
$errorDetails .= "<br>File: " . htmlspecialchars($e->getFile(), ENT_QUOTES, 'UTF-8');
|
|
886
|
+
$errorDetails .= "<br>Line: " . htmlspecialchars((string)$e->getLine(), ENT_QUOTES, 'UTF-8');
|
|
887
|
+
$errorDetails .= "<br/>TraceAsString: " . htmlspecialchars($e->getTraceAsString(), ENT_QUOTES, 'UTF-8');
|
|
888
|
+
$errorDetails = "<div class='error'>{$errorDetails}</div>";
|
|
920
889
|
}
|
|
921
|
-
|
|
890
|
+
ErrorHandler::modifyOutputLayoutForError($errorDetails);
|
|
891
|
+
}
|