create-prisma-php-app 1.26.526 → 1.26.527

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.
@@ -12,7 +12,9 @@ class MainLayout
12
12
  public static string $childLayoutChildren = '';
13
13
 
14
14
  private static array $headScripts = [];
15
+ private static array $headScriptsMap = [];
15
16
  private static array $footerScripts = [];
17
+ private static array $footerScriptsMap = [];
16
18
  private static array $customMetadata = [];
17
19
 
18
20
  /**
@@ -24,8 +26,9 @@ class MainLayout
24
26
  public static function addHeadScript(string ...$scripts): void
25
27
  {
26
28
  foreach ($scripts as $script) {
27
- if (!in_array($script, self::$headScripts)) {
29
+ if (!isset(self::$headScriptsMap[$script])) {
28
30
  self::$headScripts[] = $script;
31
+ self::$headScriptsMap[$script] = true;
29
32
  }
30
33
  }
31
34
  }
@@ -43,8 +46,9 @@ class MainLayout
43
46
  public static function addFooterScript(string ...$scripts): void
44
47
  {
45
48
  foreach ($scripts as $script) {
46
- if (!in_array($script, self::$footerScripts)) {
49
+ if (!isset(self::$footerScriptsMap[$script])) {
47
50
  self::$footerScripts[] = $script;
51
+ self::$footerScriptsMap[$script] = true;
48
52
  }
49
53
  }
50
54
  }
@@ -8,7 +8,8 @@ namespace Lib\PHPX;
8
8
  * The interface for the PHPX component classes.
9
9
  */
10
10
 
11
- interface IPHPX {
11
+ interface IPHPX
12
+ {
12
13
  /**
13
14
  * Constructor to initialize the component with the given properties.
14
15
  *
@@ -18,8 +19,10 @@ interface IPHPX {
18
19
 
19
20
  /**
20
21
  * Registers or initializes any necessary components or settings. (Placeholder method).
22
+ *
23
+ * @param array<string, mixed> $props Optional properties to customize the component.
21
24
  */
22
- public static function init(): void;
25
+ public static function init(array $props = []): void;
23
26
 
24
27
  /**
25
28
  * Renders the component with the given properties and children.
@@ -27,4 +30,4 @@ interface IPHPX {
27
30
  * @return string The rendered HTML content.
28
31
  */
29
32
  public function render(): string;
30
- }
33
+ }
@@ -0,0 +1,121 @@
1
+ <?php
2
+
3
+ namespace Lib\PHPX;
4
+
5
+ use Lib\PHPX\IPHPX;
6
+ use Lib\PHPX\TwMerge;
7
+
8
+ class PHPX implements IPHPX
9
+ {
10
+ /**
11
+ * @var array<string, mixed> The properties or attributes passed to the component.
12
+ */
13
+ protected array $props;
14
+
15
+ /**
16
+ * @var mixed The children elements or content to be rendered within the component.
17
+ */
18
+ protected mixed $children;
19
+
20
+ /**
21
+ * @var string The CSS class for custom styling.
22
+ */
23
+ protected string $class;
24
+
25
+ /**
26
+ * Constructor to initialize the component with the given properties.
27
+ *
28
+ * @param array<string, mixed> $props Optional properties to customize the component.
29
+ */
30
+ public function __construct(array $props = [])
31
+ {
32
+ $this->props = $props;
33
+ $this->children = $props['children'] ?? '';
34
+ $this->class = $props['class'] ?? '';
35
+ }
36
+
37
+ /**
38
+ * Registers or initializes any necessary components or settings. (Placeholder method).
39
+ *
40
+ * @param array<string, mixed> $props Optional properties to customize the initialization.
41
+ */
42
+ public static function init(array $props = []): void
43
+ {
44
+ // Register the component or any necessary initialization
45
+ }
46
+
47
+ /**
48
+ * Combines and returns the CSS classes for the component.
49
+ *
50
+ * This method merges the optional base CSS class with additional classes
51
+ * defined in the component's `$class` property. If no base class is provided,
52
+ * only the component's `$class` property will be used. It ensures that there
53
+ * are no duplicate classes and the classes are properly formatted.
54
+ *
55
+ * @param string|null $baseClass The optional base CSS class to be merged. Defaults to `null`.
56
+ * @return string The merged CSS class string.
57
+ */
58
+ protected function getMergeClasses(?string $baseClass = null): string
59
+ {
60
+ return TwMerge::mergeClasses($baseClass, $this->class);
61
+ }
62
+
63
+ /**
64
+ * Generates and returns a string of HTML attributes from the provided props.
65
+ * Excludes 'class' and 'children' props from being added as attributes.
66
+ *
67
+ * @return string The generated HTML attributes.
68
+ */
69
+ protected function getAttributes(): string
70
+ {
71
+ // Filter out 'class' and 'children' props
72
+ $filteredProps = array_filter($this->props, function ($key) {
73
+ return !in_array($key, ['class', 'children']);
74
+ }, ARRAY_FILTER_USE_KEY);
75
+
76
+ // Build attributes string by escaping keys and values
77
+ $attributes = [];
78
+ foreach ($filteredProps as $key => $value) {
79
+ $escapedKey = htmlspecialchars($key, ENT_QUOTES, 'UTF-8');
80
+ $escapedValue = htmlspecialchars((string) $value, ENT_QUOTES, 'UTF-8');
81
+ $attributes[] = "$escapedKey='$escapedValue'";
82
+ }
83
+
84
+ return implode(' ', $attributes);
85
+ }
86
+
87
+ /**
88
+ * Renders the component as an HTML string with the appropriate classes and attributes.
89
+ * Also, allows for dynamic children rendering if a callable is passed.
90
+ *
91
+ * @return string The final rendered HTML of the component.
92
+ */
93
+ public function render(): string
94
+ {
95
+ $attributes = $this->getAttributes();
96
+ $class = $this->getMergeClasses();
97
+
98
+ return <<<HTML
99
+ <div class="$class" $attributes>{$this->children}</div>
100
+ HTML;
101
+ }
102
+
103
+ /**
104
+ * Converts the object to its string representation by rendering the component.
105
+ *
106
+ * This method allows the object to be used directly in string contexts, such as
107
+ * when echoing or concatenating, by automatically invoking the `render()` method.
108
+ * If an exception occurs during rendering, it safely returns an empty string
109
+ * to prevent runtime errors, ensuring robustness in all scenarios.
110
+ *
111
+ * @return string The rendered HTML output of the component, or an empty string if rendering fails.
112
+ */
113
+ public function __toString(): string
114
+ {
115
+ try {
116
+ return $this->render();
117
+ } catch (\Exception) {
118
+ return ''; // Return an empty string or a fallback message in case of errors
119
+ }
120
+ }
121
+ }
@@ -12,6 +12,24 @@ use DOMComment;
12
12
  class TemplateCompiler
