@vituum/vite-plugin-latte 1.1.0 → 1.2.0
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 +2 -0
- package/vendor/composer/autoload_real.php +4 -4
- package/vendor/composer/autoload_static.php +4 -2
- package/vendor/composer/installed.json +7 -7
- package/vendor/composer/installed.php +5 -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
|
@@ -33,7 +33,7 @@ final class TemplateGenerator
|
|
|
33
33
|
public function generate(
|
|
34
34
|
Nodes\TemplateNode $node,
|
|
35
35
|
string $className,
|
|
36
|
-
?string $
|
|
36
|
+
?string $templateName = null,
|
|
37
37
|
bool $strictMode = false,
|
|
38
38
|
): string
|
|
39
39
|
{
|
|
@@ -54,8 +54,8 @@ final class TemplateGenerator
|
|
|
54
54
|
$this->addConstant('ContentType', $node->contentType);
|
|
55
55
|
}
|
|
56
56
|
|
|
57
|
-
if ($
|
|
58
|
-
$this->addConstant('Source', $
|
|
57
|
+
if ($templateName !== null && !preg_match('#\n|\?#', $templateName)) {
|
|
58
|
+
$this->addConstant('Source', $templateName);
|
|
59
59
|
}
|
|
60
60
|
|
|
61
61
|
$this->generateBlocks($context->blocks, $context);
|
|
@@ -80,7 +80,7 @@ final class TemplateGenerator
|
|
|
80
80
|
$code = "<?php\n\n"
|
|
81
81
|
. ($strictMode ? "declare(strict_types=1);\n\n" : '')
|
|
82
82
|
. "use Latte\\Runtime as LR;\n\n"
|
|
83
|
-
. ($
|
|
83
|
+
. ($templateName === null ? '' : '/** source: ' . str_replace('*/', '* /', $templateName) . " */\n")
|
|
84
84
|
. "final class $className extends Latte\\Runtime\\Template\n{\n"
|
|
85
85
|
. implode("\n\n", $members)
|
|
86
86
|
. "\n}\n";
|
|
@@ -35,8 +35,9 @@ final class TemplateLexer
|
|
|
35
35
|
/** HTML attribute name/value (\p{C} means \x00-\x1F except space) */
|
|
36
36
|
private const ReAttrName = '[^\p{C} "\'<>=`/]';
|
|
37
37
|
|
|
38
|
-
|
|
39
|
-
|
|
38
|
+
private string $openDelimiter = '';
|
|
39
|
+
private string $closeDelimiter = '';
|
|
40
|
+
private array $delimiters = [];
|
|
40
41
|
private TagLexer $tagLexer;
|
|
41
42
|
|
|
42
43
|
/** @var array<array{name: string, args: mixed[]}> */
|
|
@@ -278,6 +279,7 @@ final class TemplateLexer
|
|
|
278
279
|
$left = '\{(?![\s\'"{}])';
|
|
279
280
|
$end = $endTag ? '\{/' . preg_quote($endTag, '~') . '\}' : null;
|
|
280
281
|
|
|
282
|
+
$this->delimiters[] = [$this->openDelimiter, $this->closeDelimiter];
|
|
281
283
|
[$this->openDelimiter, $this->closeDelimiter] = match ($type) {
|
|
282
284
|
null => [$left, '\}'], // {...}
|
|
283
285
|
'off' => [$endTag ? '(?=' . $end . ')\{' : '(?!x)x', '\}'],
|
|
@@ -290,6 +292,12 @@ final class TemplateLexer
|
|
|
290
292
|
}
|
|
291
293
|
|
|
292
294
|
|
|
295
|
+
public function popSyntax(): void
|
|
296
|
+
{
|
|
297
|
+
[$this->openDelimiter, $this->closeDelimiter] = array_pop($this->delimiters);
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
|
|
293
301
|
private function normalize(string $str): string
|
|
294
302
|
{
|
|
295
303
|
if (str_starts_with($str, "\u{FEFF}")) { // BOM
|
|
@@ -217,13 +217,17 @@ final class TemplateParserHtml
|
|
|
217
217
|
private function parseEndTag(): array
|
|
218
218
|
{
|
|
219
219
|
$stream = $this->parser->getStream();
|
|
220
|
+
$lexer = $this->parser->getLexer();
|
|
220
221
|
$stream->consume(Token::Html_TagOpen);
|
|
221
|
-
$
|
|
222
|
+
$lexer->setState(TemplateLexer::StateHtmlTag);
|
|
222
223
|
$stream->consume(Token::Slash);
|
|
224
|
+
if (isset($this->element->nAttributes['syntax'])) { // hardcoded
|
|
225
|
+
$lexer->popSyntax();
|
|
226
|
+
}
|
|
223
227
|
$name = $this->parseTagName();
|
|
224
228
|
$stream->tryConsume(Token::Whitespace);
|
|
225
229
|
$stream->consume(Token::Html_TagClose);
|
|
226
|
-
$
|
|
230
|
+
$lexer->setState(TemplateLexer::StateHtmlText);
|
|
227
231
|
return $name;
|
|
228
232
|
}
|
|
229
233
|
|
|
@@ -17,8 +17,8 @@ use Latte\Compiler\Nodes\TemplateNode;
|
|
|
17
17
|
*/
|
|
18
18
|
class Engine
|
|
19
19
|
{
|
|
20
|
-
public const Version = '3.0.
|
|
21
|
-
public const VersionId =
|
|
20
|
+
public const Version = '3.0.16';
|
|
21
|
+
public const VersionId = 30016;
|
|
22
22
|
|
|
23
23
|
/** @deprecated use Engine::Version */
|
|
24
24
|
public const
|
|
@@ -36,7 +36,7 @@ class Engine
|
|
|
36
36
|
|
|
37
37
|
private ?Loader $loader = null;
|
|
38
38
|
private Runtime\FilterExecutor $filters;
|
|
39
|
-
private \
|
|
39
|
+
private Runtime\FunctionExecutor $functions;
|
|
40
40
|
private \stdClass $providers;
|
|
41
41
|
|
|
42
42
|
/** @var Extension[] */
|
|
@@ -49,12 +49,13 @@ class Engine
|
|
|
49
49
|
private ?Policy $policy = null;
|
|
50
50
|
private bool $sandboxed = false;
|
|
51
51
|
private ?string $phpBinary = null;
|
|
52
|
+
private ?string $cacheKey;
|
|
52
53
|
|
|
53
54
|
|
|
54
55
|
public function __construct()
|
|
55
56
|
{
|
|
56
57
|
$this->filters = new Runtime\FilterExecutor;
|
|
57
|
-
$this->functions = new \
|
|
58
|
+
$this->functions = new Runtime\FunctionExecutor;
|
|
58
59
|
$this->providers = new \stdClass;
|
|
59
60
|
$this->addExtension(new Essential\CoreExtension);
|
|
60
61
|
$this->addExtension(new Sandbox\SandboxExtension);
|
|
@@ -89,8 +90,9 @@ class Engine
|
|
|
89
90
|
* Creates template object.
|
|
90
91
|
* @param mixed[] $params
|
|
91
92
|
*/
|
|
92
|
-
public function createTemplate(string $name, array $params = []): Runtime\Template
|
|
93
|
+
public function createTemplate(string $name, array $params = [], $clearCache = true): Runtime\Template
|
|
93
94
|
{
|
|
95
|
+
$this->cacheKey = $clearCache ? null : $this->cacheKey;
|
|
94
96
|
$class = $this->getTemplateClass($name);
|
|
95
97
|
if (!class_exists($class, false)) {
|
|
96
98
|
$this->loadTemplate($name);
|
|
@@ -116,33 +118,33 @@ class Engine
|
|
|
116
118
|
throw new \LogicException('In sandboxed mode you need to set a security policy.');
|
|
117
119
|
}
|
|
118
120
|
|
|
119
|
-
$
|
|
121
|
+
$template = $this->getLoader()->getContent($name);
|
|
120
122
|
|
|
121
123
|
try {
|
|
122
|
-
$node = $this->parse($
|
|
124
|
+
$node = $this->parse($template);
|
|
123
125
|
$this->applyPasses($node);
|
|
124
|
-
$
|
|
126
|
+
$compiled = $this->generate($node, $name);
|
|
125
127
|
|
|
126
128
|
} catch (\Throwable $e) {
|
|
127
129
|
if (!$e instanceof CompileException && !$e instanceof SecurityViolationException) {
|
|
128
130
|
$e = new CompileException("Thrown exception '{$e->getMessage()}'", previous: $e);
|
|
129
131
|
}
|
|
130
132
|
|
|
131
|
-
throw $e->setSource($
|
|
133
|
+
throw $e->setSource($template, $name);
|
|
132
134
|
}
|
|
133
135
|
|
|
134
136
|
if ($this->phpBinary) {
|
|
135
|
-
Compiler\PhpHelpers::checkCode($this->phpBinary, $
|
|
137
|
+
Compiler\PhpHelpers::checkCode($this->phpBinary, $compiled, "(compiled $name)");
|
|
136
138
|
}
|
|
137
139
|
|
|
138
|
-
return $
|
|
140
|
+
return $compiled;
|
|
139
141
|
}
|
|
140
142
|
|
|
141
143
|
|
|
142
144
|
/**
|
|
143
145
|
* Parses template to AST node.
|
|
144
146
|
*/
|
|
145
|
-
public function parse(string $
|
|
147
|
+
public function parse(string $template): TemplateNode
|
|
146
148
|
{
|
|
147
149
|
$parser = new Compiler\TemplateParser;
|
|
148
150
|
$parser->strict = $this->strictParsing;
|
|
@@ -155,7 +157,7 @@ class Engine
|
|
|
155
157
|
return $parser
|
|
156
158
|
->setContentType($this->contentType)
|
|
157
159
|
->setPolicy($this->getPolicy(effective: true))
|
|
158
|
-
->parse($
|
|
160
|
+
->parse($template);
|
|
159
161
|
}
|
|
160
162
|
|
|
161
163
|
|
|
@@ -178,16 +180,15 @@ class Engine
|
|
|
178
180
|
|
|
179
181
|
|
|
180
182
|
/**
|
|
181
|
-
* Generates
|
|
183
|
+
* Generates compiled PHP code.
|
|
182
184
|
*/
|
|
183
185
|
public function generate(TemplateNode $node, string $name): string
|
|
184
186
|
{
|
|
185
|
-
$sourceName = preg_match('#\n|\?#', $name) ? null : $name;
|
|
186
187
|
$generator = new Compiler\TemplateGenerator;
|
|
187
188
|
return $generator->generate(
|
|
188
189
|
$node,
|
|
189
190
|
$this->getTemplateClass($name),
|
|
190
|
-
$
|
|
191
|
+
$name,
|
|
191
192
|
$this->strictTypes,
|
|
192
193
|
);
|
|
193
194
|
}
|
|
@@ -213,10 +214,10 @@ class Engine
|
|
|
213
214
|
private function loadTemplate(string $name): void
|
|
214
215
|
{
|
|
215
216
|
if (!$this->tempDirectory) {
|
|
216
|
-
$
|
|
217
|
-
if (@eval(substr($
|
|
217
|
+
$compiled = $this->compile($name);
|
|
218
|
+
if (@eval(substr($compiled, 5)) === false) { // @ is escalated to exception, substr removes <?php
|
|
218
219
|
throw (new CompileException('Error in template: ' . error_get_last()['message']))
|
|
219
|
-
->setSource($
|
|
220
|
+
->setSource($compiled, "$name (compiled)");
|
|
220
221
|
}
|
|
221
222
|
|
|
222
223
|
return;
|
|
@@ -226,36 +227,50 @@ class Engine
|
|
|
226
227
|
// 1) We want to do as little as possible IO calls on production and also directory and file can be not writable
|
|
227
228
|
// so on Linux we include the file directly without shared lock, therefore, the file must be created atomically by renaming.
|
|
228
229
|
// 2) On Windows file cannot be renamed-to while is open (ie by include), so we have to acquire a lock.
|
|
229
|
-
$
|
|
230
|
-
$
|
|
231
|
-
? $this->
|
|
230
|
+
$cacheFile = $this->getCacheFile($name);
|
|
231
|
+
$cacheKey = $this->autoRefresh
|
|
232
|
+
? md5(serialize($this->getCacheSignature($name)))
|
|
233
|
+
: null;
|
|
234
|
+
$lock = defined('PHP_WINDOWS_VERSION_BUILD') || $this->autoRefresh
|
|
235
|
+
? $this->acquireLock("$cacheFile.lock", LOCK_SH)
|
|
232
236
|
: null;
|
|
233
237
|
|
|
234
|
-
if (
|
|
238
|
+
if (
|
|
239
|
+
!($this->autoRefresh && $cacheKey !== stream_get_contents($lock))
|
|
240
|
+
&& (@include $cacheFile) !== false // @ - file may not exist
|
|
241
|
+
) {
|
|
235
242
|
return;
|
|
236
243
|
}
|
|
237
244
|
|
|
238
245
|
if ($lock) {
|
|
239
246
|
flock($lock, LOCK_UN); // release shared lock so we can get exclusive
|
|
247
|
+
fseek($lock, 0);
|
|
240
248
|
}
|
|
241
249
|
|
|
242
|
-
$lock = $this->acquireLock("$
|
|
250
|
+
$lock = $this->acquireLock("$cacheFile.lock", LOCK_EX);
|
|
243
251
|
|
|
244
252
|
// while waiting for exclusive lock, someone might have already created the cache
|
|
245
|
-
if (!is_file($
|
|
246
|
-
$
|
|
247
|
-
if (
|
|
248
|
-
|
|
249
|
-
|
|
253
|
+
if (!is_file($cacheFile) || ($this->autoRefresh && $cacheKey !== stream_get_contents($lock))) {
|
|
254
|
+
$compiled = $this->compile($name);
|
|
255
|
+
if (
|
|
256
|
+
file_put_contents("$cacheFile.tmp", $compiled) !== strlen($compiled)
|
|
257
|
+
|| !rename("$cacheFile.tmp", $cacheFile)
|
|
258
|
+
) {
|
|
259
|
+
@unlink("$cacheFile.tmp"); // @ - file may not exist
|
|
260
|
+
throw new RuntimeException("Unable to create '$cacheFile'.");
|
|
250
261
|
}
|
|
251
262
|
|
|
263
|
+
fseek($lock, 0);
|
|
264
|
+
fwrite($lock, $cacheKey ?? md5(serialize($this->getCacheSignature($name))));
|
|
265
|
+
ftruncate($lock, ftell($lock));
|
|
266
|
+
|
|
252
267
|
if (function_exists('opcache_invalidate')) {
|
|
253
|
-
@opcache_invalidate($
|
|
268
|
+
@opcache_invalidate($cacheFile, true); // @ can be restricted
|
|
254
269
|
}
|
|
255
270
|
}
|
|
256
271
|
|
|
257
|
-
if ((include $
|
|
258
|
-
throw new RuntimeException("Unable to load '$
|
|
272
|
+
if ((include $cacheFile) === false) {
|
|
273
|
+
throw new RuntimeException("Unable to load '$cacheFile'.");
|
|
259
274
|
}
|
|
260
275
|
|
|
261
276
|
flock($lock, LOCK_UN);
|
|
@@ -272,7 +287,7 @@ class Engine
|
|
|
272
287
|
throw new RuntimeException("Unable to create directory '$dir'. " . error_get_last()['message']);
|
|
273
288
|
}
|
|
274
289
|
|
|
275
|
-
$handle = @fopen($file, '
|
|
290
|
+
$handle = @fopen($file, 'c+'); // @ is escalated to exception
|
|
276
291
|
if (!$handle) {
|
|
277
292
|
throw new RuntimeException("Unable to create file '$file'. " . error_get_last()['message']);
|
|
278
293
|
} elseif (!@flock($handle, $mode)) { // @ is escalated to exception
|
|
@@ -283,54 +298,62 @@ class Engine
|
|
|
283
298
|
}
|
|
284
299
|
|
|
285
300
|
|
|
286
|
-
|
|
301
|
+
public function getCacheFile(string $name): string
|
|
287
302
|
{
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
if ($time === false) {
|
|
294
|
-
return true;
|
|
295
|
-
}
|
|
303
|
+
$base = preg_match('#([/\\\\][\w@.-]{3,35}){1,3}$#D', '/' . $name, $m)
|
|
304
|
+
? preg_replace('#[^\w@.-]+#', '-', substr($m[0], 1)) . '--'
|
|
305
|
+
: '';
|
|
306
|
+
return $this->tempDirectory . '/' . $base . $this->generateCacheHash($name) . '.php';
|
|
307
|
+
}
|
|
296
308
|
|
|
297
|
-
foreach ($this->extensions as $extension) {
|
|
298
|
-
$r = new \ReflectionObject($extension);
|
|
299
|
-
if (is_file($r->getFileName()) && filemtime($r->getFileName()) > $time) {
|
|
300
|
-
return true;
|
|
301
|
-
}
|
|
302
|
-
}
|
|
303
309
|
|
|
304
|
-
|
|
310
|
+
public function getTemplateClass(string $name): string
|
|
311
|
+
{
|
|
312
|
+
return 'Template_' . $this->generateCacheHash($name);
|
|
305
313
|
}
|
|
306
314
|
|
|
307
315
|
|
|
308
|
-
|
|
316
|
+
private function generateCacheHash(string $name): string
|
|
309
317
|
{
|
|
310
|
-
$
|
|
311
|
-
$
|
|
312
|
-
|
|
313
|
-
: '';
|
|
314
|
-
return "$this->tempDirectory/$base$hash.php";
|
|
318
|
+
$this->cacheKey ??= md5(serialize($this->getCacheKey()));
|
|
319
|
+
$hash = $this->cacheKey . $this->getLoader()->getUniqueId($name);
|
|
320
|
+
return substr(md5($hash), 0, 10);
|
|
315
321
|
}
|
|
316
322
|
|
|
317
323
|
|
|
318
|
-
|
|
324
|
+
/**
|
|
325
|
+
* Values that affect the results of compilation and the name of the cache file.
|
|
326
|
+
*/
|
|
327
|
+
protected function getCacheKey(): array
|
|
319
328
|
{
|
|
320
|
-
|
|
321
|
-
$this->getLoader()->getUniqueId($name),
|
|
322
|
-
self::Version,
|
|
323
|
-
array_keys((array) $this->functions),
|
|
329
|
+
return [
|
|
324
330
|
$this->contentType,
|
|
331
|
+
array_keys($this->getFunctions()),
|
|
332
|
+
array_map(
|
|
333
|
+
fn($extension) => [
|
|
334
|
+
get_debug_type($extension),
|
|
335
|
+
$extension->getCacheKey($this),
|
|
336
|
+
filemtime((new \ReflectionObject($extension))->getFileName()),
|
|
337
|
+
],
|
|
338
|
+
$this->extensions,
|
|
339
|
+
),
|
|
325
340
|
];
|
|
326
|
-
|
|
327
|
-
$key[] = [
|
|
328
|
-
get_debug_type($extension),
|
|
329
|
-
$extension->getCacheKey($this),
|
|
330
|
-
];
|
|
331
|
-
}
|
|
341
|
+
}
|
|
332
342
|
|
|
333
|
-
|
|
343
|
+
|
|
344
|
+
/**
|
|
345
|
+
* Values that check the expiration of the compiled template.
|
|
346
|
+
*/
|
|
347
|
+
protected function getCacheSignature(string $name): array
|
|
348
|
+
{
|
|
349
|
+
return [
|
|
350
|
+
self::Version,
|
|
351
|
+
$this->getLoader()->getContent($name),
|
|
352
|
+
array_map(
|
|
353
|
+
fn($extension) => filemtime((new \ReflectionObject($extension))->getFileName()),
|
|
354
|
+
$this->extensions,
|
|
355
|
+
),
|
|
356
|
+
];
|
|
334
357
|
}
|
|
335
358
|
|
|
336
359
|
|
|
@@ -351,9 +374,9 @@ class Engine
|
|
|
351
374
|
/**
|
|
352
375
|
* Registers filter loader.
|
|
353
376
|
*/
|
|
354
|
-
public function addFilterLoader(callable $
|
|
377
|
+
public function addFilterLoader(callable $loader): static
|
|
355
378
|
{
|
|
356
|
-
$this->filters->add(null, $
|
|
379
|
+
$this->filters->add(null, $loader);
|
|
357
380
|
return $this;
|
|
358
381
|
}
|
|
359
382
|
|
|
@@ -389,7 +412,7 @@ class Engine
|
|
|
389
412
|
}
|
|
390
413
|
|
|
391
414
|
foreach ($extension->getFunctions() as $name => $value) {
|
|
392
|
-
$this->functions
|
|
415
|
+
$this->functions->add($name, $value);
|
|
393
416
|
}
|
|
394
417
|
|
|
395
418
|
foreach ($extension->getProviders() as $name => $value) {
|
|
@@ -415,7 +438,7 @@ class Engine
|
|
|
415
438
|
throw new \LogicException("Invalid function name '$name'.");
|
|
416
439
|
}
|
|
417
440
|
|
|
418
|
-
$this->functions
|
|
441
|
+
$this->functions->add($name, $callback);
|
|
419
442
|
return $this;
|
|
420
443
|
}
|
|
421
444
|
|
|
@@ -426,14 +449,7 @@ class Engine
|
|
|
426
449
|
*/
|
|
427
450
|
public function invokeFunction(string $name, array $args): mixed
|
|
428
451
|
{
|
|
429
|
-
|
|
430
|
-
$hint = ($t = Helpers::getSuggestion(array_keys((array) $this->functions), $name))
|
|
431
|
-
? ", did you mean '$t'?"
|
|
432
|
-
: '.';
|
|
433
|
-
throw new \LogicException("Function '$name' is not defined$hint");
|
|
434
|
-
}
|
|
435
|
-
|
|
436
|
-
return ($this->functions->$name)(...$args);
|
|
452
|
+
return ($this->functions->$name)(null, ...$args);
|
|
437
453
|
}
|
|
438
454
|
|
|
439
455
|
|
|
@@ -442,20 +458,20 @@ class Engine
|
|
|
442
458
|
*/
|
|
443
459
|
public function getFunctions(): array
|
|
444
460
|
{
|
|
445
|
-
return
|
|
461
|
+
return $this->functions->getAll();
|
|
446
462
|
}
|
|
447
463
|
|
|
448
464
|
|
|
449
465
|
/**
|
|
450
466
|
* Adds new provider.
|
|
451
467
|
*/
|
|
452
|
-
public function addProvider(string $name, mixed $
|
|
468
|
+
public function addProvider(string $name, mixed $provider): static
|
|
453
469
|
{
|
|
454
470
|
if (!preg_match('#^[a-z]\w*$#iD', $name)) {
|
|
455
471
|
throw new \LogicException("Invalid provider name '$name'.");
|
|
456
472
|
}
|
|
457
473
|
|
|
458
|
-
$this->providers->$name = $
|
|
474
|
+
$this->providers->$name = $provider;
|
|
459
475
|
return $this;
|
|
460
476
|
}
|
|
461
477
|
|
|
@@ -485,16 +501,16 @@ class Engine
|
|
|
485
501
|
}
|
|
486
502
|
|
|
487
503
|
|
|
488
|
-
public function setExceptionHandler(callable $
|
|
504
|
+
public function setExceptionHandler(callable $handler): static
|
|
489
505
|
{
|
|
490
|
-
$this->providers->coreExceptionHandler = $
|
|
506
|
+
$this->providers->coreExceptionHandler = $handler;
|
|
491
507
|
return $this;
|
|
492
508
|
}
|
|
493
509
|
|
|
494
510
|
|
|
495
|
-
public function setSandboxMode(bool $
|
|
511
|
+
public function setSandboxMode(bool $state = true): static
|
|
496
512
|
{
|
|
497
|
-
$this->sandboxed = $
|
|
513
|
+
$this->sandboxed = $state;
|
|
498
514
|
return $this;
|
|
499
515
|
}
|
|
500
516
|
|
|
@@ -519,9 +535,9 @@ class Engine
|
|
|
519
535
|
/**
|
|
520
536
|
* Sets auto-refresh mode.
|
|
521
537
|
*/
|
|
522
|
-
public function setAutoRefresh(bool $
|
|
538
|
+
public function setAutoRefresh(bool $state = true): static
|
|
523
539
|
{
|
|
524
|
-
$this->autoRefresh = $
|
|
540
|
+
$this->autoRefresh = $state;
|
|
525
541
|
return $this;
|
|
526
542
|
}
|
|
527
543
|
|
|
@@ -529,16 +545,16 @@ class Engine
|
|
|
529
545
|
/**
|
|
530
546
|
* Enables declare(strict_types=1) in templates.
|
|
531
547
|
*/
|
|
532
|
-
public function setStrictTypes(bool $
|
|
548
|
+
public function setStrictTypes(bool $state = true): static
|
|
533
549
|
{
|
|
534
|
-
$this->strictTypes = $
|
|
550
|
+
$this->strictTypes = $state;
|
|
535
551
|
return $this;
|
|
536
552
|
}
|
|
537
553
|
|
|
538
554
|
|
|
539
|
-
public function setStrictParsing(bool $
|
|
555
|
+
public function setStrictParsing(bool $state = true): static
|
|
540
556
|
{
|
|
541
|
-
$this->strictParsing = $
|
|
557
|
+
$this->strictParsing = $state;
|
|
542
558
|
return $this;
|
|
543
559
|
}
|
|
544
560
|
|
|
@@ -558,11 +574,7 @@ class Engine
|
|
|
558
574
|
|
|
559
575
|
public function getLoader(): Loader
|
|
560
576
|
{
|
|
561
|
-
|
|
562
|
-
$this->loader = new Loaders\FileLoader;
|
|
563
|
-
}
|
|
564
|
-
|
|
565
|
-
return $this->loader;
|
|
577
|
+
return $this->loader ??= new Loaders\FileLoader;
|
|
566
578
|
}
|
|
567
579
|
|
|
568
580
|
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
<?php
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* This file is part of the Latte (https://latte.nette.org)
|
|
5
|
+
* Copyright (c) 2008 David Grudl (https://davidgrudl.com)
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
declare(strict_types=1);
|
|
9
|
+
|
|
10
|
+
namespace Latte\Essential;
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Iterates over key-value pairs.
|
|
15
|
+
* @internal
|
|
16
|
+
* @template K
|
|
17
|
+
* @template V
|
|
18
|
+
* @implements \IteratorAggregate<K, V>
|
|
19
|
+
*/
|
|
20
|
+
class AuxiliaryIterator implements \IteratorAggregate, \Countable
|
|
21
|
+
{
|
|
22
|
+
/**
|
|
23
|
+
* @param array<array{K, V}> $pairs
|
|
24
|
+
*/
|
|
25
|
+
public function __construct(
|
|
26
|
+
private array $pairs,
|
|
27
|
+
) {
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* @return \Generator<K, V>
|
|
33
|
+
*/
|
|
34
|
+
public function getIterator(): \Generator
|
|
35
|
+
{
|
|
36
|
+
foreach ($this->pairs as [$key, $value]) {
|
|
37
|
+
yield $key => $value;
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
public function count(): int
|
|
43
|
+
{
|
|
44
|
+
return count($this->pairs);
|
|
45
|
+
}
|
|
46
|
+
}
|
|
@@ -9,8 +9,6 @@ declare(strict_types=1);
|
|
|
9
9
|
|
|
10
10
|
namespace Latte\Essential;
|
|
11
11
|
|
|
12
|
-
use Latte;
|
|
13
|
-
use Latte\Runtime\Template;
|
|
14
12
|
use Nette\PhpGenerator as Php;
|
|
15
13
|
|
|
16
14
|
|
|
@@ -20,24 +18,46 @@ use Nette\PhpGenerator as Php;
|
|
|
20
18
|
*/
|
|
21
19
|
final class Blueprint
|
|
22
20
|
{
|
|
23
|
-
public function
|
|
21
|
+
public function generateTemplateClass(
|
|
22
|
+
array $params,
|
|
23
|
+
?string $name = 'Template',
|
|
24
|
+
?string $extends = null,
|
|
25
|
+
): Php\ClassType
|
|
24
26
|
{
|
|
25
27
|
if (!class_exists(Php\ClassType::class)) {
|
|
26
|
-
throw new \LogicException('Nette PhpGenerator is required to
|
|
28
|
+
throw new \LogicException('Nette PhpGenerator is required to generate blueprint, install package `nette/php-generator`.');
|
|
27
29
|
}
|
|
28
30
|
|
|
29
|
-
$name = $name ?: 'Template';
|
|
30
31
|
$namespace = new Php\PhpNamespace(Php\Helpers::extractNamespace($name));
|
|
31
32
|
$class = $namespace->addClass(Php\Helpers::extractShortName($name));
|
|
33
|
+
if ($extends) {
|
|
34
|
+
if (class_exists($extends)) {
|
|
35
|
+
if ((new \ReflectionClass($extends))->isFinal()) {
|
|
36
|
+
throw new \LogicException("Blueprint error: Unable to extend final class $extends");
|
|
37
|
+
}
|
|
38
|
+
$class->setExtends($extends);
|
|
39
|
+
} elseif (trait_exists($extends)) {
|
|
40
|
+
$class->addTrait($extends);
|
|
41
|
+
} else {
|
|
42
|
+
throw new \LogicException("Blueprint error: Class '$extends' doesn't exist.");
|
|
43
|
+
}
|
|
44
|
+
$params = array_diff_key($params, get_class_vars($extends));
|
|
45
|
+
}
|
|
46
|
+
$this->addProperties($class, $params);
|
|
47
|
+
return $class;
|
|
48
|
+
}
|
|
49
|
+
|
|
32
50
|
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
$this->
|
|
51
|
+
public function printClass(Php\ClassType $class): void
|
|
52
|
+
{
|
|
53
|
+
$this->printCode((string) $class->getNamespace());
|
|
54
|
+
}
|
|
36
55
|
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
56
|
+
|
|
57
|
+
public function clickableFile(string $file, int $line = 1): string
|
|
58
|
+
{
|
|
59
|
+
$link = 'editor://open/?file=' . rawurlencode(strtr($file, '/', DIRECTORY_SEPARATOR)) . '&line=' . $line;
|
|
60
|
+
return '<a href="' . htmlspecialchars($link) . '">' . htmlspecialchars($file) . '</a>';
|
|
41
61
|
}
|
|
42
62
|
|
|
43
63
|
|
|
@@ -46,24 +66,16 @@ final class Blueprint
|
|
|
46
66
|
*/
|
|
47
67
|
public function printVars(array $vars): void
|
|
48
68
|
{
|
|
49
|
-
if (!class_exists(Php\Type::class)) {
|
|
50
|
-
throw new \LogicException('Nette PhpGenerator is required to print template, install package `nette/php-generator`.');
|
|
51
|
-
}
|
|
52
|
-
|
|
53
69
|
$res = '';
|
|
54
70
|
foreach ($vars as $name => $value) {
|
|
55
|
-
if (str_starts_with($name, 'ʟ_')) {
|
|
56
|
-
|
|
71
|
+
if (!str_starts_with($name, 'ʟ_')) {
|
|
72
|
+
$type = $this->getType($value);
|
|
73
|
+
$res .= "{varType $type $$name}\n";
|
|
57
74
|
}
|
|
58
|
-
|
|
59
|
-
$type = $this->getType($value);
|
|
60
|
-
$res .= "{varType $type $$name}\n";
|
|
61
75
|
}
|
|
62
76
|
|
|
63
|
-
$end = $this->printCanvas();
|
|
64
77
|
$this->printHeader('varPrint');
|
|
65
78
|
$this->printCode($res ?: 'No variables', 'latte');
|
|
66
|
-
echo $end;
|
|
67
79
|
}
|
|
68
80
|
|
|
69
81
|
|
|
@@ -134,12 +146,17 @@ final class Blueprint
|
|
|
134
146
|
}
|
|
135
147
|
|
|
136
148
|
|
|
137
|
-
public function
|
|
149
|
+
public function printBegin(): void
|
|
138
150
|
{
|
|
139
151
|
echo '<script src="https://nette.github.io/resources/prism/prism.js"></script>';
|
|
140
152
|
echo '<link rel="stylesheet" href="https://nette.github.io/resources/prism/prism.css">';
|
|
141
153
|
echo "<div style='all:initial;position:fixed;overflow:auto;z-index:1000;left:0;right:0;top:0;bottom:0;color:black;background:white;padding:1em'>\n";
|
|
142
|
-
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
|
|
157
|
+
public function printEnd(): void
|
|
158
|
+
{
|
|
159
|
+
echo "</div>\n";
|
|
143
160
|
}
|
|
144
161
|
|
|
145
162
|
|