@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
@@ -10,10 +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;
19
+ use Latte\Compiler\Nodes\Php\ListNode;
16
20
  use Latte\Compiler\Nodes\StatementNode;
21
+ use Latte\Compiler\Nodes\TemplateNode;
22
+ use Latte\Compiler\NodeTraverser;
17
23
  use Latte\Compiler\Position;
18
24
  use Latte\Compiler\PrintContext;
19
25
  use Latte\Compiler\Tag;
@@ -28,7 +34,7 @@ class ForeachNode extends StatementNode
28
34
  public ExpressionNode $expression;
29
35
  public ?ExpressionNode $key = null;
30
36
  public bool $byRef = false;
31
- public ExpressionNode $value;
37
+ public ExpressionNode|ListNode $value;
32
38
  public AreaNode $content;
33
39
  public ?AreaNode $else = null;
34
40
  public ?Position $elseLine = null;
@@ -40,9 +46,8 @@ class ForeachNode extends StatementNode
40
46
  public static function create(Tag $tag): \Generator
41
47
  {
42
48
  $tag->expectArguments();
43
- $node = new static;
49
+ $node = $tag->node = new static;
44
50
  self::parseArguments($tag->parser, $node);
45
- $tag->data->iterateWhile = [$node->key, $node->value];
46
51
 
47
52
  $modifier = $tag->parser->parseModifier();
48
53
  foreach ($modifier->filters as $filter) {
@@ -73,16 +78,7 @@ class ForeachNode extends StatementNode
73
78
  $stream = $parser->stream;
74
79
  $node->expression = $parser->parseExpression();
75
80
  $stream->consume('as');
76
- if (!$stream->is('&')) {
77
- $node->value = $parser->parseExpression();
78
- if (!$stream->tryConsume('=>')) {
79
- return;
80
- }
81
- $node->key = $node->value;
82
- }
83
-
84
- $node->byRef = (bool) $stream->tryConsume('&');
85
- $node->value = $parser->parseExpression();
81
+ [$node->key, $node->value, $node->byRef] = $parser->parseForeach();
86
82
  }
87
83
 
88
84
 
@@ -152,4 +148,35 @@ class ForeachNode extends StatementNode
152
148
  yield $this->else;
153
149
  }
154
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
+ }
155
182
  }
@@ -31,7 +31,7 @@ class IfChangedNode extends StatementNode
31
31
  /** @return \Generator<int, ?array, array{AreaNode, ?Tag}, static> */
32
32
  public static function create(Tag $tag): \Generator
