@vituum/vite-plugin-latte 2.0.0-next.4 → 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (234) hide show
  1. package/README.md +1 -1
  2. package/index.js +4 -2
  3. package/package.json +11 -9
  4. package/types/index.d.ts +1 -1
  5. package/vendor/autoload.php +1 -4
  6. package/vendor/composer/InstalledVersions.php +36 -2
  7. package/vendor/composer/autoload_classmap.php +14 -15
  8. package/vendor/composer/autoload_psr4.php +2 -0
  9. package/vendor/composer/autoload_static.php +38 -15
  10. package/vendor/composer/installed.json +32 -22
  11. package/vendor/composer/installed.php +12 -12
  12. package/vendor/composer/platform_check.php +4 -5
  13. package/vendor/latte/latte/bin/latte-lint +10 -3
  14. package/vendor/latte/latte/composer.json +16 -6
  15. package/vendor/latte/latte/src/Bridges/Tracy/BlueScreenPanel.php +13 -38
  16. package/vendor/latte/latte/src/Bridges/Tracy/LattePanel.php +9 -7
  17. package/vendor/latte/latte/src/Bridges/Tracy/TracyExtension.php +2 -4
  18. package/vendor/latte/latte/src/Bridges/Tracy/dist/panel.phtml +94 -0
  19. package/vendor/latte/latte/src/Bridges/Tracy/dist/tab.phtml +10 -0
  20. package/vendor/latte/latte/src/Latte/Compiler/Block.php +4 -6
  21. package/vendor/latte/latte/src/Latte/Compiler/Escaper.php +67 -86
  22. package/vendor/latte/latte/src/Latte/Compiler/Node.php +6 -3
  23. package/vendor/latte/latte/src/Latte/Compiler/NodeHelpers.php +16 -6
  24. package/vendor/latte/latte/src/Latte/Compiler/NodeTraverser.php +25 -12
  25. package/vendor/latte/latte/src/Latte/Compiler/Nodes/AreaNode.php +7 -3
  26. package/vendor/latte/latte/src/Latte/Compiler/Nodes/AuxiliaryNode.php +5 -4
  27. package/vendor/latte/latte/src/Latte/Compiler/Nodes/FragmentNode.php +8 -4
  28. package/vendor/latte/latte/src/Latte/Compiler/Nodes/Html/AttributeNode.php +7 -15
  29. package/vendor/latte/latte/src/Latte/Compiler/Nodes/Html/BogusTagNode.php +4 -6
  30. package/vendor/latte/latte/src/Latte/Compiler/Nodes/Html/CommentNode.php +4 -3
  31. package/vendor/latte/latte/src/Latte/Compiler/Nodes/Html/ElementNode.php +37 -67
  32. package/vendor/latte/latte/src/Latte/Compiler/Nodes/Html/ExpressionAttributeNode.php +60 -0
  33. package/vendor/latte/latte/src/Latte/Compiler/Nodes/Html/TagNode.php +70 -0
  34. package/vendor/latte/latte/src/Latte/Compiler/Nodes/NopNode.php +4 -3
  35. package/vendor/latte/latte/src/Latte/Compiler/Nodes/Php/ArgumentNode.php +4 -3
  36. package/vendor/latte/latte/src/Latte/Compiler/Nodes/Php/ArrayItemNode.php +4 -15
  37. package/vendor/latte/latte/src/Latte/Compiler/Nodes/Php/ClosureUseNode.php +4 -3
  38. package/vendor/latte/latte/src/Latte/Compiler/Nodes/Php/ComplexTypeNode.php +4 -3
  39. package/vendor/latte/latte/src/Latte/Compiler/Nodes/Php/Expression/ArrayAccessNode.php +4 -3
  40. package/vendor/latte/latte/src/Latte/Compiler/Nodes/Php/Expression/ArrayNode.php +6 -22
  41. package/vendor/latte/latte/src/Latte/Compiler/Nodes/Php/Expression/AssignNode.php +18 -5
  42. package/vendor/latte/latte/src/Latte/Compiler/Nodes/Php/Expression/AssignOpNode.php +18 -7
  43. package/vendor/latte/latte/src/Latte/Compiler/Nodes/Php/Expression/AuxiliaryNode.php +5 -4
  44. package/vendor/latte/latte/src/Latte/Compiler/Nodes/Php/Expression/BinaryOpNode.php +38 -9
  45. package/vendor/latte/latte/src/Latte/Compiler/Nodes/Php/Expression/CastNode.php +14 -6
  46. package/vendor/latte/latte/src/Latte/Compiler/Nodes/Php/Expression/ClassConstantFetchNode.php +4 -3
  47. package/vendor/latte/latte/src/Latte/Compiler/Nodes/Php/Expression/CloneNode.php +12 -4
  48. package/vendor/latte/latte/src/Latte/Compiler/Nodes/Php/Expression/ClosureNode.php +10 -6
  49. package/vendor/latte/latte/src/Latte/Compiler/Nodes/Php/Expression/ConstantFetchNode.php +7 -6
  50. package/vendor/latte/latte/src/Latte/Compiler/Nodes/Php/Expression/EmptyNode.php +4 -3
  51. package/vendor/latte/latte/src/Latte/Compiler/Nodes/Php/Expression/FilterCallNode.php +10 -4
  52. package/vendor/latte/latte/src/Latte/Compiler/Nodes/Php/Expression/FunctionCallNode.php +14 -5
  53. package/vendor/latte/latte/src/Latte/Compiler/Nodes/Php/Expression/InNode.php +4 -3
  54. package/vendor/latte/latte/src/Latte/Compiler/Nodes/Php/Expression/InstanceofNode.php +14 -5
  55. package/vendor/latte/latte/src/Latte/Compiler/Nodes/Php/Expression/IssetNode.php +6 -3
  56. package/vendor/latte/latte/src/Latte/Compiler/Nodes/Php/Expression/MatchNode.php +6 -3
  57. package/vendor/latte/latte/src/Latte/Compiler/Nodes/Php/Expression/MethodCallNode.php +18 -5
  58. package/vendor/latte/latte/src/Latte/Compiler/Nodes/Php/Expression/NewNode.php +14 -4
  59. package/vendor/latte/latte/src/Latte/Compiler/Nodes/Php/Expression/PostOpNode.php +14 -6
  60. package/vendor/latte/latte/src/Latte/Compiler/Nodes/Php/Expression/PreOpNode.php +14 -6
  61. package/vendor/latte/latte/src/Latte/Compiler/Nodes/Php/Expression/PropertyFetchNode.php +4 -3
  62. package/vendor/latte/latte/src/Latte/Compiler/Nodes/Php/Expression/StaticMethodCallNode.php +14 -15
  63. package/vendor/latte/latte/src/Latte/Compiler/Nodes/Php/Expression/StaticPropertyFetchNode.php +4 -3
  64. package/vendor/latte/latte/src/Latte/Compiler/Nodes/Php/Expression/TemporaryNode.php +7 -6
  65. package/vendor/latte/latte/src/Latte/Compiler/Nodes/Php/Expression/TernaryNode.php +16 -11
  66. package/vendor/latte/latte/src/Latte/Compiler/Nodes/Php/Expression/UnaryOpNode.php +19 -9
  67. package/vendor/latte/latte/src/Latte/Compiler/Nodes/Php/Expression/VariableNode.php +4 -3
  68. package/vendor/latte/latte/src/Latte/Compiler/Nodes/Php/ExpressionNode.php +4 -3
  69. package/vendor/latte/latte/src/Latte/Compiler/Nodes/Php/FilterNode.php +33 -8
  70. package/vendor/latte/latte/src/Latte/Compiler/Nodes/Php/IdentifierNode.php +4 -3
  71. package/vendor/latte/latte/src/Latte/Compiler/Nodes/Php/InterpolatedStringPartNode.php +4 -3
  72. package/vendor/latte/latte/src/Latte/Compiler/Nodes/Php/IntersectionTypeNode.php +6 -3
  73. package/vendor/latte/latte/src/Latte/Compiler/Nodes/Php/ListItemNode.php +4 -3
  74. package/vendor/latte/latte/src/Latte/Compiler/Nodes/Php/ListNode.php +4 -3
  75. package/vendor/latte/latte/src/Latte/Compiler/Nodes/Php/MatchArmNode.php +6 -3
  76. package/vendor/latte/latte/src/Latte/Compiler/Nodes/Php/ModifierNode.php +29 -41
  77. package/vendor/latte/latte/src/Latte/Compiler/Nodes/Php/NameNode.php +9 -4
  78. package/vendor/latte/latte/src/Latte/Compiler/Nodes/Php/NullableTypeNode.php +4 -3
  79. package/vendor/latte/latte/src/Latte/Compiler/Nodes/Php/OperatorNode.php +27 -0
  80. package/vendor/latte/latte/src/Latte/Compiler/Nodes/Php/ParameterNode.php +4 -3
  81. package/vendor/latte/latte/src/Latte/Compiler/Nodes/Php/Scalar/BooleanNode.php +4 -3
  82. package/vendor/latte/latte/src/Latte/Compiler/Nodes/Php/Scalar/FloatNode.php +9 -6
  83. package/vendor/latte/latte/src/Latte/Compiler/Nodes/Php/Scalar/IntegerNode.php +8 -6
  84. package/vendor/latte/latte/src/Latte/Compiler/Nodes/Php/Scalar/InterpolatedStringNode.php +9 -5
  85. package/vendor/latte/latte/src/Latte/Compiler/Nodes/Php/Scalar/NullNode.php +4 -3
  86. package/vendor/latte/latte/src/Latte/Compiler/Nodes/Php/Scalar/StringNode.php +6 -4
  87. package/vendor/latte/latte/src/Latte/Compiler/Nodes/Php/ScalarNode.php +4 -3
  88. package/vendor/latte/latte/src/Latte/Compiler/Nodes/Php/SuperiorTypeNode.php +4 -3
  89. package/vendor/latte/latte/src/Latte/Compiler/Nodes/Php/UnionTypeNode.php +6 -3
  90. package/vendor/latte/latte/src/Latte/Compiler/Nodes/Php/VarLikeIdentifierNode.php +4 -3
  91. package/vendor/latte/latte/src/Latte/Compiler/Nodes/Php/{Expression/NotNode.php → VariadicPlaceholderNode.php} +9 -9
  92. package/vendor/latte/latte/src/Latte/{Essential → Compiler}/Nodes/PrintNode.php +9 -15
  93. package/vendor/latte/latte/src/Latte/Compiler/Nodes/StatementNode.php +7 -3
  94. package/vendor/latte/latte/src/Latte/Compiler/Nodes/TemplateNode.php +4 -3
  95. package/vendor/latte/latte/src/Latte/Compiler/Nodes/TextNode.php +6 -4
  96. package/vendor/latte/latte/src/Latte/Compiler/PhpHelpers.php +32 -18
  97. package/vendor/latte/latte/src/Latte/Compiler/Position.php +7 -7
  98. package/vendor/latte/latte/src/Latte/Compiler/PrintContext.php +72 -104
  99. package/vendor/latte/latte/src/Latte/Compiler/Tag.php +17 -16
  100. package/vendor/latte/latte/src/Latte/Compiler/TagLexer.php +11 -8
  101. package/vendor/latte/latte/src/Latte/Compiler/TagParser.php +28 -23
  102. package/vendor/latte/latte/src/Latte/Compiler/TagParserData.php +360 -336
  103. package/vendor/latte/latte/src/Latte/Compiler/TemplateGenerator.php +49 -38
  104. package/vendor/latte/latte/src/Latte/Compiler/TemplateLexer.php +37 -12
  105. package/vendor/latte/latte/src/Latte/Compiler/TemplateParser.php +35 -21
  106. package/vendor/latte/latte/src/Latte/Compiler/TemplateParserHtml.php +121 -71
  107. package/vendor/latte/latte/src/Latte/Compiler/Token.php +79 -70
  108. package/vendor/latte/latte/src/Latte/Compiler/TokenStream.php +21 -14
  109. package/vendor/latte/latte/src/Latte/ContentType.php +4 -3
  110. package/vendor/latte/latte/src/Latte/Engine.php +147 -188
  111. package/vendor/latte/latte/src/Latte/Essential/AuxiliaryIterator.php +5 -7
  112. package/vendor/latte/latte/src/Latte/Essential/Blueprint.php +5 -5
  113. package/vendor/latte/latte/src/Latte/Essential/CachingIterator.php +5 -4
  114. package/vendor/latte/latte/src/Latte/Essential/CoreExtension.php +155 -127
  115. package/vendor/latte/latte/src/Latte/Essential/Filters.php +99 -44
  116. package/vendor/latte/latte/src/Latte/Essential/Nodes/BlockNode.php +14 -13
  117. package/vendor/latte/latte/src/Latte/Essential/Nodes/CaptureNode.php +5 -6
  118. package/vendor/latte/latte/src/Latte/Essential/Nodes/ContentTypeNode.php +3 -4
  119. package/vendor/latte/latte/src/Latte/Essential/Nodes/CustomFunctionCallNode.php +46 -0
  120. package/vendor/latte/latte/src/Latte/Essential/Nodes/DebugbreakNode.php +2 -3
  121. package/vendor/latte/latte/src/Latte/Essential/Nodes/DefineNode.php +10 -7
  122. package/vendor/latte/latte/src/Latte/Essential/Nodes/DoNode.php +3 -4
  123. package/vendor/latte/latte/src/Latte/Essential/Nodes/DumpNode.php +2 -3
  124. package/vendor/latte/latte/src/Latte/Essential/Nodes/EmbedNode.php +8 -9
  125. package/vendor/latte/latte/src/Latte/Essential/Nodes/ExtendsNode.php +3 -5
  126. package/vendor/latte/latte/src/Latte/Essential/Nodes/FirstLastSepNode.php +4 -7
  127. package/vendor/latte/latte/src/Latte/Essential/Nodes/ForNode.php +6 -5
  128. package/vendor/latte/latte/src/Latte/Essential/Nodes/ForeachNode.php +7 -7
  129. package/vendor/latte/latte/src/Latte/Essential/Nodes/IfChangedNode.php +4 -5
  130. package/vendor/latte/latte/src/Latte/Essential/Nodes/IfContentNode.php +7 -6
  131. package/vendor/latte/latte/src/Latte/Essential/Nodes/IfNode.php +11 -11
  132. package/vendor/latte/latte/src/Latte/Essential/Nodes/ImportNode.php +5 -6
  133. package/vendor/latte/latte/src/Latte/Essential/Nodes/IncludeBlockNode.php +26 -27
  134. package/vendor/latte/latte/src/Latte/Essential/Nodes/IncludeFileNode.php +7 -10
  135. package/vendor/latte/latte/src/Latte/Essential/Nodes/IterateWhileNode.php +6 -6
  136. package/vendor/latte/latte/src/Latte/Essential/Nodes/JumpNode.php +4 -8
  137. package/vendor/latte/latte/src/Latte/Essential/Nodes/NAttrNode.php +35 -47
  138. package/vendor/latte/latte/src/Latte/Essential/Nodes/NClassNode.php +4 -5
  139. package/vendor/latte/latte/src/Latte/Essential/Nodes/NElseNode.php +69 -34
  140. package/vendor/latte/latte/src/Latte/Essential/Nodes/NTagNode.php +9 -33
  141. package/vendor/latte/latte/src/Latte/Essential/Nodes/ParametersNode.php +7 -4
  142. package/vendor/latte/latte/src/Latte/Essential/Nodes/RawPhpNode.php +3 -4
  143. package/vendor/latte/latte/src/Latte/Essential/Nodes/RollbackNode.php +2 -3
  144. package/vendor/latte/latte/src/Latte/Essential/Nodes/SpacelessNode.php +4 -5
  145. package/vendor/latte/latte/src/Latte/Essential/Nodes/SwitchNode.php +9 -7
  146. package/vendor/latte/latte/src/Latte/Essential/Nodes/TemplatePrintNode.php +4 -4
  147. package/vendor/latte/latte/src/Latte/Essential/Nodes/TemplateTypeNode.php +3 -4
  148. package/vendor/latte/latte/src/Latte/Essential/Nodes/TraceNode.php +2 -3
  149. package/vendor/latte/latte/src/Latte/Essential/Nodes/TranslateNode.php +5 -5
  150. package/vendor/latte/latte/src/Latte/Essential/Nodes/TryNode.php +4 -5
  151. package/vendor/latte/latte/src/Latte/Essential/Nodes/VarNode.php +10 -6
  152. package/vendor/latte/latte/src/Latte/Essential/Nodes/VarPrintNode.php +2 -3
  153. package/vendor/latte/latte/src/Latte/Essential/Nodes/VarTypeNode.php +3 -4
  154. package/vendor/latte/latte/src/Latte/Essential/Nodes/WhileNode.php +5 -5
  155. package/vendor/latte/latte/src/Latte/Essential/Passes.php +82 -21
  156. package/vendor/latte/latte/src/Latte/Essential/RawPhpExtension.php +2 -4
  157. package/vendor/latte/latte/src/Latte/Essential/RollbackException.php +1 -3
  158. package/vendor/latte/latte/src/Latte/Essential/Tracer.php +24 -28
  159. package/vendor/latte/latte/src/Latte/Essential/TranslatorExtension.php +14 -14
  160. package/vendor/latte/latte/src/Latte/Extension.php +12 -6
  161. package/vendor/latte/latte/src/Latte/Feature.php +24 -0
  162. package/vendor/latte/latte/src/Latte/Helpers.php +65 -15
  163. package/vendor/latte/latte/src/Latte/Loader.php +1 -9
  164. package/vendor/latte/latte/src/Latte/Loaders/FileLoader.php +11 -17
  165. package/vendor/latte/latte/src/Latte/Loaders/StringLoader.php +7 -21
  166. package/vendor/latte/latte/src/Latte/Policy.php +4 -3
  167. package/vendor/latte/latte/src/Latte/Runtime/Block.php +2 -4
  168. package/vendor/latte/latte/src/Latte/Runtime/Cache.php +143 -0
  169. package/vendor/latte/latte/src/Latte/Runtime/FilterExecutor.php +5 -6
  170. package/vendor/latte/latte/src/Latte/Runtime/FilterInfo.php +11 -11
  171. package/vendor/latte/latte/src/Latte/Runtime/FunctionExecutor.php +5 -6
  172. package/vendor/latte/latte/src/Latte/Runtime/Helpers.php +91 -0
  173. package/vendor/latte/latte/src/Latte/Runtime/Html.php +3 -5
  174. package/vendor/latte/latte/src/Latte/Runtime/HtmlHelpers.php +374 -0
  175. package/vendor/latte/latte/src/Latte/Runtime/HtmlStringable.php +1 -3
  176. package/vendor/latte/latte/src/Latte/Runtime/Template.php +162 -182
  177. package/vendor/latte/latte/src/Latte/Runtime/XmlHelpers.php +108 -0
  178. package/vendor/latte/latte/src/Latte/Sandbox/Nodes/FunctionCallNode.php +12 -4
  179. package/vendor/latte/latte/src/Latte/Sandbox/Nodes/MethodCallNode.php +15 -5
  180. package/vendor/latte/latte/src/Latte/Sandbox/Nodes/PropertyFetchNode.php +4 -3
  181. package/vendor/latte/latte/src/Latte/Sandbox/Nodes/SandboxNode.php +5 -6
  182. package/vendor/latte/latte/src/Latte/Sandbox/Nodes/StaticMethodCallNode.php +14 -4
  183. package/vendor/latte/latte/src/Latte/Sandbox/Nodes/StaticPropertyFetchNode.php +4 -3
  184. package/vendor/latte/latte/src/Latte/Sandbox/RuntimeChecker.php +8 -6
  185. package/vendor/latte/latte/src/Latte/Sandbox/SandboxExtension.php +16 -12
  186. package/vendor/latte/latte/src/Latte/Sandbox/SecurityPolicy.php +18 -16
  187. package/vendor/latte/latte/src/Latte/attributes.php +7 -3
  188. package/vendor/latte/latte/src/Latte/exceptions.php +52 -6
  189. package/vendor/latte/latte/src/Tools/Linter.php +74 -28
  190. package/vendor/latte/latte/src/Tools/LinterExtension.php +176 -0
  191. package/vendor/nette/utils/composer.json +15 -5
  192. package/vendor/nette/utils/readme.md +1 -1
  193. package/vendor/nette/utils/src/HtmlStringable.php +4 -1
  194. package/vendor/nette/utils/src/Iterators/CachingIterator.php +10 -25
  195. package/vendor/nette/utils/src/Iterators/Mapper.php +2 -3
  196. package/vendor/nette/utils/src/SmartObject.php +3 -0
  197. package/vendor/nette/utils/src/StaticClass.php +1 -11
  198. package/vendor/nette/utils/src/Translator.php +1 -1
  199. package/vendor/nette/utils/src/Utils/ArrayHash.php +6 -10
  200. package/vendor/nette/utils/src/Utils/ArrayList.php +8 -12
  201. package/vendor/nette/utils/src/Utils/Arrays.php +38 -18
  202. package/vendor/nette/utils/src/Utils/Callback.php +13 -8
  203. package/vendor/nette/utils/src/Utils/DateTime.php +95 -26
  204. package/vendor/nette/utils/src/Utils/FileInfo.php +6 -7
  205. package/vendor/nette/utils/src/Utils/FileSystem.php +10 -5
  206. package/vendor/nette/utils/src/Utils/Finder.php +31 -12
  207. package/vendor/nette/utils/src/Utils/Floats.php +1 -0
  208. package/vendor/nette/utils/src/Utils/Helpers.php +22 -2
  209. package/vendor/nette/utils/src/Utils/Html.php +130 -127
  210. package/vendor/nette/utils/src/Utils/Image.php +80 -60
  211. package/vendor/nette/utils/src/Utils/ImageColor.php +5 -0
  212. package/vendor/nette/utils/src/Utils/ImageType.php +2 -0
  213. package/vendor/nette/utils/src/Utils/Iterables.php +41 -7
  214. package/vendor/nette/utils/src/Utils/Json.php +2 -0
  215. package/vendor/nette/utils/src/Utils/ObjectHelpers.php +2 -0
  216. package/vendor/nette/utils/src/Utils/Paginator.php +10 -43
  217. package/vendor/nette/utils/src/Utils/Random.php +2 -0
  218. package/vendor/nette/utils/src/Utils/Reflection.php +22 -15
  219. package/vendor/nette/utils/src/Utils/ReflectionMethod.php +6 -1
  220. package/vendor/nette/utils/src/Utils/Strings.php +62 -57
  221. package/vendor/nette/utils/src/Utils/Type.php +91 -42
  222. package/vendor/nette/utils/src/Utils/Validators.php +9 -7
  223. package/vendor/latte/latte/src/Bridges/Tracy/templates/LattePanel.panel.phtml +0 -101
  224. package/vendor/latte/latte/src/Bridges/Tracy/templates/LattePanel.tab.phtml +0 -15
  225. package/vendor/latte/latte/src/Latte/Compiler/ExpressionBuilder.php +0 -129
  226. package/vendor/latte/latte/src/Latte/Compiler/Nodes/Php/Expression/ErrorSuppressNode.php +0 -36
  227. package/vendor/latte/latte/src/Latte/Compiler/Nodes/Php/Expression/FunctionCallableNode.php +0 -39
  228. package/vendor/latte/latte/src/Latte/Compiler/Nodes/Php/Expression/MethodCallableNode.php +0 -42
  229. package/vendor/latte/latte/src/Latte/Compiler/Nodes/Php/Expression/StaticMethodCallableNode.php +0 -57
  230. package/vendor/latte/latte/src/Latte/PositionAwareException.php +0 -47
  231. package/vendor/latte/latte/src/Latte/Runtime/Filters.php +0 -283
  232. package/vendor/latte/latte/src/Latte/Sandbox/Nodes/FunctionCallableNode.php +0 -29
  233. package/vendor/latte/latte/src/Latte/Sandbox/Nodes/MethodCallableNode.php +0 -30
  234. package/vendor/latte/latte/src/Latte/Sandbox/Nodes/StaticMethodCallableNode.php +0 -30
