@vituum/vite-plugin-latte 0.1.8

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 (201) hide show
  1. package/README.md +39 -0
  2. package/handler.js +9 -0
  3. package/index.js +138 -0
  4. package/index.php +173 -0
  5. package/latte/AssetFilter.php +11 -0
  6. package/latte/FetchFunction.php +28 -0
  7. package/latte/IconFilter.php +14 -0
  8. package/latte/JsonTag.php +64 -0
  9. package/latte/PlaceholderFunction.php +11 -0
  10. package/latte/RandomColorFunction.php +14 -0
  11. package/latte/TelFilter.php +22 -0
  12. package/package.json +35 -0
  13. package/vendor/autoload.php +7 -0
  14. package/vendor/bin/latte-lint +107 -0
  15. package/vendor/composer/ClassLoader.php +572 -0
  16. package/vendor/composer/InstalledVersions.php +350 -0
  17. package/vendor/composer/LICENSE +21 -0
  18. package/vendor/composer/autoload_classmap.php +183 -0
  19. package/vendor/composer/autoload_namespaces.php +9 -0
  20. package/vendor/composer/autoload_psr4.php +9 -0
  21. package/vendor/composer/autoload_real.php +57 -0
  22. package/vendor/composer/autoload_static.php +193 -0
  23. package/vendor/composer/installed.json +93 -0
  24. package/vendor/composer/installed.php +32 -0
  25. package/vendor/composer/platform_check.php +26 -0
  26. package/vendor/latte/latte/bin/latte-lint +29 -0
  27. package/vendor/latte/latte/composer.json +53 -0
  28. package/vendor/latte/latte/license.md +60 -0
  29. package/vendor/latte/latte/ncs.php +14 -0
  30. package/vendor/latte/latte/ncs.xml +9 -0
  31. package/vendor/latte/latte/readme.md +21 -0
  32. package/vendor/latte/latte/src/Bridges/Tracy/BlueScreenPanel.php +114 -0
  33. package/vendor/latte/latte/src/Bridges/Tracy/LattePanel.php +99 -0
  34. package/vendor/latte/latte/src/Bridges/Tracy/templates/LattePanel.panel.phtml +98 -0
  35. package/vendor/latte/latte/src/Bridges/Tracy/templates/LattePanel.tab.phtml +15 -0
  36. package/vendor/latte/latte/src/Latte/Compiler/Block.php +44 -0
  37. package/vendor/latte/latte/src/Latte/Compiler/Escaper.php +244 -0
  38. package/vendor/latte/latte/src/Latte/Compiler/ExpressionBuilder.php +128 -0
  39. package/vendor/latte/latte/src/Latte/Compiler/Node.php +28 -0
  40. package/vendor/latte/latte/src/Latte/Compiler/NodeHelpers.php +132 -0
  41. package/vendor/latte/latte/src/Latte/Compiler/NodeTraverser.php +77 -0
  42. package/vendor/latte/latte/src/Latte/Compiler/Nodes/AreaNode.php +17 -0
  43. package/vendor/latte/latte/src/Latte/Compiler/Nodes/AuxiliaryNode.php +27 -0
  44. package/vendor/latte/latte/src/Latte/Compiler/Nodes/FragmentNode.php +59 -0
  45. package/vendor/latte/latte/src/Latte/Compiler/Nodes/Html/AttributeNode.php +48 -0
  46. package/vendor/latte/latte/src/Latte/Compiler/Nodes/Html/BogusTagNode.php +46 -0
  47. package/vendor/latte/latte/src/Latte/Compiler/Nodes/Html/CommentNode.php +39 -0
  48. package/vendor/latte/latte/src/Latte/Compiler/Nodes/Html/ElementNode.php +129 -0
  49. package/vendor/latte/latte/src/Latte/Compiler/Nodes/Html/QuotedValue.php +53 -0
  50. package/vendor/latte/latte/src/Latte/Compiler/Nodes/NopNode.php +21 -0
  51. package/vendor/latte/latte/src/Latte/Compiler/Nodes/Php/ArgumentNode.php +45 -0
  52. package/vendor/latte/latte/src/Latte/Compiler/Nodes/Php/ComplexTypeNode.php +17 -0
  53. package/vendor/latte/latte/src/Latte/Compiler/Nodes/Php/Expression/ArrayAccessNode.php +41 -0
  54. package/vendor/latte/latte/src/Latte/Compiler/Nodes/Php/Expression/ArrayItemNode.php +52 -0
  55. package/vendor/latte/latte/src/Latte/Compiler/Nodes/Php/Expression/ArrayNode.php +94 -0
  56. package/vendor/latte/latte/src/Latte/Compiler/Nodes/Php/Expression/AssignNode.php +39 -0
  57. package/vendor/latte/latte/src/Latte/Compiler/Nodes/Php/Expression/AssignOpNode.php +45 -0
  58. package/vendor/latte/latte/src/Latte/Compiler/Nodes/Php/Expression/BinaryOpNode.php +65 -0
  59. package/vendor/latte/latte/src/Latte/Compiler/Nodes/Php/Expression/CastNode.php +43 -0
  60. package/vendor/latte/latte/src/Latte/Compiler/Nodes/Php/Expression/ClassConstantFetchNode.php +42 -0
  61. package/vendor/latte/latte/src/Latte/Compiler/Nodes/Php/Expression/CloneNode.php +36 -0
  62. package/vendor/latte/latte/src/Latte/Compiler/Nodes/Php/Expression/ClosureNode.php +73 -0
  63. package/vendor/latte/latte/src/Latte/Compiler/Nodes/Php/Expression/ClosureUseNode.php +37 -0
  64. package/vendor/latte/latte/src/Latte/Compiler/Nodes/Php/Expression/ConstantFetchNode.php +37 -0
  65. package/vendor/latte/latte/src/Latte/Compiler/Nodes/Php/Expression/EmptyNode.php +36 -0
  66. package/vendor/latte/latte/src/Latte/Compiler/Nodes/Php/Expression/ErrorSuppressNode.php +36 -0
  67. package/vendor/latte/latte/src/Latte/Compiler/Nodes/Php/Expression/FilterCallNode.php +39 -0
  68. package/vendor/latte/latte/src/Latte/Compiler/Nodes/Php/Expression/FunctionCallNode.php +45 -0
  69. package/vendor/latte/latte/src/Latte/Compiler/Nodes/Php/Expression/FunctionCallableNode.php +39 -0
  70. package/vendor/latte/latte/src/Latte/Compiler/Nodes/Php/Expression/InRangeNode.php +42 -0
  71. package/vendor/latte/latte/src/Latte/Compiler/Nodes/Php/Expression/InstanceofNode.php +40 -0
  72. package/vendor/latte/latte/src/Latte/Compiler/Nodes/Php/Expression/IssetNode.php +40 -0
  73. package/vendor/latte/latte/src/Latte/Compiler/Nodes/Php/Expression/MatchNode.php +49 -0
  74. package/vendor/latte/latte/src/Latte/Compiler/Nodes/Php/Expression/MethodCallNode.php +50 -0
  75. package/vendor/latte/latte/src/Latte/Compiler/Nodes/Php/Expression/MethodCallableNode.php +42 -0
  76. package/vendor/latte/latte/src/Latte/Compiler/Nodes/Php/Expression/NewNode.php +45 -0
  77. package/vendor/latte/latte/src/Latte/Compiler/Nodes/Php/Expression/NotNode.php +36 -0
  78. package/vendor/latte/latte/src/Latte/Compiler/Nodes/Php/Expression/PostOpNode.php +43 -0
  79. package/vendor/latte/latte/src/Latte/Compiler/Nodes/Php/Expression/PreOpNode.php +43 -0
  80. package/vendor/latte/latte/src/Latte/Compiler/Nodes/Php/Expression/PropertyFetchNode.php +42 -0
  81. package/vendor/latte/latte/src/Latte/Compiler/Nodes/Php/Expression/StaticCallNode.php +55 -0
  82. package/vendor/latte/latte/src/Latte/Compiler/Nodes/Php/Expression/StaticCallableNode.php +47 -0
  83. package/vendor/latte/latte/src/Latte/Compiler/Nodes/Php/Expression/StaticPropertyFetchNode.php +42 -0
  84. package/vendor/latte/latte/src/Latte/Compiler/Nodes/Php/Expression/TernaryNode.php +50 -0
  85. package/vendor/latte/latte/src/Latte/Compiler/Nodes/Php/Expression/UnaryOpNode.php +45 -0
  86. package/vendor/latte/latte/src/Latte/Compiler/Nodes/Php/Expression/VariableNode.php +40 -0
  87. package/vendor/latte/latte/src/Latte/Compiler/Nodes/Php/ExpressionNode.php +17 -0
  88. package/vendor/latte/latte/src/Latte/Compiler/Nodes/Php/FilterNode.php +67 -0
  89. package/vendor/latte/latte/src/Latte/Compiler/Nodes/Php/IdentifierNode.php +36 -0
  90. package/vendor/latte/latte/src/Latte/Compiler/Nodes/Php/IntersectionTypeNode.php +39 -0
  91. package/vendor/latte/latte/src/Latte/Compiler/Nodes/Php/MatchArmNode.php +45 -0
  92. package/vendor/latte/latte/src/Latte/Compiler/Nodes/Php/ModifierNode.php +107 -0
  93. package/vendor/latte/latte/src/Latte/Compiler/Nodes/Php/NameNode.php +89 -0
  94. package/vendor/latte/latte/src/Latte/Compiler/Nodes/Php/NullableTypeNode.php +35 -0
  95. package/vendor/latte/latte/src/Latte/Compiler/Nodes/Php/ParameterNode.php +54 -0
  96. package/vendor/latte/latte/src/Latte/Compiler/Nodes/Php/Scalar/BooleanNode.php +30 -0
  97. package/vendor/latte/latte/src/Latte/Compiler/Nodes/Php/Scalar/EncapsedStringNode.php +80 -0
  98. package/vendor/latte/latte/src/Latte/Compiler/Nodes/Php/Scalar/EncapsedStringPartNode.php +30 -0
  99. package/vendor/latte/latte/src/Latte/Compiler/Nodes/Php/Scalar/FloatNode.php +63 -0
  100. package/vendor/latte/latte/src/Latte/Compiler/Nodes/Php/Scalar/IntegerNode.php +70 -0
  101. package/vendor/latte/latte/src/Latte/Compiler/Nodes/Php/Scalar/NullNode.php +29 -0
  102. package/vendor/latte/latte/src/Latte/Compiler/Nodes/Php/Scalar/StringNode.php +40 -0
  103. package/vendor/latte/latte/src/Latte/Compiler/Nodes/Php/ScalarNode.php +15 -0
  104. package/vendor/latte/latte/src/Latte/Compiler/Nodes/Php/SuperiorTypeNode.php +29 -0
  105. package/vendor/latte/latte/src/Latte/Compiler/Nodes/Php/UnionTypeNode.php +39 -0
  106. package/vendor/latte/latte/src/Latte/Compiler/Nodes/Php/VarLikeIdentifierNode.php +21 -0
  107. package/vendor/latte/latte/src/Latte/Compiler/Nodes/StatementNode.php +15 -0
  108. package/vendor/latte/latte/src/Latte/Compiler/Nodes/TemplateNode.php +34 -0
  109. package/vendor/latte/latte/src/Latte/Compiler/Nodes/TextNode.php +37 -0
  110. package/vendor/latte/latte/src/Latte/Compiler/PhpHelpers.php +227 -0
  111. package/vendor/latte/latte/src/Latte/Compiler/Position.php +43 -0
  112. package/vendor/latte/latte/src/Latte/Compiler/PrintContext.php +334 -0
  113. package/vendor/latte/latte/src/Latte/Compiler/Tag.php +117 -0
  114. package/vendor/latte/latte/src/Latte/Compiler/TagLexer.php +399 -0
  115. package/vendor/latte/latte/src/Latte/Compiler/TagParser.php +388 -0
  116. package/vendor/latte/latte/src/Latte/Compiler/TagParserData.php +580 -0
  117. package/vendor/latte/latte/src/Latte/Compiler/TemplateGenerator.php +206 -0
  118. package/vendor/latte/latte/src/Latte/Compiler/TemplateLexer.php +391 -0
  119. package/vendor/latte/latte/src/Latte/Compiler/TemplateParser.php +448 -0
  120. package/vendor/latte/latte/src/Latte/Compiler/TemplateParserHtml.php +506 -0
  121. package/vendor/latte/latte/src/Latte/Compiler/Token.php +259 -0
  122. package/vendor/latte/latte/src/Latte/Compiler/TokenStream.php +151 -0
  123. package/vendor/latte/latte/src/Latte/ContentType.php +22 -0
  124. package/vendor/latte/latte/src/Latte/Engine.php +591 -0
  125. package/vendor/latte/latte/src/Latte/Essential/Blueprint.php +160 -0
  126. package/vendor/latte/latte/src/Latte/Essential/CachingIterator.php +232 -0
  127. package/vendor/latte/latte/src/Latte/Essential/CoreExtension.php +232 -0
  128. package/vendor/latte/latte/src/Latte/Essential/Filters.php +591 -0
  129. package/vendor/latte/latte/src/Latte/Essential/Nodes/BlockNode.php +161 -0
  130. package/vendor/latte/latte/src/Latte/Essential/Nodes/CaptureNode.php +98 -0
  131. package/vendor/latte/latte/src/Latte/Essential/Nodes/ContentTypeNode.php +74 -0
  132. package/vendor/latte/latte/src/Latte/Essential/Nodes/DebugbreakNode.php +53 -0
  133. package/vendor/latte/latte/src/Latte/Essential/Nodes/DefineNode.php +134 -0
  134. package/vendor/latte/latte/src/Latte/Essential/Nodes/DoNode.php +49 -0
  135. package/vendor/latte/latte/src/Latte/Essential/Nodes/DumpNode.php +58 -0
  136. package/vendor/latte/latte/src/Latte/Essential/Nodes/EmbedNode.php +125 -0
  137. package/vendor/latte/latte/src/Latte/Essential/Nodes/ExtendsNode.php +60 -0
  138. package/vendor/latte/latte/src/Latte/Essential/Nodes/FirstLastSepNode.php +81 -0
  139. package/vendor/latte/latte/src/Latte/Essential/Nodes/ForNode.php +91 -0
  140. package/vendor/latte/latte/src/Latte/Essential/Nodes/ForeachNode.php +155 -0
  141. package/vendor/latte/latte/src/Latte/Essential/Nodes/IfChangedNode.php +141 -0
  142. package/vendor/latte/latte/src/Latte/Essential/Nodes/IfContentNode.php +82 -0
  143. package/vendor/latte/latte/src/Latte/Essential/Nodes/IfNode.php +180 -0
  144. package/vendor/latte/latte/src/Latte/Essential/Nodes/ImportNode.php +49 -0
  145. package/vendor/latte/latte/src/Latte/Essential/Nodes/IncludeBlockNode.php +140 -0
  146. package/vendor/latte/latte/src/Latte/Essential/Nodes/IncludeFileNode.php +81 -0
  147. package/vendor/latte/latte/src/Latte/Essential/Nodes/IterateWhileNode.php +94 -0
  148. package/vendor/latte/latte/src/Latte/Essential/Nodes/NAttrNode.php +105 -0
  149. package/vendor/latte/latte/src/Latte/Essential/Nodes/NClassNode.php +54 -0
  150. package/vendor/latte/latte/src/Latte/Essential/Nodes/NTagNode.php +79 -0
  151. package/vendor/latte/latte/src/Latte/Essential/Nodes/ParametersNode.php +84 -0
  152. package/vendor/latte/latte/src/Latte/Essential/Nodes/PrintNode.php +70 -0
  153. package/vendor/latte/latte/src/Latte/Essential/Nodes/RawPhpNode.php +46 -0
  154. package/vendor/latte/latte/src/Latte/Essential/Nodes/RollbackNode.php +37 -0
  155. package/vendor/latte/latte/src/Latte/Essential/Nodes/SkipNode.php +71 -0
  156. package/vendor/latte/latte/src/Latte/Essential/Nodes/SpacelessNode.php +62 -0
  157. package/vendor/latte/latte/src/Latte/Essential/Nodes/SwitchNode.php +118 -0
  158. package/vendor/latte/latte/src/Latte/Essential/Nodes/TemplatePrintNode.php +39 -0
  159. package/vendor/latte/latte/src/Latte/Essential/Nodes/TemplateTypeNode.php +38 -0
  160. package/vendor/latte/latte/src/Latte/Essential/Nodes/TraceNode.php +35 -0
  161. package/vendor/latte/latte/src/Latte/Essential/Nodes/TranslateNode.php +104 -0
  162. package/vendor/latte/latte/src/Latte/Essential/Nodes/TryNode.php +75 -0
  163. package/vendor/latte/latte/src/Latte/Essential/Nodes/VarNode.php +106 -0
  164. package/vendor/latte/latte/src/Latte/Essential/Nodes/VarPrintNode.php +40 -0
  165. package/vendor/latte/latte/src/Latte/Essential/Nodes/VarTypeNode.php +36 -0
  166. package/vendor/latte/latte/src/Latte/Essential/Nodes/WhileNode.php +81 -0
  167. package/vendor/latte/latte/src/Latte/Essential/Passes.php +115 -0
  168. package/vendor/latte/latte/src/Latte/Essential/RawPhpExtension.php +28 -0
  169. package/vendor/latte/latte/src/Latte/Essential/RollbackException.php +16 -0
  170. package/vendor/latte/latte/src/Latte/Essential/Tracer.php +117 -0
  171. package/vendor/latte/latte/src/Latte/Essential/TranslatorExtension.php +101 -0
  172. package/vendor/latte/latte/src/Latte/Extension.php +97 -0
  173. package/vendor/latte/latte/src/Latte/Helpers.php +96 -0
  174. package/vendor/latte/latte/src/Latte/Loader.php +37 -0
  175. package/vendor/latte/latte/src/Latte/Loaders/FileLoader.php +95 -0
  176. package/vendor/latte/latte/src/Latte/Loaders/StringLoader.php +76 -0
  177. package/vendor/latte/latte/src/Latte/Policy.php +24 -0
  178. package/vendor/latte/latte/src/Latte/PositionAwareException.php +50 -0
  179. package/vendor/latte/latte/src/Latte/Runtime/Block.php +24 -0
  180. package/vendor/latte/latte/src/Latte/Runtime/FilterExecutor.php +151 -0
  181. package/vendor/latte/latte/src/Latte/Runtime/FilterInfo.php +39 -0
  182. package/vendor/latte/latte/src/Latte/Runtime/Filters.php +234 -0
  183. package/vendor/latte/latte/src/Latte/Runtime/Html.php +35 -0
  184. package/vendor/latte/latte/src/Latte/Runtime/HtmlStringable.php +17 -0
  185. package/vendor/latte/latte/src/Latte/Runtime/Template.php +381 -0
  186. package/vendor/latte/latte/src/Latte/Sandbox/Nodes/FunctionCallNode.php +30 -0
  187. package/vendor/latte/latte/src/Latte/Sandbox/Nodes/FunctionCallableNode.php +29 -0
  188. package/vendor/latte/latte/src/Latte/Sandbox/Nodes/MethodCallNode.php +32 -0
  189. package/vendor/latte/latte/src/Latte/Sandbox/Nodes/MethodCallableNode.php +30 -0
  190. package/vendor/latte/latte/src/Latte/Sandbox/Nodes/PropertyFetchNode.php +32 -0
  191. package/vendor/latte/latte/src/Latte/Sandbox/Nodes/SandboxNode.php +73 -0
  192. package/vendor/latte/latte/src/Latte/Sandbox/Nodes/StaticCallNode.php +31 -0
  193. package/vendor/latte/latte/src/Latte/Sandbox/Nodes/StaticCallableNode.php +30 -0
  194. package/vendor/latte/latte/src/Latte/Sandbox/Nodes/StaticPropertyFetchNode.php +32 -0
  195. package/vendor/latte/latte/src/Latte/Sandbox/RuntimeChecker.php +116 -0
  196. package/vendor/latte/latte/src/Latte/Sandbox/SandboxExtension.php +132 -0
  197. package/vendor/latte/latte/src/Latte/Sandbox/SecurityPolicy.php +185 -0
  198. package/vendor/latte/latte/src/Latte/Strict.php +101 -0
  199. package/vendor/latte/latte/src/Latte/attributes.php +24 -0
  200. package/vendor/latte/latte/src/Latte/exceptions.php +69 -0
  201. package/vendor/latte/latte/src/Tools/Linter.php +175 -0
