@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.
Files changed (71) hide show
  1. package/latte/PlaceholderFunction.php +2 -2
  2. package/package.json +1 -1
  3. package/vendor/autoload.php +1 -1
  4. package/vendor/composer/autoload_classmap.php +51 -0
  5. package/vendor/composer/autoload_real.php +4 -4
  6. package/vendor/composer/autoload_static.php +53 -2
  7. package/vendor/composer/installed.json +96 -7
  8. package/vendor/composer/installed.php +14 -5
  9. package/vendor/latte/latte/src/Latte/Compiler/Escaper.php +11 -2
  10. package/vendor/latte/latte/src/Latte/Compiler/Nodes/Php/FilterNode.php +1 -1
  11. package/vendor/latte/latte/src/Latte/Compiler/Nodes/Php/ModifierNode.php +2 -2
  12. package/vendor/latte/latte/src/Latte/Compiler/Nodes/Php/NameNode.php +11 -21
  13. package/vendor/latte/latte/src/Latte/Compiler/PrintContext.php +1 -1
  14. package/vendor/latte/latte/src/Latte/Compiler/TagLexer.php +2 -2
  15. package/vendor/latte/latte/src/Latte/Compiler/TagParserData.php +275 -280
  16. package/vendor/latte/latte/src/Latte/Compiler/TemplateGenerator.php +4 -4
  17. package/vendor/latte/latte/src/Latte/Compiler/TemplateLexer.php +10 -2
  18. package/vendor/latte/latte/src/Latte/Compiler/TemplateParserHtml.php +6 -2
  19. package/vendor/latte/latte/src/Latte/Engine.php +109 -97
  20. package/vendor/latte/latte/src/Latte/Essential/AuxiliaryIterator.php +46 -0
  21. package/vendor/latte/latte/src/Latte/Essential/Blueprint.php +42 -25
  22. package/vendor/latte/latte/src/Latte/Essential/CoreExtension.php +76 -72
  23. package/vendor/latte/latte/src/Latte/Essential/Filters.php +95 -37
  24. package/vendor/latte/latte/src/Latte/Essential/Nodes/ContentTypeNode.php +7 -0
  25. package/vendor/latte/latte/src/Latte/Essential/Nodes/ForeachNode.php +36 -0
  26. package/vendor/latte/latte/src/Latte/Essential/Nodes/TemplatePrintNode.php +25 -3
  27. package/vendor/latte/latte/src/Latte/Essential/Nodes/VarPrintNode.php +9 -2
  28. package/vendor/latte/latte/src/Latte/Essential/Passes.php +10 -50
  29. package/vendor/latte/latte/src/Latte/Helpers.php +3 -1
  30. package/vendor/latte/latte/src/Latte/Loader.php +1 -0
  31. package/vendor/latte/latte/src/Latte/Loaders/FileLoader.php +1 -2
  32. package/vendor/latte/latte/src/Latte/Runtime/Filters.php +1 -4
  33. package/vendor/latte/latte/src/Latte/Runtime/FunctionExecutor.php +68 -0
  34. package/vendor/latte/latte/src/Latte/Runtime/Template.php +1 -3
  35. package/vendor/nette/utils/.phpstorm.meta.php +13 -0
  36. package/vendor/nette/utils/composer.json +51 -0
  37. package/vendor/nette/utils/license.md +60 -0
  38. package/vendor/nette/utils/readme.md +56 -0
  39. package/vendor/nette/utils/src/HtmlStringable.php +22 -0
  40. package/vendor/nette/utils/src/Iterators/CachingIterator.php +164 -0
  41. package/vendor/nette/utils/src/Iterators/Mapper.php +34 -0
  42. package/vendor/nette/utils/src/SmartObject.php +140 -0
  43. package/vendor/nette/utils/src/StaticClass.php +34 -0
  44. package/vendor/nette/utils/src/Translator.php +25 -0
  45. package/vendor/nette/utils/src/Utils/ArrayHash.php +106 -0
  46. package/vendor/nette/utils/src/Utils/ArrayList.php +136 -0
  47. package/vendor/nette/utils/src/Utils/Arrays.php +522 -0
  48. package/vendor/nette/utils/src/Utils/Callback.php +137 -0
  49. package/vendor/nette/utils/src/Utils/DateTime.php +140 -0
  50. package/vendor/nette/utils/src/Utils/FileInfo.php +69 -0
  51. package/vendor/nette/utils/src/Utils/FileSystem.php +326 -0
  52. package/vendor/nette/utils/src/Utils/Finder.php +510 -0
  53. package/vendor/nette/utils/src/Utils/Floats.php +107 -0
  54. package/vendor/nette/utils/src/Utils/Helpers.php +104 -0
  55. package/vendor/nette/utils/src/Utils/Html.php +839 -0
  56. package/vendor/nette/utils/src/Utils/Image.php +829 -0
  57. package/vendor/nette/utils/src/Utils/ImageColor.php +75 -0
  58. package/vendor/nette/utils/src/Utils/ImageType.php +25 -0
  59. package/vendor/nette/utils/src/Utils/Iterables.php +159 -0
  60. package/vendor/nette/utils/src/Utils/Json.php +84 -0
  61. package/vendor/nette/utils/src/Utils/ObjectHelpers.php +229 -0
  62. package/vendor/nette/utils/src/Utils/Paginator.php +245 -0
  63. package/vendor/nette/utils/src/Utils/Random.php +52 -0
  64. package/vendor/nette/utils/src/Utils/Reflection.php +320 -0
  65. package/vendor/nette/utils/src/Utils/ReflectionMethod.php +36 -0
  66. package/vendor/nette/utils/src/Utils/Strings.php +708 -0
  67. package/vendor/nette/utils/src/Utils/Type.php +267 -0
  68. package/vendor/nette/utils/src/Utils/Validators.php +416 -0
  69. package/vendor/nette/utils/src/Utils/exceptions.php +50 -0
  70. package/vendor/nette/utils/src/compatibility.php +32 -0
  71. package/vendor/nette/utils/src/exceptions.php +109 -0
