@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.
- package/README.md +39 -0
- package/handler.js +9 -0
- package/index.js +138 -0
- package/index.php +173 -0
- package/latte/AssetFilter.php +11 -0
- package/latte/FetchFunction.php +28 -0
- package/latte/IconFilter.php +14 -0
- package/latte/JsonTag.php +64 -0
- package/latte/PlaceholderFunction.php +11 -0
- package/latte/RandomColorFunction.php +14 -0
- package/latte/TelFilter.php +22 -0
- package/package.json +35 -0
- package/vendor/autoload.php +7 -0
- package/vendor/bin/latte-lint +107 -0
- package/vendor/composer/ClassLoader.php +572 -0
- package/vendor/composer/InstalledVersions.php +350 -0
- package/vendor/composer/LICENSE +21 -0
- package/vendor/composer/autoload_classmap.php +183 -0
- package/vendor/composer/autoload_namespaces.php +9 -0
- package/vendor/composer/autoload_psr4.php +9 -0
- package/vendor/composer/autoload_real.php +57 -0
- package/vendor/composer/autoload_static.php +193 -0
- package/vendor/composer/installed.json +93 -0
- package/vendor/composer/installed.php +32 -0
- package/vendor/composer/platform_check.php +26 -0
- package/vendor/latte/latte/bin/latte-lint +29 -0
- package/vendor/latte/latte/composer.json +53 -0
- package/vendor/latte/latte/license.md +60 -0
- package/vendor/latte/latte/ncs.php +14 -0
- package/vendor/latte/latte/ncs.xml +9 -0
- package/vendor/latte/latte/readme.md +21 -0
- package/vendor/latte/latte/src/Bridges/Tracy/BlueScreenPanel.php +114 -0
- package/vendor/latte/latte/src/Bridges/Tracy/LattePanel.php +99 -0
- package/vendor/latte/latte/src/Bridges/Tracy/templates/LattePanel.panel.phtml +98 -0
- package/vendor/latte/latte/src/Bridges/Tracy/templates/LattePanel.tab.phtml +15 -0
- package/vendor/latte/latte/src/Latte/Compiler/Block.php +44 -0
- package/vendor/latte/latte/src/Latte/Compiler/Escaper.php +244 -0
- package/vendor/latte/latte/src/Latte/Compiler/ExpressionBuilder.php +128 -0
- package/vendor/latte/latte/src/Latte/Compiler/Node.php +28 -0
- package/vendor/latte/latte/src/Latte/Compiler/NodeHelpers.php +132 -0
- package/vendor/latte/latte/src/Latte/Compiler/NodeTraverser.php +77 -0
- package/vendor/latte/latte/src/Latte/Compiler/Nodes/AreaNode.php +17 -0
- package/vendor/latte/latte/src/Latte/Compiler/Nodes/AuxiliaryNode.php +27 -0
- package/vendor/latte/latte/src/Latte/Compiler/Nodes/FragmentNode.php +59 -0
- package/vendor/latte/latte/src/Latte/Compiler/Nodes/Html/AttributeNode.php +48 -0
- package/vendor/latte/latte/src/Latte/Compiler/Nodes/Html/BogusTagNode.php +46 -0
- package/vendor/latte/latte/src/Latte/Compiler/Nodes/Html/CommentNode.php +39 -0
- package/vendor/latte/latte/src/Latte/Compiler/Nodes/Html/ElementNode.php +129 -0
- package/vendor/latte/latte/src/Latte/Compiler/Nodes/Html/QuotedValue.php +53 -0
- package/vendor/latte/latte/src/Latte/Compiler/Nodes/NopNode.php +21 -0
- package/vendor/latte/latte/src/Latte/Compiler/Nodes/Php/ArgumentNode.php +45 -0
- package/vendor/latte/latte/src/Latte/Compiler/Nodes/Php/ComplexTypeNode.php +17 -0
- package/vendor/latte/latte/src/Latte/Compiler/Nodes/Php/Expression/ArrayAccessNode.php +41 -0
- package/vendor/latte/latte/src/Latte/Compiler/Nodes/Php/Expression/ArrayItemNode.php +52 -0
- package/vendor/latte/latte/src/Latte/Compiler/Nodes/Php/Expression/ArrayNode.php +94 -0
- package/vendor/latte/latte/src/Latte/Compiler/Nodes/Php/Expression/AssignNode.php +39 -0
- package/vendor/latte/latte/src/Latte/Compiler/Nodes/Php/Expression/AssignOpNode.php +45 -0
- package/vendor/latte/latte/src/Latte/Compiler/Nodes/Php/Expression/BinaryOpNode.php +65 -0
- package/vendor/latte/latte/src/Latte/Compiler/Nodes/Php/Expression/CastNode.php +43 -0
- package/vendor/latte/latte/src/Latte/Compiler/Nodes/Php/Expression/ClassConstantFetchNode.php +42 -0
- package/vendor/latte/latte/src/Latte/Compiler/Nodes/Php/Expression/CloneNode.php +36 -0
- package/vendor/latte/latte/src/Latte/Compiler/Nodes/Php/Expression/ClosureNode.php +73 -0
- package/vendor/latte/latte/src/Latte/Compiler/Nodes/Php/Expression/ClosureUseNode.php +37 -0
- package/vendor/latte/latte/src/Latte/Compiler/Nodes/Php/Expression/ConstantFetchNode.php +37 -0
- package/vendor/latte/latte/src/Latte/Compiler/Nodes/Php/Expression/EmptyNode.php +36 -0
- package/vendor/latte/latte/src/Latte/Compiler/Nodes/Php/Expression/ErrorSuppressNode.php +36 -0
- package/vendor/latte/latte/src/Latte/Compiler/Nodes/Php/Expression/FilterCallNode.php +39 -0
- package/vendor/latte/latte/src/Latte/Compiler/Nodes/Php/Expression/FunctionCallNode.php +45 -0
- package/vendor/latte/latte/src/Latte/Compiler/Nodes/Php/Expression/FunctionCallableNode.php +39 -0
- package/vendor/latte/latte/src/Latte/Compiler/Nodes/Php/Expression/InRangeNode.php +42 -0
- package/vendor/latte/latte/src/Latte/Compiler/Nodes/Php/Expression/InstanceofNode.php +40 -0
- package/vendor/latte/latte/src/Latte/Compiler/Nodes/Php/Expression/IssetNode.php +40 -0
- package/vendor/latte/latte/src/Latte/Compiler/Nodes/Php/Expression/MatchNode.php +49 -0
- package/vendor/latte/latte/src/Latte/Compiler/Nodes/Php/Expression/MethodCallNode.php +50 -0
- package/vendor/latte/latte/src/Latte/Compiler/Nodes/Php/Expression/MethodCallableNode.php +42 -0
- package/vendor/latte/latte/src/Latte/Compiler/Nodes/Php/Expression/NewNode.php +45 -0
- package/vendor/latte/latte/src/Latte/Compiler/Nodes/Php/Expression/NotNode.php +36 -0
- package/vendor/latte/latte/src/Latte/Compiler/Nodes/Php/Expression/PostOpNode.php +43 -0
- package/vendor/latte/latte/src/Latte/Compiler/Nodes/Php/Expression/PreOpNode.php +43 -0
- package/vendor/latte/latte/src/Latte/Compiler/Nodes/Php/Expression/PropertyFetchNode.php +42 -0
- package/vendor/latte/latte/src/Latte/Compiler/Nodes/Php/Expression/StaticCallNode.php +55 -0
- package/vendor/latte/latte/src/Latte/Compiler/Nodes/Php/Expression/StaticCallableNode.php +47 -0
- package/vendor/latte/latte/src/Latte/Compiler/Nodes/Php/Expression/StaticPropertyFetchNode.php +42 -0
- package/vendor/latte/latte/src/Latte/Compiler/Nodes/Php/Expression/TernaryNode.php +50 -0
- package/vendor/latte/latte/src/Latte/Compiler/Nodes/Php/Expression/UnaryOpNode.php +45 -0
- package/vendor/latte/latte/src/Latte/Compiler/Nodes/Php/Expression/VariableNode.php +40 -0
- package/vendor/latte/latte/src/Latte/Compiler/Nodes/Php/ExpressionNode.php +17 -0
- package/vendor/latte/latte/src/Latte/Compiler/Nodes/Php/FilterNode.php +67 -0
- package/vendor/latte/latte/src/Latte/Compiler/Nodes/Php/IdentifierNode.php +36 -0
- package/vendor/latte/latte/src/Latte/Compiler/Nodes/Php/IntersectionTypeNode.php +39 -0
- package/vendor/latte/latte/src/Latte/Compiler/Nodes/Php/MatchArmNode.php +45 -0
- package/vendor/latte/latte/src/Latte/Compiler/Nodes/Php/ModifierNode.php +107 -0
- package/vendor/latte/latte/src/Latte/Compiler/Nodes/Php/NameNode.php +89 -0
- package/vendor/latte/latte/src/Latte/Compiler/Nodes/Php/NullableTypeNode.php +35 -0
- package/vendor/latte/latte/src/Latte/Compiler/Nodes/Php/ParameterNode.php +54 -0
- package/vendor/latte/latte/src/Latte/Compiler/Nodes/Php/Scalar/BooleanNode.php +30 -0
- package/vendor/latte/latte/src/Latte/Compiler/Nodes/Php/Scalar/EncapsedStringNode.php +80 -0
- package/vendor/latte/latte/src/Latte/Compiler/Nodes/Php/Scalar/EncapsedStringPartNode.php +30 -0
- package/vendor/latte/latte/src/Latte/Compiler/Nodes/Php/Scalar/FloatNode.php +63 -0
- package/vendor/latte/latte/src/Latte/Compiler/Nodes/Php/Scalar/IntegerNode.php +70 -0
- package/vendor/latte/latte/src/Latte/Compiler/Nodes/Php/Scalar/NullNode.php +29 -0
- package/vendor/latte/latte/src/Latte/Compiler/Nodes/Php/Scalar/StringNode.php +40 -0
- package/vendor/latte/latte/src/Latte/Compiler/Nodes/Php/ScalarNode.php +15 -0
- package/vendor/latte/latte/src/Latte/Compiler/Nodes/Php/SuperiorTypeNode.php +29 -0
- package/vendor/latte/latte/src/Latte/Compiler/Nodes/Php/UnionTypeNode.php +39 -0
- package/vendor/latte/latte/src/Latte/Compiler/Nodes/Php/VarLikeIdentifierNode.php +21 -0
- package/vendor/latte/latte/src/Latte/Compiler/Nodes/StatementNode.php +15 -0
- package/vendor/latte/latte/src/Latte/Compiler/Nodes/TemplateNode.php +34 -0
- package/vendor/latte/latte/src/Latte/Compiler/Nodes/TextNode.php +37 -0
- package/vendor/latte/latte/src/Latte/Compiler/PhpHelpers.php +227 -0
- package/vendor/latte/latte/src/Latte/Compiler/Position.php +43 -0
- package/vendor/latte/latte/src/Latte/Compiler/PrintContext.php +334 -0
- package/vendor/latte/latte/src/Latte/Compiler/Tag.php +117 -0
- package/vendor/latte/latte/src/Latte/Compiler/TagLexer.php +399 -0
- package/vendor/latte/latte/src/Latte/Compiler/TagParser.php +388 -0
- package/vendor/latte/latte/src/Latte/Compiler/TagParserData.php +580 -0
- package/vendor/latte/latte/src/Latte/Compiler/TemplateGenerator.php +206 -0
- package/vendor/latte/latte/src/Latte/Compiler/TemplateLexer.php +391 -0
- package/vendor/latte/latte/src/Latte/Compiler/TemplateParser.php +448 -0
- package/vendor/latte/latte/src/Latte/Compiler/TemplateParserHtml.php +506 -0
- package/vendor/latte/latte/src/Latte/Compiler/Token.php +259 -0
- package/vendor/latte/latte/src/Latte/Compiler/TokenStream.php +151 -0
- package/vendor/latte/latte/src/Latte/ContentType.php +22 -0
- package/vendor/latte/latte/src/Latte/Engine.php +591 -0
- package/vendor/latte/latte/src/Latte/Essential/Blueprint.php +160 -0
- package/vendor/latte/latte/src/Latte/Essential/CachingIterator.php +232 -0
- package/vendor/latte/latte/src/Latte/Essential/CoreExtension.php +232 -0
- package/vendor/latte/latte/src/Latte/Essential/Filters.php +591 -0
- package/vendor/latte/latte/src/Latte/Essential/Nodes/BlockNode.php +161 -0
- package/vendor/latte/latte/src/Latte/Essential/Nodes/CaptureNode.php +98 -0
- package/vendor/latte/latte/src/Latte/Essential/Nodes/ContentTypeNode.php +74 -0
- package/vendor/latte/latte/src/Latte/Essential/Nodes/DebugbreakNode.php +53 -0
- package/vendor/latte/latte/src/Latte/Essential/Nodes/DefineNode.php +134 -0
- package/vendor/latte/latte/src/Latte/Essential/Nodes/DoNode.php +49 -0
- package/vendor/latte/latte/src/Latte/Essential/Nodes/DumpNode.php +58 -0
- package/vendor/latte/latte/src/Latte/Essential/Nodes/EmbedNode.php +125 -0
- package/vendor/latte/latte/src/Latte/Essential/Nodes/ExtendsNode.php +60 -0
- package/vendor/latte/latte/src/Latte/Essential/Nodes/FirstLastSepNode.php +81 -0
- package/vendor/latte/latte/src/Latte/Essential/Nodes/ForNode.php +91 -0
- package/vendor/latte/latte/src/Latte/Essential/Nodes/ForeachNode.php +155 -0
- package/vendor/latte/latte/src/Latte/Essential/Nodes/IfChangedNode.php +141 -0
- package/vendor/latte/latte/src/Latte/Essential/Nodes/IfContentNode.php +82 -0
- package/vendor/latte/latte/src/Latte/Essential/Nodes/IfNode.php +180 -0
- package/vendor/latte/latte/src/Latte/Essential/Nodes/ImportNode.php +49 -0
- package/vendor/latte/latte/src/Latte/Essential/Nodes/IncludeBlockNode.php +140 -0
- package/vendor/latte/latte/src/Latte/Essential/Nodes/IncludeFileNode.php +81 -0
- package/vendor/latte/latte/src/Latte/Essential/Nodes/IterateWhileNode.php +94 -0
- package/vendor/latte/latte/src/Latte/Essential/Nodes/NAttrNode.php +105 -0
- package/vendor/latte/latte/src/Latte/Essential/Nodes/NClassNode.php +54 -0
- package/vendor/latte/latte/src/Latte/Essential/Nodes/NTagNode.php +79 -0
- package/vendor/latte/latte/src/Latte/Essential/Nodes/ParametersNode.php +84 -0
- package/vendor/latte/latte/src/Latte/Essential/Nodes/PrintNode.php +70 -0
- package/vendor/latte/latte/src/Latte/Essential/Nodes/RawPhpNode.php +46 -0
- package/vendor/latte/latte/src/Latte/Essential/Nodes/RollbackNode.php +37 -0
- package/vendor/latte/latte/src/Latte/Essential/Nodes/SkipNode.php +71 -0
- package/vendor/latte/latte/src/Latte/Essential/Nodes/SpacelessNode.php +62 -0
- package/vendor/latte/latte/src/Latte/Essential/Nodes/SwitchNode.php +118 -0
- package/vendor/latte/latte/src/Latte/Essential/Nodes/TemplatePrintNode.php +39 -0
- package/vendor/latte/latte/src/Latte/Essential/Nodes/TemplateTypeNode.php +38 -0
- package/vendor/latte/latte/src/Latte/Essential/Nodes/TraceNode.php +35 -0
- package/vendor/latte/latte/src/Latte/Essential/Nodes/TranslateNode.php +104 -0
- package/vendor/latte/latte/src/Latte/Essential/Nodes/TryNode.php +75 -0
- package/vendor/latte/latte/src/Latte/Essential/Nodes/VarNode.php +106 -0
- package/vendor/latte/latte/src/Latte/Essential/Nodes/VarPrintNode.php +40 -0
- package/vendor/latte/latte/src/Latte/Essential/Nodes/VarTypeNode.php +36 -0
- package/vendor/latte/latte/src/Latte/Essential/Nodes/WhileNode.php +81 -0
- package/vendor/latte/latte/src/Latte/Essential/Passes.php +115 -0
- package/vendor/latte/latte/src/Latte/Essential/RawPhpExtension.php +28 -0
- package/vendor/latte/latte/src/Latte/Essential/RollbackException.php +16 -0
- package/vendor/latte/latte/src/Latte/Essential/Tracer.php +117 -0
- package/vendor/latte/latte/src/Latte/Essential/TranslatorExtension.php +101 -0
- package/vendor/latte/latte/src/Latte/Extension.php +97 -0
- package/vendor/latte/latte/src/Latte/Helpers.php +96 -0
- package/vendor/latte/latte/src/Latte/Loader.php +37 -0
- package/vendor/latte/latte/src/Latte/Loaders/FileLoader.php +95 -0
- package/vendor/latte/latte/src/Latte/Loaders/StringLoader.php +76 -0
- package/vendor/latte/latte/src/Latte/Policy.php +24 -0
- package/vendor/latte/latte/src/Latte/PositionAwareException.php +50 -0
- package/vendor/latte/latte/src/Latte/Runtime/Block.php +24 -0
- package/vendor/latte/latte/src/Latte/Runtime/FilterExecutor.php +151 -0
- package/vendor/latte/latte/src/Latte/Runtime/FilterInfo.php +39 -0
- package/vendor/latte/latte/src/Latte/Runtime/Filters.php +234 -0
- package/vendor/latte/latte/src/Latte/Runtime/Html.php +35 -0
- package/vendor/latte/latte/src/Latte/Runtime/HtmlStringable.php +17 -0
- package/vendor/latte/latte/src/Latte/Runtime/Template.php +381 -0
- package/vendor/latte/latte/src/Latte/Sandbox/Nodes/FunctionCallNode.php +30 -0
- package/vendor/latte/latte/src/Latte/Sandbox/Nodes/FunctionCallableNode.php +29 -0
- package/vendor/latte/latte/src/Latte/Sandbox/Nodes/MethodCallNode.php +32 -0
- package/vendor/latte/latte/src/Latte/Sandbox/Nodes/MethodCallableNode.php +30 -0
- package/vendor/latte/latte/src/Latte/Sandbox/Nodes/PropertyFetchNode.php +32 -0
- package/vendor/latte/latte/src/Latte/Sandbox/Nodes/SandboxNode.php +73 -0
- package/vendor/latte/latte/src/Latte/Sandbox/Nodes/StaticCallNode.php +31 -0
- package/vendor/latte/latte/src/Latte/Sandbox/Nodes/StaticCallableNode.php +30 -0
- package/vendor/latte/latte/src/Latte/Sandbox/Nodes/StaticPropertyFetchNode.php +32 -0
- package/vendor/latte/latte/src/Latte/Sandbox/RuntimeChecker.php +116 -0
- package/vendor/latte/latte/src/Latte/Sandbox/SandboxExtension.php +132 -0
- package/vendor/latte/latte/src/Latte/Sandbox/SecurityPolicy.php +185 -0
- package/vendor/latte/latte/src/Latte/Strict.php +101 -0
- package/vendor/latte/latte/src/Latte/attributes.php +24 -0
- package/vendor/latte/latte/src/Latte/exceptions.php +69 -0
- 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
|
+
}
|