@@ -0,0 +1,591 @@
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;
11
+
12
+ use Latte\Compiler\Nodes\TemplateNode;
13
+
14
+
15
+ /**
16
+ * Templating engine Latte.
17
+ */
18
+ class Engine
19
+ {
20
+ use Strict;
21
+
22
+ public const VERSION = '3.0.2';
23
+ public const VERSION_ID = 30002;
24
+
25
+ /** @deprecated use ContentType::* */
26
+ public const
27
+ CONTENT_HTML = ContentType::Html,
28
+ CONTENT_XML = ContentType::Xml,
29
+ CONTENT_JS = ContentType::JavaScript,
30
+ CONTENT_CSS = ContentType::Css,
31
+ CONTENT_ICAL = ContentType::ICal,
32
+ CONTENT_TEXT = ContentType::Text;
33
+
34
+ /** @internal */
35
+ public $probe;
36
+
37
+ private ?Loader $loader = null;
38
+ private Runtime\FilterExecutor $filters;
39
+ private \stdClass $functions;
40
+
41
+ /** @var mixed[] */
42
+ private array $providers = [];
43
+
44
+ /** @var Extension[] */
45
+ private array $extensions = [];
46
+ private string $contentType = ContentType::Html;
47
+ private ?string $tempDirectory = null;
48
+ private bool $autoRefresh = true;
49
+ private bool $strictTypes = false;
50
+ private ?Policy $policy = null;
51
+ private bool $sandboxed = false;
52
+
53
+
54
+ public function __construct()
55
+ {
56
+ $this->filters = new Runtime\FilterExecutor;
57
+ $this->functions = new \stdClass;
58
+ $this->probe = function () {};
59
+ $this->addExtension(new Essential\CoreExtension);
60
+ $this->addExtension(new Sandbox\SandboxExtension);
61
+ }
62
+
63
+
64
+ /**
65
+ * Renders template to output.
66
+ * @param object|mixed[] $params
67
+ */
68
+ public function render(string $name, object|array $params = [], ?string $block = null): void
69
+ {
70
+ $template = $this->createTemplate($name, $this->processParams($params));
71
+ $template->global->coreCaptured = false;
72
+ ($this->probe)($template);
73
+ $template->render($block);
74
+ }
75
+
76
+
77
+ /**
78
+ * Renders template to string.
79
+ * @param object|mixed[] $params
80
+ */
81
+ public function renderToString(string $name, object|array $params = [], ?string $block = null): string
82
+ {
83
+ $template = $this->createTemplate($name, $this->processParams($params));
84
+ $template->global->coreCaptured = true;
85
+ ($this->probe)($template);
86
+ return $template->capture(function () use ($template, $block) { $template->render($block); });
87
+ }
88
+
89
+
90
+ /**
91
+ * Creates template object.
92
+ * @param mixed[] $params
93
+ */
94
+ public function createTemplate(string $name, array $params = []): Runtime\Template
95
+ {
96
+ $class = $this->getTemplateClass($name);
97
+ if (!class_exists($class, false)) {
98
+ $this->loadTemplate($name);
99
+ }
100
+
101
+ foreach ($this->extensions as $extension) {
102
+ $extension->beforeRender($this);
103
+ }
104
+
105
+ $this->providers['fn'] = $this->functions;
106
+ return new $class(
107
+ $this,
108
+ $params,
109
+ $this->filters,
110
+ $this->providers,
111
+ $name,
112
+ );
113
+ }
114
+
115
+
116
+ /**
117
+ * Compiles template to PHP code.
118
+ */
119
+ public function compile(string $name): string
120
+ {
121
+ if ($this->sandboxed && !$this->policy) {
122
+ throw new \LogicException('In sandboxed mode you need to set a security policy.');
123
+ }
124
+
125
+ $source = $this->getLoader()->getContent($name);
126
+
127
+ try {
128
+ $node = $this->parse($source);
129
+ $this->applyPasses($node);
130
+ $code = $this->generate($node, $name);
131
+
132
+ } catch (\Throwable $e) {
133
+ if (!$e instanceof CompileException && !$e instanceof SecurityViolationException) {
134
+ $e = new CompileException("Thrown exception '{$e->getMessage()}'", previous: $e);
135
+ }
136
+
137
+ throw $e->setSource($source, $name);
138
+ }
139
+
140
+ return $code;
141
+ }
142
+
143
+
144
+ /**
145
+ * Parses template to AST node.
146
+ */
147
+ public function parse(string $source): TemplateNode
148
+ {
149
+ $lexer = new Compiler\TemplateLexer;
150
+ $parser = new Compiler\TemplateParser;
151
+
152
+ foreach ($this->extensions as $extension) {
153
+ $extension->beforeCompile($this);
154
+ $parser->addTags($extension->getTags());
155
+ }
156
+
157
+ return $parser
158
+ ->setContentType($this->contentType)
159
+ ->setPolicy($this->getPolicy(effective: true))
160
+ ->parse($source, $lexer);
161
+ }
162
+
163
+
164
+ /**
165
+ * Calls node visitors.
166
+ */
167
+ public function applyPasses(TemplateNode &$node): void
168
+ {
169
+ $passes = [];
170
+ foreach ($this->extensions as $extension) {
171
+ $passes = array_merge($passes, $extension->getPasses());
172
+ }
173
+
174
+ $passes = Helpers::sortBeforeAfter($passes);
175
+ foreach ($passes as $pass) {
176
+ $pass = $pass instanceof \stdClass ? $pass->subject : $pass;
177
+ ($pass)($node);
178
+ }
179
+ }
180
+
181
+
182
+ /**
183
+ * Generates template PHP code.
184
+ */
185
+ public function generate(TemplateNode $node, string $name): string
186
+ {
187
+ $comment = preg_match('#\n|\?#', $name) ? null : "source: $name";
188
+ $generator = new Compiler\TemplateGenerator;
189
+ return $generator->generate(
190
+ $node,
191
+ $this->getTemplateClass($name),
192
+ $comment,
193
+ $this->strictTypes,
194
+ );
195
+ }
196
+
197
+
198
+ /**
199
+ * Compiles template to cache.
200
+ * @throws \LogicException
201
+ */
202
+ public function warmupCache(string $name): void
203
+ {
204
+ if (!$this->tempDirectory) {
205
+ throw new \LogicException('Path to temporary directory is not set.');
206
+ }
207
+
208
+ $class = $this->getTemplateClass($name);
209
+ if (!class_exists($class, false)) {
210
+ $this->loadTemplate($name);
211
+ }
212
+ }
213
+
214
+
215
+ private function loadTemplate(string $name): void
216
+ {
217
+ if (!$this->tempDirectory) {
218
+ $code = $this->compile($name);
219
+ if (@eval(substr($code, 5)) === false) { // @ is escalated to exception, substr removes <?php
220
+ throw (new CompileException('Error in template: ' . error_get_last()['message']))
221
+ ->setSource($code, "$name (compiled)");
222
+ }
223
+
224
+ return;
225
+ }
226
+
227
+ // Solving atomicity to work everywhere is really pain in the ass.
228
+ // 1) We want to do as little as possible IO calls on production and also directory and file can be not writable
229
+ // so on Linux we include the file directly without shared lock, therefore, the file must be created atomically by renaming.
230
+ // 2) On Windows file cannot be renamed-to while is open (ie by include), so we have to acquire a lock.
231
+ $file = $this->getCacheFile($name);
232
+ $lock = defined('PHP_WINDOWS_VERSION_BUILD')
233
+ ? $this->acquireLock("$file.lock", LOCK_SH)
234
+ : null;
235
+
236
+ if (!$this->isExpired($file, $name) && (@include $file) !== false) { // @ - file may not exist
237
+ return;
238
+ }
239
+
240
+ if ($lock) {
241
+ flock($lock, LOCK_UN); // release shared lock so we can get exclusive
242
+ }
243
+
244
+ $lock = $this->acquireLock("$file.lock", LOCK_EX);
245
+
246
+ // while waiting for exclusive lock, someone might have already created the cache
247
+ if (!is_file($file) || $this->isExpired($file, $name)) {
248
+ $code = $this->compile($name);
249
+ if (file_put_contents("$file.tmp", $code) !== strlen($code) || !rename("$file.tmp", $file)) {
250
+ @unlink("$file.tmp"); // @ - file may not exist
251
+ throw new RuntimeException("Unable to create '$file'.");
252
+ }
253
+
254
+ if (function_exists('opcache_invalidate')) {
255
+ @opcache_invalidate($file, true); // @ can be restricted
256
+ }
257
+ }
258
+
259
+ if ((include $file) === false) {
260
+ throw new RuntimeException("Unable to load '$file'.");
261
+ }
262
+ }
263
+
264
+
265
+ /**
266
+ * @return resource
267
+ */
268
+ private function acquireLock(string $file, int $mode)
269
+ {
270
+ $dir = dirname($file);
271
+ if (!is_dir($dir) && !@mkdir($dir) && !is_dir($dir)) { // @ - dir may already exist
272
+ throw new RuntimeException("Unable to create directory '$dir'. " . error_get_last()['message']);
273
+ }
274
+
275
+ $handle = @fopen($file, 'w'); // @ is escalated to exception
276
+ if (!$handle) {
277
+ throw new RuntimeException("Unable to create file '$file'. " . error_get_last()['message']);
278
+ } elseif (!@flock($handle, $mode)) { // @ is escalated to exception
279
+ throw new RuntimeException('Unable to acquire ' . ($mode & LOCK_EX ? 'exclusive' : 'shared') . " lock on file '$file'. " . error_get_last()['message']);
280
+ }
281
+
282
+ return $handle;
283
+ }
284
+
285
+
286
+ private function isExpired(string $file, string $name): bool
287
+ {
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
+ }
296
+
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
+
304
+ return $this->getLoader()->isExpired($name, $time);
305
+ }
306
+
307
+
308
+ public function getCacheFile(string $name): string
309
+ {
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";
315
+ }
316
+
317
+
318
+ public function getTemplateClass(string $name): string
319
+ {
320
+ $key = [
321
+ $this->getLoader()->getUniqueId($name),
322
+ self::VERSION,
323
+ array_keys((array) $this->functions),
324
+ $this->contentType,
325
+ ];
326
+ foreach ($this->extensions as $extension) {
327
+ $key[] = [$extension::class, $extension->getCacheKey($this)];
328
+ }
329
+
330
+ return 'Template' . substr(md5(serialize($key)), 0, 10);
331
+ }
332
+
333
+
334
+ /**
335
+ * Registers run-time filter.
336
+ */
337
+ public function addFilter(string $name, callable $callback): static
338
+ {
339
+ if (!preg_match('#^[a-z]\w*$#iD', $name)) {
340
+ throw new \LogicException("Invalid filter name '$name'.");
341
+ }
342
+
343
+ $this->filters->add($name, $callback);
344
+ return $this;
345
+ }
346
+
347
+
348
+ /**
349
+ * Registers filter loader.
350
+ */
351
+ public function addFilterLoader(callable $callback): static
352
+ {
353
+ $this->filters->add(null, $callback);
354
+ return $this;
355
+ }
356
+
357
+
358
+ /**
359
+ * Returns all run-time filters.
360
+ * @return callable[]
361
+ */
362
+ public function getFilters(): array
363
+ {
364
+ return $this->filters->getAll();
365
+ }
366
+
367
+
368
+ /**
369
+ * Call a run-time filter.
370
+ * @param mixed[] $args
371
+ */
372
+ public function invokeFilter(string $name, array $args): mixed
373
+ {
374
+ return ($this->filters->$name)(...$args);
375
+ }
376
+
377
+
378
+ /**
379
+ * Adds new extension.
380
+ */
381
+ public function addExtension(Extension $extension): static
382
+ {
383
+ $this->extensions[] = $extension;
384
+ foreach ($extension->getFilters() as $name => $callback) {
385
+ $this->filters->add($name, $callback);
386
+ }
387
+
388
+ foreach ($extension->getFunctions() as $name => $callback) {
389
+ $this->functions->$name = $callback;
390
+ }
391
+
392
+ $this->providers = array_merge($this->providers, $extension->getProviders());
393
+ return $this;
394
+ }
395
+
396
+
397
+ /**
398
+ * Registers run-time function.
399
+ */
400
+ public function addFunction(string $name, callable $callback): static
401
+ {
402
+ if (!preg_match('#^[a-z]\w*$#iD', $name)) {
403
+ throw new \LogicException("Invalid function name '$name'.");
404
+ }
405
+
406
+ $this->functions->$name = $callback;
407
+ return $this;
408
+ }
409
+
410
+
411
+ /**
412
+ * Call a run-time function.
413
+ * @param mixed[] $args
414
+ */
415
+ public function invokeFunction(string $name, array $args): mixed
416
+ {
417
+ if (!isset($this->functions->$name)) {
418
+ $hint = ($t = Helpers::getSuggestion(array_keys((array) $this->functions), $name))
419
+ ? ", did you mean '$t'?"
420
+ : '.';
421
+ throw new \LogicException("Function '$name' is not defined$hint");
422
+ }
423
+
424
+ return ($this->functions->$name)(...$args);
425
+ }
426
+
427
+
428
+ /**
429
+ * @return callable[]
430
+ */
431
+ public function getFunctions(): array
432
+ {
433
+ return (array) $this->functions;
434
+ }
435
+
436
+
437
+ /**
438
+ * Adds new provider.
439
+ */
440
+ public function addProvider(string $name, mixed $value): static
441
+ {
442
+ if (!preg_match('#^[a-z]\w*$#iD', $name)) {
443
+ throw new \LogicException("Invalid provider name '$name'.");
444
+ }
445
+
446
+ $this->providers[$name] = $value;
447
+ return $this;
448
+ }
449
+
450
+
451
+ /**
452
+ * Returns all providers.
453
+ * @return mixed[]
454
+ */
455
+ public function getProviders(): array
456
+ {
457
+ return $this->providers;
458
+ }
459
+
460
+
461
+ public function setPolicy(?Policy $policy): static
462
+ {
463
+ $this->policy = $policy;
464
+ return $this;
465
+ }
466
+
467
+
468
+ public function getPolicy(bool $effective = false): ?Policy
469
+ {
470
+ return !$effective || $this->sandboxed
471
+ ? $this->policy
472
+ : null;
473
+ }
474
+
475
+
476
+ public function setExceptionHandler(callable $callback): static
477
+ {
478
+ $this->providers['coreExceptionHandler'] = $callback;
479
+ return $this;
480
+ }
481
+
482
+
483
+ public function setSandboxMode(bool $on = true): static
484
+ {
485
+ $this->sandboxed = $on;
486
+ return $this;
487
+ }
488
+
489
+
490
+ public function setContentType(string $type): static
491
+ {
492
+ $this->contentType = $type;
493
+ return $this;
494
+ }
495
+
496
+
497
+ /**
498
+ * Sets path to temporary directory.
499
+ */
500
+ public function setTempDirectory(?string $path): static
501
+ {
502
+ $this->tempDirectory = $path;
503
+ return $this;
504
+ }
505
+
506
+
507
+ /**
508
+ * Sets auto-refresh mode.
509
+ */
510
+ public function setAutoRefresh(bool $on = true): static
511
+ {
512
+ $this->autoRefresh = $on;
513
+ return $this;
514
+ }
515
+
516
+
517
+ /**
518
+ * Enables declare(strict_types=1) in templates.
519
+ */
520
+ public function setStrictTypes(bool $on = true): static
521
+ {
522
+ $this->strictTypes = $on;
523
+ return $this;
524
+ }
525
+
526
+
527
+ public function setLoader(Loader $loader): static
528
+ {
529
+ $this->loader = $loader;
530
+ return $this;
531
+ }
532
+
533
+
534
+ public function getLoader(): Loader
535
+ {
536
+ if (!$this->loader) {
537
+ $this->loader = new Loaders\FileLoader;
538
+ }
539
+
540
+ return $this->loader;
541
+ }
542
+
543
+
544
+ /**
545
+ * @param object|mixed[] $params
546
+ * @return mixed[]
547
+ */
548
+ private function processParams(object|array $params): array
549
+ {
550
+ if (is_array($params)) {
551
+ return $params;
552
+ } elseif (!is_object($params)) {
553
+ throw new \InvalidArgumentException(sprintf('Engine::render() expects array|object, %s given.', gettype($params)));
554
+ }
555
+
556
+ $methods = (new \ReflectionClass($params))->getMethods(\ReflectionMethod::IS_PUBLIC);
557
+ foreach ($methods as $method) {
558
+ if ($method->getAttributes(Attributes\TemplateFilter::class)) {
559
+ $this->addFilter($method->name, [$params, $method->name]);
560
+ }
561
+
562
+ if ($method->getAttributes(Attributes\TemplateFunction::class)) {
563
+ $this->addFunction($method->name, [$params, $method->name]);
564
+ }
565
+
566
+ if (strpos((string) $method->getDocComment(), '@filter')) {
567
+ trigger_error('Annotation @filter is deprecated, use attribute #[Latte\Attributes\TemplateFilter]', E_USER_DEPRECATED);
568
+ $this->addFilter($method->name, [$params, $method->name]);
569
+ }
570
+
571
+ if (strpos((string) $method->getDocComment(), '@function')) {
572
+ trigger_error('Annotation @function is deprecated, use attribute #[Latte\Attributes\TemplateFunction]', E_USER_DEPRECATED);
573
+ $this->addFunction($method->name, [$params, $method->name]);
574
+ }
575
+ }
576
+
577
+ return array_filter((array) $params, fn($key) => $key[0] !== "\0", ARRAY_FILTER_USE_KEY);
578
+ }
579
+
580
+
581
+ public function __get(string $name)
582
+ {
583
+ if ($name === 'onCompile') {
584
+ $trace = debug_backtrace(0)[0];
585
+ $loc = isset($trace['file'], $trace['line'])
586
+ ? ' (in ' . $trace['file'] . ' on ' . $trace['line'] . ')'
587
+ : '';
588
+ throw new \LogicException('You use Latte 3 together with the code designed for Latte 2' . $loc);
589
+ }
590
+ }
591
+ }