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.
@@ -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
- * Output all the head scripts
58
+ * Generate all the head scripts with dynamic attributes.
58
59
  *
59
- * @return void
60
+ * @return string
60
61
  */
61
- public static function outputHeadScripts(): void
62
+ public static function outputHeadScripts(): string
62
63
  {
63
64
  $headScriptsWithAttributes = array_map(function ($tag) {
64
- // Check if the tag is a <link> or <script> and add the dynamic attribute
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; // Return the tag unchanged if it's neither <script> nor <link>
73
+ return $tag;
71
74
  }, self::$headScripts);
72
75
 
73
- echo implode("\n", $headScriptsWithAttributes);
76
+ return implode("\n", $headScriptsWithAttributes);
74
77
  }
75
78
 
76
79
  /**
77
- * Output all the footer scripts
80
+ * Generate all the footer scripts.
78
81
  *
79
- * @return void
82
+ * @return string
80
83
  */
81
- public static function outputFooterScripts(): void
84
+ public static function outputFooterScripts(): string
82
85
  {
83
- echo implode("\n", self::$footerScripts);
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
- * Output the metadata as meta tags for the head section
133
+ * Generate the metadata as meta tags for the head section.
131
134
  *
132
- * @return void
135
+ * @return string
133
136
  */
134
- public static function outputMetadata(): void
137
+ public static function outputMetadata(): string
135
138
  {
136
- // Output standard metadata
137
- echo '<title>' . htmlspecialchars(self::$title) . '</title>' . "\n";
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
- // Add the dynamic meta ID attribute to each <meta> tag
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
- if (!empty($customMetadataContent)) {
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 convertToXml(string $templateContent): DOMDocument
55
+ public static function injectDynamicContent(string $htmlContent): string
57
56
  {
58
- // Escape `&` characters that are not part of valid XML entities
59
- $templateContent = preg_replace_callback(
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 '&amp;' . substr($str, 1);
72
102
  },
73
- $templateContent
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 \RuntimeException(
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 \RuntimeException("Class $className does not exist.");
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 (similar logic)**
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