@vituum/vite-plugin-latte 1.1.0 → 1.2.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/latte/PlaceholderFunction.php +2 -2
- package/package.json +1 -1
- package/vendor/autoload.php +1 -1
- package/vendor/composer/autoload_classmap.php +51 -0
- package/vendor/composer/autoload_real.php +4 -4
- package/vendor/composer/autoload_static.php +53 -2
- package/vendor/composer/installed.json +96 -7
- package/vendor/composer/installed.php +14 -5
- package/vendor/latte/latte/src/Latte/Compiler/Escaper.php +11 -2
- package/vendor/latte/latte/src/Latte/Compiler/Nodes/Php/FilterNode.php +1 -1
- package/vendor/latte/latte/src/Latte/Compiler/Nodes/Php/ModifierNode.php +2 -2
- package/vendor/latte/latte/src/Latte/Compiler/Nodes/Php/NameNode.php +11 -21
- package/vendor/latte/latte/src/Latte/Compiler/PrintContext.php +1 -1
- package/vendor/latte/latte/src/Latte/Compiler/TagLexer.php +2 -2
- package/vendor/latte/latte/src/Latte/Compiler/TagParserData.php +275 -280
- package/vendor/latte/latte/src/Latte/Compiler/TemplateGenerator.php +4 -4
- package/vendor/latte/latte/src/Latte/Compiler/TemplateLexer.php +10 -2
- package/vendor/latte/latte/src/Latte/Compiler/TemplateParserHtml.php +6 -2
- package/vendor/latte/latte/src/Latte/Engine.php +109 -97
- package/vendor/latte/latte/src/Latte/Essential/AuxiliaryIterator.php +46 -0
- package/vendor/latte/latte/src/Latte/Essential/Blueprint.php +42 -25
- package/vendor/latte/latte/src/Latte/Essential/CoreExtension.php +76 -72
- package/vendor/latte/latte/src/Latte/Essential/Filters.php +95 -37
- package/vendor/latte/latte/src/Latte/Essential/Nodes/ContentTypeNode.php +7 -0
- package/vendor/latte/latte/src/Latte/Essential/Nodes/ForeachNode.php +36 -0
- package/vendor/latte/latte/src/Latte/Essential/Nodes/TemplatePrintNode.php +25 -3
- package/vendor/latte/latte/src/Latte/Essential/Nodes/VarPrintNode.php +9 -2
- package/vendor/latte/latte/src/Latte/Essential/Passes.php +10 -50
- package/vendor/latte/latte/src/Latte/Helpers.php +3 -1
- package/vendor/latte/latte/src/Latte/Loader.php +1 -0
- package/vendor/latte/latte/src/Latte/Loaders/FileLoader.php +1 -2
- package/vendor/latte/latte/src/Latte/Runtime/Filters.php +1 -4
- package/vendor/latte/latte/src/Latte/Runtime/FunctionExecutor.php +68 -0
- package/vendor/latte/latte/src/Latte/Runtime/Template.php +1 -3
- package/vendor/nette/utils/.phpstorm.meta.php +13 -0
- package/vendor/nette/utils/composer.json +51 -0
- package/vendor/nette/utils/license.md +60 -0
- package/vendor/nette/utils/readme.md +56 -0
- package/vendor/nette/utils/src/HtmlStringable.php +22 -0
- package/vendor/nette/utils/src/Iterators/CachingIterator.php +164 -0
- package/vendor/nette/utils/src/Iterators/Mapper.php +34 -0
- package/vendor/nette/utils/src/SmartObject.php +140 -0
- package/vendor/nette/utils/src/StaticClass.php +34 -0
- package/vendor/nette/utils/src/Translator.php +25 -0
- package/vendor/nette/utils/src/Utils/ArrayHash.php +106 -0
- package/vendor/nette/utils/src/Utils/ArrayList.php +136 -0
- package/vendor/nette/utils/src/Utils/Arrays.php +522 -0
- package/vendor/nette/utils/src/Utils/Callback.php +137 -0
- package/vendor/nette/utils/src/Utils/DateTime.php +140 -0
- package/vendor/nette/utils/src/Utils/FileInfo.php +69 -0
- package/vendor/nette/utils/src/Utils/FileSystem.php +326 -0
- package/vendor/nette/utils/src/Utils/Finder.php +510 -0
- package/vendor/nette/utils/src/Utils/Floats.php +107 -0
- package/vendor/nette/utils/src/Utils/Helpers.php +104 -0
- package/vendor/nette/utils/src/Utils/Html.php +839 -0
- package/vendor/nette/utils/src/Utils/Image.php +829 -0
- package/vendor/nette/utils/src/Utils/ImageColor.php +75 -0
- package/vendor/nette/utils/src/Utils/ImageType.php +25 -0
- package/vendor/nette/utils/src/Utils/Iterables.php +159 -0
- package/vendor/nette/utils/src/Utils/Json.php +84 -0
- package/vendor/nette/utils/src/Utils/ObjectHelpers.php +229 -0
- package/vendor/nette/utils/src/Utils/Paginator.php +245 -0
- package/vendor/nette/utils/src/Utils/Random.php +52 -0
- package/vendor/nette/utils/src/Utils/Reflection.php +320 -0
- package/vendor/nette/utils/src/Utils/ReflectionMethod.php +36 -0
- package/vendor/nette/utils/src/Utils/Strings.php +708 -0
- package/vendor/nette/utils/src/Utils/Type.php +267 -0
- package/vendor/nette/utils/src/Utils/Validators.php +416 -0
- package/vendor/nette/utils/src/Utils/exceptions.php +50 -0
- package/vendor/nette/utils/src/compatibility.php +32 -0
- package/vendor/nette/utils/src/exceptions.php +109 -0
|
@@ -11,7 +11,6 @@ namespace Latte\Essential;
|
|
|
11
11
|
|
|
12
12
|
use Latte;
|
|
13
13
|
use Latte\Compiler\Nodes\Php\Scalar;
|
|
14
|
-
use Latte\Compiler\Nodes\TemplateNode;
|
|
15
14
|
use Latte\Compiler\Nodes\TextNode;
|
|
16
15
|
use Latte\Compiler\Tag;
|
|
17
16
|
use Latte\Compiler\TemplateParser;
|
|
@@ -25,21 +24,19 @@ use Nette;
|
|
|
25
24
|
*/
|
|
26
25
|
final class CoreExtension extends Latte\Extension
|
|
27
26
|
{
|
|
28
|
-
private
|
|
29
|
-
private
|
|
30
|
-
private Runtime\Template $template;
|
|
27
|
+
private Latte\Engine $engine;
|
|
28
|
+
private Filters $filters;
|
|
31
29
|
|
|
32
30
|
|
|
33
|
-
public function
|
|
31
|
+
public function __construct()
|
|
34
32
|
{
|
|
35
|
-
$this->
|
|
36
|
-
$this->strict = $engine->isStrictParsing();
|
|
33
|
+
$this->filters = new Filters;
|
|
37
34
|
}
|
|
38
35
|
|
|
39
36
|
|
|
40
|
-
public function
|
|
37
|
+
public function beforeCompile(Latte\Engine $engine): void
|
|
41
38
|
{
|
|
42
|
-
$this->
|
|
39
|
+
$this->engine = $engine;
|
|
43
40
|
}
|
|
44
41
|
|
|
45
42
|
|
|
@@ -109,18 +106,19 @@ final class CoreExtension extends Latte\Extension
|
|
|
109
106
|
public function getFilters(): array
|
|
110
107
|
{
|
|
111
108
|
return [
|
|
112
|
-
'batch' => [
|
|
113
|
-
'breakLines' => [
|
|
114
|
-
'breaklines' => [
|
|
115
|
-
'bytes' => [
|
|
109
|
+
'batch' => [$this->filters, 'batch'],
|
|
110
|
+
'breakLines' => [$this->filters, 'breaklines'],
|
|
111
|
+
'breaklines' => [$this->filters, 'breaklines'],
|
|
112
|
+
'bytes' => [$this->filters, 'bytes'],
|
|
116
113
|
'capitalize' => extension_loaded('mbstring')
|
|
117
|
-
? [
|
|
118
|
-
:
|
|
119
|
-
'ceil' => [
|
|
120
|
-
'
|
|
121
|
-
'
|
|
122
|
-
'
|
|
123
|
-
'
|
|
114
|
+
? [$this->filters, 'capitalize']
|
|
115
|
+
: fn() => throw new RuntimeException('Filter |capitalize requires mbstring extension.'),
|
|
116
|
+
'ceil' => [$this->filters, 'ceil'],
|
|
117
|
+
'checkUrl' => [Latte\Runtime\Filters::class, 'safeUrl'],
|
|
118
|
+
'clamp' => [$this->filters, 'clamp'],
|
|
119
|
+
'dataStream' => [$this->filters, 'dataStream'],
|
|
120
|
+
'datastream' => [$this->filters, 'dataStream'],
|
|
121
|
+
'date' => [$this->filters, 'date'],
|
|
124
122
|
'escape' => [Latte\Runtime\Filters::class, 'nop'],
|
|
125
123
|
'escapeCss' => [Latte\Runtime\Filters::class, 'escapeCss'],
|
|
126
124
|
'escapeHtml' => [Latte\Runtime\Filters::class, 'escapeHtml'],
|
|
@@ -129,50 +127,50 @@ final class CoreExtension extends Latte\Extension
|
|
|
129
127
|
'escapeJs' => [Latte\Runtime\Filters::class, 'escapeJs'],
|
|
130
128
|
'escapeUrl' => 'rawurlencode',
|
|
131
129
|
'escapeXml' => [Latte\Runtime\Filters::class, 'escapeXml'],
|
|
132
|
-
'explode' => [
|
|
133
|
-
'first' => [
|
|
130
|
+
'explode' => [$this->filters, 'explode'],
|
|
131
|
+
'first' => [$this->filters, 'first'],
|
|
134
132
|
'firstUpper' => extension_loaded('mbstring')
|
|
135
|
-
? [
|
|
136
|
-
:
|
|
137
|
-
'floor' => [
|
|
138
|
-
'
|
|
139
|
-
'implode' => [
|
|
140
|
-
'indent' => [
|
|
141
|
-
'join' => [
|
|
142
|
-
'last' => [
|
|
143
|
-
'length' => [
|
|
133
|
+
? [$this->filters, 'firstUpper']
|
|
134
|
+
: fn() => throw new RuntimeException('Filter |firstUpper requires mbstring extension.'),
|
|
135
|
+
'floor' => [$this->filters, 'floor'],
|
|
136
|
+
'group' => [$this->filters, 'group'],
|
|
137
|
+
'implode' => [$this->filters, 'implode'],
|
|
138
|
+
'indent' => [$this->filters, 'indent'],
|
|
139
|
+
'join' => [$this->filters, 'implode'],
|
|
140
|
+
'last' => [$this->filters, 'last'],
|
|
141
|
+
'length' => [$this->filters, 'length'],
|
|
144
142
|
'lower' => extension_loaded('mbstring')
|
|
145
|
-
? [
|
|
146
|
-
:
|
|
143
|
+
? [$this->filters, 'lower']
|
|
144
|
+
: fn() => throw new RuntimeException('Filter |lower requires mbstring extension.'),
|
|
147
145
|
'number' => 'number_format',
|
|
148
|
-
'padLeft' => [
|
|
149
|
-
'padRight' => [
|
|
150
|
-
'query' => [
|
|
151
|
-
'random' => [
|
|
152
|
-
'repeat' => [
|
|
153
|
-
'replace' => [
|
|
154
|
-
'replaceRe' => [
|
|
155
|
-
'replaceRE' => [
|
|
156
|
-
'reverse' => [
|
|
157
|
-
'round' => [
|
|
158
|
-
'slice' => [
|
|
159
|
-
'sort' => [
|
|
160
|
-
'spaceless' => [
|
|
161
|
-
'split' => [
|
|
162
|
-
'strip' => [
|
|
163
|
-
'stripHtml' => [
|
|
164
|
-
'striphtml' => [
|
|
165
|
-
'stripTags' => [
|
|
166
|
-
'striptags' => [
|
|
167
|
-
'substr' => [
|
|
168
|
-
'trim' => [
|
|
169
|
-
'truncate' => [
|
|
146
|
+
'padLeft' => [$this->filters, 'padLeft'],
|
|
147
|
+
'padRight' => [$this->filters, 'padRight'],
|
|
148
|
+
'query' => [$this->filters, 'query'],
|
|
149
|
+
'random' => [$this->filters, 'random'],
|
|
150
|
+
'repeat' => [$this->filters, 'repeat'],
|
|
151
|
+
'replace' => [$this->filters, 'replace'],
|
|
152
|
+
'replaceRe' => [$this->filters, 'replaceRe'],
|
|
153
|
+
'replaceRE' => [$this->filters, 'replaceRe'],
|
|
154
|
+
'reverse' => [$this->filters, 'reverse'],
|
|
155
|
+
'round' => [$this->filters, 'round'],
|
|
156
|
+
'slice' => [$this->filters, 'slice'],
|
|
157
|
+
'sort' => [$this->filters, 'sort'],
|
|
158
|
+
'spaceless' => [$this->filters, 'strip'],
|
|
159
|
+
'split' => [$this->filters, 'explode'],
|
|
160
|
+
'strip' => [$this->filters, 'strip'], // obsolete
|
|
161
|
+
'stripHtml' => [$this->filters, 'stripHtml'],
|
|
162
|
+
'striphtml' => [$this->filters, 'stripHtml'],
|
|
163
|
+
'stripTags' => [$this->filters, 'stripTags'],
|
|
164
|
+
'striptags' => [$this->filters, 'stripTags'],
|
|
165
|
+
'substr' => [$this->filters, 'substring'],
|
|
166
|
+
'trim' => [$this->filters, 'trim'],
|
|
167
|
+
'truncate' => [$this->filters, 'truncate'],
|
|
170
168
|
'upper' => extension_loaded('mbstring')
|
|
171
|
-
? [
|
|
172
|
-
:
|
|
169
|
+
? [$this->filters, 'upper']
|
|
170
|
+
: fn() => throw new RuntimeException('Filter |upper requires mbstring extension.'),
|
|
173
171
|
'webalize' => class_exists(Nette\Utils\Strings::class)
|
|
174
172
|
? [Nette\Utils\Strings::class, 'webalize']
|
|
175
|
-
:
|
|
173
|
+
: fn() => throw new RuntimeException('Filter |webalize requires nette/utils package.'),
|
|
176
174
|
];
|
|
177
175
|
}
|
|
178
176
|
|
|
@@ -180,25 +178,27 @@ final class CoreExtension extends Latte\Extension
|
|
|
180
178
|
public function getFunctions(): array
|
|
181
179
|
{
|
|
182
180
|
return [
|
|
183
|
-
'clamp' => [
|
|
184
|
-
'divisibleBy' => [
|
|
185
|
-
'even' => [
|
|
186
|
-
'first' => [
|
|
187
|
-
'
|
|
188
|
-
'
|
|
189
|
-
'
|
|
190
|
-
'
|
|
181
|
+
'clamp' => [$this->filters, 'clamp'],
|
|
182
|
+
'divisibleBy' => [$this->filters, 'divisibleBy'],
|
|
183
|
+
'even' => [$this->filters, 'even'],
|
|
184
|
+
'first' => [$this->filters, 'first'],
|
|
185
|
+
'group' => [$this->filters, 'group'],
|
|
186
|
+
'last' => [$this->filters, 'last'],
|
|
187
|
+
'odd' => [$this->filters, 'odd'],
|
|
188
|
+
'slice' => [$this->filters, 'slice'],
|
|
189
|
+
'hasBlock' => fn(Runtime\Template $template, string $name): bool => $template->hasBlock($name),
|
|
191
190
|
];
|
|
192
191
|
}
|
|
193
192
|
|
|
194
193
|
|
|
195
194
|
public function getPasses(): array
|
|
196
195
|
{
|
|
196
|
+
$passes = new Passes($this->engine);
|
|
197
197
|
return [
|
|
198
|
-
'internalVariables' =>
|
|
199
|
-
'overwrittenVariables' => [
|
|
200
|
-
'customFunctions' =>
|
|
201
|
-
'moveTemplatePrintToHead' => [
|
|
198
|
+
'internalVariables' => [$passes, 'forbiddenVariablesPass'],
|
|
199
|
+
'overwrittenVariables' => [Nodes\ForeachNode::class, 'overwrittenVariablesPass'],
|
|
200
|
+
'customFunctions' => [$passes, 'customFunctionsPass'],
|
|
201
|
+
'moveTemplatePrintToHead' => [Nodes\TemplatePrintNode::class, 'moveToHeadPass'],
|
|
202
202
|
'nElse' => [Nodes\NElseNode::class, 'processPass'],
|
|
203
203
|
];
|
|
204
204
|
}
|
|
@@ -233,13 +233,17 @@ final class CoreExtension extends Latte\Extension
|
|
|
233
233
|
*/
|
|
234
234
|
private function parseSyntax(Tag $tag, TemplateParser $parser): \Generator
|
|
235
235
|
{
|
|
236
|
+
if ($tag->isNAttribute() && $tag->prefix !== $tag::PrefixNone) {
|
|
237
|
+
throw new Latte\CompileException("Use n:syntax instead of {$tag->getNotation()}", $tag->position);
|
|
238
|
+
}
|
|
236
239
|
$tag->expectArguments();
|
|
237
240
|
$token = $tag->parser->stream->consume();
|
|
238
241
|
$lexer = $parser->getLexer();
|
|
239
|
-
$saved = [$lexer->openDelimiter, $lexer->closeDelimiter];
|
|
240
242
|
$lexer->setSyntax($token->text, $tag->isNAttribute() ? null : $tag->name);
|
|
241
243
|
[$inner] = yield;
|
|
242
|
-
|
|
244
|
+
if (!$tag->isNAttribute()) {
|
|
245
|
+
$lexer->popSyntax();
|
|
246
|
+
}
|
|
243
247
|
return $inner;
|
|
244
248
|
}
|
|
245
249
|
}
|
|
@@ -172,17 +172,12 @@ final class Filters
|
|
|
172
172
|
return null;
|
|
173
173
|
}
|
|
174
174
|
|
|
175
|
-
|
|
176
|
-
$format = Latte\Runtime\Filters::$dateFormat;
|
|
177
|
-
}
|
|
178
|
-
|
|
175
|
+
$format ??= Latte\Runtime\Filters::$dateFormat;
|
|
179
176
|
if ($time instanceof \DateInterval) {
|
|
180
177
|
return $time->format($format);
|
|
181
178
|
|
|
182
179
|
} elseif (is_numeric($time)) {
|
|
183
|
-
$time = new \DateTime(
|
|
184
|
-
$time->setTimeZone(new \DateTimeZone(date_default_timezone_get()));
|
|
185
|
-
|
|
180
|
+
$time = (new \DateTime)->setTimestamp((int) $time);
|
|
186
181
|
} elseif (!$time instanceof \DateTimeInterface) {
|
|
187
182
|
$time = new \DateTime($time);
|
|
188
183
|
}
|
|
@@ -200,7 +195,7 @@ final class Filters
|
|
|
200
195
|
|
|
201
196
|
|
|
202
197
|
/**
|
|
203
|
-
* Converts to human
|
|
198
|
+
* Converts to human-readable file size.
|
|
204
199
|
*/
|
|
205
200
|
public static function bytes(float $bytes, int $precision = 2): string
|
|
206
201
|
{
|
|
@@ -262,10 +257,7 @@ final class Filters
|
|
|
262
257
|
*/
|
|
263
258
|
public static function dataStream(string $data, ?string $type = null): string
|
|
264
259
|
{
|
|
265
|
-
|
|
266
|
-
$type = finfo_buffer(finfo_open(FILEINFO_MIME_TYPE), $data);
|
|
267
|
-
}
|
|
268
|
-
|
|
260
|
+
$type ??= finfo_buffer(finfo_open(FILEINFO_MIME_TYPE), $data);
|
|
269
261
|
return 'data:' . ($type ? "$type;" : '') . 'base64,' . base64_encode($data);
|
|
270
262
|
}
|
|
271
263
|
|
|
@@ -273,7 +265,7 @@ final class Filters
|
|
|
273
265
|
public static function breaklines(string|Stringable|null $s): Html
|
|
274
266
|
{
|
|
275
267
|
$s = htmlspecialchars((string) $s, ENT_NOQUOTES | ENT_SUBSTITUTE, 'UTF-8');
|
|
276
|
-
return new Html(nl2br($s,
|
|
268
|
+
return new Html(nl2br($s, false));
|
|
277
269
|
}
|
|
278
270
|
|
|
279
271
|
|
|
@@ -283,15 +275,11 @@ final class Filters
|
|
|
283
275
|
public static function substring(string|Stringable|null $s, int $start, ?int $length = null): string
|
|
284
276
|
{
|
|
285
277
|
$s = (string) $s;
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
return mb_substr($s, $start, $length, 'UTF-8'); // MB is much faster
|
|
292
|
-
}
|
|
293
|
-
|
|
294
|
-
return iconv_substr($s, $start, $length, 'UTF-8');
|
|
278
|
+
return match (true) {
|
|
279
|
+
extension_loaded('mbstring') => mb_substr($s, $start, $length, 'UTF-8'),
|
|
280
|
+
extension_loaded('iconv') => iconv_substr($s, $start, $length, 'UTF-8'),
|
|
281
|
+
default => throw new Latte\RuntimeException("Filter |substr requires 'mbstring' or 'iconv' extension."),
|
|
282
|
+
};
|
|
295
283
|
}
|
|
296
284
|
|
|
297
285
|
|
|
@@ -422,7 +410,7 @@ final class Filters
|
|
|
422
410
|
/**
|
|
423
411
|
* Reverses string or array.
|
|
424
412
|
*/
|
|
425
|
-
public static function reverse(string|
|
|
413
|
+
public static function reverse(string|iterable $val, bool $preserveKeys = false): string|array
|
|
426
414
|
{
|
|
427
415
|
if (is_array($val)) {
|
|
428
416
|
return array_reverse($val, $preserveKeys);
|
|
@@ -437,7 +425,7 @@ final class Filters
|
|
|
437
425
|
/**
|
|
438
426
|
* Chunks items by returning an array of arrays with the given number of items.
|
|
439
427
|
*/
|
|
440
|
-
public static function batch(
|
|
428
|
+
public static function batch(iterable $list, int $length, $rest = null): \Generator
|
|
441
429
|
{
|
|
442
430
|
$batch = [];
|
|
443
431
|
foreach ($list as $key => $value) {
|
|
@@ -461,14 +449,78 @@ final class Filters
|
|
|
461
449
|
|
|
462
450
|
|
|
463
451
|
/**
|
|
464
|
-
* Sorts
|
|
465
|
-
* @
|
|
466
|
-
* @
|
|
452
|
+
* Sorts elements using the comparison function and preserves the key association.
|
|
453
|
+
* @template K
|
|
454
|
+
* @template V
|
|
455
|
+
* @param iterable<K, V> $data
|
|
456
|
+
* @return iterable<K, V>
|
|
457
|
+
*/
|
|
458
|
+
public static function sort(
|
|
459
|
+
iterable $data,
|
|
460
|
+
?\Closure $comparison = null,
|
|
461
|
+
string|int|\Closure|null $by = null,
|
|
462
|
+
string|int|\Closure|bool $byKey = false,
|
|
463
|
+
): iterable
|
|
464
|
+
{
|
|
465
|
+
if ($byKey !== false) {
|
|
466
|
+
if ($by !== null) {
|
|
467
|
+
throw new \InvalidArgumentException('Filter |sort cannot use both $by and $byKey.');
|
|
468
|
+
}
|
|
469
|
+
$by = $byKey === true ? null : $byKey;
|
|
470
|
+
}
|
|
471
|
+
|
|
472
|
+
$comparison ??= fn($a, $b) => $a <=> $b;
|
|
473
|
+
$comparison = match (true) {
|
|
474
|
+
$by === null => $comparison,
|
|
475
|
+
$by instanceof \Closure => fn($a, $b) => $comparison($by($a), $by($b)),
|
|
476
|
+
default => fn($a, $b) => $comparison(is_array($a) ? $a[$by] : $a->$by, is_array($b) ? $b[$by] : $b->$by),
|
|
477
|
+
};
|
|
478
|
+
|
|
479
|
+
if (is_array($data)) {
|
|
480
|
+
$byKey ? uksort($data, $comparison) : uasort($data, $comparison);
|
|
481
|
+
return $data;
|
|
482
|
+
}
|
|
483
|
+
|
|
484
|
+
$pairs = [];
|
|
485
|
+
foreach ($data as $key => $value) {
|
|
486
|
+
$pairs[] = [$key, $value];
|
|
487
|
+
}
|
|
488
|
+
uasort($pairs, fn($a, $b) => $byKey ? $comparison($a[0], $b[0]) : $comparison($a[1], $b[1]));
|
|
489
|
+
|
|
490
|
+
return new AuxiliaryIterator($pairs);
|
|
491
|
+
}
|
|
492
|
+
|
|
493
|
+
|
|
494
|
+
/**
|
|
495
|
+
* Groups elements by the element indices and preserves the key association and order.
|
|
496
|
+
* @template K
|
|
497
|
+
* @template V
|
|
498
|
+
* @param iterable<K, V> $data
|
|
499
|
+
* @return iterable<iterable<K, V>>
|
|
467
500
|
*/
|
|
468
|
-
public static function
|
|
501
|
+
public static function group(iterable $data, string|int|\Closure $by): iterable
|
|
469
502
|
{
|
|
470
|
-
$
|
|
471
|
-
|
|
503
|
+
$fn = $by instanceof \Closure ? $by : fn($a) => is_array($a) ? $a[$by] : $a->$by;
|
|
504
|
+
$keys = $groups = [];
|
|
505
|
+
|
|
506
|
+
foreach ($data as $k => $v) {
|
|
507
|
+
$groupKey = $fn($v, $k);
|
|
508
|
+
if (!$groups || $prevKey !== $groupKey) {
|
|
509
|
+
$index = array_search($groupKey, $keys, true);
|
|
510
|
+
if ($index === false) {
|
|
511
|
+
$index = count($keys);
|
|
512
|
+
$keys[$index] = $groupKey;
|
|
513
|
+
}
|
|
514
|
+
$prevKey = $groupKey;
|
|
515
|
+
}
|
|
516
|
+
$groups[$index][] = [$k, $v];
|
|
517
|
+
}
|
|
518
|
+
|
|
519
|
+
return new AuxiliaryIterator(array_map(
|
|
520
|
+
fn($key, $group) => [$key, new AuxiliaryIterator($group)],
|
|
521
|
+
$keys,
|
|
522
|
+
$groups,
|
|
523
|
+
));
|
|
472
524
|
}
|
|
473
525
|
|
|
474
526
|
|
|
@@ -524,23 +576,29 @@ final class Filters
|
|
|
524
576
|
|
|
525
577
|
|
|
526
578
|
/**
|
|
527
|
-
* Returns the first
|
|
579
|
+
* Returns the first element in an array or character in a string, or null if none.
|
|
528
580
|
*/
|
|
529
|
-
public static function first(string|
|
|
581
|
+
public static function first(string|iterable $value): mixed
|
|
530
582
|
{
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
583
|
+
if (is_string($value)) {
|
|
584
|
+
return self::substring($value, 0, 1);
|
|
585
|
+
}
|
|
586
|
+
|
|
587
|
+
foreach ($value as $item) {
|
|
588
|
+
return $item;
|
|
589
|
+
}
|
|
590
|
+
|
|
591
|
+
return null;
|
|
534
592
|
}
|
|
535
593
|
|
|
536
594
|
|
|
537
595
|
/**
|
|
538
|
-
* Returns the last
|
|
596
|
+
* Returns the last element in an array or character in a string, or null if none.
|
|
539
597
|
*/
|
|
540
598
|
public static function last(string|array $value): mixed
|
|
541
599
|
{
|
|
542
600
|
return is_array($value)
|
|
543
|
-
? (
|
|
601
|
+
? ($value[array_key_last($value)] ?? null)
|
|
544
602
|
: self::substring($value, -1);
|
|
545
603
|
}
|
|
546
604
|
|
|
@@ -24,6 +24,7 @@ class ContentTypeNode extends StatementNode
|
|
|
24
24
|
{
|
|
25
25
|
public string $contentType;
|
|
26
26
|
public ?string $mimeType = null;
|
|
27
|
+
public bool $inScript;
|
|
27
28
|
|
|
28
29
|
|
|
29
30
|
public static function create(Tag $tag, TemplateParser $parser): static
|
|
@@ -37,6 +38,7 @@ class ContentTypeNode extends StatementNode
|
|
|
37
38
|
}
|
|
38
39
|
|
|
39
40
|
$node = new static;
|
|
41
|
+
$node->inScript = (bool) $tag->htmlElement;
|
|
40
42
|
$node->contentType = match (true) {
|
|
41
43
|
str_contains($type, 'html') => ContentType::Html,
|
|
42
44
|
str_contains($type, 'xml') => ContentType::Xml,
|
|
@@ -56,6 +58,11 @@ class ContentTypeNode extends StatementNode
|
|
|
56
58
|
|
|
57
59
|
public function print(PrintContext $context): string
|
|
58
60
|
{
|
|
61
|
+
if ($this->inScript) {
|
|
62
|
+
$context->getEscaper()->enterHtmlRaw($this->contentType);
|
|
63
|
+
return '';
|
|
64
|
+
}
|
|
65
|
+
|
|
59
66
|
$context->beginEscape()->enterContentType($this->contentType);
|
|
60
67
|
|
|
61
68
|
return $this->mimeType
|
|
@@ -10,11 +10,16 @@ declare(strict_types=1);
|
|
|
10
10
|
namespace Latte\Essential\Nodes;
|
|
11
11
|
|
|
12
12
|
use Latte\CompileException;
|
|
13
|
+
use Latte\Compiler\Node;
|
|
13
14
|
use Latte\Compiler\Nodes\AreaNode;
|
|
15
|
+
use Latte\Compiler\Nodes\AuxiliaryNode;
|
|
14
16
|
use Latte\Compiler\Nodes\NopNode;
|
|
17
|
+
use Latte\Compiler\Nodes\Php\Expression\VariableNode;
|
|
15
18
|
use Latte\Compiler\Nodes\Php\ExpressionNode;
|
|
16
19
|
use Latte\Compiler\Nodes\Php\ListNode;
|
|
17
20
|
use Latte\Compiler\Nodes\StatementNode;
|
|
21
|
+
use Latte\Compiler\Nodes\TemplateNode;
|
|
22
|
+
use Latte\Compiler\NodeTraverser;
|
|
18
23
|
use Latte\Compiler\Position;
|
|
19
24
|
use Latte\Compiler\PrintContext;
|
|
20
25
|
use Latte\Compiler\Tag;
|
|
@@ -143,4 +148,35 @@ class ForeachNode extends StatementNode
|
|
|
143
148
|
yield $this->else;
|
|
144
149
|
}
|
|
145
150
|
}
|
|
151
|
+
|
|
152
|
+
|
|
153
|
+
/**
|
|
154
|
+
* Pass: checks if foreach overrides template variables.
|
|
155
|
+
*/
|
|
156
|
+
public static function overwrittenVariablesPass(TemplateNode $node): void
|
|
157
|
+
{
|
|
158
|
+
$vars = [];
|
|
159
|
+
(new NodeTraverser)->traverse($node, function (Node $node) use (&$vars) {
|
|
160
|
+
if ($node instanceof self && $node->checkArgs) {
|
|
161
|
+
foreach ([$node->key, $node->value] as $var) {
|
|
162
|
+
if ($var instanceof VariableNode) {
|
|
163
|
+
$vars[$var->name][] = $node->position->line;
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
});
|
|
168
|
+
if ($vars) {
|
|
169
|
+
array_unshift($node->head->children, new AuxiliaryNode(fn(PrintContext $context) => $context->format(
|
|
170
|
+
<<<'XX'
|
|
171
|
+
if (!$this->getReferringTemplate() || $this->getReferenceType() === 'extends') {
|
|
172
|
+
foreach (array_intersect_key(%dump, $this->params) as $ʟ_v => $ʟ_l) {
|
|
173
|
+
trigger_error("Variable \$$ʟ_v overwritten in foreach on line $ʟ_l");
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
XX,
|
|
178
|
+
array_map(fn($l) => implode(', ', $l), $vars),
|
|
179
|
+
)));
|
|
180
|
+
}
|
|
181
|
+
}
|
|
146
182
|
}
|
|
@@ -9,15 +9,17 @@ declare(strict_types=1);
|
|
|
9
9
|
|
|
10
10
|
namespace Latte\Essential\Nodes;
|
|
11
11
|
|
|
12
|
+
use Latte\Compiler\Node;
|
|
13
|
+
use Latte\Compiler\Nodes;
|
|
12
14
|
use Latte\Compiler\Nodes\StatementNode;
|
|
13
|
-
use Latte\Compiler\
|
|
15
|
+
use Latte\Compiler\NodeTraverser;
|
|
14
16
|
use Latte\Compiler\PrintContext;
|
|
15
17
|
use Latte\Compiler\Tag;
|
|
16
18
|
use Latte\Compiler\Token;
|
|
17
19
|
|
|
18
20
|
|
|
19
21
|
/**
|
|
20
|
-
* {templatePrint [
|
|
22
|
+
* {templatePrint [ParentClass]}
|
|
21
23
|
*/
|
|
22
24
|
class TemplatePrintNode extends StatementNode
|
|
23
25
|
{
|
|
@@ -34,7 +36,13 @@ class TemplatePrintNode extends StatementNode
|
|
|
34
36
|
|
|
35
37
|
public function print(PrintContext $context): string
|
|
36
38
|
{
|
|
37
|
-
return
|
|
39
|
+
return $context->format(<<<'XX'
|
|
40
|
+
$ʟ_bp = new Latte\Essential\Blueprint;
|
|
41
|
+
$ʟ_bp->printBegin();
|
|
42
|
+
$ʟ_bp->printClass($ʟ_bp->generateTemplateClass($this->getParameters(), extends: %dump));
|
|
43
|
+
$ʟ_bp->printEnd();
|
|
44
|
+
exit;
|
|
45
|
+
XX, $this->template);
|
|
38
46
|
}
|
|
39
47
|
|
|
40
48
|
|
|
@@ -42,4 +50,18 @@ class TemplatePrintNode extends StatementNode
|
|
|
42
50
|
{
|
|
43
51
|
false && yield;
|
|
44
52
|
}
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Pass: moves this node to head.
|
|
57
|
+
*/
|
|
58
|
+
public static function moveToHeadPass(Nodes\TemplateNode $templateNode): void
|
|
59
|
+
{
|
|
60
|
+
(new NodeTraverser)->traverse($templateNode->main, function (Node $node) use ($templateNode) {
|
|
61
|
+
if ($node instanceof self) {
|
|
62
|
+
array_unshift($templateNode->head->children, $node);
|
|
63
|
+
return new Nodes\NopNode;
|
|
64
|
+
}
|
|
65
|
+
});
|
|
66
|
+
}
|
|
45
67
|
}
|
|
@@ -33,9 +33,16 @@ class VarPrintNode extends StatementNode
|
|
|
33
33
|
|
|
34
34
|
public function print(PrintContext $context): string
|
|
35
35
|
{
|
|
36
|
-
$vars = $this->all
|
|
36
|
+
$vars = $this->all
|
|
37
|
+
? 'get_defined_vars()'
|
|
37
38
|
: 'array_diff_key(get_defined_vars(), $this->getParameters())';
|
|
38
|
-
return
|
|
39
|
+
return <<<XX
|
|
40
|
+
\$ʟ_bp = new Latte\\Essential\\Blueprint;
|
|
41
|
+
\$ʟ_bp->printBegin();
|
|
42
|
+
\$ʟ_bp->printVars($vars);
|
|
43
|
+
\$ʟ_bp->printEnd();
|
|
44
|
+
exit;
|
|
45
|
+
XX;
|
|
39
46
|
}
|
|
40
47
|
|
|
41
48
|
|