13
13
  {
14
14
  protected static $classMappings = [];
15
+ protected static $selfClosingTags = [
16
+ 'area',
17
+ 'base',
18
+ 'br',
19
+ 'col',
20
+ 'command',
21
+ 'embed',
22
+ 'hr',
23
+ 'img',
24
+ 'input',
25
+ 'keygen',
26
+ 'link',
27
+ 'meta',
28
+ 'param',
29
+ 'source',
30
+ 'track',
31
+ 'wbr'
32
+ ];
15
33
 
16
34
  public static function compile(string $templateContent): string
17
35
  {
@@ -26,12 +44,14 @@ class TemplateCompiler
26
44
 
27
45
  if (!$dom->loadXML($wrappedContent)) {
28
46
  $errors = self::getXmlErrors();
29
- throw new \RuntimeException('XML Parsing Failed: ' . implode('; ', $errors));
47
+ throw new \RuntimeException(
48
+ "XML Parsing Failed: " . implode("; ", $errors)
49
+ );
30
50
  }
31
51
  libxml_clear_errors();
32
52
 
33
53
  $root = $dom->documentElement;
34
- $output = '';
54
+ $output = "";
35
55
  foreach ($root->childNodes as $child) {
36
56
  $output .= self::processNode($child);
37
57
  }
@@ -55,21 +75,21 @@ class TemplateCompiler
55
75
  protected static function formatLibxmlError(\LibXMLError $error): string
56
76
  {
57
77
  $errorType = match ($error->level) {
58
- LIBXML_ERR_WARNING => 'Warning',
59
- LIBXML_ERR_ERROR => 'Error',
60
- LIBXML_ERR_FATAL => 'Fatal',
61
- default => 'Unknown',
78
+ LIBXML_ERR_WARNING => "Warning",
79
+ LIBXML_ERR_ERROR => "Error",
80
+ LIBXML_ERR_FATAL => "Fatal",
81
+ default => "Unknown",
62
82
  };
63
83
 
64
84
  // Highlight the tag name in the error message
65
85
  $message = trim($error->message);
66
- if (preg_match('/tag (.*?) /', $message, $matches)) {
86
+ if (preg_match("/tag (.*?) /", $message, $matches)) {
67
87
  $tag = $matches[1];
68
88
  $message = str_replace($tag, "`{$tag}`", $message);
69
89
  }
70
90
 
71
91
  return sprintf(
72
- '[%s] Line %d, Column %d: %s',
92
+ "[%s] Line %d, Column %d: %s",
73
93
  $errorType,
74
94
  $error->line,
75
95
  $error->column,
@@ -79,7 +99,7 @@ class TemplateCompiler
79
99
 
80
100
  protected static function processNode($node): string
81
101
  {
82
- $output = '';
102
+ $output = "";
83
103
 
84
104
  if ($node instanceof DOMElement) {
85
105
  $componentName = $node->nodeName;
@@ -91,7 +111,7 @@ class TemplateCompiler
91
111
  }
92
112
 
93
113
  // Process child nodes
94
- $innerContent = '';
114
+ $innerContent = "";
95
115
  if ($node->hasChildNodes()) {
96
116
  foreach ($node->childNodes as $child) {
97
117
  $innerContent .= self::processNode($child);
@@ -100,11 +120,15 @@ class TemplateCompiler
100
120
 
101
121
  // Include inner content as 'children' if it's not empty
102
122
  if (trim($innerContent)) {
103
- $attributes['children'] = $innerContent;
123
+ $attributes["children"] = $innerContent;
104
124
  }
105
125
 
106
- $output .= self::processComponent($componentName, $attributes, $innerContent);
107
- } else if ($node instanceof DOMComment) {
126
+ $output .= self::processComponent(
127
+ $componentName,
128
+ $attributes,
129
+ $innerContent
130
+ );
131
+ } elseif ($node instanceof DOMComment) {
108
132
  $output .= "<!--{$node->textContent}-->";
109
133
  } else {
110
134
  // For text nodes and others
@@ -114,30 +138,38 @@ class TemplateCompiler
114
138
  return $output;
115
139
  }
116
140
 
117
- protected static function initializeClassMappings()
141
+ protected static function initializeClassMappings(): void
118
142
  {
119
- // Assuming PrismaPHPSettings::$classLogFiles is an associative array
120
- // with class names as keys and class paths as values
121
- foreach (PrismaPHPSettings::$classLogFiles as $classPath => $classInfo) {
122
- $className = pathinfo($classPath, PATHINFO_FILENAME);
143
+ foreach (
144
+ PrismaPHPSettings::$classLogFiles
145
+ as $classPath => $classInfo
146
+ ) {
147
+ $normalizedClassPath = str_replace("\\", "/", $classPath);
148
+ $className = pathinfo($normalizedClassPath, PATHINFO_FILENAME);
123
149
  self::$classMappings[$className] = $classPath;
124
150
  }
125
151
  }
126
152
 
127
- protected static function processComponent(string $componentName, array $attributes, string $innerContent): string
128
- {
153
+ protected static function processComponent(
154
+ string $componentName,
155
+ array $attributes,
156
+ string $innerContent
157
+ ): string {
129
158
  // Check if the component class exists in the mappings
130
- if (isset(self::$classMappings[$componentName]) && class_exists(self::$classMappings[$componentName])) {
159
+ if (
160
+ isset(self::$classMappings[$componentName]) &&
161
+ class_exists(self::$classMappings[$componentName])
162
+ ) {
131
163
  $classPath = self::$classMappings[$componentName];
132
164
  // Instantiate the component
133
165
  $componentInstance = new $classPath($attributes);
134
166
  return $componentInstance->render();
135
167
  } else {
136
- // Render as an XML tag
168
+ // Render as an HTML tag
137
169
  $attributesString = self::renderAttributes($attributes);
138
170
 
139
- // Self-closing tag if no inner content
140
- if (empty($innerContent)) {
171
+ // Determine if the tag should be self-closing
172
+ if (in_array(strtolower($componentName), self::$selfClosingTags)) {
141
173
  return "<$componentName $attributesString />";
142
174
  } else {
143
175
  return "<$componentName $attributesString>$innerContent</$componentName>";
@@ -148,16 +180,16 @@ class TemplateCompiler
148
180
  protected static function renderAttributes(array $attributes): string
149
181
  {
150
182
  if (empty($attributes)) {
151
- return '';
183
+ return "";
152
184
  }
153
185
 
154
186
  $attrArray = [];
155
187
  foreach ($attributes as $key => $value) {
156
- if ($key !== 'children') {
188
+ if ($key !== "children") {
157
189
  $attrArray[] = "{$key}=\"{$value}\"";
158
190
  }
159
191
  }
160
192
 
161
- return ' ' . implode(' ', $attrArray);
193
+ return " " . implode(" ", $attrArray);
162
194
  }
163
195
  }
@@ -0,0 +1,214 @@
1
+ <?php
2
+
3
+ declare(strict_types=1);
4
+
5
+ namespace Lib\PHPX;
6
+
7
+ class TwMerge
8
+ {
9
+ private static $classGroupPatterns = [
10
+ // **General Padding classes**
11
+ "p" => "/^p-/",
12
+
13
+ // **Specific Padding classes**
14
+ "pt" => "/^pt-/",
15
+ "pr" => "/^pr-/",
16
+ "pb" => "/^pb-/",
17
+ "pl" => "/^pl-/",
18
+ "px" => "/^px-/",
19
+ "py" => "/^py-/",
20
+
21
+ // **Margin classes (similar logic)**
22
+ "m" => "/^m-/",
23
+ "mt" => "/^mt-/",
24
+ "mr" => "/^mr-/",
25
+ "mb" => "/^mb-/",
26
+ "ml" => "/^ml-/",
27
+ "mx" => "/^mx-/",
28
+ "my" => "/^my-/",
29
+
30
+ // **Background color classes**
31
+ "bg" => "/^bg-/",
32
+
33
+ // **Text size classes**
34
+ "text-size" => '/^text-(xs|sm|base|lg|xl|[2-9]xl)$/',
35
+
36
+ // **Text color classes**
37
+ "text-color" => '/^text-(?!xs$|sm$|base$|lg$|xl$|[2-9]xl$).+$/',
38
+
39
+ // **Text alignment classes**
40
+ "text-alignment" => '/^text-(left|center|right|justify)$/',
41
+
42
+ // **Text transform classes**
43
+ "text-transform" =>
44
+ '/^text-(uppercase|lowercase|capitalize|normal-case)$/',
45
+
46
+ // **Text decoration classes**
47
+ "text-decoration" => '/^text-(underline|line-through|no-underline)$/',
48
+
49
+ // **Border width classes**
50
+ "border-width" => '/^border(-[0-9]+)?$/',
51
+
52
+ // **Border color classes**
53
+ "border-color" => "/^border-(?![0-9])/",
54
+
55
+ // **Border radius classes**
56
+ "rounded" => '/^rounded(-.*)?$/',
57
+
58
+ // **Font weight classes**
59
+ "font" =>
60
+ '/^font-(thin|extralight|light|normal|medium|semibold|bold|extrabold|black)$/',
61
+
62
+ // **Hover background color classes**
63
+ "hover:bg" => "/^hover:bg-/",
64
+
65
+ // **Hover text color classes**
66
+ "hover:text" => "/^hover:text-/",
67
+
68
+ // **Transition classes**
69
+ "transition" => '/^transition(-[a-z]+)?$/',
70
+
71
+ // **Opacity classes
72
+ "opacity" => '/^opacity(-[0-9]+)?$/',
73
+
74
+ // **Other utility classes can be added here**
75
+ ];
76
+
77
+ private static $conflictGroups = [
78
+ // **Padding conflict groups**
79
+ "p" => ["p", "px", "py", "pt", "pr", "pb", "pl"],
80
+ "px" => ["px", "pl", "pr"],
81
+ "py" => ["py", "pt", "pb"],
82
+ "pt" => ["pt"],
83
+ "pr" => ["pr"],
84
+ "pb" => ["pb"],
85
+ "pl" => ["pl"],
86
+
87
+ // **Margin conflict groups**
88
+ "m" => ["m", "mx", "my", "mt", "mr", "mb", "ml"],
89
+ "mx" => ["mx", "ml", "mr"],
90
+ "my" => ["my", "mt", "mb"],
91
+ "mt" => ["mt"],
92
+ "mr" => ["mr"],
93
+ "mb" => ["mb"],
94
+ "ml" => ["ml"],
95
+
96
+ // **Border width conflict group**
97
+ "border-width" => ["border-width"],
98
+
99
+ // **Border color conflict group**
100
+ "border-color" => ["border-color"],
101
+
102
+ // **Text size conflict group**
103
+ "text-size" => ["text-size"],
104
+
105
+ // **Text color conflict group**
106
+ "text-color" => ["text-color"],
107
+
108
+ // **Text alignment conflict group**
109
+ "text-alignment" => ["text-alignment"],
110
+
111
+ // **Text transform conflict group**
112
+ "text-transform" => ["text-transform"],
113
+
114
+ // **Text decoration conflict group**
115
+ "text-decoration" => ["text-decoration"],
116
+
117
+ // **Opacity conflict group
118
+ "opacity" => ["opacity"],
119
+
120
+ // **Add other conflict groups as needed**
121
+ ];
122
+
123
+ /**
124
+ * Merges multiple CSS class strings or arrays of CSS class strings into a single, optimized CSS class string.
125
+ *
126
+ * This method processes the provided classes, which can be either strings or arrays of strings, removes
127
+ * duplicate or conflicting classes, and prioritizes the last occurrence of a class. It splits class strings
128
+ * by whitespace, handles conflicting class groups, and ensures a clean and well-formatted output.
129
+ *
130
+ * ### Features:
131
+ * - Accepts individual class strings or arrays of class strings.
132
+ * - Automatically handles arrays by flattening them into individual strings.
133
+ * - Removes duplicate or conflicting classes based on class groups.
134
+ * - Combines all classes into a single string, properly formatted and optimized.
135
+ *
136
+ * @param string|array ...$classes The CSS classes to be merged. Each argument can be a string or an array of strings.
137
+ * @return string A single CSS class string with duplicates and conflicts resolved.
138
+ */
139
+ public static function mergeClasses(string|array ...$classes): string
140
+ {
141
+ $classArray = [];
142
+
143
+ foreach ($classes as $class) {
144
+ // Handle arrays by flattening them into strings
145
+ $classList = is_array($class) ? $class : [$class];
146
+ foreach ($classList as $item) {
147
+ if (!empty(trim($item))) {
148
+ // Split the classes by any whitespace characters
149
+ $splitClasses = preg_split("/\s+/", $item);
150
+ foreach ($splitClasses as $individualClass) {
151
+ $classKey = self::getClassGroup($individualClass);
152
+ $conflictingKeys = self::getConflictingKeys($classKey);
153
+
154
+ // Remove any conflicting classes
155
+ foreach ($conflictingKeys as $key) {
156
+ unset($classArray[$key]);
157
+ }
158
+
159
+ // Update the array, prioritizing the last occurrence
160
+ $classArray[$classKey] = $individualClass;
161
+ }
162
+ }
163
+ }
164
+ }
165
+
166
+ // Combine the final classes into a single string
167
+ return implode(" ", array_values($classArray));
168
+ }
169
+
170
+ private static function getClassGroup($class)
171
+ {
172
+ // Match optional prefixes (responsive and variants)
173
+ $pattern = '/^((?:[a-z-]+:)*)(.+)$/';
174
+
175
+ if (preg_match($pattern, $class, $matches)) {
176
+ $prefixes = $matches[1]; // Includes responsive and variant prefixes
177
+ $utilityClass = $matches[2]; // The utility class
178
+
179
+ // Now match utilityClass against patterns
180
+ foreach (self::$classGroupPatterns as $groupKey => $regex) {
181
+ if (preg_match($regex, $utilityClass)) {
182
+ return $prefixes . $groupKey;
183
+ }
184
+ }
185
+
186
+ // If no match, use the full class
187
+ return $prefixes . $utilityClass;
188
+ }
189
+
190
+ // For classes without a recognizable prefix, return the class itself
191
+ return $class;
192
+ }
193
+
194
+ private static function getConflictingKeys($classKey)
195
+ {
196
+ // Remove any responsive or variant prefixes
197
+ $baseClassKey = preg_replace("/^(?:[a-z-]+:)+/", "", $classKey);
198
+
199
+ // Check for conflicts
200
+ if (isset(self::$conflictGroups[$baseClassKey])) {
201
+ // Add responsive and variant prefixes back to the conflicting keys
202
+ $prefix = preg_replace(
203
+ "/" . preg_quote($baseClassKey, "/") . '$/',
204
+ "",
205
+ $classKey
206
+ );
207
+ return array_map(function ($conflict) use ($prefix) {
208
+ return $prefix . $conflict;
209
+ }, self::$conflictGroups[$baseClassKey]);
210
+ }
211
+
212
+ return [$classKey];
213
+ }
214
+ }