@vituum/vite-plugin-latte 1.0.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.
Files changed (123) hide show
  1. package/index.js +2 -2
  2. package/latte/PlaceholderFunction.php +2 -2
  3. package/package.json +10 -11
  4. package/vendor/autoload.php +18 -0
  5. package/vendor/bin/latte-lint +16 -4
  6. package/vendor/composer/ClassLoader.php +72 -65
  7. package/vendor/composer/InstalledVersions.php +21 -12
  8. package/vendor/composer/autoload_classmap.php +14 -8
  9. package/vendor/composer/autoload_namespaces.php +1 -1
  10. package/vendor/composer/autoload_psr4.php +1 -1
  11. package/vendor/composer/autoload_real.php +3 -22
  12. package/vendor/composer/autoload_static.php +13 -7
  13. package/vendor/composer/installed.json +8 -8
  14. package/vendor/composer/installed.php +10 -10
  15. package/vendor/latte/latte/bin/latte-lint +8 -2
  16. package/vendor/latte/latte/composer.json +1 -1
  17. package/vendor/latte/latte/readme.md +6 -6
  18. package/vendor/latte/latte/src/Bridges/Tracy/BlueScreenPanel.php +1 -0
  19. package/vendor/latte/latte/src/Bridges/Tracy/LattePanel.php +3 -2
  20. package/vendor/latte/latte/src/Latte/Compiler/Block.php +0 -3
  21. package/vendor/latte/latte/src/Latte/Compiler/Escaper.php +113 -89
  22. package/vendor/latte/latte/src/Latte/Compiler/ExpressionBuilder.php +2 -1
  23. package/vendor/latte/latte/src/Latte/Compiler/Node.php +0 -4
  24. package/vendor/latte/latte/src/Latte/Compiler/NodeHelpers.php +0 -4
  25. package/vendor/latte/latte/src/Latte/Compiler/NodeTraverser.php +0 -4
  26. package/vendor/latte/latte/src/Latte/Compiler/Nodes/AuxiliaryNode.php +11 -3
  27. package/vendor/latte/latte/src/Latte/Compiler/Nodes/FragmentNode.php +10 -0
  28. package/vendor/latte/latte/src/Latte/Compiler/Nodes/Html/AttributeNode.php +22 -4
  29. package/vendor/latte/latte/src/Latte/Compiler/Nodes/Html/ElementNode.php +35 -12
  30. package/vendor/latte/latte/src/Latte/Compiler/Nodes/Php/ArgumentNode.php +6 -0
  31. package/vendor/latte/latte/src/Latte/Compiler/Nodes/Php/ArrayItemNode.php +12 -0
  32. package/vendor/latte/latte/src/Latte/Compiler/Nodes/Php/Expression/ArrayNode.php +4 -31
  33. package/vendor/latte/latte/src/Latte/Compiler/Nodes/Php/Expression/AssignNode.php +15 -1
  34. package/vendor/latte/latte/src/Latte/Compiler/Nodes/Php/Expression/AssignOpNode.php +11 -0
  35. package/vendor/latte/latte/src/Latte/Compiler/Nodes/Php/Expression/AuxiliaryNode.php +42 -0
  36. package/vendor/latte/latte/src/Latte/Compiler/Nodes/Php/Expression/ClassConstantFetchNode.php +2 -2
  37. package/vendor/latte/latte/src/Latte/Compiler/Nodes/Php/Expression/ClosureNode.php +1 -1
  38. package/vendor/latte/latte/src/Latte/Compiler/Nodes/Php/Expression/ConstantFetchNode.php +8 -0
  39. package/vendor/latte/latte/src/Latte/Compiler/Nodes/Php/Expression/IssetNode.php +15 -1
  40. package/vendor/latte/latte/src/Latte/Compiler/Nodes/Php/Expression/PostOpNode.php +11 -0
  41. package/vendor/latte/latte/src/Latte/Compiler/Nodes/Php/Expression/PreOpNode.php +11 -0
  42. package/vendor/latte/latte/src/Latte/Compiler/Nodes/Php/Expression/{StaticCallNode.php → StaticMethodCallNode.php} +11 -1
  43. package/vendor/latte/latte/src/Latte/Compiler/Nodes/Php/Expression/{StaticCallableNode.php → StaticMethodCallableNode.php} +11 -1
  44. package/vendor/latte/latte/src/Latte/Compiler/Nodes/Php/Expression/TemporaryNode.php +38 -0
  45. package/vendor/latte/latte/src/Latte/Compiler/Nodes/Php/ExpressionNode.php +24 -0
  46. package/vendor/latte/latte/src/Latte/Compiler/Nodes/Php/FilterNode.php +1 -1
  47. package/vendor/latte/latte/src/Latte/Compiler/Nodes/Php/ListItemNode.php +48 -0
  48. package/vendor/latte/latte/src/Latte/Compiler/Nodes/Php/ListNode.php +56 -0
  49. package/vendor/latte/latte/src/Latte/Compiler/Nodes/Php/ModifierNode.php +5 -5
  50. package/vendor/latte/latte/src/Latte/Compiler/Nodes/Php/NameNode.php +11 -21
  51. package/vendor/latte/latte/src/Latte/Compiler/Nodes/Php/Scalar/InterpolatedStringNode.php +1 -8
  52. package/vendor/latte/latte/src/Latte/Compiler/PhpHelpers.php +30 -0
  53. package/vendor/latte/latte/src/Latte/Compiler/Position.php +4 -8
  54. package/vendor/latte/latte/src/Latte/Compiler/PrintContext.php +15 -8
  55. package/vendor/latte/latte/src/Latte/Compiler/Tag.php +13 -14
  56. package/vendor/latte/latte/src/Latte/Compiler/TagLexer.php +5 -9
  57. package/vendor/latte/latte/src/Latte/Compiler/TagParser.php +52 -3
  58. package/vendor/latte/latte/src/Latte/Compiler/TagParserData.php +353 -326
  59. package/vendor/latte/latte/src/Latte/Compiler/TemplateGenerator.php +6 -5
  60. package/vendor/latte/latte/src/Latte/Compiler/TemplateLexer.php +105 -178
  61. package/vendor/latte/latte/src/Latte/Compiler/TemplateParser.php +40 -33
  62. package/vendor/latte/latte/src/Latte/Compiler/TemplateParserHtml.php +186 -126
  63. package/vendor/latte/latte/src/Latte/Compiler/Token.php +5 -9
  64. package/vendor/latte/latte/src/Latte/Compiler/TokenStream.php +6 -22
  65. package/vendor/latte/latte/src/Latte/Engine.php +136 -95
  66. package/vendor/latte/latte/src/Latte/Essential/AuxiliaryIterator.php +46 -0
  67. package/vendor/latte/latte/src/Latte/Essential/Blueprint.php +42 -27
  68. package/vendor/latte/latte/src/Latte/Essential/CachingIterator.php +0 -4
  69. package/vendor/latte/latte/src/Latte/Essential/CoreExtension.php +81 -66
  70. package/vendor/latte/latte/src/Latte/Essential/Filters.php +103 -43
  71. package/vendor/latte/latte/src/Latte/Essential/Nodes/BlockNode.php +1 -2
  72. package/vendor/latte/latte/src/Latte/Essential/Nodes/CaptureNode.php +2 -13
  73. package/vendor/latte/latte/src/Latte/Essential/Nodes/ContentTypeNode.php +8 -1
  74. package/vendor/latte/latte/src/Latte/Essential/Nodes/DefineNode.php +1 -2
  75. package/vendor/latte/latte/src/Latte/Essential/Nodes/EmbedNode.php +1 -1
  76. package/vendor/latte/latte/src/Latte/Essential/Nodes/ExtendsNode.php +0 -3
  77. package/vendor/latte/latte/src/Latte/Essential/Nodes/FirstLastSepNode.php +1 -1
  78. package/vendor/latte/latte/src/Latte/Essential/Nodes/ForNode.php +1 -1
  79. package/vendor/latte/latte/src/Latte/Essential/Nodes/ForeachNode.php +40 -13
  80. package/vendor/latte/latte/src/Latte/Essential/Nodes/IfChangedNode.php +1 -1
  81. package/vendor/latte/latte/src/Latte/Essential/Nodes/IfContentNode.php +4 -1
  82. package/vendor/latte/latte/src/Latte/Essential/Nodes/IfNode.php +5 -3
  83. package/vendor/latte/latte/src/Latte/Essential/Nodes/IncludeBlockNode.php +5 -2
  84. package/vendor/latte/latte/src/Latte/Essential/Nodes/IterateWhileNode.php +6 -4
  85. package/vendor/latte/latte/src/Latte/Essential/Nodes/JumpNode.php +26 -23
  86. package/vendor/latte/latte/src/Latte/Essential/Nodes/NElseNode.php +88 -0
  87. package/vendor/latte/latte/src/Latte/Essential/Nodes/NTagNode.php +20 -28
  88. package/vendor/latte/latte/src/Latte/Essential/Nodes/PrintNode.php +7 -12
  89. package/vendor/latte/latte/src/Latte/Essential/Nodes/RollbackNode.php +1 -1
  90. package/vendor/latte/latte/src/Latte/Essential/Nodes/SpacelessNode.php +1 -1
  91. package/vendor/latte/latte/src/Latte/Essential/Nodes/SwitchNode.php +1 -1
  92. package/vendor/latte/latte/src/Latte/Essential/Nodes/TemplatePrintNode.php +25 -3
  93. package/vendor/latte/latte/src/Latte/Essential/Nodes/TranslateNode.php +1 -1
  94. package/vendor/latte/latte/src/Latte/Essential/Nodes/TryNode.php +3 -4
  95. package/vendor/latte/latte/src/Latte/Essential/Nodes/VarPrintNode.php +9 -2
  96. package/vendor/latte/latte/src/Latte/Essential/Nodes/WhileNode.php +1 -1
  97. package/vendor/latte/latte/src/Latte/Essential/Passes.php +16 -58
  98. package/vendor/latte/latte/src/Latte/Essential/RawPhpExtension.php +0 -2
  99. package/vendor/latte/latte/src/Latte/Essential/TranslatorExtension.php +6 -1
  100. package/vendor/latte/latte/src/Latte/Helpers.php +3 -1
  101. package/vendor/latte/latte/src/Latte/Loader.php +1 -0
  102. package/vendor/latte/latte/src/Latte/Loaders/FileLoader.php +1 -4
  103. package/vendor/latte/latte/src/Latte/Loaders/StringLoader.php +0 -2
  104. package/vendor/latte/latte/src/Latte/PositionAwareException.php +1 -1
  105. package/vendor/latte/latte/src/Latte/Runtime/Block.php +0 -4
  106. package/vendor/latte/latte/src/Latte/Runtime/FilterExecutor.php +43 -51
  107. package/vendor/latte/latte/src/Latte/Runtime/FilterInfo.php +0 -2
  108. package/vendor/latte/latte/src/Latte/Runtime/Filters.php +64 -33
  109. package/vendor/latte/latte/src/Latte/Runtime/FunctionExecutor.php +68 -0
  110. package/vendor/latte/latte/src/Latte/Runtime/Html.php +0 -4
  111. package/vendor/latte/latte/src/Latte/Runtime/Template.php +3 -5
  112. package/vendor/latte/latte/src/Latte/Sandbox/Nodes/FunctionCallNode.php +2 -1
  113. package/vendor/latte/latte/src/Latte/Sandbox/Nodes/MethodCallNode.php +1 -1
  114. package/vendor/latte/latte/src/Latte/Sandbox/Nodes/SandboxNode.php +3 -3
  115. package/vendor/latte/latte/src/Latte/Sandbox/Nodes/{StaticCallNode.php → StaticMethodCallNode.php} +3 -3
  116. package/vendor/latte/latte/src/Latte/Sandbox/Nodes/{StaticCallableNode.php → StaticMethodCallableNode.php} +2 -2
  117. package/vendor/latte/latte/src/Latte/Sandbox/RuntimeChecker.php +0 -2
  118. package/vendor/latte/latte/src/Latte/Sandbox/SandboxExtension.php +11 -9
  119. package/vendor/latte/latte/src/Latte/Sandbox/SecurityPolicy.php +0 -2
  120. package/vendor/latte/latte/src/Latte/exceptions.php +2 -11
  121. package/vendor/latte/latte/src/Tools/Linter.php +13 -37
  122. package/vendor/latte/latte/src/Latte/Compiler/Nodes/Html/QuotedValue.php +0 -53
  123. package/vendor/latte/latte/src/Latte/Strict.php +0 -101
