@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\Essential;
11
+
12
+ use Latte;
13
+ use Latte\ContentType;
14
+ use Latte\Runtime\FilterInfo;
15
+ use Latte\Runtime\Html;
16
+ use Stringable;
17
+ use function is_array, is_string, count, strlen;
18
+
19
+
20
+ /**
21
+ * Template filters. Uses UTF-8 only.
22
+ * @internal
23
+ */
24
+ final class Filters
25
+ {
26
+ /**
27
+ * Converts HTML to plain text.
28
+ */
29
+ public static function stripHtml(FilterInfo $info, $s): string
30
+ {
31
+ $info->validate([null, 'html', 'html/attr', 'xml', 'xml/attr'], __FUNCTION__);
32
+ $info->contentType = ContentType::Text;
33
+ return html_entity_decode(strip_tags((string) $s), ENT_QUOTES | ENT_HTML5, 'UTF-8');
34
+ }
35
+
36
+
37
+ /**
38
+ * Removes tags from HTML (but remains HTML entites).
39
+ */
40
+ public static function stripTags(FilterInfo $info, $s): string
41
+ {
42
+ $info->contentType ??= ContentType::Html;
43
+ $info->validate(['html', 'html/attr', 'xml', 'xml/attr'], __FUNCTION__);
44
+ return strip_tags((string) $s);
45
+ }
46
+
47
+
48
+ /**
49
+ * Replaces all repeated white spaces with a single space.
50
+ */
51
+ public static function strip(FilterInfo $info, string $s): string
52
+ {
53
+ return $info->contentType === ContentType::Html
54
+ ? trim(self::spacelessHtml($s))
55
+ : trim(self::spacelessText($s));
56
+ }
57
+
58
+
59
+ /**
60
+ * Replaces all repeated white spaces with a single space.
61
+ */
62
+ public static function spacelessHtml(string $s, bool &$strip = true): string
63
+ {
64
+ return preg_replace_callback(
65
+ '#[ \t\r\n]+|<(/)?(textarea|pre|script)(?=\W)#si',
66
+ function ($m) use (&$strip) {
67
+ if (empty($m[2])) {
68
+ return $strip ? ' ' : $m[0];
69
+ } else {
70
+ $strip = !empty($m[1]);
71
+ return $m[0];
72
+ }
73
+ },
74
+ $s,
75
+ );
76
+ }
77
+
78
+
79
+ /**
80
+ * Output buffering handler for spacelessHtml.
81
+ */
82
+ public static function spacelessHtmlHandler(string $s, ?int $phase = null): string
83
+ {
84
+ static $strip;
85
+ $left = $right = '';
86
+
87
+ if ($phase & PHP_OUTPUT_HANDLER_START) {
88
+ $strip = true;
89
+ $tmp = ltrim($s);
90
+ $left = substr($s, 0, strlen($s) - strlen($tmp));
91
+ $s = $tmp;
92
+ }
93
+
94
+ if ($phase & PHP_OUTPUT_HANDLER_FINAL) {
95
+ $tmp = rtrim($s);
96
+ $right = substr($s, strlen($tmp));
97
+ $s = $tmp;
98
+ }
99
+
100
+ return $left . self::spacelessHtml($s, $strip) . $right;
101
+ }
102
+
103
+
104
+ /**
105
+ * Replaces all repeated white spaces with a single space.
106
+ */
107
+ public static function spacelessText(string $s): string
108
+ {
109
+ return preg_replace('#[ \t\r\n]+#', ' ', $s);
110
+ }
111
+
112
+
113
+ /**
114
+ * Indents plain text or HTML the content from the left.
115
+ */
116
+ public static function indent(FilterInfo $info, string $s, int $level = 1, string $chars = "\t"): string
117
+ {
118
+ if ($level < 1) {
119
+ // do nothing
120
+ } elseif ($info->contentType === ContentType::Html) {
121
+ $s = preg_replace_callback('#<(textarea|pre).*?</\1#si', fn($m) => strtr($m[0], " \t\r\n", "\x1F\x1E\x1D\x1A"), $s);
122
+ if (preg_last_error()) {
123
+ throw new Latte\RegexpException;
124
+ }
125
+
126
+ $s = preg_replace('#(?:^|[\r\n]+)(?=[^\r\n])#', '$0' . str_repeat($chars, $level), $s);
127
+ $s = strtr($s, "\x1F\x1E\x1D\x1A", " \t\r\n");
128
+ } else {
129
+ $s = preg_replace('#(?:^|[\r\n]+)(?=[^\r\n])#', '$0' . str_repeat($chars, $level), $s);
130
+ }
131
+
132
+ return $s;
133
+ }
134
+
135
+
136
+ /**
137
+ * Join array of text or HTML elements with a string.
138
+ * @param string[] $arr
139
+ */
140
+ public static function implode(array $arr, string $glue = ''): string
141
+ {
142
+ return implode($glue, $arr);
143
+ }
144
+
145
+
146
+ /**
147
+ * Splits a string by a string.
148
+ */
149
+ public static function explode(string $value, string $separator = ''): array
150
+ {
151
+ return $separator === ''
152
+ ? preg_split('//u', $value, -1, PREG_SPLIT_NO_EMPTY)
153
+ : explode($separator, $value);
154
+ }
155
+
156
+
157
+ /**
158
+ * Repeats text.
159
+ */
160
+ public static function repeat(FilterInfo $info, $s, int $count): string
161
+ {
162
+ return str_repeat((string) $s, $count);
163
+ }
164
+
165
+
166
+ /**
167
+ * Date/time formatting.
168
+ */
169
+ public static function date(string|int|\DateTimeInterface|\DateInterval|null $time, ?string $format = null): ?string
170
+ {
171
+ if ($time == null) { // intentionally ==
172
+ return null;
173
+ }
174
+
175
+ if (!isset($format)) {
176
+ $format = Latte\Runtime\Filters::$dateFormat;
177
+ }
178
+
179
+ if ($time instanceof \DateInterval) {
180
+ return $time->format($format);
181
+
182
+ } elseif (is_numeric($time)) {
183
+ $time = new \DateTime('@' . $time);
184
+ $time->setTimeZone(new \DateTimeZone(date_default_timezone_get()));
185
+
186
+ } elseif (!$time instanceof \DateTimeInterface) {
187
+ $time = new \DateTime($time);
188
+ }
189
+
190
+ if (str_contains($format, '%')) {
191
+ if (PHP_VERSION_ID >= 80100) {
192
+ trigger_error("Function strftime() used by filter |date is deprecated since PHP 8.1, use format without % characters like 'Y-m-d'.", E_USER_DEPRECATED);
193
+ }
194
+
195
+ return @strftime($format, $time->format('U') + 0);
196
+ }
197
+
198
+ return $time->format($format);
199
+ }
200
+
201
+
202
+ /**
203
+ * Converts to human readable file size.
204
+ */
205
+ public static function bytes(float $bytes, int $precision = 2): string
206
+ {
207
+ $bytes = round($bytes);
208
+ $units = ['B', 'kB', 'MB', 'GB', 'TB', 'PB'];
209
+ foreach ($units as $unit) {
210
+ if (abs($bytes) < 1024 || $unit === end($units)) {
211
+ break;
212
+ }
213
+
214
+ $bytes /= 1024;
215
+ }
216
+
217
+ return round($bytes, $precision) . ' ' . $unit;
218
+ }
219
+
220
+
221
+ /**
222
+ * Performs a search and replace.
223
+ */
224
+ public static function replace(
225
+ FilterInfo $info,
226
+ string|array $subject,
227
+ string|array $search,
228
+ string|array|null $replace = null,
229
+ ): string {
230
+ $subject = (string) $subject;
231
+ if (is_array($search)) {
232
+ if (is_array($replace)) {
233
+ return strtr($subject, array_combine($search, $replace));
234
+ } elseif ($replace === null && is_string(key($search))) {
235
+ return strtr($subject, $search);
236
+ } else {
237
+ return strtr($subject, array_fill_keys($search, $replace));
238
+ }
239
+ }
240
+
241
+ return str_replace($search, $replace ?? '', $subject);
242
+ }
243
+
244
+
245
+ /**
246
+ * Perform a regular expression search and replace.
247
+ */
248
+ public static function replaceRe(string $subject, string $pattern, string $replacement = ''): string
249
+ {
250
+ $res = preg_replace($pattern, $replacement, $subject);
251
+ if (preg_last_error()) {
252
+ throw new Latte\RegexpException;
253
+ }
254
+
255
+ return $res;
256
+ }
257
+
258
+
259
+ /**
260
+ * The data: URI generator.
261
+ */
262
+ public static function dataStream(string $data, ?string $type = null): string
263
+ {
264
+ if ($type === null) {
265
+ $type = finfo_buffer(finfo_open(FILEINFO_MIME_TYPE), $data);
266
+ }
267
+
268
+ return 'data:' . ($type ? "$type;" : '') . 'base64,' . base64_encode($data);
269
+ }
270
+
271
+
272
+ public static function breaklines(string|Stringable|null $s): Html
273
+ {
274
+ $s = htmlspecialchars((string) $s, ENT_NOQUOTES | ENT_SUBSTITUTE, 'UTF-8');
275
+ return new Html(nl2br($s, Latte\Runtime\Filters::$xml));
276
+ }
277
+
278
+
279
+ /**
280
+ * Returns a part of string.
281
+ */
282
+ public static function substring(string|Stringable|null $s, int $start, ?int $length = null): string
283
+ {
284
+ $s = (string) $s;
285
+ if ($length === null) {
286
+ $length = self::strLength($s);
287
+ }
288
+
289
+ if (function_exists('mb_substr')) {
290
+ return mb_substr($s, $start, $length, 'UTF-8'); // MB is much faster
291
+ }
292
+
293
+ return iconv_substr($s, $start, $length, 'UTF-8');
294
+ }
295
+
296
+
297
+ /**
298
+ * Truncates string to maximal length.
299
+ */
300
+ public static function truncate(string|Stringable|null $s, int $length, string $append = "\u{2026}"): string
301
+ {
302
+ $s = (string) $s;
303
+ if (self::strLength($s) > $length) {
304
+ $length -= self::strLength($append);
305
+ if ($length < 1) {
306
+ return $append;
307
+
308
+ } elseif (preg_match('#^.{1,' . $length . '}(?=[\s\x00-/:-@\[-`{-~])#us', $s, $matches)) {
309
+ return $matches[0] . $append;
310
+
311
+ } else {
312
+ return self::substring($s, 0, $length) . $append;
313
+ }
314
+ }
315
+
316
+ return $s;
317
+ }
318
+
319
+
320
+ /**
321
+ * Convert to lower case.
322
+ */
323
+ public static function lower($s): string
324
+ {
325
+ return mb_strtolower((string) $s, 'UTF-8');
326
+ }
327
+
328
+
329
+ /**
330
+ * Convert to upper case.
331
+ */
332
+ public static function upper($s): string
333
+ {
334
+ return mb_strtoupper((string) $s, 'UTF-8');
335
+ }
336
+
337
+
338
+ /**
339
+ * Convert first character to upper case.
340
+ */
341
+ public static function firstUpper($s): string
342
+ {
343
+ $s = (string) $s;
344
+ return self::upper(self::substring($s, 0, 1)) . self::substring($s, 1);
345
+ }
346
+
347
+
348
+ /**
349
+ * Capitalize string.
350
+ */
351
+ public static function capitalize($s): string
352
+ {
353
+ return mb_convert_case((string) $s, MB_CASE_TITLE, 'UTF-8');
354
+ }
355
+
356
+
357
+ /**
358
+ * Returns length of string or iterable.
359
+ */
360
+ public static function length(array|\Countable|\Traversable|string $val): int
361
+ {
362
+ if (is_array($val) || $val instanceof \Countable) {
363
+ return count($val);
364
+ } elseif ($val instanceof \Traversable) {
365
+ return iterator_count($val);
366
+ } else {
367
+ return self::strLength($val);
368
+ }
369
+ }
370
+
371
+
372
+ private static function strLength(string $s): int
373
+ {
374
+ return function_exists('mb_strlen')
375
+ ? mb_strlen($s, 'UTF-8')
376
+ : strlen(utf8_decode($s));
377
+ }
378
+
379
+
380
+ /**
381
+ * Strips whitespace.
382
+ */
383
+ public static function trim(FilterInfo $info, string $s, string $charlist = " \t\n\r\0\x0B\u{A0}"): string
384
+ {
385
+ $charlist = preg_quote($charlist, '#');
386
+ $s = preg_replace('#^[' . $charlist . ']+|[' . $charlist . ']+$#Du', '', (string) $s);
387
+ if (preg_last_error()) {
388
+ throw new Latte\RegexpException;
389
+ }
390
+
391
+ return $s;
392
+ }
393
+
394
+
395
+ /**
396
+ * Pad a string to a certain length with another string.
397
+ */
398
+ public static function padLeft($s, int $length, string $append = ' '): string
399
+ {
400
+ $s = (string) $s;
401
+ $length = max(0, $length - self::strLength($s));
402
+ $l = self::strLength($append);
403
+ return str_repeat($append, (int) ($length / $l)) . self::substring($append, 0, $length % $l) . $s;
404
+ }
405
+
406
+
407
+ /**
408
+ * Pad a string to a certain length with another string.
409
+ */
410
+ public static function padRight($s, int $length, string $append = ' '): string
411
+ {
412
+ $s = (string) $s;
413
+ $length = max(0, $length - self::strLength($s));
414
+ $l = self::strLength($append);
415
+ return $s . str_repeat($append, (int) ($length / $l)) . self::substring($append, 0, $length % $l);
416
+ }
417
+
418
+
419
+ /**
420
+ * Reverses string or array.
421
+ */
422
+ public static function reverse(string|array|\Traversable $val, bool $preserveKeys = false): string|array
423
+ {
424
+ if (is_array($val)) {
425
+ return array_reverse($val, $preserveKeys);
426
+ } elseif ($val instanceof \Traversable) {
427
+ return array_reverse(iterator_to_array($val), $preserveKeys);
428
+ } else {
429
+ return iconv('UTF-32LE', 'UTF-8', strrev(iconv('UTF-8', 'UTF-32BE', (string) $val)));
430
+ }
431
+ }
432
+
433
+
434
+ /**
435
+ * Chunks items by returning an array of arrays with the given number of items.
436
+ */
437
+ public static function batch(array|\Traversable $list, int $length, $rest = null): \Generator
438
+ {
439
+ $batch = [];
440
+ foreach ($list as $key => $value) {
441
+ $batch[$key] = $value;
442
+ if (count($batch) >= $length) {
443
+ yield $batch;
444
+ $batch = [];
445
+ }
446
+ }
447
+
448
+ if ($batch) {
449
+ if ($rest !== null) {
450
+ while (count($batch) < $length) {
451
+ $batch[] = $rest;
452
+ }
453
+ }
454
+
455
+ yield $batch;
456
+ }
457
+ }
458
+
459
+
460
+ /**
461
+ * Sorts an array.
462
+ * @param mixed[] $array
463
+ * @return mixed[]
464
+ */
465
+ public static function sort(array $array, ?\Closure $callback = null): array
466
+ {
467
+ $callback ? uasort($array, $callback) : asort($array);
468
+ return $array;
469
+ }
470
+
471
+
472
+ /**
473
+ * Returns value clamped to the inclusive range of min and max.
474
+ */
475
+ public static function clamp(int|float $value, int|float $min, int|float $max): int|float
476
+ {
477
+ if ($min > $max) {
478
+ throw new \InvalidArgumentException("Minimum ($min) is not less than maximum ($max).");
479
+ }
480
+
481
+ return min(max($value, $min), $max);
482
+ }
483
+
484
+
485
+ /**
486
+ * Generates URL-encoded query string
487
+ */
488
+ public static function query(string|array $data): string
489
+ {
490
+ return is_array($data)
491
+ ? http_build_query($data, '', '&')
492
+ : urlencode($data);
493
+ }
494
+
495
+
496
+ /**
497
+ * Is divisible by?
498
+ */
499
+ public static function divisibleBy(int $value, int $by): bool
500
+ {
501
+ return $value % $by === 0;
502
+ }
503
+
504
+
505
+ /**
506
+ * Is odd?
507
+ */
508
+ public static function odd(int $value): bool
509
+ {
510
+ return $value % 2 !== 0;
511
+ }
512
+
513
+
514
+ /**
515
+ * Is even?
516
+ */
517
+ public static function even(int $value): bool
518
+ {
519
+ return $value % 2 === 0;
520
+ }
521
+
522
+
523
+ /**
524
+ * Returns the first item from the array or null if array is empty.
525
+ */
526
+ public static function first(string|array $value): mixed
527
+ {
528
+ return is_array($value)
529
+ ? (count($value) ? reset($value) : null)
530
+ : self::substring($value, 0, 1);
531
+ }
532
+
533
+
534
+ /**
535
+ * Returns the last item from the array or null if array is empty.
536
+ */
537
+ public static function last(string|array $value): mixed
538
+ {
539
+ return is_array($value)
540
+ ? (count($value) ? end($value) : null)
541
+ : self::substring($value, -1);
542
+ }
543
+
544
+
545
+ /**
546
+ * Extracts a slice of an array or string.
547
+ */
548
+ public static function slice(
549
+ string|array $value,
550
+ int $start,
551
+ ?int $length = null,
552
+ bool $preserveKeys = false,
553
+ ): string|array {
554
+ return is_array($value)
555
+ ? array_slice($value, $start, $length, $preserveKeys)
556
+ : self::substring($value, $start, $length);
557
+ }
558
+
559
+
560
+ public static function round(float $value, int $precision = 0): float
561
+ {
562
+ return round($value, $precision);
563
+ }
564
+
565
+
566
+ public static function floor(float $value, int $precision = 0): float
567
+ {
568
+ return floor($value * 10 ** $precision) / 10 ** $precision;
569
+ }
570
+
571
+
572
+ public static function ceil(float $value, int $precision = 0): float
573
+ {
574
+ return ceil($value * 10 ** $precision) / 10 ** $precision;
575
+ }
576
+
577
+
578
+ /**
579
+ * Picks random element/char.
580
+ */
581
+ public static function random(string|array $values): mixed
582
+ {
583
+ if (is_string($values)) {
584
+ $values = preg_split('//u', $values, -1, PREG_SPLIT_NO_EMPTY);
585
+ }
586
+
587
+ return $values
588
+ ? $values[array_rand($values, 1)]
589
+ : null;
590
+ }
591
+ }