@@ -33,7 +33,7 @@ final class TemplateGenerator
33
33
  public function generate(
34
34
  Nodes\TemplateNode $node,
35
35
  string $className,
36
- ?string $sourceName = null,
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 ($sourceName !== null) {
58
- $this->addConstant('Source', $sourceName);
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
- . ($sourceName === null ? '' : '/** source: ' . str_replace('*/', '* /', $sourceName) . " */\n")
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
- public string $openDelimiter;
39
- public string $closeDelimiter;
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
- $this->parser->getLexer()->setState(TemplateLexer::StateHtmlTag);
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
- $this->parser->getLexer()->setState(TemplateLexer::StateHtmlText);
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.12';
21
- public const VersionId = 30012;
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 \stdClass $functions;
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 \stdClass;
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
- $source = $this->getLoader()->getContent($name);
121
+ $template = $this->getLoader()->getContent($name);
120
122
 
121
123
  try {
122
- $node = $this->parse($source);
124
+ $node = $this->parse($template);
123
125
  $this->applyPasses($node);
124
- $code = $this->generate($node, $name);
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($source, $name);
133
+ throw $e->setSource($template, $name);
132
134
  }
133
135
 
134
136
  if ($this->phpBinary) {
135
- Compiler\PhpHelpers::checkCode($this->phpBinary, $code, "(compiled $name)");
137
+ Compiler\PhpHelpers::checkCode($this->phpBinary, $compiled, "(compiled $name)");
136
138
  }
137
139
 
138
- return $code;
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 $source): TemplateNode
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($source);
160
+ ->parse($template);
159
161
  }
160
162
 
161
163
 
@@ -178,16 +180,15 @@ class Engine
178
180
 
179
181
 
