create-prisma-php-app 1.28.7 → 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 +51 -30
- 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
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
<?php
|
|
2
|
+
|
|
3
|
+
namespace Lib;
|
|
4
|
+
|
|
5
|
+
use Bootstrap;
|
|
6
|
+
use Lib\MainLayout;
|
|
7
|
+
|
|
8
|
+
class ErrorHandler
|
|
9
|
+
{
|
|
10
|
+
public static string $content = '';
|
|
11
|
+
|
|
12
|
+
public static function registerHandlers(): void
|
|
13
|
+
{
|
|
14
|
+
self::registerExceptionHandler();
|
|
15
|
+
self::registerShutdownFunction();
|
|
16
|
+
self::registerErrorHandler();
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
private static function registerExceptionHandler(): void
|
|
20
|
+
{
|
|
21
|
+
set_exception_handler(function ($exception) {
|
|
22
|
+
$errorContent = Bootstrap::isAjaxOrXFileRequestOrRouteFile()
|
|
23
|
+
? "Exception: " . $exception->getMessage()
|
|
24
|
+
: "<div class='error'>Exception: " . htmlspecialchars($exception->getMessage(), ENT_QUOTES, 'UTF-8') . "</div>";
|
|
25
|
+
|
|
26
|
+
self::modifyOutputLayoutForError($errorContent);
|
|
27
|
+
});
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
private static function registerShutdownFunction(): void
|
|
31
|
+
{
|
|
32
|
+
register_shutdown_function(function () {
|
|
33
|
+
$error = error_get_last();
|
|
34
|
+
if (
|
|
35
|
+
$error !== null &&
|
|
36
|
+
in_array($error['type'], [E_ERROR, E_PARSE, E_CORE_ERROR, E_COMPILE_ERROR, E_RECOVERABLE_ERROR], true)
|
|
37
|
+
) {
|
|
38
|
+
$errorContent = Bootstrap::isAjaxOrXFileRequestOrRouteFile()
|
|
39
|
+
? "Fatal Error: " . $error['message'] . " in " . $error['file'] . " on line " . $error['line']
|
|
40
|
+
: "<div class='error'>Fatal Error: " . htmlspecialchars($error['message'], ENT_QUOTES, 'UTF-8') .
|
|
41
|
+
" in " . htmlspecialchars($error['file'], ENT_QUOTES, 'UTF-8') .
|
|
42
|
+
" on line " . $error['line'] . "</div>";
|
|
43
|
+
|
|
44
|
+
self::modifyOutputLayoutForError($errorContent);
|
|
45
|
+
}
|
|
46
|
+
});
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
private static function registerErrorHandler(): void
|
|
50
|
+
{
|
|
51
|
+
set_error_handler(function ($severity, $message, $file, $line) {
|
|
52
|
+
if (!(error_reporting() & $severity)) {
|
|
53
|
+
return;
|
|
54
|
+
}
|
|
55
|
+
$errorContent = Bootstrap::isAjaxOrXFileRequestOrRouteFile()
|
|
56
|
+
? "Error: {$severity} - {$message} in {$file} on line {$line}"
|
|
57
|
+
: "<div class='error'>Error: {$message} in {$file} on line {$line}</div>";
|
|
58
|
+
|
|
59
|
+
if ($severity === E_WARNING || $severity === E_NOTICE) {
|
|
60
|
+
self::modifyOutputLayoutForError($errorContent);
|
|
61
|
+
}
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
public static function checkFatalError(): void
|
|
66
|
+
{
|
|
67
|
+
$error = error_get_last();
|
|
68
|
+
if (
|
|
69
|
+
$error !== null &&
|
|
70
|
+
in_array($error['type'], [E_ERROR, E_PARSE, E_CORE_ERROR, E_COMPILE_ERROR, E_RECOVERABLE_ERROR], true)
|
|
71
|
+
) {
|
|
72
|
+
$errorContent = Bootstrap::isAjaxOrXFileRequestOrRouteFile()
|
|
73
|
+
? "Fatal Error: " . $error['message'] . " in " . $error['file'] . " on line " . $error['line']
|
|
74
|
+
: "<div class='error'>Fatal Error: " . htmlspecialchars($error['message'], ENT_QUOTES, 'UTF-8') .
|
|
75
|
+
" in " . htmlspecialchars($error['file'], ENT_QUOTES, 'UTF-8') .
|
|
76
|
+
" on line " . $error['line'] . "</div>";
|
|
77
|
+
|
|
78
|
+
self::modifyOutputLayoutForError($errorContent);
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
public static function modifyOutputLayoutForError($contentToAdd): void
|
|
83
|
+
{
|
|
84
|
+
$errorFile = APP_PATH . '/error.php';
|
|
85
|
+
$errorFileExists = file_exists($errorFile);
|
|
86
|
+
|
|
87
|
+
if ($_ENV['SHOW_ERRORS'] === "false") {
|
|
88
|
+
if ($errorFileExists) {
|
|
89
|
+
$contentToAdd = Bootstrap::isAjaxOrXFileRequestOrRouteFile() ? "An error occurred" : "<div class='error'>An error occurred</div>";
|
|
90
|
+
} else {
|
|
91
|
+
exit;
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
if ($errorFileExists) {
|
|
96
|
+
self::$content = $contentToAdd;
|
|
97
|
+
if (Bootstrap::isAjaxOrXFileRequestOrRouteFile()) {
|
|
98
|
+
header('Content-Type: application/json');
|
|
99
|
+
echo json_encode(['success' => false, 'error' => self::$content]);
|
|
100
|
+
http_response_code(403);
|
|
101
|
+
} else {
|
|
102
|
+
$layoutFile = APP_PATH . '/layout.php';
|
|
103
|
+
if (file_exists($layoutFile)) {
|
|
104
|
+
ob_start();
|
|
105
|
+
require_once $errorFile;
|
|
106
|
+
MainLayout::$children = ob_get_clean();
|
|
107
|
+
require $layoutFile;
|
|
108
|
+
} else {
|
|
109
|
+
echo self::$content;
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
} else {
|
|
113
|
+
if (Bootstrap::isAjaxOrXFileRequestOrRouteFile()) {
|
|
114
|
+
header('Content-Type: application/json');
|
|
115
|
+
echo json_encode(['success' => false, 'error' => $contentToAdd]);
|
|
116
|
+
http_response_code(403);
|
|
117
|
+
} else {
|
|
118
|
+
echo $contentToAdd;
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
exit;
|
|
122
|
+
}
|
|
123
|
+
}
|
|
@@ -10,6 +10,7 @@ class MainLayout
|
|
|
10
10
|
public static string $description = '';
|
|
11
11
|
public static string $children = '';
|
|
12
12
|
public static string $childLayoutChildren = '';
|
|
13
|
+
public static string $html = '';
|
|
13
14
|
|
|
14
15
|
private static array $headScripts = [];
|
|
15
16
|
private static array $headScriptsMap = [];
|
|
@@ -54,33 +55,35 @@ class MainLayout
|
|
|
54
55
|
}
|
|
55
56
|
|
|
56
57
|
/**
|
|
57
|
-
*
|
|
58
|
+
* Generate all the head scripts with dynamic attributes.
|
|
58
59
|
*
|
|
59
|
-
* @return
|
|
60
|
+
* @return string
|
|
60
61
|
*/
|
|
61
|
-
public static function outputHeadScripts():
|
|
62
|
+
public static function outputHeadScripts(): string
|
|
62
63
|
{
|
|
63
64
|
$headScriptsWithAttributes = array_map(function ($tag) {
|
|
64
|
-
// Check if the tag is a <link
|
|
65
|
+
// Check if the tag is a <script>, <link>, or <style> and add the dynamic attribute
|
|
65
66
|
if (strpos($tag, '<script') !== false) {
|
|
66
67
|
return str_replace('<script', '<script pp-dynamic-script="81D7D"', $tag);
|
|
67
68
|
} elseif (strpos($tag, '<link') !== false) {
|
|
68
69
|
return str_replace('<link', '<link pp-dynamic-link="81D7D"', $tag);
|
|
70
|
+
} elseif (strpos($tag, '<style') !== false) {
|
|
71
|
+
return str_replace('<style', '<style pp-dynamic-style="81D7D"', $tag);
|
|
69
72
|
}
|
|
70
|
-
return $tag;
|
|
73
|
+
return $tag;
|
|
71
74
|
}, self::$headScripts);
|
|
72
75
|
|
|
73
|
-
|
|
76
|
+
return implode("\n", $headScriptsWithAttributes);
|
|
74
77
|
}
|
|
75
78
|
|
|
76
79
|
/**
|
|
77
|
-
*
|
|
80
|
+
* Generate all the footer scripts.
|
|
78
81
|
*
|
|
79
|
-
* @return
|
|
82
|
+
* @return string
|
|
80
83
|
*/
|
|
81
|
-
public static function outputFooterScripts():
|
|
84
|
+
public static function outputFooterScripts(): string
|
|
82
85
|
{
|
|
83
|
-
|
|
86
|
+
return implode("\n", self::$footerScripts);
|
|
84
87
|
}
|
|
85
88
|
|
|
86
89
|
/**
|
|
@@ -127,29 +130,27 @@ class MainLayout
|
|
|
127
130
|
}
|
|
128
131
|
|
|
129
132
|
/**
|
|
130
|
-
*
|
|
133
|
+
* Generate the metadata as meta tags for the head section.
|
|
131
134
|
*
|
|
132
|
-
* @return
|
|
135
|
+
* @return string
|
|
133
136
|
*/
|
|
134
|
-
public static function outputMetadata():
|
|
137
|
+
public static function outputMetadata(): string
|
|
135
138
|
{
|
|
136
|
-
|
|
137
|
-
|
|
139
|
+
$metadataContent = [
|
|
140
|
+
'<meta charset="UTF-8">',
|
|
141
|
+
'<meta name="viewport" content="width=device-width, initial-scale=1.0">',
|
|
142
|
+
];
|
|
143
|
+
$metadataContent[] = '<title>' . htmlspecialchars(self::$title) . '</title>';
|
|
138
144
|
|
|
139
|
-
// Ensure the description is included in custom metadata if not already present
|
|
140
145
|
if (!isset(self::$customMetadata['description'])) {
|
|
141
146
|
self::$customMetadata['description'] = self::$description;
|
|
142
147
|
}
|
|
143
148
|
|
|
144
|
-
$customMetadataContent = [];
|
|
145
149
|
foreach (self::$customMetadata as $key => $value) {
|
|
146
|
-
|
|
147
|
-
$customMetadataContent[] = '<meta name="' . htmlspecialchars($key) . '" content="' . htmlspecialchars($value) . '" pp-dynamic-meta="81D7D">';
|
|
150
|
+
$metadataContent[] = '<meta name="' . htmlspecialchars($key) . '" content="' . htmlspecialchars($value) . '" pp-dynamic-meta="81D7D">';
|
|
148
151
|
}
|
|
149
152
|
|
|
150
|
-
|
|
151
|
-
echo implode("\n", $customMetadataContent) . "\n";
|
|
152
|
-
}
|
|
153
|
+
return implode("\n", $metadataContent);
|
|
153
154
|
}
|
|
154
155
|
|
|
155
156
|
/**
|
|
@@ -5,10 +5,12 @@ declare(strict_types=1);
|
|
|
5
5
|
namespace Lib\PHPX;
|
|
6
6
|
|
|
7
7
|
use Lib\PrismaPHPSettings;
|
|
8
|
+
use Lib\MainLayout;
|
|
8
9
|
use DOMDocument;
|
|
9
10
|
use DOMElement;
|
|
10
11
|
use DOMComment;
|
|
11
12
|
use DOMNode;
|
|
13
|
+
use RuntimeException;
|
|
12
14
|
|
|
13
15
|
class TemplateCompiler
|
|
14
16
|
{
|
|
@@ -38,12 +40,9 @@ class TemplateCompiler
|
|
|
38
40
|
self::initializeClassMappings();
|
|
39
41
|
}
|
|
40
42
|
|
|
41
|
-
// Convert template to valid XML
|
|
42
43
|
$dom = self::convertToXml($templateContent);
|
|
43
44
|
|
|
44
|
-
// Process the converted XML
|
|
45
45
|
$root = $dom->documentElement;
|
|
46
|
-
$output = "";
|
|
47
46
|
|
|
48
47
|
$outputParts = [];
|
|
49
48
|
foreach ($root->childNodes as $child) {
|
|
@@ -53,25 +52,61 @@ class TemplateCompiler
|
|
|
53
52
|
return implode('', $outputParts);
|
|
54
53
|
}
|
|
55
54
|
|
|
56
|
-
public static function
|
|
55
|
+
public static function injectDynamicContent(string $htmlContent): string
|
|
57
56
|
{
|
|
58
|
-
|
|
59
|
-
$
|
|
57
|
+
$patternHeadOpen = '/(<head\b[^>]*>)/i';
|
|
58
|
+
if (preg_match($patternHeadOpen, $htmlContent)) {
|
|
59
|
+
$htmlContent = preg_replace(
|
|
60
|
+
$patternHeadOpen,
|
|
61
|
+
'$1' . MainLayout::outputMetadata(),
|
|
62
|
+
$htmlContent,
|
|
63
|
+
1
|
|
64
|
+
);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
$patternHeadClose = '/(<\/head\s*>)/i';
|
|
68
|
+
if (preg_match($patternHeadClose, $htmlContent)) {
|
|
69
|
+
$htmlContent = preg_replace(
|
|
70
|
+
$patternHeadClose,
|
|
71
|
+
MainLayout::outputHeadScripts() . '$1',
|
|
72
|
+
$htmlContent,
|
|
73
|
+
1
|
|
74
|
+
);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
$patternBodyClose = '/(<\/body\s*>)/i';
|
|
78
|
+
if (preg_match($patternBodyClose, $htmlContent)) {
|
|
79
|
+
$htmlContent = preg_replace(
|
|
80
|
+
$patternBodyClose,
|
|
81
|
+
MainLayout::outputFooterScripts() . '$1',
|
|
82
|
+
$htmlContent,
|
|
83
|
+
1
|
|
84
|
+
);
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
return $htmlContent;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
private static function escapeAmpersands(string $content): string
|
|
91
|
+
{
|
|
92
|
+
return preg_replace_callback(
|
|
60
93
|
'/&(.*?)/',
|
|
61
94
|
function ($matches) {
|
|
62
95
|
$str = $matches[0];
|
|
63
96
|
|
|
64
|
-
// If it already looks like a valid entity, leave it alone.
|
|
65
|
-
// This check can be as simple or as robust as you need.
|
|
66
97
|
if (preg_match('/^&(?:[a-zA-Z]+|#[0-9]+|#x[0-9A-Fa-f]+);$/', $str)) {
|
|
67
98
|
return $str;
|
|
68
99
|
}
|
|
69
100
|
|
|
70
|
-
// Otherwise, escape it.
|
|
71
101
|
return '&' . substr($str, 1);
|
|
72
102
|
},
|
|
73
|
-
$
|
|
103
|
+
$content
|
|
74
104
|
);
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
public static function convertToXml(string $templateContent): DOMDocument
|
|
108
|
+
{
|
|
109
|
+
$templateContent = self::escapeAmpersands($templateContent);
|
|
75
110
|
|
|
76
111
|
$dom = new DOMDocument();
|
|
77
112
|
libxml_use_internal_errors(true);
|
|
@@ -80,7 +115,7 @@ class TemplateCompiler
|
|
|
80
115
|
|
|
81
116
|
if (!$dom->loadXML($wrappedContent)) {
|
|
82
117
|
$errors = self::getXmlErrors();
|
|
83
|
-
throw new
|
|
118
|
+
throw new RuntimeException(
|
|
84
119
|
"XML Parsing Failed: " . implode("; ", $errors)
|
|
85
120
|
);
|
|
86
121
|
}
|
|
@@ -112,7 +147,6 @@ class TemplateCompiler
|
|
|
112
147
|
default => "Unknown",
|
|
113
148
|
};
|
|
114
149
|
|
|
115
|
-
// Highlight the tag name in the error message
|
|
116
150
|
$message = trim($error->message);
|
|
117
151
|
if (preg_match("/tag (.*?) /", $message, $matches)) {
|
|
118
152
|
$tag = $matches[1];
|
|
@@ -134,29 +168,23 @@ class TemplateCompiler
|
|
|
134
168
|
$componentName = $node->nodeName;
|
|
135
169
|
$attributes = [];
|
|
136
170
|
|
|
137
|
-
// Gather element attributes
|
|
138
171
|
foreach ($node->attributes as $attr) {
|
|
139
172
|
$attributes[$attr->name] = $attr->value;
|
|
140
173
|
}
|
|
141
174
|
|
|
142
|
-
// Recursively get child content
|
|
143
175
|
$childOutput = [];
|
|
144
176
|
foreach ($node->childNodes as $child) {
|
|
145
177
|
$childOutput[] = self::processNode($child);
|
|
146
178
|
}
|
|
147
179
|
$innerContent = implode('', $childOutput);
|
|
148
180
|
|
|
149
|
-
// We'll store 'children' for potential component logic
|
|
150
181
|
$attributes['children'] = $innerContent;
|
|
151
182
|
|
|
152
|
-
// Pass to processComponent for final decision
|
|
153
183
|
return self::processComponent($componentName, $attributes);
|
|
154
184
|
} elseif ($node instanceof DOMComment) {
|
|
155
|
-
// Preserve HTML comments
|
|
156
185
|
return "<!--{$node->textContent}-->";
|
|
157
186
|
}
|
|
158
187
|
|
|
159
|
-
// For text/cdata nodes, return text
|
|
160
188
|
return $node->textContent;
|
|
161
189
|
}
|
|
162
190
|
|
|
@@ -173,18 +201,15 @@ class TemplateCompiler
|
|
|
173
201
|
$className = self::$classMappings[$componentName]['className'];
|
|
174
202
|
$filePath = self::$classMappings[$componentName]['filePath'];
|
|
175
203
|
|
|
176
|
-
// Ensure the required file is included
|
|
177
204
|
require_once str_replace('\\', '/', SRC_PATH . '/' . $filePath);
|
|
178
205
|
|
|
179
206
|
if (!class_exists($className)) {
|
|
180
|
-
throw new
|
|
207
|
+
throw new RuntimeException("Class $className does not exist.");
|
|
181
208
|
}
|
|
182
209
|
|
|
183
|
-
// Instantiate the component
|
|
184
210
|
$componentInstance = new $className($attributes);
|
|
185
211
|
$renderedContent = $componentInstance->render();
|
|
186
212
|
|
|
187
|
-
// re-compile to handle further components
|
|
188
213
|
if (strpos($renderedContent, '<') !== false) {
|
|
189
214
|
return self::compile($renderedContent);
|
|
190
215
|
}
|
|
@@ -198,12 +223,10 @@ class TemplateCompiler
|
|
|
198
223
|
{
|
|
199
224
|
$attrs = self::renderAttributes($attributes);
|
|
200
225
|
|
|
201
|
-
// Check if it's self-closing
|
|
202
226
|
if (in_array(strtolower($tagName), self::$selfClosingTags)) {
|
|
203
227
|
return "<{$tagName}{$attrs} />";
|
|
204
228
|
}
|
|
205
229
|
|
|
206
|
-
// Normal open/close
|
|
207
230
|
$innerContent = $attributes['children'] ?? '';
|
|
208
231
|
return "<{$tagName}{$attrs}>{$innerContent}</{$tagName}>";
|
|
209
232
|
}
|
|
@@ -18,7 +18,7 @@ class TwMerge
|
|
|
18
18
|
"px" => "/^px-/",
|
|
19
19
|
"py" => "/^py-/",
|
|
20
20
|
|
|
21
|
-
// **Margin classes
|
|
21
|
+
// **Margin classes**
|
|
22
22
|
"m" => "/^m-/",
|
|
23
23
|
"mt" => "/^mt-/",
|
|
24
24
|
"mr" => "/^mr-/",
|
|
@@ -74,6 +74,9 @@ class TwMerge
|
|
|
74
74
|
// **Flexbox alignment classes**
|
|
75
75
|
"justify" => "/^justify-(start|end|center|between|around|evenly)$/",
|
|
76
76
|
|
|
77
|
+
// **Flexbox alignment classes**
|
|
78
|
+
"items" => "/^items-(start|end|center|baseline|stretch)$/",
|
|
79
|
+
|
|
77
80
|
// **Width classes**
|
|
78
81
|
"w" => "/^w-(full|[0-9]+|\\[.+\\])$/",
|
|
79
82
|
|
|
@@ -126,6 +129,9 @@ class TwMerge
|
|
|
126
129
|
// **Flexbox alignment conflict group**
|
|
127
130
|
"justify" => ["justify"],
|
|
128
131
|
|
|
132
|
+
// **Flexbox alignment conflict group**
|
|
133
|
+
"items" => ["items"],
|
|
134
|
+
|
|
129
135
|
// **Width conflict group**
|
|
130
136
|
"w" => ["w"],
|
|
131
137
|
|