create-prisma-php-app 3.1.11 → 4.0.0-alpha.2
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 +10 -10
- package/dist/index.js +12 -8
- package/dist/settings/restart-websocket.bat +1 -1
- package/dist/settings/restart-websocket.ts +2 -9
- package/dist/src/{Lib/Websocket → Websocket}/ConnectionManager.php +1 -1
- package/dist/src/app/error.php +1 -1
- package/dist/src/app/index.php +1 -1
- package/dist/src/app/js/index.js +1 -1
- package/dist/src/app/layout.php +2 -2
- package/dist/{src/Lib/Websocket/websocket-server.php → websocket-server.php} +2 -7
- package/package.json +1 -1
- package/dist/src/Lib/AI/ChatGPTClient.php +0 -147
- package/dist/src/Lib/Auth/Auth.php +0 -544
- package/dist/src/Lib/Auth/AuthConfig.php +0 -89
- package/dist/src/Lib/CacheHandler.php +0 -121
- package/dist/src/Lib/ErrorHandler.php +0 -322
- package/dist/src/Lib/FileManager/UploadFile.php +0 -383
- package/dist/src/Lib/Headers/Boom.php +0 -208
- package/dist/src/Lib/IncludeTracker.php +0 -59
- package/dist/src/Lib/MainLayout.php +0 -215
- package/dist/src/Lib/Middleware/AuthMiddleware.php +0 -154
- package/dist/src/Lib/PHPMailer/Mailer.php +0 -169
- package/dist/src/Lib/PHPX/Exceptions/ComponentValidationException.php +0 -49
- package/dist/src/Lib/PHPX/IPHPX.php +0 -22
- package/dist/src/Lib/PHPX/PHPX.php +0 -173
- package/dist/src/Lib/PHPX/TemplateCompiler.php +0 -571
- package/dist/src/Lib/PHPX/TwMerge.php +0 -195
- package/dist/src/Lib/PHPX/TypeCoercer.php +0 -490
- package/dist/src/Lib/PartialRenderer.php +0 -40
- package/dist/src/Lib/PrismaPHPSettings.php +0 -181
- package/dist/src/Lib/Request.php +0 -476
- package/dist/src/Lib/Set.php +0 -102
- package/dist/src/Lib/StateManager.php +0 -127
- package/dist/src/Lib/Validator.php +0 -738
|
@@ -1,89 +0,0 @@
|
|
|
1
|
-
<?php
|
|
2
|
-
|
|
3
|
-
declare(strict_types=1);
|
|
4
|
-
|
|
5
|
-
namespace Lib\Auth;
|
|
6
|
-
|
|
7
|
-
use ArrayObject;
|
|
8
|
-
|
|
9
|
-
enum AuthRole: string
|
|
10
|
-
{
|
|
11
|
-
case Admin = 'Admin';
|
|
12
|
-
case User = 'User';
|
|
13
|
-
|
|
14
|
-
public function equals($role)
|
|
15
|
-
{
|
|
16
|
-
return $this->value === $role;
|
|
17
|
-
}
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
final class AuthConfig
|
|
21
|
-
{
|
|
22
|
-
public const ROLE_IDENTIFIER = 'role';
|
|
23
|
-
public const IS_ROLE_BASE = false;
|
|
24
|
-
public const IS_TOKEN_AUTO_REFRESH = false;
|
|
25
|
-
public const IS_ALL_ROUTES_PRIVATE = false;
|
|
26
|
-
|
|
27
|
-
/**
|
|
28
|
-
* This is the (default) option for authentication. If IS_ALL_ROUTES_PRIVATE is set to false,
|
|
29
|
-
* An array of private routes that are accessible to all authenticated users
|
|
30
|
-
* without specific role-based access control. Routes should be listed as string paths.
|
|
31
|
-
* Example: public static $privateRoutes = ['/']; // This makes the home page private
|
|
32
|
-
* Example: public static $privateRoutes = ['/profile', '/dashboard/settings']; // These routes are private
|
|
33
|
-
*/
|
|
34
|
-
public static array $privateRoutes = [];
|
|
35
|
-
|
|
36
|
-
/**
|
|
37
|
-
* This is the (default) option for authentication. If IS_ALL_ROUTES_PRIVATE is set to true,
|
|
38
|
-
* An array of public routes that are accessible to all users, authenticated or not.
|
|
39
|
-
*/
|
|
40
|
-
public const DEFAULT_SIGNIN_REDIRECT = '/dashboard'; // Default redirect route after sign in
|
|
41
|
-
public const API_AUTH_PREFIX = '/api/auth'; // Prefix for third-party API authentication routes (github, google, etc.)
|
|
42
|
-
|
|
43
|
-
/**
|
|
44
|
-
* An array of public routes that are accessible to all users, authenticated or not.
|
|
45
|
-
* Routes should be listed as string paths.
|
|
46
|
-
* Example: public static $publicRoutes = ['/']; // This makes the home page public
|
|
47
|
-
* Example: public static $publicRoutes = ['/about', '/contact']; // These routes are public
|
|
48
|
-
*/
|
|
49
|
-
public static array $publicRoutes = ['/'];
|
|
50
|
-
public static array $authRoutes = [
|
|
51
|
-
'/signin',
|
|
52
|
-
'/signup',
|
|
53
|
-
];
|
|
54
|
-
|
|
55
|
-
/**
|
|
56
|
-
* An associative array mapping specific routes to required user roles for access control.
|
|
57
|
-
* Each route is a key with an array of roles that are allowed access.
|
|
58
|
-
* Format:
|
|
59
|
-
* 'route_path' => [self::ROLE_IDENTIFIER => [AuthRole::Role1, AuthRole::Role2, ...]],
|
|
60
|
-
* Example:
|
|
61
|
-
* public static $roleBasedRoutes = [
|
|
62
|
-
* 'dashboard' => [self::ROLE_IDENTIFIER => [AuthRole::Admin, AuthRole::User]],
|
|
63
|
-
* 'dashboard/users' => [self::ROLE_IDENTIFIER => [AuthRole::Admin]],
|
|
64
|
-
* 'sales' => [self::ROLE_IDENTIFIER => [AuthRole::Admin, AuthRole::User]]
|
|
65
|
-
* ];
|
|
66
|
-
*/
|
|
67
|
-
public static array $roleBasedRoutes = [];
|
|
68
|
-
|
|
69
|
-
/**
|
|
70
|
-
* Checks if the given user role is authorized to access a set of roles.
|
|
71
|
-
*
|
|
72
|
-
* @param ArrayObject|string $userRole The user's role to check.
|
|
73
|
-
* @param array<AuthRole> $roles An array of AuthRole instances specifying allowed roles.
|
|
74
|
-
* @return bool Returns true if the user's role matches any of the allowed roles, false otherwise.
|
|
75
|
-
*/
|
|
76
|
-
public static function checkAuthRole(ArrayObject|string $userRole, array $roles): bool
|
|
77
|
-
{
|
|
78
|
-
if ($userRole instanceof ArrayObject) {
|
|
79
|
-
$userRole = $userRole[Auth::ROLE_NAME] ?? '';
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
foreach ($roles as $role) {
|
|
83
|
-
if ($userRole === $role->value) {
|
|
84
|
-
return true;
|
|
85
|
-
}
|
|
86
|
-
}
|
|
87
|
-
return false;
|
|
88
|
-
}
|
|
89
|
-
}
|
|
@@ -1,121 +0,0 @@
|
|
|
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
|
-
}
|
|
@@ -1,322 +0,0 @@
|
|
|
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
|
-
}
|