@@ -1,21 +1,21 @@
1
- <?php
1
+ <?php declare(strict_types=1);
2
2
 
3
3
  /**
4
4
  * This file is part of the Latte (https://latte.nette.org)
5
5
  * Copyright (c) 2008 David Grudl (https://davidgrudl.com)
6
6
  */
7
7
 
8
- declare(strict_types=1);
9
-
10
8
  namespace Latte\Essential;
11
9
 
12
10
  use Latte;
13
11
  use Latte\Compiler\NodeHelpers;
14
12
  use Latte\Compiler\Nodes\Php;
13
+ use Latte\Compiler\Nodes\Php\ExpressionNode;
14
+ use Latte\Compiler\Nodes\PrintNode;
15
15
  use Latte\Compiler\Tag;
16
16
  use Latte\Engine;
17
- use Latte\Essential\Nodes\PrintNode;
18
17
  use Nette\Localization\Translator;
18
+ use function array_unshift, is_array, is_string;
19
19
 
20
20
 
21
21
  /**
@@ -23,25 +23,25 @@ use Nette\Localization\Translator;
23
23
  */
24
24
  final class TranslatorExtension extends Latte\Extension
25
25
  {
26
- /** @var callable|Translator|null */
27
- private $translator;
26
+ private ?\Closure $translator;
28
27
 
29
28
 
30
29
  public function __construct(
31
30
  callable|Translator|null $translator,
32
31
  private ?string $key = null,
33
32
  ) {
34
- $this->translator = $translator;
35
- if ($translator instanceof Translator) {
36
- $this->translator = [$translator, 'translate'];
37
- }
33
+ $this->translator = match (true) {
34
+ $translator === null => null,
35
+ $translator instanceof Translator => $translator->translate(...),
36
+ default => $translator(...),
37
+ };
38
38
  }
39
39
 
40
40
 
41
41
  public function getTags(): array
42
42
  {
43
43
  return [
44
- '_' => [$this, 'parseTranslate'],
44
+ '_' => $this->parseTranslate(...),
45
45
  'translate' => fn(Tag $tag) => yield from Nodes\TranslateNode::create($tag, $this->key ? $this->translator : null),
46
46
  ];
47
47
  }
@@ -66,7 +66,7 @@ final class TranslatorExtension extends Latte\Extension
66
66
  /**
67
67
  * {_ ...}
68
68
  */
69
- public function parseTranslate(Tag $tag): PrintNode
69
+ private function parseTranslate(Tag $tag): PrintNode
70
70
  {
71
71
  $tag->outputMode = $tag::OutputKeepIndentation;
72
72
  $tag->expectArguments();
@@ -78,7 +78,7 @@ final class TranslatorExtension extends Latte\Extension
78
78
  }
79
79
 
80
80
  $node->modifier = $tag->parser->parseModifier();
81
- $node->modifier->escape = true;
81
+ $node->modifier->escape = !$node->modifier->removeFilter('noescape');
82
82
 
83
83
  if ($this->translator
84
84
  && $this->key
@@ -95,7 +95,7 @@ final class TranslatorExtension extends Latte\Extension
95
95
  }
96
96
 
97
97
 
98
- public static function toValue($args): mixed
98
+ public static function toValue(ExpressionNode $args): mixed
99
99
  {
100
100
  try {
101
101
  return NodeHelpers::toValue($args, constants: true);
@@ -1,14 +1,14 @@
1
- <?php
1
+ <?php declare(strict_types=1);
2
2
 
3
3
  /**
4
4
  * This file is part of the Latte (https://latte.nette.org)
5
5
  * Copyright (c) 2008 David Grudl (https://davidgrudl.com)
6
6
  */
7
7
 
8
- declare(strict_types=1);
9
-
10
8
  namespace Latte;
11
9
 
10
+ use function get_defined_vars;
11
+
12
12
 
13
13
  /**
14
14
  * Latte extension.
@@ -16,7 +16,7 @@ namespace Latte;
16
16
  abstract class Extension
17
17
  {
18
18
  /**
19
- * Initializes before template is compiler.
19
+ * Initializes before template is compiled.
20
20
  */
21
21
  public function beforeCompile(Engine $engine): void
22
22
  {
@@ -34,7 +34,7 @@ abstract class Extension
34
34
 
35
35
 
36
36
  /**
37
- * Returns a list of parsers for Latte tags.
37
+ * Returns a list of compiler passes.
38
38
  * @return array<string, callable(Compiler\Nodes\TemplateNode): void|\stdClass>
39
39
  */
40
40
  public function getPasses(): array
@@ -65,7 +65,7 @@ abstract class Extension
65
65
 
66
66
  /**
67
67
  * Returns a list of providers.
68
- * @return array<mixed>
68
+ * @return array<string, mixed>
69
69
  */
70
70
  public function getProviders(): array
71
71
  {
@@ -90,8 +90,14 @@ abstract class Extension
90
90
  }
91
91
 
92
92
 
93
+ /**
94
+ * Wraps callable with ordering metadata for tags and passes.
95
+ * @param array<string>|string $before
96
+ * @param array<string>|string $after
97
+ */
93
98
  public static function order(callable $subject, array|string $before = [], array|string $after = []): \stdClass
94
99
  {
100
+ $subject = $subject(...);
95
101
  return (object) get_defined_vars();
96
102
  }
97
103
  }
@@ -0,0 +1,24 @@
1
+ <?php declare(strict_types=1);
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
+ namespace Latte;
9
+
10
+
11
+ /**
12
+ * Latte engine feature flags.
13
+ */
14
+ enum Feature
15
+ {
16
+ /** Adds declare(strict_types=1) to compiled templates (enabled by default) */
17
+ case StrictTypes;
18
+
19
+ /** Enforces strict HTML parsing */
20
+ case StrictParsing;
21
+
22
+ /** Shows warnings for deprecated Latte 3.0 syntax */
23
+ case MigrationWarnings;
24
+ }
@@ -1,14 +1,15 @@
1
- <?php
1
+ <?php declare(strict_types=1);
2
2
 
3
3
  /**
4
4
  * This file is part of the Latte (https://latte.nette.org)
5
5
  * Copyright (c) 2008 David Grudl (https://davidgrudl.com)
6
6
  */
7
7
 
8
- declare(strict_types=1);
9
-
10
8
  namespace Latte;
11
9
 
10
+ use function array_filter, array_keys, array_search, array_slice, array_unique, count, is_array, is_object, is_string, levenshtein, max, min, strlen, strpos;
11
+ use const PHP_VERSION_ID;
12
+
12
13
 
13
14
  /**
14
15
  * Latte helpers.
@@ -16,13 +17,6 @@ namespace Latte;
16
17
  */
17
18
  class Helpers
18
19
  {
19
- /** @var array<string, int> empty (void) HTML elements */
20
- public static array $emptyElements = [
21
- 'img' => 1, 'hr' => 1, 'br' => 1, 'input' => 1, 'meta' => 1, 'area' => 1, 'embed' => 1, 'keygen' => 1, 'source' => 1, 'base' => 1,
22
- 'col' => 1, 'link' => 1, 'param' => 1, 'basefont' => 1, 'frame' => 1, 'isindex' => 1, 'wbr' => 1, 'command' => 1, 'track' => 1,
23
- ];
24
-
25
-
26
20
  /**
27
21
  * Finds the best suggestion.
28
22
  * @param string[] $items
@@ -43,7 +37,7 @@ class Helpers
43
37
 
44
38
 
45
39
  /** intentionally without callable typehint, because it generates bad error messages */
46
- public static function toReflection($callable): \ReflectionFunctionAbstract
40
+ public static function toReflection(mixed $callable): \ReflectionFunctionAbstract
47
41
  {
48
42
  if (is_string($callable) && strpos($callable, '::')) {
49
43
  return PHP_VERSION_ID < 80300
@@ -59,6 +53,10 @@ class Helpers
59
53
  }
60
54
 
61
55
 
56
+ /**
57
+ * @param array<string, mixed|\stdClass> $list
58
+ * @return array<string, mixed|\stdClass>
59
+ */
62
60
  public static function sortBeforeAfter(array $list): array
63
61
  {
64
62
  foreach ($list as $name => $info) {
@@ -74,7 +72,7 @@ class Helpers
74
72
  if ($target === '*') {
75
73
  $best = 0;
76
74
  } elseif (isset($list[$target])) {
77
- $pos = array_search($target, $names, true);
75
+ $pos = (int) array_search($target, $names, strict: true);
78
76
  $best = min($pos, $best ?? $pos);
79
77
  }
80
78
  }
@@ -83,16 +81,68 @@ class Helpers
83
81
  if ($target === '*') {
84
82
  $best = count($names);
85
83
  } elseif (isset($list[$target])) {
86
- $pos = array_search($target, $names, true);
84
+ $pos = (int) array_search($target, $names, strict: true);
87
85
  $best = max($pos + 1, $best);
88
86
  }
89
87
  }
90
88
 
91
- $list = array_slice($list, 0, $best, true)
89
+ $best ??= count($names);
90
+ $list = array_slice($list, 0, $best, preserve_keys: true)
92
91
  + [$name => $info]
93
- + array_slice($list, $best, null, true);
92
+ + array_slice($list, $best, null, preserve_keys: true);
94
93
  }
95
94
 
96
95
  return $list;
97
96
  }
97
+
98
+
99
+ /** @param mixed[] $items */
100
+ public static function removeNulls(array &$items): void
101
+ {
102
+ $items = array_values(array_filter($items, fn($item) => $item !== null));
103
+ }
104
+
105
+
106
+ /**
107
+ * Attempts to map the compiled template to the source.
108
+ * @return array{name: ?string, line: ?int, column: ?int}|null
109
+ */
110
+ public static function mapCompiledToSource(string $compiledFile, ?int $compiledLine = null): ?array
111
+ {
112
+ if (!Runtime\Cache::isCacheFile($compiledFile)) {
113
+ return null;
114
+ }
115
+
116
+ $content = file_get_contents($compiledFile);
117
+ if ($content === false) {
118
+ return null;
119
+ }
120
+ $name = preg_match('#^/\*\* source: (\S.+) \*/#m', $content, $m) ? $m[1] : null;
121
+ $compiledLine && preg_match('~/\* pos (\d+)(?::(\d+))? \*/~', explode("\n", $content)[$compiledLine - 1], $pos);
122
+ $line = isset($pos[1]) ? (int) $pos[1] : null;
123
+ $column = isset($pos[2]) ? (int) $pos[2] : null;
124
+ return $name || $line ? compact('name', 'line', 'column') : null;
125
+ }
126
+
127
+
128
+ /**
129
+ * Tries to guess the position in the template from the backtrace
130
+ */
131
+ public static function guessTemplatePosition(): ?string
132
+ {
133
+ $trace = debug_backtrace();
134
+ foreach ($trace as $item) {
135
+ if (isset($item['file']) && ($source = self::mapCompiledToSource($item['file'], $item['line'] ?? null))) {
136
+ $res = [];
137
+ if ($source['name'] && is_file($source['name'])) {
138
+ $res[] = "in '" . str_replace(dirname($source['name'], 2), '...', $source['name']) . "'";
139
+ }
140
+ if ($source['line']) {
141
+ $res[] = 'on line ' . $source['line'] . ($source['column'] ? ' at column ' . $source['column'] : '');
142
+ }
143
+ return implode(' ', $res);
144
+ }
145
+ }
146
+ return null;
147
+ }
98
148
  }
@@ -1,12 +1,10 @@
1
- <?php
1
+ <?php declare(strict_types=1);
2
2
 
3
3
  /**
4
4
  * This file is part of the Latte (https://latte.nette.org)
5
5
  * Copyright (c) 2008 David Grudl (https://davidgrudl.com)
6
6
  */
7
7
 
8
- declare(strict_types=1);
9
-
10
8
  namespace Latte;
11
9
 
12
10
 
@@ -20,12 +18,6 @@ interface Loader
20
18
  */
21
19
  function getContent(string $name): string;
22
20
 
23
- /**
24
- * Checks whether template is expired.
25
- * @deprecated
26
- */
27
- function isExpired(string $name, int $time): bool;
28
-
29
21
  /**
30
22
  * Returns referred template name.
31
23
  */
@@ -1,19 +1,19 @@
1
- <?php
1
+ <?php declare(strict_types=1);
2
2
 
3
3
  /**
4
4
  * This file is part of the Latte (https://latte.nette.org)
5
5
  * Copyright (c) 2008 David Grudl (https://davidgrudl.com)
6
6
  */
7
7
 
8
- declare(strict_types=1);
9
-
10
8
  namespace Latte\Loaders;
11
9
 
12
10
  use Latte;
11
+ use function array_pop, end, explode, file_get_contents, implode, is_file, preg_match, str_starts_with, strtr;
12
+ use const DIRECTORY_SEPARATOR;
13
13
 
14
14
 
15
15
  /**
16
- * Template loader.
16
+ * Loads templates from filesystem.
17
17
  */
18
18
  class FileLoader implements Latte\Loader
19
19
  {
@@ -36,21 +36,15 @@ class FileLoader implements Latte\Loader
36
36
  throw new Latte\RuntimeException("Template '$file' is not within the allowed path '{$this->baseDir}'.");
37
37
 
38
38
  } elseif (!is_file($file)) {
39
- throw new Latte\RuntimeException("Missing template file '$file'.");
40
-
41
- } elseif ($this->isExpired($fileName, time())) {
42
- if (@touch($file) === false) {
43
- trigger_error("File's modification time is in the future. Cannot update it: " . error_get_last()['message'], E_USER_WARNING);
44
- }
39
+ throw new Latte\TemplateNotFoundException("Missing template file '$file'.");
45
40
  }
46
41
 
47
- return file_get_contents($file);
48
- }
49
-
42
+ $content = file_get_contents($file);
43
+ if ($content === false) {
44
+ throw new Latte\RuntimeException("Unable to read file '$file'.");
45
+ }
50
46
 
51
- public function isExpired(string $file, int $time): bool
52
- {
53
- return false;
47
+ return $content;
54
48
  }
55
49
 
56
50
 
@@ -59,7 +53,7 @@ class FileLoader implements Latte\Loader
59
53
  */
60
54
  public function getReferredName(string $file, string $referringFile): string
61
55
  {
62
- if ($this->baseDir || !preg_match('#/|\\\\|[a-z]:|phar:#iA', $file)) {
56
+ if ($this->baseDir || !preg_match('#/|\\\|[a-z]:|phar:#iA', $file)) {
63
57
  $file = $this->normalizePath($referringFile . '/../' . $file);
64
58
  }
65
59
 
@@ -1,12 +1,10 @@
1
- <?php
1
+ <?php declare(strict_types=1);
2
2
 
3
3
  /**
4
4
  * This file is part of the Latte (https://latte.nette.org)
5
5
  * Copyright (c) 2008 David Grudl (https://davidgrudl.com)
6
6
  */
7
7
 
8
- declare(strict_types=1);
9
-
10
8
  namespace Latte\Loaders;
11
9
 
12
10
  use Latte;
@@ -17,16 +15,10 @@ use Latte;
17
15
  */
18
16
  class StringLoader implements Latte\Loader
19
17
  {
20
- /** @var string[]|null [name => content] */
21
- private ?array $templates = null;
22
-
23
-
24
- /**
25
- * @param string[] $templates
26
- */
27
- public function __construct(?array $templates = null)
28
- {
29
- $this->templates = $templates;
18
+ public function __construct(
19
+ /** @var array<string, string>|null */
20
+ private ?array $templates = null,
21
+ ) {
30
22
  }
31
23
 
32
24
 
@@ -40,24 +32,18 @@ class StringLoader implements Latte\Loader
40
32
  } elseif (isset($this->templates[$name])) {
41
33
  return $this->templates[$name];
42
34
  } else {
43
- throw new Latte\RuntimeException("Missing template '$name'.");
35
+ throw new Latte\TemplateNotFoundException("Missing template '$name'.");
44
36
  }
45
37
  }
46
38
 
47
39
 
48
- public function isExpired(string $name, int $time): bool
49
- {
50
- return false;
51
- }
52
-
53
-
54
40
  /**
55
41
  * Returns referred template name.
56
42
  */
57
43
  public function getReferredName(string $name, string $referringName): string
58
44
  {
59
45
  if ($this->templates === null) {
60
- throw new \LogicException("Missing template '$name'.");
46
+ throw new Latte\TemplateNotFoundException("Missing template '$name'.");
61
47
  }
62
48
 
63
49
  return $name;
@@ -1,15 +1,16 @@
1
- <?php
1
+ <?php declare(strict_types=1);
2
2
 
3
3
  /**
4
4
  * This file is part of the Latte (https://latte.nette.org)
5
5
  * Copyright (c) 2008 David Grudl (https://davidgrudl.com)
6
6
  */
7
7
 
8
- declare(strict_types=1);
9
-
10
8
  namespace Latte;
11
9
 
12
10
 
11
+ /**
12
+ * Security policy for sandbox mode.
13
+ */
13
14
  interface Policy
14
15
  {
15
16
  function isTagAllowed(string $tag): bool;
@@ -1,12 +1,10 @@
1
- <?php
1
+ <?php declare(strict_types=1);
2
2
 
3
3
  /**
4
4
  * This file is part of the Latte (https://latte.nette.org)
5
5
  * Copyright (c) 2008 David Grudl (https://davidgrudl.com)
6
6
  */
7
7
 
8
- declare(strict_types=1);
9
-
10
8
  namespace Latte\Runtime;
11
9
 
12
10
 
@@ -15,6 +13,6 @@ final class Block
15
13
  {
16
14
  public ?string $contentType = null;
17
15
 
18
- /** @var callable[] */
16
+ /** @var \Closure[] */
19
17
  public array $functions = [];
20
18
  }
@@ -0,0 +1,143 @@
1
+ <?php declare(strict_types=1);
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
+ namespace Latte\Runtime;
9
+
10
+ use Latte\Engine;
11
+ use Latte\RuntimeException;
12
+ use function array_map, defined, dirname, file_put_contents, filemtime, flock, fopen, fseek, ftell, ftruncate, function_exists, fwrite, is_dir, is_file, mkdir, preg_match, preg_replace, rename, serialize, stream_get_contents, strlen, substr, unlink;
13
+ use const LOCK_EX, LOCK_SH, LOCK_UN;
14
+
15
+
16
+ /**
17
+ * Caching of compiled templates.
18
+ * @internal
19
+ */
20
+ final class Cache
21
+ {
22
+ public ?string $directory = null;
23
+ public bool $autoRefresh = true;
24
+
25
+
26
+ /**
27
+ * Loads existing cache or compiles template if needed.
28
+ * Uses file locking to ensure atomic operations and prevent race conditions.
29
+ */
30
+ public function loadOrCreate(Engine $engine, string $name): void
31
+ {
32
+ // Solving atomicity to work everywhere is really pain in the ass.
33
+ // 1) We want to do as little as possible IO calls on production and also directory and file can be not writable
34
+ // so on Linux we include the file directly without shared lock, therefore, the file must be created atomically by renaming.
35
+ // 2) On Windows file cannot be renamed-to while is open (ie by include), so we have to acquire a lock.
36
+ $file = $this->generateFilePath($engine, $name);
37
+ $signature = $this->autoRefresh
38
+ ? hash('xxh128', serialize($this->generateRefreshSignature($engine, $name)))
39
+ : null;
40
+ $lock = defined('PHP_WINDOWS_VERSION_BUILD') || $signature
41
+ ? $this->acquireLock("$file.lock", LOCK_SH)
42
+ : null;
43
+
44
+ if (
45
+ !($signature && $lock && $signature !== stream_get_contents($lock))
46
+ && (@include $file) !== false // @ - file may not exist
47
+ ) {
48
+ return;
49
+ }
50
+
51
+ if ($lock) {
52
+ flock($lock, LOCK_UN); // release shared lock so we can get exclusive
53
+ fseek($lock, 0);
54
+ }
55
+
56
+ $lock = $this->acquireLock("$file.lock", LOCK_EX);
57
+
58
+ // while waiting for exclusive lock, someone might have already created the cache
59
+ if (!is_file($file) || ($signature && $signature !== stream_get_contents($lock))) {
60
+ $compiled = $engine->compile($name);
61
+ if (
62
+ file_put_contents("$file.tmp", $compiled) !== strlen($compiled)
63
+ || !rename("$file.tmp", $file)
64
+ ) {
65
+ @unlink("$file.tmp"); // @ - file may not exist
66
+ throw new RuntimeException("Unable to create '$file'.");
67
+ }
68
+
69
+ fseek($lock, 0);
70
+ fwrite($lock, $signature ?? hash('xxh128', serialize($this->generateRefreshSignature($engine, $name))));
71
+ ftruncate($lock, (int) ftell($lock));
72
+
73
+ if (function_exists('opcache_invalidate')) {
74
+ @opcache_invalidate($file, force: true); // @ can be restricted
75
+ }
76
+ }
77
+
78
+ if ((include $file) === false) {
79
+ throw new RuntimeException("Unable to load '$file'.");
80
+ }
81
+
82
+ flock($lock, LOCK_UN);
83
+ }
84
+
85
+
86
+ /** @return resource */
87
+ private function acquireLock(string $file, int $mode)
88
+ {
89
+ $dir = dirname($file);
90
+ if (!is_dir($dir) && !@mkdir($dir) && !is_dir($dir)) { // @ - dir may already exist
91
+ throw new RuntimeException("Unable to create directory '$dir'. " . (error_get_last()['message'] ?? ''));
92
+ }
93
+
94
+ $handle = @fopen($file, 'c+'); // @ is escalated to exception
95
+ if (!$handle) {
96
+ throw new RuntimeException("Unable to create file '$file'. " . (error_get_last()['message'] ?? ''));
97
+ } elseif (!@flock($handle, $mode)) { // @ is escalated to exception
98
+ throw new RuntimeException('Unable to acquire ' . ($mode & LOCK_EX ? 'exclusive' : 'shared') . " lock on file '$file'. " . (error_get_last()['message'] ?? ''));
99
+ }
100
+
101
+ return $handle;
102
+ }
103
+
104
+
105
+ /**
106
+ * Returns the file path where compiled template will be cached.
107
+ * Different configurations produce different file paths.
108
+ */
109
+ public function generateFilePath(Engine $engine, string $name): string
110
+ {
111
+ $base = preg_match('#([/\\\][\w@.-]{3,35}){1,3}$#D', '/' . $name, $m)
112
+ ? preg_replace('#[^\w@.-]+#', '-', substr($m[0], 1))
113
+ : '';
114
+ if (!str_ends_with($base, 'latte')) {
115
+ $base .= '-latte';
116
+ }
117
+ return $this->directory . '/' . $base . '--' . $engine->generateTemplateHash($name) . '.php';
118
+ }
119
+
120
+
121
+ public static function isCacheFile(string $file): bool
122
+ {
123
+ return (bool) preg_match('/latte--\w{10}\.php$/', $file);
124
+ }
125
+
126
+
127
+ /**
128
+ * Returns values used to detect if cached template needs recompilation when autoRefresh is enabled.
129
+ * Triggers recompilation when template code, Latte engine, or any extension changes.
130
+ * @return list<mixed>
131
+ */
132
+ protected function generateRefreshSignature(Engine $engine, string $name): array
133
+ {
134
+ return [
135
+ Engine::Version,
136
+ $engine->getLoader()->getContent($name),
137
+ array_map(
138
+ fn($extension) => @filemtime((string) (new \ReflectionObject($extension))->getFileName()), // @ file may not exist
139
+ $engine->getExtensions(),
140
+ ),
141
+ ];
142
+ }
143
+ }
@@ -1,17 +1,16 @@
1
- <?php
1
+ <?php declare(strict_types=1);
2
2
 
3
3
  /**
4
4
  * This file is part of the Latte (https://latte.nette.org)
5
5
  * Copyright (c) 2008 David Grudl (https://davidgrudl.com)
6
6
  */
7
7
 
8
- declare(strict_types=1);
9
-
10
8
  namespace Latte\Runtime;
11
9
 
12
10
  use Latte;
13
11
  use Latte\ContentType;
14
12
  use Latte\Helpers;
13
+ use function array_column, array_combine, array_keys, array_unshift, strtoupper;
15
14
 
16
15
 
17
16
  /**
@@ -21,7 +20,7 @@ use Latte\Helpers;
21
20
  #[\AllowDynamicProperties]
22
21
  class FilterExecutor
23
22
  {
24
- /** @var callable[] */
23
+ /** @var array<callable> */
25
24
  private array $_dynamic = [];
26
25
 
27
26
  /** @var array<string, array{callable, ?bool}> */
@@ -46,7 +45,7 @@ class FilterExecutor
46
45
 
47
46
  /**
48
47
  * Returns all run-time filters.
49
- * @return callable[]
48
+ * @return array<string, callable>
50
49
  */
51
50
  public function getAll(): array
52
51
  {
@@ -117,7 +116,7 @@ class FilterExecutor
117
116
  $hint = ($t = Helpers::getSuggestion(array_keys($this->_static), $name))
118
117
  ? ", did you mean '$t'?"
119
118
  : '.';
120
- throw new \LogicException("Filter '$name' is not defined$hint");
119
+ throw new \LogicException("Filter '$name' is not defined or not allowed here$hint");
121
120
  }
122
121
 
123
122