180
182
  /**
181
- * Generates template PHP code.
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
- $sourceName,
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
- $code = $this->compile($name);
217
- if (@eval(substr($code, 5)) === false) { // @ is escalated to exception, substr removes <?php
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($code, "$name (compiled)");
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
- $file = $this->getCacheFile($name);
230
- $lock = defined('PHP_WINDOWS_VERSION_BUILD')
231
- ? $this->acquireLock("$file.lock", LOCK_SH)
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 (!$this->isExpired($file, $name) && (@include $file) !== false) { // @ - file may not exist
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("$file.lock", LOCK_EX);
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($file) || $this->isExpired($file, $name)) {
246
- $code = $this->compile($name);
247
- if (file_put_contents("$file.tmp", $code) !== strlen($code) || !rename("$file.tmp", $file)) {
248
- @unlink("$file.tmp"); // @ - file may not exist
249
- throw new RuntimeException("Unable to create '$file'.");
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($file, true); // @ can be restricted
268
+ @opcache_invalidate($cacheFile, true); // @ can be restricted
254
269
  }
255
270
  }
256
271
 
257
- if ((include $file) === false) {
258
- throw new RuntimeException("Unable to load '$file'.");
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, 'w'); // @ is escalated to exception
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
- private function isExpired(string $file, string $name): bool
301
+ public function getCacheFile(string $name): string
287
302
  {
288
- if (!$this->autoRefresh) {
289
- return false;
290
- }
291
-
292
- $time = @filemtime($file); // @ - file may not exist
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
- return $this->getLoader()->isExpired($name, $time);
310
+ public function getTemplateClass(string $name): string
311
+ {
312
+ return 'Template_' . $this->generateCacheHash($name);
305
313
  }
306
314
 
307
315
 
308
- public function getCacheFile(string $name): string
316
+ private function generateCacheHash(string $name): string
309
317
  {
310
- $hash = substr($this->getTemplateClass($name), 8);
311
- $base = preg_match('#([/\\\\][\w@.-]{3,35}){1,3}$#D', $name, $m)
312
- ? preg_replace('#[^\w@.-]+#', '-', substr($m[0], 1)) . '--'
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
- public function getTemplateClass(string $name): string
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
- $key = [
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
- foreach ($this->extensions as $extension) {
327
- $key[] = [
328
- get_debug_type($extension),
329
- $extension->getCacheKey($this),
330
- ];
331
- }
341
+ }
332
342
 
333
- return 'Template' . substr(md5(serialize($key)), 0, 10);
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 $callback): static
377
+ public function addFilterLoader(callable $loader): static
355
378
  {
356
- $this->filters->add(null, $callback);
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->$name = $value;
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->$name = $callback;
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
- if (!isset($this->functions->$name)) {
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 (array) $this->functions;
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 $value): static
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 = $value;
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 $callback): static
504
+ public function setExceptionHandler(callable $handler): static
489
505
  {
490
- $this->providers->coreExceptionHandler = $callback;
506
+ $this->providers->coreExceptionHandler = $handler;
491
507
  return $this;
492
508
  }
493
509
 
494
510
 
495
- public function setSandboxMode(bool $on = true): static
511
+ public function setSandboxMode(bool $state = true): static
496
512
  {
497
- $this->sandboxed = $on;
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 $on = true): static
538
+ public function setAutoRefresh(bool $state = true): static
523
539
  {
524
- $this->autoRefresh = $on;
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 $on = true): static
548
+ public function setStrictTypes(bool $state = true): static
533
549
  {
534
- $this->strictTypes = $on;
550
+ $this->strictTypes = $state;
535
551
  return $this;
536
552
  }
537
553
 
538
554
 
539
- public function setStrictParsing(bool $on = true): static
555
+ public function setStrictParsing(bool $state = true): static
540
556
  {
541
- $this->strictParsing = $on;
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
- if (!$this->loader) {
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 printClass(Template $template, ?string $name = null): void
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 print template, install package `nette/php-generator`.');
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
- $this->addProperties($class, $template->getParameters());
34
- $functions = array_diff_key((array) $template->global->fn, (new Latte\Essential\CoreExtension)->getFunctions());
35
- $this->addFunctions($class, $functions);
51
+ public function printClass(Php\ClassType $class): void
52
+ {
53
+ $this->printCode((string) $class->getNamespace());
54
+ }
36
55
 
37
- $end = $this->printCanvas();
38
- $this->printHeader('Native types');
39
- $this->printCode((string) $namespace);
40
- echo $end;
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
- continue;
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 printCanvas(): string
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
- return "</div>\n";
154
+ }
155
+
156
+
157
+ public function printEnd(): void
158
+ {
159
+ echo "</div>\n";
143
160
  }
144
161
 
145
162