@@ -17,10 +17,13 @@ use Latte\Compiler\Nodes\TemplateNode;
17
17
  */
18
18
  class Engine
19
19
  {
20
- use Strict;
20
+ public const Version = '3.0.16';
21
+ public const VersionId = 30016;
21
22
 
22
- public const VERSION = '3.0.6';
23
- public const VERSION_ID = 30006;
23
+ /** @deprecated use Engine::Version */
24
+ public const
25
+ VERSION = self::Version,
26
+ VERSION_ID = self::VersionId;
24
27
 
25
28
  /** @deprecated use ContentType::* */
26
29
  public const
@@ -33,7 +36,7 @@ class Engine
33
36
 
34
37
  private ?Loader $loader = null;
35
38
  private Runtime\FilterExecutor $filters;
36
- private \stdClass $functions;
39
+ private Runtime\FunctionExecutor $functions;
37
40
  private \stdClass $providers;
38
41
 
39
42
  /** @var Extension[] */
@@ -42,14 +45,17 @@ class Engine
42
45
  private ?string $tempDirectory = null;
43
46
  private bool $autoRefresh = true;
44
47
  private bool $strictTypes = false;
48
+ private bool $strictParsing = false;
45
49
  private ?Policy $policy = null;
46
50
  private bool $sandboxed = false;
51
+ private ?string $phpBinary = null;
52
+ private ?string $cacheKey;
47
53
 
48
54
 
49
55
  public function __construct()
50
56
  {
51
57
  $this->filters = new Runtime\FilterExecutor;
52
- $this->functions = new \stdClass;
58
+ $this->functions = new Runtime\FunctionExecutor;
53
59
  $this->providers = new \stdClass;
54
60
  $this->addExtension(new Essential\CoreExtension);
55
61
  $this->addExtension(new Sandbox\SandboxExtension);
@@ -84,8 +90,9 @@ class Engine
84
90
  * Creates template object.
85
91
  * @param mixed[] $params
86
92
  */
87
- public function createTemplate(string $name, array $params = []): Runtime\Template
93
+ public function createTemplate(string $name, array $params = [], $clearCache = true): Runtime\Template
88
94
  {
95
+ $this->cacheKey = $clearCache ? null : $this->cacheKey;
89
96
  $class = $this->getTemplateClass($name);
90
97
  if (!class_exists($class, false)) {
91
98
  $this->loadTemplate($name);
@@ -111,32 +118,36 @@ class Engine
111
118
  throw new \LogicException('In sandboxed mode you need to set a security policy.');
112
119
  }
113
120
 
114
- $source = $this->getLoader()->getContent($name);
121
+ $template = $this->getLoader()->getContent($name);
115
122
 
116
123
  try {
117
- $node = $this->parse($source);
124
+ $node = $this->parse($template);
118
125
  $this->applyPasses($node);
119
- $code = $this->generate($node, $name);
126
+ $compiled = $this->generate($node, $name);
120
127
 
121
128
  } catch (\Throwable $e) {
122
129
  if (!$e instanceof CompileException && !$e instanceof SecurityViolationException) {
123
130
  $e = new CompileException("Thrown exception '{$e->getMessage()}'", previous: $e);
124
131
  }
125
132
 
126
- throw $e->setSource($source, $name);
133
+ throw $e->setSource($template, $name);
134
+ }
135
+
136
+ if ($this->phpBinary) {
137
+ Compiler\PhpHelpers::checkCode($this->phpBinary, $compiled, "(compiled $name)");
127
138
  }
128
139
 
129
- return $code;
140
+ return $compiled;
130
141
  }
131
142
 
132
143
 
133
144
  /**
134
145
  * Parses template to AST node.
135
146
  */
136
- public function parse(string $source): TemplateNode
147
+ public function parse(string $template): TemplateNode
137
148
  {
138
- $lexer = new Compiler\TemplateLexer;
139
149
  $parser = new Compiler\TemplateParser;
150
+ $parser->strict = $this->strictParsing;
140
151
 
141
152
  foreach ($this->extensions as $extension) {
142
153
  $extension->beforeCompile($this);
@@ -146,7 +157,7 @@ class Engine
146
157
  return $parser
147
158
  ->setContentType($this->contentType)
148
159
  ->setPolicy($this->getPolicy(effective: true))
149
- ->parse($source, $lexer);
160
+ ->parse($template);
150
161
  }
151
162
 
152
163
 
@@ -169,16 +180,15 @@ class Engine
169
180
 
170
181
 
171
182
  /**
172
- * Generates template PHP code.
183
+ * Generates compiled PHP code.
173
184
  */
174
185
  public function generate(TemplateNode $node, string $name): string
175
186
  {
176
- $comment = preg_match('#\n|\?#', $name) ? null : "source: $name";
177
187
  $generator = new Compiler\TemplateGenerator;
178
188
  return $generator->generate(
179
189
  $node,
180
190
  $this->getTemplateClass($name),
181
- $comment,
191
+ $name,
182
192
  $this->strictTypes,
183
193
  );
184
194
  }
@@ -204,10 +214,10 @@ class Engine
204
214
  private function loadTemplate(string $name): void
205
215
  {
206
216
  if (!$this->tempDirectory) {
207
- $code = $this->compile($name);
208
- 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
209
219
  throw (new CompileException('Error in template: ' . error_get_last()['message']))
210
- ->setSource($code, "$name (compiled)");
220
+ ->setSource($compiled, "$name (compiled)");
211
221
  }
212
222
 
213
223
  return;
@@ -217,36 +227,50 @@ class Engine
217
227
  // 1) We want to do as little as possible IO calls on production and also directory and file can be not writable
218
228
  // so on Linux we include the file directly without shared lock, therefore, the file must be created atomically by renaming.
219
229
  // 2) On Windows file cannot be renamed-to while is open (ie by include), so we have to acquire a lock.
220
- $file = $this->getCacheFile($name);
221
- $lock = defined('PHP_WINDOWS_VERSION_BUILD')
222
- ? $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)
223
236
  : null;
224
237
 
225
- 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
+ ) {
226
242
  return;
227
243
  }
228
244
 
229
245
  if ($lock) {
230
246
  flock($lock, LOCK_UN); // release shared lock so we can get exclusive
247
+ fseek($lock, 0);
231
248
  }
232
249
 
233
- $lock = $this->acquireLock("$file.lock", LOCK_EX);
250
+ $lock = $this->acquireLock("$cacheFile.lock", LOCK_EX);
234
251
 
235
252
  // while waiting for exclusive lock, someone might have already created the cache
236
- if (!is_file($file) || $this->isExpired($file, $name)) {
237
- $code = $this->compile($name);
238
- if (file_put_contents("$file.tmp", $code) !== strlen($code) || !rename("$file.tmp", $file)) {
239
- @unlink("$file.tmp"); // @ - file may not exist
240
- 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'.");
241
261
  }
242
262
 
263
+ fseek($lock, 0);
264
+ fwrite($lock, $cacheKey ?? md5(serialize($this->getCacheSignature($name))));
265
+ ftruncate($lock, ftell($lock));
266
+
243
267
  if (function_exists('opcache_invalidate')) {
244
- @opcache_invalidate($file, true); // @ can be restricted
268
+ @opcache_invalidate($cacheFile, true); // @ can be restricted
245
269
  }
246
270
  }
247
271
 
248
- if ((include $file) === false) {
249
- throw new RuntimeException("Unable to load '$file'.");
272
+ if ((include $cacheFile) === false) {
273
+ throw new RuntimeException("Unable to load '$cacheFile'.");
250
274
  }
251
275
 
252
276
  flock($lock, LOCK_UN);
@@ -263,7 +287,7 @@ class Engine
263
287
  throw new RuntimeException("Unable to create directory '$dir'. " . error_get_last()['message']);
264
288
  }
265
289
 
266
- $handle = @fopen($file, 'w'); // @ is escalated to exception
290
+ $handle = @fopen($file, 'c+'); // @ is escalated to exception
267
291
  if (!$handle) {
268
292
  throw new RuntimeException("Unable to create file '$file'. " . error_get_last()['message']);
269
293
  } elseif (!@flock($handle, $mode)) { // @ is escalated to exception
@@ -274,54 +298,62 @@ class Engine
274
298
  }
275
299
 
276
300
 
277
- private function isExpired(string $file, string $name): bool
301
+ public function getCacheFile(string $name): string
278
302
  {
279
- if (!$this->autoRefresh) {
280
- return false;
281
- }
282
-
283
- $time = @filemtime($file); // @ - file may not exist
284
- if ($time === false) {
285
- return true;
286
- }
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
+ }
287
308
 
288
- foreach ($this->extensions as $extension) {
289
- $r = new \ReflectionObject($extension);
290
- if (is_file($r->getFileName()) && filemtime($r->getFileName()) > $time) {
291
- return true;
292
- }
293
- }
294
309
 
295
- return $this->getLoader()->isExpired($name, $time);
310
+ public function getTemplateClass(string $name): string
311
+ {
312
+ return 'Template_' . $this->generateCacheHash($name);
296
313
  }
297
314
 
298
315
 
299
- public function getCacheFile(string $name): string
316
+ private function generateCacheHash(string $name): string
300
317
  {
301
- $hash = substr($this->getTemplateClass($name), 8);
302
- $base = preg_match('#([/\\\\][\w@.-]{3,35}){1,3}$#D', $name, $m)
303
- ? preg_replace('#[^\w@.-]+#', '-', substr($m[0], 1)) . '--'
304
- : '';
305
- 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);
306
321
  }
307
322
 
308
323
 
309
- 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
310
328
  {
311
- $key = [
312
- $this->getLoader()->getUniqueId($name),
313
- self::VERSION,
314
- array_keys((array) $this->functions),
329
+ return [
315
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
+ ),
316
340
  ];
317
- foreach ($this->extensions as $extension) {
318
- $key[] = [
319
- get_debug_type($extension),
320
- $extension->getCacheKey($this),
321
- ];
322
- }
341
+ }
323
342
 
324
- 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
+ ];
325
357
  }