33
33
  {
34
- $node = new static;
34
+ $node = $tag->node = new static;
35
35
  $node->conditions = $tag->parser->parseArguments();
36
36
 
37
37
  [$node->then, $nextTag] = yield ['else'];
@@ -27,12 +27,13 @@ class IfContentNode extends StatementNode
27
27
  public AreaNode $content;
28
28
  public int $id;
29
29
  public ElementNode $htmlElement;
30
+ public ?AreaNode $else = null;
30
31
 
31
32
 
32
33
  /** @return \Generator<int, ?array, array{AreaNode, ?Tag}, static> */
33
34
  public static function create(Tag $tag, TemplateParser $parser): \Generator
34
35
  {
35
- $node = new static;
36
+ $node = $tag->node = new static;
36
37
  $node->id = $parser->generateId();
37
38
  [$node->content] = yield;
38
39
  $node->htmlElement = $tag->htmlElement;
@@ -47,6 +48,7 @@ class IfContentNode extends StatementNode
47
48
  {
48
49
  try {
49
50
  $saved = $this->htmlElement->content;
51
+ $else = $this->else ?? new AuxiliaryNode(fn() => '');
50
52
  $this->htmlElement->content = new AuxiliaryNode(fn() => <<<XX
51
53
  ob_start();
52
54
  try {
@@ -63,6 +65,7 @@ class IfContentNode extends StatementNode
63
65
  } finally {
64
66
  if (\$ʟ_ifc[$this->id] ?? null) {
65
67
  ob_end_clean();
68
+ {$else->print($context)}
66
69
  } else {
67
70
  echo ob_get_clean();
68
71
  }
@@ -10,7 +10,6 @@ declare(strict_types=1);
10
10
  namespace Latte\Essential\Nodes;
11
11
 
12
12
  use Latte\CompileException;
13
- use Latte\Compiler\ExpressionBuilder;
14
13
  use Latte\Compiler\Nodes\AreaNode;
15
14
  use Latte\Compiler\Nodes\Php\Expression;
16
15
  use Latte\Compiler\Nodes\Php\ExpressionNode;
@@ -42,7 +41,7 @@ class IfNode extends StatementNode
42
41
  /** @return \Generator<int, ?array, array{AreaNode, ?Tag}, static> */
43
42
  public static function create(Tag $tag, TemplateParser $parser): \Generator
44
43
  {
45
- $node = new static;
44
+ $node = $tag->node = new static;
46
45
  $node->ifset = in_array($tag->name, ['ifset', 'elseifset'], true);
47
46
  $node->capture = !$tag->isNAttribute() && $tag->name === 'if' && $tag->parser->isEnd();
48
47
  $node->position = $tag->position;
@@ -83,7 +82,10 @@ class IfNode extends StatementNode
83
82
  $block = $parser->tryConsumeTokenBeforeUnquotedString('block') ?? $parser->stream->tryConsume('#');
84
83
  $name = $parser->parseUnquotedStringOrExpression();
85
84
  $list[] = $block || $name instanceof StringNode
86
- ? ExpressionBuilder::variable('$this')->method('hasBlock', [$name])->build()
85
+ ? new Expression\AuxiliaryNode(
86
+ fn(PrintContext $context, ExpressionNode $name) => '$this->hasBlock(' . $name->print($context) . ')',
87
+ [$name],
88
+ )
87
89
  : new Expression\IssetNode([$name]);
88
90
  } while ($parser->stream->tryConsume(','));
89
91
 
@@ -65,12 +65,15 @@ class IncludeBlockNode extends StatementNode
65
65
  throw new CompileException('Filters are not allowed in {include parent}', $tag->position);
66
66
 
67
67
  } elseif ($node->parent || $tokenName->is('this')) {
68
- $item = $tag->closestTag(['block', 'define'], fn($item) => isset($item->data->block) && $item->data->block->name !== '');
68
+ $item = $tag->closestTag(
69
+ [BlockNode::class, DefineNode::class],
70
+ fn($item) => $item->node?->block && !$item->node->block->isDynamic() && $item->node->block->name !== ''
71
+ );
69
72
  if (!$item) {
70
73
  throw new CompileException("Cannot include $tokenName->text block outside of any block.", $tag->position);
71
74
  }
72
75
 
73
- $node->name = $item->data->block->name;
76
+ $node->name = $item->node->block->name;
74
77
  }
75
78
 
76
79
  $node->blocks = &$parser->blocks;
@@ -12,6 +12,7 @@ namespace Latte\Essential\Nodes;
12
12
  use Latte\CompileException;
13
13
  use Latte\Compiler\Nodes\AreaNode;
14
14
  use Latte\Compiler\Nodes\Php\ExpressionNode;
15
+ use Latte\Compiler\Nodes\Php\ListNode;
15
16
  use Latte\Compiler\Nodes\StatementNode;
16
17
  use Latte\Compiler\PrintContext;
17
18
  use Latte\Compiler\Tag;
@@ -25,25 +26,26 @@ class IterateWhileNode extends StatementNode
25
26
  public ExpressionNode $condition;
26
27
  public AreaNode $content;
27
28
  public ?ExpressionNode $key;
28
- public ExpressionNode $value;
29
+ public ExpressionNode|ListNode $value;
29
30
  public bool $postTest;
30
31
 
31
32
 
32
33
  /** @return \Generator<int, ?array, array{AreaNode, ?Tag}, static> */
33
34
  public static function create(Tag $tag): \Generator
34
35
  {
35
- $foreach = $tag->closestTag(['foreach']);
36
+ $foreach = $tag->closestTag([ForeachNode::class])?->node;
36
37
  if (!$foreach) {
37
38
  throw new CompileException("Tag {{$tag->name}} must be inside {foreach} ... {/foreach}.", $tag->position);
38
39
  }
39
40
 
40
- $node = new static;
41
+ $node = $tag->node = new static;
41
42
  $node->postTest = $tag->parser->isEnd();
42
43
  if (!$node->postTest) {
43
44
  $node->condition = $tag->parser->parseExpression();
44
45
  }
45
46
 
46
- [$node->key, $node->value] = $foreach->data->iterateWhile;
47
+ $node->key = $foreach->key;
48
+ $node->value = $foreach->value;
47
49
  [$node->content, $nextTag] = yield;
48
50
  if ($node->postTest) {
49
51
  $nextTag->expectArguments();
@@ -26,54 +26,57 @@ class JumpNode extends StatementNode
26
26
  {
27
27
  public string $type;
28
28
  public ExpressionNode $condition;
29
- public ?string $endTag = null;
30
29
 
31
30
 
32
31
  public static function create(Tag $tag): static
33
32
  {
34
33
  $tag->expectArguments();
35
- $allowed = match ($tag->name) {
36
- 'breakIf', 'continueIf' => ['for', 'foreach', 'while'],
37
- 'skipIf' => ['foreach'],
38
- 'exitIf' => ['block', null],
39
- };
34
+ $tag->outputMode = $tag->name === 'exitIf' // to not be in prepare()
35
+ ? $tag::OutputRemoveIndentation
36
+ : $tag::OutputNone;
37
+
40
38
  for (
41
39
  $parent = $tag->parent;
42
- in_array($parent?->name, ['if', 'ifset', 'ifcontent'], true);
40
+ $parent?->node instanceof IfNode || $parent?->node instanceof IfContentNode;
43
41
  $parent = $parent->parent
44
42
  );
45
- if (!in_array($parent?->name, $allowed, true)) {
43
+ $pnode = $parent?->node;
44
+ if (!match ($tag->name) {
45
+ 'breakIf', 'continueIf' => $pnode instanceof ForNode || $pnode instanceof ForeachNode || $pnode instanceof WhileNode,
46
+ 'skipIf' => $pnode instanceof ForeachNode,
47
+ 'exitIf' => !$pnode || $pnode instanceof BlockNode || $pnode instanceof DefineNode,
48
+ }) {
46
49
  throw new CompileException("Tag {{$tag->name}} is unexpected here.", $tag->position);
47
50
  }
48
51
 
52
+ $last = $parent?->prefix === Tag::PrefixNone
53
+ ? $parent->htmlElement->parent
54
+ : $parent?->htmlElement;
55
+ $el = $tag->htmlElement;
56
+ while ($el && $el !== $last) {
57
+ $el->breakable = true;
58
+ $el = $el->parent;
59
+ }
60
+
49
61
  $node = new static;
50
62
  $node->type = $tag->name;
51
63
  $node->condition = $tag->parser->parseExpression();
52
- if (isset($tag->htmlElement->nAttributes['foreach'])) {
53
- $node->endTag = $tag->htmlElement->name;
54
- }
55
64
  return $node;
56
65
  }
57
66
 
58
67
 
59
68
  public function print(PrintContext $context): string
60
69
  {
61
- $cmd = match ($this->type) {
62
- 'breakIf' => 'break;',
63
- 'continueIf' => 'continue;',
64
- 'skipIf' => '{ $iterator->skipRound(); continue; }',
65
- 'exitIf' => 'return;',
66
- };
67
-
68
- if ($this->endTag) {
69
- $cmd = "{ echo \"</$this->endTag>\\n\"; $cmd; } ";
70
- }
71
-
72
70
  return $context->format(
73
71
  "if (%node) %line %raw\n",
74
72
  $this->condition,
75
73
  $this->position,
76
- $cmd,
74
+ match ($this->type) {
75
+ 'breakIf' => 'break;',
76
+ 'continueIf' => 'continue;',
77
+ 'skipIf' => '{ $iterator->skipRound(); continue; }',
78
+ 'exitIf' => 'return;',
79
+ },
77
80
  );
78
81
  }
79
82
 
@@ -0,0 +1,88 @@
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\Nodes;
11
+
12
+ use Latte\CompileException;
13
+ use Latte\Compiler\Node;
14
+ use Latte\Compiler\Nodes;
15
+ use Latte\Compiler\Nodes\AreaNode;
16
+ use Latte\Compiler\Nodes\StatementNode;
17
+ use Latte\Compiler\NodeTraverser;
18
+ use Latte\Compiler\PrintContext;
19
+ use Latte\Compiler\Tag;
20
+
21
+
22
+ /**
23
+ * n:else
24
+ */
25
+ final class NElseNode extends StatementNode
26
+ {
27
+ public AreaNode $content;
28
+
29
+
30
+ /** @return \Generator<int, ?array, array{AreaNode, ?Tag}, static> */
31
+ public static function create(Tag $tag): \Generator
32
+ {
33
+ $node = $tag->node = new static;
34
+ [$node->content] = yield;
35
+ return $node;
36
+ }
37
+
38
+
39
+ public function print(PrintContext $context): string
40
+ {
41
+ throw new \LogicException('Cannot directly print');
42
+ }
43
+
44
+
45
+ public function &getIterator(): \Generator
46
+ {
47
+ yield $this->content;
48
+ }
49
+
50
+
51
+ public static function processPass(Node $node): void
52
+ {
53
+ (new NodeTraverser)->traverse($node, function (Node $node) {
54
+ if ($node instanceof Nodes\FragmentNode) {
55
+ for ($i = count($node->children) - 1; $i >= 0; $i--) {
56
+ $nElse = $node->children[$i];
57
+ if (!$nElse instanceof self) {
58
+ continue;
59
+ }
60
+
61
+ array_splice($node->children, $i, 1);
62
+ $prev = $node->children[--$i] ?? null;
63
+ if ($prev instanceof Nodes\TextNode && trim($prev->content) === '') {
64
+ array_splice($node->children, $i, 1);
65
+ $prev = $node->children[--$i] ?? null;
66
+ }
67
+
68
+ if (
69
+ $prev instanceof IfNode
70
+ || $prev instanceof ForeachNode
71
+ || $prev instanceof TryNode
72
+ || $prev instanceof IfChangedNode
73
+ || $prev instanceof IfContentNode
74
+ ) {
75
+ if ($prev->else) {
76
+ throw new CompileException('Multiple "else" found.', $nElse->position);
77
+ }
78
+ $prev->else = $nElse->content;
79
+ } else {
80
+ throw new CompileException('n:else must be immediately after n:if, n:foreach etc', $nElse->position);
81
+ }
82
+ }
83
+ } elseif ($node instanceof self) {
84
+ throw new CompileException('n:else must be immediately after n:if, n:foreach etc', $node->position);
85
+ }
86
+ });
87
+ }
88
+ }
@@ -11,10 +11,12 @@ namespace Latte\Essential\Nodes;
11
11
 
12
12
  use Latte;
13
13
  use Latte\CompileException;
14
- use Latte\Compiler\Nodes\Php\ExpressionNode;
14
+ use Latte\Compiler\Nodes\Php\Expression\AuxiliaryNode;
15
15
  use Latte\Compiler\Nodes\StatementNode;
16
16
  use Latte\Compiler\PrintContext;
17
17
  use Latte\Compiler\Tag;
18
+ use Latte\Compiler\TemplateParser;
19
+ use Latte\ContentType;
18
20
 
19
21
 
20
22
  /**
@@ -22,50 +24,40 @@ use Latte\Compiler\Tag;
22
24
  */
23
25
  final class NTagNode extends StatementNode
24
26
  {
25
- public ExpressionNode $name;
26
- public string $origName;
27
-
28
-
29
- public static function create(Tag $tag): void
27
+ public static function create(Tag $tag, TemplateParser $parser): void
30
28
  {
31
29
  if (preg_match('(style$|script$)iA', $tag->htmlElement->name)) {
32
30
  throw new CompileException('Attribute n:tag is not allowed in <script> or <style>', $tag->position);
33
31
  }
34
32
 
35
33
  $tag->expectArguments();
36
- $node = new static;
37
- $node->name = $tag->parser->parseExpression();
38
- $node->origName = $tag->htmlElement->name;
39
- $tag->htmlElement->customName = $node;
34
+ $tag->htmlElement->variableName = new AuxiliaryNode(
35
+ fn(PrintContext $context, $newName) => $context->format(
36
+ self::class . '::check(%dump, %node, %dump)',
37
+ $tag->htmlElement->name,
38
+ $newName,
39
+ $parser->getContentType() === ContentType::Xml,
40
+ ),
41
+ [$tag->parser->parseExpression()],
42
+ );
40
43
  }
41
44
 
42
45
 
43
46
  public function print(PrintContext $context): string
44
47
  {
45
- return self::class . '::check('
46
- . var_export($this->origName, true)
47
- . ', '
48
- . $this->name->print($context)
49
- . ')';
48
+ throw new \LogicException('Cannot directly print');
50
49
  }
51
50
 
52
51
 
53
- public static function check(string $orig, $new): string
52
+ public static function check(string $orig, mixed $new, bool $xml): mixed
54
53
  {
55
54
  if ($new === null) {
56
55
  return $orig;
57
-
58
- } elseif (
59
- !is_string($new)
60
- || !preg_match('~' . Latte\Compiler\TemplateLexer::ReTagName . '$~DA', $new)
61
- ) {
62
- throw new Latte\RuntimeException('Invalid tag name ' . var_export($new, true));
63
-
64
- } elseif (
65
- in_array($lower = strtolower($new), ['style', 'script'], true)
66
- || isset(Latte\Helpers::$emptyElements[strtolower($orig)]) !== isset(Latte\Helpers::$emptyElements[$lower])
56
+ } elseif (!$xml
57
+ && is_string($new)
58
+ && isset(Latte\Helpers::$emptyElements[strtolower($orig)]) !== isset(Latte\Helpers::$emptyElements[strtolower($new)])
67
59
  ) {
68
- throw new Latte\RuntimeException("Forbidden tag <$orig> change to <$new>.");
60
+ throw new Latte\RuntimeException("Forbidden tag <$orig> change to <$new>");
69
61
  }
70
62
 
71
63
  return $new;
@@ -74,6 +66,6 @@ final class NTagNode extends StatementNode
74
66
 
75
67
  public function &getIterator(): \Generator
76
68
  {
77
- yield $this->name;
69
+ false && yield;
78
70
  }
79
71
  }
@@ -16,7 +16,6 @@ use Latte\Compiler\Nodes\StatementNode;
16
16
  use Latte\Compiler\PrintContext;
17
17
  use Latte\Compiler\Tag;
18
18
  use Latte\Compiler\TemplateParser;
19
- use Latte\ContentType;
20
19
 
21
20
 
22
21
  /**
@@ -26,24 +25,17 @@ class PrintNode extends StatementNode
26
25
  {
27
26
  public ExpressionNode $expression;
28
27
  public ModifierNode $modifier;
28
+ private ?string $followsQuote = null;
29
29
 
30
30
 
31
31
  public static function create(Tag $tag, TemplateParser $parser): static
32
32
  {
33
33
  $tag->outputMode = $tag::OutputKeepIndentation;
34
-
35
- $stream = $parser->getStream();
36
- if (
37
- $tag->isInText()
38
- && $parser->getContentType() === ContentType::Html
39
- && $tag->htmlElement?->name === 'script'
40
- && preg_match('#["\']#A', $stream->peek()->text)
41
- ) {
42
- throw new CompileException("Do not place {$tag->getNotation(true)} inside quotes in JavaScript.", $tag->position);
43
- }
44
-
45
34
  $tag->expectArguments();
46
35
  $node = new static;
36
+ $node->followsQuote = preg_match('#["\']#A', $parser->getStream()->peek()->text)
37
+ ? $tag->getNotation(true)
38
+ : null;
47
39
  $node->expression = $tag->parser->parseExpression();
48
40
  $node->modifier = $tag->parser->parseModifier();
49
41
  $node->modifier->escape = true;
@@ -53,6 +45,9 @@ class PrintNode extends StatementNode
53
45
 
54
46
  public function print(PrintContext $context): string
55
47
  {
48
+ if ($this->followsQuote && $context->getEscaper()->export() === 'html/raw/js') {
49
+ throw new CompileException("Do not place {$this->followsQuote} inside quotes in JavaScript.", $this->position);
50
+ }
56
51
  return $context->format(
57
52
  "echo %modify(%node) %line;\n",
58
53
  $this->modifier,
@@ -22,7 +22,7 @@ class RollbackNode extends StatementNode
22
22
  {
23
23
  public static function create(Tag $tag): static
24
24
  {
25
- if (!$tag->closestTag(['try'])) {
25
+ if (!$tag->closestTag([TryNode::class])) {
26
26
  throw new CompileException('Tag {rollback} must be inside {try} ... {/try}.', $tag->position);
27
27
  }
28
28
 
@@ -27,7 +27,7 @@ class SpacelessNode extends StatementNode
27
27
  /** @return \Generator<int, ?array, array{AreaNode, ?Tag}, static> */
28
28
  public static function create(Tag $tag): \Generator
29
29
  {
30
- $node = new static;
30
+ $node = $tag->node = new static;
31
31
  [$node->content] = yield;
32
32
  return $node;
33
33
  }
@@ -37,7 +37,7 @@ class SwitchNode extends StatementNode
37
37
  throw new CompileException('Attribute n:switch is not supported.', $tag->position);
38
38
  }
39
39
 
40
- $node = new static;
40
+ $node = $tag->node = new static;
41
41
  $node->expression = $tag->parser->isEnd()
42
42
  ? null
43
43
  : $tag->parser->parseExpression();
@@ -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\PhpHelpers;
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 [ClassName]}
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 '(new Latte\Essential\Blueprint)->printClass($this, ' . PhpHelpers::dump($this->template) . '); exit;';
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
  }
@@ -35,7 +35,7 @@ class TranslateNode extends StatementNode
35
35
  {
36
36
  $tag->outputMode = $tag::OutputKeepIndentation;
37
37
 
38
- $node = new static;
38
+ $node = $tag->node = new static;
39
39
  $args = $tag->parser->parseArguments();
40
40
  $node->modifier = $tag->parser->parseModifier();
41
41
  $node->modifier->escape = true;
@@ -25,9 +25,9 @@ class TryNode extends StatementNode
25
25
 
26
26
 
27
27
  /** @return \Generator<int, ?array, array{AreaNode, ?Tag}, static> */
28
- public static function create(): \Generator
28
+ public static function create(Tag $tag): \Generator
29
29
  {
30
- $node = new static;
30
+ $node = $tag->node = new static;
31
31
  [$node->try, $nextTag] = yield ['else'];
32
32
  if ($nextTag?->name === 'else') {
33
33
  [$node->else] = yield;
@@ -46,12 +46,11 @@ class TryNode extends StatementNode
46
46
  try %line {
47
47
  %node
48
48
  } catch (Throwable $ʟ_e) {
49
- ob_end_clean();
49
+ ob_clean();
50
50
  if (!($ʟ_e instanceof Latte\Essential\RollbackException) && isset($this->global->coreExceptionHandler)) {
51
51
  ($this->global->coreExceptionHandler)($ʟ_e, $this);
52
52
  }
53
53
  %node
54
- ob_start();
55
54
  } finally {
56
55
  echo ob_get_clean();
57
56
  $iterator = $ʟ_it = $ʟ_try[%0.dump][0];
@@ -33,9 +33,16 @@ class VarPrintNode extends StatementNode
33
33
 
34
34
  public function print(PrintContext $context): string
35
35
  {
36
- $vars = $this->all ? 'get_defined_vars()'
36
+ $vars = $this->all
37
+ ? 'get_defined_vars()'
37
38
  : 'array_diff_key(get_defined_vars(), $this->getParameters())';
38
- return "(new Latte\\Essential\\Blueprint)->printVars($vars); exit;";
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
 
@@ -29,7 +29,7 @@ class WhileNode extends StatementNode
29
29
  /** @return \Generator<int, ?array, array{AreaNode, ?Tag}, static> */
30
30
  public static function create(Tag $tag): \Generator
31
31
  {
32
- $node = new static;
32
+ $node = $tag->node = new static;
33
33
  $node->postTest = $tag->parser->isEnd();
34
34
  if (!$node->postTest) {
35
35
  $node->condition = $tag->parser->parseExpression();