326
358
 
327
359
 
@@ -342,9 +374,9 @@ class Engine
342
374
  /**
343
375
  * Registers filter loader.
344
376
  */
345
- public function addFilterLoader(callable $callback): static
377
+ public function addFilterLoader(callable $loader): static
346
378
  {
347
- $this->filters->add(null, $callback);
379
+ $this->filters->add(null, $loader);
348
380
  return $this;
349
381
  }
350
382
 
@@ -380,7 +412,7 @@ class Engine
380
412
  }
381
413
 
382
414
  foreach ($extension->getFunctions() as $name => $value) {
383
- $this->functions->$name = $value;
415
+ $this->functions->add($name, $value);
384
416
  }
385
417
 
386
418
  foreach ($extension->getProviders() as $name => $value) {
@@ -406,7 +438,7 @@ class Engine
406
438
  throw new \LogicException("Invalid function name '$name'.");
407
439
  }
408
440
 
409
- $this->functions->$name = $callback;
441
+ $this->functions->add($name, $callback);
410
442
  return $this;
411
443
  }
412
444
 
@@ -417,14 +449,7 @@ class Engine
417
449
  */
418
450
  public function invokeFunction(string $name, array $args): mixed
419
451
  {
420
- if (!isset($this->functions->$name)) {
421
- $hint = ($t = Helpers::getSuggestion(array_keys((array) $this->functions), $name))
422
- ? ", did you mean '$t'?"
423
- : '.';
424
- throw new \LogicException("Function '$name' is not defined$hint");
425
- }
426
-
427
- return ($this->functions->$name)(...$args);
452
+ return ($this->functions->$name)(null, ...$args);
428
453
  }
429
454
 
430
455
 
@@ -433,20 +458,20 @@ class Engine
433
458
  */
434
459
  public function getFunctions(): array
435
460
  {
436
- return (array) $this->functions;
461
+ return $this->functions->getAll();
437
462
  }
438
463
 
439
464
 
440
465
  /**
441
466
  * Adds new provider.
442
467
  */
443
- public function addProvider(string $name, mixed $value): static
468
+ public function addProvider(string $name, mixed $provider): static
444
469
  {
445
470
  if (!preg_match('#^[a-z]\w*$#iD', $name)) {
446
471
  throw new \LogicException("Invalid provider name '$name'.");
447
472
  }
448
473
 
449
- $this->providers->$name = $value;
474
+ $this->providers->$name = $provider;
450
475
  return $this;
451
476
  }
452
477
 
@@ -476,16 +501,16 @@ class Engine
476
501
  }
477
502
 
478
503
 
479
- public function setExceptionHandler(callable $callback): static
504
+ public function setExceptionHandler(callable $handler): static
480
505
  {
481
- $this->providers->coreExceptionHandler = $callback;
506
+ $this->providers->coreExceptionHandler = $handler;
482
507
  return $this;
483
508
  }
484
509
 
485
510
 
486
- public function setSandboxMode(bool $on = true): static
511
+ public function setSandboxMode(bool $state = true): static
487
512
  {
488
- $this->sandboxed = $on;
513
+ $this->sandboxed = $state;
489
514
  return $this;
490
515
  }
491
516
 
@@ -510,9 +535,9 @@ class Engine
510
535
  /**
511
536
  * Sets auto-refresh mode.
512
537
  */
513
- public function setAutoRefresh(bool $on = true): static
538
+ public function setAutoRefresh(bool $state = true): static
514
539
  {
515
- $this->autoRefresh = $on;
540
+ $this->autoRefresh = $state;
516
541
  return $this;
517
542
  }
518
543
 
@@ -520,13 +545,26 @@ class Engine
520
545
  /**
521
546
  * Enables declare(strict_types=1) in templates.
522
547
  */
523
- public function setStrictTypes(bool $on = true): static
548
+ public function setStrictTypes(bool $state = true): static
524
549
  {
525
- $this->strictTypes = $on;
550
+ $this->strictTypes = $state;
526
551
  return $this;
527
552
  }
528
553
 
529
554
 
555
+ public function setStrictParsing(bool $state = true): static
556
+ {
557
+ $this->strictParsing = $state;
558
+ return $this;
559
+ }
560
+
561
+
562
+ public function isStrictParsing(): bool
563
+ {
564
+ return $this->strictParsing;
565
+ }
566
+
567
+
530
568
  public function setLoader(Loader $loader): static
531
569
  {
532
570
  $this->loader = $loader;
@@ -536,11 +574,14 @@ class Engine
536
574
 
537
575
  public function getLoader(): Loader
538
576
  {
539
- if (!$this->loader) {
540
- $this->loader = new Loaders\FileLoader;
541
- }
577
+ return $this->loader ??= new Loaders\FileLoader;
578
+ }
579
+
542
580
 
543
- return $this->loader;
581
+ public function enablePhpLinter(?string $phpBinary): static
582
+ {
583
+ $this->phpBinary = $phpBinary;
584
+ return $this;
544
585
  }
545
586
 
546
587
 
@@ -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,26 +18,46 @@ use Nette\PhpGenerator as Php;
20
18
  */
21
19
  final class Blueprint
22
20
  {
23
- use Latte\Strict;
24
-
25
- 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
26
26
  {
27
27
  if (!class_exists(Php\ClassType::class)) {
28
- 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`.');
29
29
  }
30
30
 
31
- $name = $name ?: 'Template';
32
31
  $namespace = new Php\PhpNamespace(Php\Helpers::extractNamespace($name));
33
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
+ }
34
49
 
35
- $this->addProperties($class, $template->getParameters());
36
- $functions = array_diff_key((array) $template->global->fn, (new Latte\Essential\CoreExtension)->getFunctions());
37
- $this->addFunctions($class, $functions);
38
50
 
39
- $end = $this->printCanvas();
40
- $this->printHeader('Native types');
41
- $this->printCode((string) $namespace);
42
- echo $end;
51
+ public function printClass(Php\ClassType $class): void
52
+ {
53
+ $this->printCode((string) $class->getNamespace());
54
+ }
55
+
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>';
43
61
  }
44
62
 
45
63
 
@@ -48,24 +66,16 @@ final class Blueprint
48
66
  */
49
67
  public function printVars(array $vars): void
50
68
  {
51
- if (!class_exists(Php\Type::class)) {
52
- throw new \LogicException('Nette PhpGenerator is required to print template, install package `nette/php-generator`.');
53
- }
54
-
55
69
  $res = '';
56
70
  foreach ($vars as $name => $value) {
57
- if (str_starts_with($name, 'ʟ_')) {
58
- continue;
71
+ if (!str_starts_with($name, 'ʟ_')) {
72
+ $type = $this->getType($value);
73
+ $res .= "{varType $type $$name}\n";
59
74
  }
60
-
61
- $type = $this->getType($value);
62
- $res .= "{varType $type $$name}\n";
63
75
  }
64
76
 
65
- $end = $this->printCanvas();
66
77
  $this->printHeader('varPrint');
67
78
  $this->printCode($res ?: 'No variables', 'latte');
68
- echo $end;
69
79
  }
70
80
 
71
81
 
@@ -136,12 +146,17 @@ final class Blueprint
136
146
  }
137
147
 
138
148
 
139
- public function printCanvas(): string
149
+ public function printBegin(): void
140
150
  {
141
151
  echo '<script src="https://nette.github.io/resources/prism/prism.js"></script>';
142
152
  echo '<link rel="stylesheet" href="https://nette.github.io/resources/prism/prism.css">';
143
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";
144
- return "</div>\n";
154
+ }
155
+
156
+
157
+ public function printEnd(): void
158
+ {
159
+ echo "</div>\n";
145
160
  }
146
161
 
147
162
 
@@ -9,8 +9,6 @@ declare(strict_types=1);
9
9
 
10
10
  namespace Latte\Essential;
11
11
 
12
- use Latte;
13
-
14
12
 
15
13
  /**
16
14
  * Smarter caching iterator.
@@ -29,8 +27,6 @@ use Latte;
29
27
  */
30
28
  class CachingIterator extends \CachingIterator implements \Countable
31
29
  {
32
- use Latte\Strict;
33
-
34
30
  private int $counter = 0;
35
31
  private ?self $parent = null;
36
32