WP Social Bookmarking Light - Version 2.0.0

Version Description

  • Breaking Changes: Drop support for PHP 5.5 or lower, Require PHP 5.6 or higher #46
  • Breaking Changes: Remove terminated some services
  • Important: If your WordPress site working on PHP 5.5 or lower, please continue to use v1.9.2 version
Download this release

Release Info

Developer utahvich
Plugin Icon wp plugin WP Social Bookmarking Light
Version 2.0.0
Comparing to
See all releases

Code changes from version 1.9.2 to 2.0.0

Files changed (321) hide show
  1. .gitattributes +5 -0
  2. composer.json +21 -0
  3. composer.lock +1394 -0
  4. images/buzzurl.gif +0 -0
  5. images/choix.gif +0 -0
  6. images/evernote.png +0 -0
  7. images/fc2.gif +0 -0
  8. images/friendfeed.png +0 -0
  9. images/google-buzz.png +0 -0
  10. images/livedoor.gif +0 -0
  11. images/newsing.gif +0 -0
  12. images/nifty.gif +0 -0
  13. images/twib.gif +0 -0
  14. images/yahoo.gif +0 -0
  15. images/yahoo_buzz.png +0 -0
  16. modules/admin.php +0 -1018
  17. modules/content.php +22 -169
  18. modules/options.php +15 -205
  19. modules/services.php +0 -681
  20. {libs/jquery/css → public/css/admin}/pepper-grinder/images/ui-bg_diagonal-maze_20_6e4f1c_10x10.png +0 -0
  21. {libs/jquery/css → public/css/admin}/pepper-grinder/images/ui-bg_diagonal-maze_40_000000_10x10.png +0 -0
  22. {libs/jquery/css → public/css/admin}/pepper-grinder/images/ui-bg_fine-grain_10_eceadf_60x60.png +0 -0
  23. {libs/jquery/css → public/css/admin}/pepper-grinder/images/ui-bg_fine-grain_10_f8f7f6_60x60.png +0 -0
  24. {libs/jquery/css → public/css/admin}/pepper-grinder/images/ui-bg_fine-grain_15_eceadf_60x60.png +0 -0
  25. {libs/jquery/css → public/css/admin}/pepper-grinder/images/ui-bg_fine-grain_15_f7f3de_60x60.png +0 -0
  26. {libs/jquery/css → public/css/admin}/pepper-grinder/images/ui-bg_fine-grain_15_ffffff_60x60.png +0 -0
  27. {libs/jquery/css → public/css/admin}/pepper-grinder/images/ui-bg_fine-grain_65_654b24_60x60.png +0 -0
  28. {libs/jquery/css → public/css/admin}/pepper-grinder/images/ui-bg_fine-grain_68_b83400_60x60.png +0 -0
  29. {libs/jquery/css → public/css/admin}/pepper-grinder/images/ui-icons_222222_256x240.png +0 -0
  30. {libs/jquery/css → public/css/admin}/pepper-grinder/images/ui-icons_3572ac_256x240.png +0 -0
  31. {libs/jquery/css → public/css/admin}/pepper-grinder/images/ui-icons_8c291d_256x240.png +0 -0
  32. {libs/jquery/css → public/css/admin}/pepper-grinder/images/ui-icons_b83400_256x240.png +0 -0
  33. {libs/jquery/css → public/css/admin}/pepper-grinder/images/ui-icons_fbdb93_256x240.png +0 -0
  34. {libs/jquery/css → public/css/admin}/pepper-grinder/images/ui-icons_ffffff_256x240.png +0 -0
  35. {libs/jquery/css → public/css/admin}/pepper-grinder/jquery-ui-1.8.6.custom.css +0 -0
  36. public/css/admin/style.css +61 -0
  37. {images → public/images}/bitcoin_donate.jpg +0 -0
  38. {images → public/images}/close_button.png +0 -0
  39. {images → public/images}/close_button2.png +0 -0
  40. {images → public/images}/delicious.png +0 -0
  41. {images → public/images}/digg.png +0 -0
  42. {images → public/images}/facebook.png +0 -0
  43. {images → public/images}/google.png +0 -0
  44. {images → public/images}/gree.png +0 -0
  45. {images → public/images}/hatena.gif +0 -0
  46. {images → public/images}/line20x20.png +0 -0
  47. {images → public/images}/line88x20.png +0 -0
  48. {images → public/images}/linkedin.png +0 -0
  49. {images → public/images}/point_left.png +0 -0
  50. {images → public/images}/reddit.png +0 -0
  51. {images → public/images}/stumbleupon.png +0 -0
  52. {images → public/images}/tumblr.png +0 -0
  53. readme.txt +11 -18
  54. screenshot-1.png +0 -0
  55. screenshot-3.png +0 -0
  56. src/WpSocialBookmarkingLight/Admin.php +84 -0
  57. src/WpSocialBookmarkingLight/Builder.php +109 -0
  58. src/WpSocialBookmarkingLight/Option.php +254 -0
  59. src/WpSocialBookmarkingLight/OptionInterface.php +26 -0
  60. src/WpSocialBookmarkingLight/Plugin.php +148 -0
  61. src/WpSocialBookmarkingLight/Renderer.php +48 -0
  62. src/WpSocialBookmarkingLight/Resources/views/Admin/head.html.twig +132 -0
  63. src/WpSocialBookmarkingLight/Resources/views/Admin/page.html.twig +676 -0
  64. src/WpSocialBookmarkingLight/Resources/views/Builder/content.html.twig +6 -0
  65. src/WpSocialBookmarkingLight/Resources/views/Builder/footer.html.twig +30 -0
  66. src/WpSocialBookmarkingLight/Resources/views/Builder/head.html.twig +27 -0
  67. src/WpSocialBookmarkingLight/Service.php +668 -0
  68. src/WpSocialBookmarkingLight/Tests/BuilderTest.php +143 -0
  69. src/WpSocialBookmarkingLight/Tests/Helper/Builder/global.php +38 -0
  70. src/WpSocialBookmarkingLight/Tests/Helper/setting.php +4 -0
  71. src/WpSocialBookmarkingLight/Tests/ServiceTest.php +62 -0
  72. src/WpSocialBookmarkingLight/Util/Text.php +21 -0
  73. src/WpSocialBookmarkingLight/Util/Url.php +56 -0
  74. vendor/autoload.php +7 -0
  75. vendor/composer/ClassLoader.php +441 -0
  76. vendor/composer/LICENSE +21 -0
  77. vendor/composer/autoload_classmap.php +9 -0
  78. vendor/composer/autoload_namespaces.php +10 -0
  79. vendor/composer/autoload_psr4.php +10 -0
  80. vendor/composer/autoload_real.php +52 -0
  81. vendor/composer/autoload_static.php +31 -0
  82. vendor/composer/installed.json +66 -0
  83. vendor/twig/twig/.editorconfig +18 -0
  84. vendor/twig/twig/.gitignore +5 -0
  85. vendor/twig/twig/.php_cs.dist +15 -0
  86. vendor/twig/twig/.travis.yml +44 -0
  87. vendor/twig/twig/CHANGELOG +933 -0
  88. vendor/twig/twig/LICENSE +31 -0
  89. vendor/twig/twig/README.rst +15 -0
  90. vendor/twig/twig/composer.json +47 -0
  91. vendor/twig/twig/doc/advanced.rst +962 -0
  92. vendor/twig/twig/doc/advanced_legacy.rst +885 -0
  93. vendor/twig/twig/doc/api.rst +590 -0
  94. vendor/twig/twig/doc/coding_standards.rst +101 -0
  95. vendor/twig/twig/doc/deprecated.rst +224 -0
  96. vendor/twig/twig/doc/filters/abs.rst +18 -0
  97. vendor/twig/twig/doc/filters/batch.rst +51 -0
  98. vendor/twig/twig/doc/filters/capitalize.rst +11 -0
  99. vendor/twig/twig/doc/filters/convert_encoding.rst +28 -0
  100. vendor/twig/twig/doc/filters/date.rst +100 -0
  101. vendor/twig/twig/doc/filters/date_modify.rst +23 -0
  102. vendor/twig/twig/doc/filters/default.rst +33 -0
  103. vendor/twig/twig/doc/filters/escape.rst +119 -0
  104. vendor/twig/twig/doc/filters/first.rst +25 -0
  105. vendor/twig/twig/doc/filters/format.rst +16 -0
  106. vendor/twig/twig/doc/filters/index.rst +37 -0
  107. vendor/twig/twig/doc/filters/join.rst +23 -0
  108. vendor/twig/twig/doc/filters/json_encode.rst +21 -0
  109. vendor/twig/twig/doc/filters/keys.rst +11 -0
  110. vendor/twig/twig/doc/filters/last.rst +25 -0
  111. vendor/twig/twig/doc/filters/length.rst +11 -0
  112. vendor/twig/twig/doc/filters/lower.rst +10 -0
  113. vendor/twig/twig/doc/filters/merge.rst +48 -0
  114. vendor/twig/twig/doc/filters/nl2br.rst +22 -0
  115. vendor/twig/twig/doc/filters/number_format.rst +48 -0
  116. vendor/twig/twig/doc/filters/raw.rst +36 -0
  117. vendor/twig/twig/doc/filters/replace.rst +19 -0
  118. vendor/twig/twig/doc/filters/reverse.rst +47 -0
  119. vendor/twig/twig/doc/filters/round.rst +37 -0
  120. vendor/twig/twig/doc/filters/slice.rst +71 -0
  121. vendor/twig/twig/doc/filters/sort.rst +18 -0
  122. vendor/twig/twig/doc/filters/split.rst +53 -0
  123. vendor/twig/twig/doc/filters/striptags.rst +29 -0
  124. vendor/twig/twig/doc/filters/title.rst +11 -0
  125. vendor/twig/twig/doc/filters/trim.rst +45 -0
  126. vendor/twig/twig/doc/filters/upper.rst +10 -0
  127. vendor/twig/twig/doc/filters/url_encode.rst +34 -0
  128. vendor/twig/twig/doc/functions/attribute.rst +26 -0
  129. vendor/twig/twig/doc/functions/block.rst +41 -0
  130. vendor/twig/twig/doc/functions/constant.rst +29 -0
  131. vendor/twig/twig/doc/functions/cycle.rst +28 -0
  132. vendor/twig/twig/doc/functions/date.rst +55 -0
  133. vendor/twig/twig/doc/functions/dump.rst +69 -0
  134. vendor/twig/twig/doc/functions/include.rst +84 -0
  135. vendor/twig/twig/doc/functions/index.rst +20 -0
  136. vendor/twig/twig/doc/functions/max.rst +20 -0
  137. vendor/twig/twig/doc/functions/min.rst +20 -0
  138. vendor/twig/twig/doc/functions/parent.rst +20 -0
  139. vendor/twig/twig/doc/functions/random.rst +29 -0
  140. vendor/twig/twig/doc/functions/range.rst +58 -0
  141. vendor/twig/twig/doc/functions/source.rst +32 -0
  142. vendor/twig/twig/doc/functions/template_from_string.rst +32 -0
  143. vendor/twig/twig/doc/index.rst +19 -0
  144. vendor/twig/twig/doc/installation.rst +116 -0
  145. vendor/twig/twig/doc/internals.rst +142 -0
  146. vendor/twig/twig/doc/intro.rst +85 -0
  147. vendor/twig/twig/doc/recipes.rst +568 -0
  148. vendor/twig/twig/doc/tags/autoescape.rst +81 -0
  149. vendor/twig/twig/doc/tags/block.rst +11 -0
  150. vendor/twig/twig/doc/tags/do.rst +12 -0
  151. vendor/twig/twig/doc/tags/embed.rst +178 -0
  152. vendor/twig/twig/doc/tags/extends.rst +272 -0
  153. vendor/twig/twig/doc/tags/filter.rst +21 -0
  154. vendor/twig/twig/doc/tags/flush.rst +17 -0
  155. vendor/twig/twig/doc/tags/for.rst +172 -0
  156. vendor/twig/twig/doc/tags/from.rst +8 -0
  157. vendor/twig/twig/doc/tags/if.rst +76 -0
  158. vendor/twig/twig/doc/tags/import.rst +57 -0
  159. vendor/twig/twig/doc/tags/include.rst +90 -0
  160. vendor/twig/twig/doc/tags/index.rst +25 -0
  161. vendor/twig/twig/doc/tags/macro.rst +103 -0
  162. vendor/twig/twig/doc/tags/sandbox.rst +30 -0
  163. vendor/twig/twig/doc/tags/set.rst +78 -0
  164. vendor/twig/twig/doc/tags/spaceless.rst +37 -0
  165. vendor/twig/twig/doc/tags/use.rst +124 -0
  166. vendor/twig/twig/doc/tags/verbatim.rst +24 -0
  167. vendor/twig/twig/doc/tags/with.rst +44 -0
  168. vendor/twig/twig/doc/templates.rst +908 -0
  169. vendor/twig/twig/doc/tests/constant.rst +22 -0
  170. vendor/twig/twig/doc/tests/defined.rst +30 -0
  171. vendor/twig/twig/doc/tests/divisibleby.rst +14 -0
  172. vendor/twig/twig/doc/tests/empty.rst +11 -0
  173. vendor/twig/twig/doc/tests/even.rst +10 -0
  174. vendor/twig/twig/doc/tests/index.rst +15 -0
  175. vendor/twig/twig/doc/tests/iterable.rst +19 -0
  176. vendor/twig/twig/doc/tests/null.rst +12 -0
  177. vendor/twig/twig/doc/tests/odd.rst +10 -0
  178. vendor/twig/twig/doc/tests/sameas.rst +14 -0
  179. vendor/twig/twig/ext/twig/.gitignore +30 -0
  180. vendor/twig/twig/ext/twig/config.m4 +8 -0
  181. vendor/twig/twig/ext/twig/config.w32 +8 -0
  182. vendor/twig/twig/ext/twig/php_twig.h +35 -0
  183. vendor/twig/twig/ext/twig/twig.c +1215 -0
  184. vendor/twig/twig/lib/Twig/Autoloader.php +54 -0
  185. vendor/twig/twig/lib/Twig/BaseNodeVisitor.php +50 -0
  186. vendor/twig/twig/lib/Twig/Cache/Filesystem.php +86 -0
  187. vendor/twig/twig/lib/Twig/Cache/Null.php +38 -0
  188. vendor/twig/twig/lib/Twig/CacheInterface.php +56 -0
  189. vendor/twig/twig/lib/Twig/Compiler.php +281 -0
  190. vendor/twig/twig/lib/Twig/CompilerInterface.php +34 -0
  191. vendor/twig/twig/lib/Twig/ContainerRuntimeLoader.php +37 -0
  192. vendor/twig/twig/lib/Twig/Environment.php +1561 -0
  193. vendor/twig/twig/lib/Twig/Error.php +365 -0
  194. vendor/twig/twig/lib/Twig/Error/Loader.php +38 -0
  195. vendor/twig/twig/lib/Twig/Error/Runtime.php +20 -0
  196. vendor/twig/twig/lib/Twig/Error/Syntax.php +53 -0
  197. vendor/twig/twig/lib/Twig/ExistsLoaderInterface.php +29 -0
  198. vendor/twig/twig/lib/Twig/ExpressionParser.php +739 -0
  199. vendor/twig/twig/lib/Twig/Extension.php +65 -0
  200. vendor/twig/twig/lib/Twig/Extension/Core.php +1578 -0
  201. vendor/twig/twig/lib/Twig/Extension/Debug.php +65 -0
  202. vendor/twig/twig/lib/Twig/Extension/Escaper.php +110 -0
  203. vendor/twig/twig/lib/Twig/Extension/GlobalsInterface.php +22 -0
  204. vendor/twig/twig/lib/Twig/Extension/InitRuntimeInterface.php +22 -0
  205. vendor/twig/twig/lib/Twig/Extension/Optimizer.php +33 -0
  206. vendor/twig/twig/lib/Twig/Extension/Profiler.php +46 -0
  207. vendor/twig/twig/lib/Twig/Extension/Sandbox.php +101 -0
  208. vendor/twig/twig/lib/Twig/Extension/Staging.php +110 -0
  209. vendor/twig/twig/lib/Twig/Extension/StringLoader.php +45 -0
  210. vendor/twig/twig/lib/Twig/ExtensionInterface.php +87 -0
  211. vendor/twig/twig/lib/Twig/FactoryRuntimeLoader.php +37 -0
  212. vendor/twig/twig/lib/Twig/FileExtensionEscapingStrategy.php +58 -0
  213. vendor/twig/twig/lib/Twig/Filter.php +84 -0
  214. vendor/twig/twig/lib/Twig/Filter/Function.php +40 -0
  215. vendor/twig/twig/lib/Twig/Filter/Method.php +42 -0
  216. vendor/twig/twig/lib/Twig/Filter/Node.php +42 -0
  217. vendor/twig/twig/lib/Twig/FilterCallableInterface.php +24 -0
  218. vendor/twig/twig/lib/Twig/FilterInterface.php +43 -0
  219. vendor/twig/twig/lib/Twig/Function.php +74 -0
  220. vendor/twig/twig/lib/Twig/Function/Function.php +41 -0
  221. vendor/twig/twig/lib/Twig/Function/Method.php +43 -0
  222. vendor/twig/twig/lib/Twig/Function/Node.php +42 -0
  223. vendor/twig/twig/lib/Twig/FunctionCallableInterface.php +24 -0
  224. vendor/twig/twig/lib/Twig/FunctionInterface.php +40 -0
  225. vendor/twig/twig/lib/Twig/Lexer.php +422 -0
  226. vendor/twig/twig/lib/Twig/LexerInterface.php +32 -0
  227. vendor/twig/twig/lib/Twig/Loader/Array.php +95 -0
  228. vendor/twig/twig/lib/Twig/Loader/Chain.php +149 -0
  229. vendor/twig/twig/lib/Twig/Loader/Filesystem.php +288 -0
  230. vendor/twig/twig/lib/Twig/Loader/String.php +58 -0
  231. vendor/twig/twig/lib/Twig/LoaderInterface.php +55 -0
  232. vendor/twig/twig/lib/Twig/Markup.php +37 -0
  233. vendor/twig/twig/lib/Twig/Node.php +253 -0
  234. vendor/twig/twig/lib/Twig/Node/AutoEscape.php +34 -0
  235. vendor/twig/twig/lib/Twig/Node/Block.php +39 -0
  236. vendor/twig/twig/lib/Twig/Node/BlockReference.php +32 -0
  237. vendor/twig/twig/lib/Twig/Node/Body.php +19 -0
  238. vendor/twig/twig/lib/Twig/Node/CheckSecurity.php +78 -0
  239. vendor/twig/twig/lib/Twig/Node/Do.php +33 -0
  240. vendor/twig/twig/lib/Twig/Node/Embed.php +44 -0
  241. vendor/twig/twig/lib/Twig/Node/Expression.php +20 -0
  242. vendor/twig/twig/lib/Twig/Node/Expression/Array.php +81 -0
  243. vendor/twig/twig/lib/Twig/Node/Expression/AssignName.php +23 -0
  244. vendor/twig/twig/lib/Twig/Node/Expression/Binary.php +35 -0
  245. vendor/twig/twig/lib/Twig/Node/Expression/Binary/Add.php +18 -0
  246. vendor/twig/twig/lib/Twig/Node/Expression/Binary/And.php +18 -0
  247. vendor/twig/twig/lib/Twig/Node/Expression/Binary/BitwiseAnd.php +18 -0
  248. vendor/twig/twig/lib/Twig/Node/Expression/Binary/BitwiseOr.php +18 -0
  249. vendor/twig/twig/lib/Twig/Node/Expression/Binary/BitwiseXor.php +18 -0
  250. vendor/twig/twig/lib/Twig/Node/Expression/Binary/Concat.php +18 -0
  251. vendor/twig/twig/lib/Twig/Node/Expression/Binary/Div.php +18 -0
  252. vendor/twig/twig/lib/Twig/Node/Expression/Binary/EndsWith.php +30 -0
  253. vendor/twig/twig/lib/Twig/Node/Expression/Binary/Equal.php +17 -0
  254. vendor/twig/twig/lib/Twig/Node/Expression/Binary/FloorDiv.php +24 -0
  255. vendor/twig/twig/lib/Twig/Node/Expression/Binary/Greater.php +17 -0
  256. vendor/twig/twig/lib/Twig/Node/Expression/Binary/GreaterEqual.php +17 -0
  257. vendor/twig/twig/lib/Twig/Node/Expression/Binary/In.php +28 -0
  258. vendor/twig/twig/lib/Twig/Node/Expression/Binary/Less.php +17 -0
  259. vendor/twig/twig/lib/Twig/Node/Expression/Binary/LessEqual.php +17 -0
  260. vendor/twig/twig/lib/Twig/Node/Expression/Binary/Matches.php +28 -0
  261. vendor/twig/twig/lib/Twig/Node/Expression/Binary/Mod.php +18 -0
  262. vendor/twig/twig/lib/Twig/Node/Expression/Binary/Mul.php +18 -0
  263. vendor/twig/twig/lib/Twig/Node/Expression/Binary/NotEqual.php +17 -0
  264. vendor/twig/twig/lib/Twig/Node/Expression/Binary/NotIn.php +28 -0
  265. vendor/twig/twig/lib/Twig/Node/Expression/Binary/Or.php +18 -0
  266. vendor/twig/twig/lib/Twig/Node/Expression/Binary/Power.php +32 -0
  267. vendor/twig/twig/lib/Twig/Node/Expression/Binary/Range.php +28 -0
  268. vendor/twig/twig/lib/Twig/Node/Expression/Binary/StartsWith.php +30 -0
  269. vendor/twig/twig/lib/Twig/Node/Expression/Binary/Sub.php +18 -0
  270. vendor/twig/twig/lib/Twig/Node/Expression/BlockReference.php +91 -0
  271. vendor/twig/twig/lib/Twig/Node/Expression/Call.php +289 -0
  272. vendor/twig/twig/lib/Twig/Node/Expression/Conditional.php +31 -0
  273. vendor/twig/twig/lib/Twig/Node/Expression/Constant.php +23 -0
  274. vendor/twig/twig/lib/Twig/Node/Expression/ExtensionReference.php +32 -0
  275. vendor/twig/twig/lib/Twig/Node/Expression/Filter.php +39 -0
  276. vendor/twig/twig/lib/Twig/Node/Expression/Filter/Default.php +43 -0
  277. vendor/twig/twig/lib/Twig/Node/Expression/Function.php +43 -0
  278. vendor/twig/twig/lib/Twig/Node/Expression/GetAttr.php +72 -0
  279. vendor/twig/twig/lib/Twig/Node/Expression/MethodCall.php +41 -0
  280. vendor/twig/twig/lib/Twig/Node/Expression/Name.php +100 -0
  281. vendor/twig/twig/lib/Twig/Node/Expression/NullCoalesce.php +46 -0
  282. vendor/twig/twig/lib/Twig/Node/Expression/Parent.php +42 -0
  283. vendor/twig/twig/lib/Twig/Node/Expression/TempName.php +26 -0
  284. vendor/twig/twig/lib/Twig/Node/Expression/Test.php +40 -0
  285. vendor/twig/twig/lib/Twig/Node/Expression/Test/Constant.php +46 -0
  286. vendor/twig/twig/lib/Twig/Node/Expression/Test/Defined.php +59 -0
  287. vendor/twig/twig/lib/Twig/Node/Expression/Test/Divisibleby.php +33 -0
  288. vendor/twig/twig/lib/Twig/Node/Expression/Test/Even.php +32 -0
  289. vendor/twig/twig/lib/Twig/Node/Expression/Test/Null.php +31 -0
  290. vendor/twig/twig/lib/Twig/Node/Expression/Test/Odd.php +32 -0
  291. vendor/twig/twig/lib/Twig/Node/Expression/Test/Sameas.php +29 -0
  292. vendor/twig/twig/lib/Twig/Node/Expression/Unary.php +27 -0
  293. vendor/twig/twig/lib/Twig/Node/Expression/Unary/Neg.php +18 -0
  294. vendor/twig/twig/lib/Twig/Node/Expression/Unary/Not.php +18 -0
  295. vendor/twig/twig/lib/Twig/Node/Expression/Unary/Pos.php +18 -0
  296. vendor/twig/twig/lib/Twig/Node/Flush.php +31 -0
  297. vendor/twig/twig/lib/Twig/Node/For.php +111 -0
  298. vendor/twig/twig/lib/Twig/Node/ForLoop.php +50 -0
  299. vendor/twig/twig/lib/Twig/Node/If.php +66 -0
  300. vendor/twig/twig/lib/Twig/Node/Import.php +49 -0
  301. vendor/twig/twig/lib/Twig/Node/Include.php +88 -0
  302. vendor/twig/twig/lib/Twig/Node/Macro.php +123 -0
  303. vendor/twig/twig/lib/Twig/Node/Module.php +459 -0
  304. vendor/twig/twig/lib/Twig/Node/Print.php +34 -0
  305. vendor/twig/twig/lib/Twig/Node/Sandbox.php +42 -0
  306. vendor/twig/twig/lib/Twig/Node/SandboxedPrint.php +49 -0
  307. vendor/twig/twig/lib/Twig/Node/Set.php +96 -0
  308. vendor/twig/twig/lib/Twig/Node/SetTemp.php +35 -0
  309. vendor/twig/twig/lib/Twig/Node/Spaceless.php +35 -0
  310. vendor/twig/twig/lib/Twig/Node/Text.php +34 -0
  311. vendor/twig/twig/lib/Twig/Node/With.php +62 -0
  312. vendor/twig/twig/lib/Twig/NodeCaptureInterface.php +19 -0
  313. vendor/twig/twig/lib/Twig/NodeInterface.php +32 -0
  314. vendor/twig/twig/lib/Twig/NodeOutputInterface.php +19 -0
  315. vendor/twig/twig/lib/Twig/NodeTraverser.php +82 -0
  316. vendor/twig/twig/lib/Twig/NodeVisitor/Escaper.php +152 -0
  317. vendor/twig/twig/lib/Twig/NodeVisitor/Optimizer.php +251 -0
  318. vendor/twig/twig/lib/Twig/NodeVisitor/SafeAnalysis.php +148 -0
  319. vendor/twig/twig/lib/Twig/NodeVisitor/Sandbox.php +75 -0
  320. vendor/twig/twig/lib/Twig/NodeVisitorInterface.php +41 -0
  321. vendor/twig/twig/lib/Twig/Parser.php +150 -0
.gitattributes ADDED
@@ -0,0 +1,5 @@
 
 
 
 
 
1
+ dockerfiles export-ignore
2
+ Makefile export-ignore
3
+ .gitignore export-ignore
4
+ phpunit.xml export-ignore
5
+ .travis.yml export-ignore
composer.json ADDED
@@ -0,0 +1,21 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "name": "utahta/wp-social-bookmarking-light",
3
+ "description": "Plugin for WordPress. Embed share buttons in your website.",
4
+ "type": "project",
5
+ "license": "GPLv2",
6
+ "authors": [
7
+ {
8
+ "name": "utahta",
9
+ "email": "labs.ninxit@gmail.com"
10
+ }
11
+ ],
12
+ "require": {
13
+ "twig/twig": "^1.0"
14
+ },
15
+ "autoload": {
16
+ "psr-4": { "": "src/" }
17
+ },
18
+ "require-dev": {
19
+ "phpunit/phpunit": "^5.0"
20
+ }
21
+ }
composer.lock ADDED
@@ -0,0 +1,1394 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "_readme": [
3
+ "This file locks the dependencies of your project to a known state",
4
+ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file",
5
+ "This file is @generated automatically"
6
+ ],
7
+ "content-hash": "2a6e5dadc4752a55f5787c4693b09689",
8
+ "packages": [
9
+ {
10
+ "name": "twig/twig",
11
+ "version": "v1.32.0",
12
+ "source": {
13
+ "type": "git",
14
+ "url": "https://github.com/twigphp/Twig.git",
15
+ "reference": "9935b662e24d6e634da88901ab534cc12e8c728f"
16
+ },
17
+ "dist": {
18
+ "type": "zip",
19
+ "url": "https://api.github.com/repos/twigphp/Twig/zipball/9935b662e24d6e634da88901ab534cc12e8c728f",
20
+ "reference": "9935b662e24d6e634da88901ab534cc12e8c728f",
21
+ "shasum": ""
22
+ },
23
+ "require": {
24
+ "php": ">=5.2.7"
25
+ },
26
+ "require-dev": {
27
+ "psr/container": "^1.0",
28
+ "symfony/debug": "~2.7",
29
+ "symfony/phpunit-bridge": "~3.2"
30
+ },
31
+ "type": "library",
32
+ "extra": {
33
+ "branch-alias": {
34
+ "dev-master": "1.32-dev"
35
+ }
36
+ },
37
+ "autoload": {
38
+ "psr-0": {
39
+ "Twig_": "lib/"
40
+ }
41
+ },
42
+ "notification-url": "https://packagist.org/downloads/",
43
+ "license": [
44
+ "BSD-3-Clause"
45
+ ],
46
+ "authors": [
47
+ {
48
+ "name": "Fabien Potencier",
49
+ "email": "fabien@symfony.com",
50
+ "homepage": "http://fabien.potencier.org",
51
+ "role": "Lead Developer"
52
+ },
53
+ {
54
+ "name": "Armin Ronacher",
55
+ "email": "armin.ronacher@active-4.com",
56
+ "role": "Project Founder"
57
+ },
58
+ {
59
+ "name": "Twig Team",
60
+ "homepage": "http://twig.sensiolabs.org/contributors",
61
+ "role": "Contributors"
62
+ }
63
+ ],
64
+ "description": "Twig, the flexible, fast, and secure template language for PHP",
65
+ "homepage": "http://twig.sensiolabs.org",
66
+ "keywords": [
67
+ "templating"
68
+ ],
69
+ "time": "2017-02-27T00:07:03+00:00"
70
+ }
71
+ ],
72
+ "packages-dev": [
73
+ {
74
+ "name": "doctrine/instantiator",
75
+ "version": "1.0.5",
76
+ "source": {
77
+ "type": "git",
78
+ "url": "https://github.com/doctrine/instantiator.git",
79
+ "reference": "8e884e78f9f0eb1329e445619e04456e64d8051d"
80
+ },
81
+ "dist": {
82
+ "type": "zip",
83
+ "url": "https://api.github.com/repos/doctrine/instantiator/zipball/8e884e78f9f0eb1329e445619e04456e64d8051d",
84
+ "reference": "8e884e78f9f0eb1329e445619e04456e64d8051d",
85
+ "shasum": ""
86
+ },
87
+ "require": {
88
+ "php": ">=5.3,<8.0-DEV"
89
+ },
90
+ "require-dev": {
91
+ "athletic/athletic": "~0.1.8",
92
+ "ext-pdo": "*",
93
+ "ext-phar": "*",
94
+ "phpunit/phpunit": "~4.0",
95
+ "squizlabs/php_codesniffer": "~2.0"
96
+ },
97
+ "type": "library",
98
+ "extra": {
99
+ "branch-alias": {
100
+ "dev-master": "1.0.x-dev"
101
+ }
102
+ },
103
+ "autoload": {
104
+ "psr-4": {
105
+ "Doctrine\\Instantiator\\": "src/Doctrine/Instantiator/"
106
+ }
107
+ },
108
+ "notification-url": "https://packagist.org/downloads/",
109
+ "license": [
110
+ "MIT"
111
+ ],
112
+ "authors": [
113
+ {
114
+ "name": "Marco Pivetta",
115
+ "email": "ocramius@gmail.com",
116
+ "homepage": "http://ocramius.github.com/"
117
+ }
118
+ ],
119
+ "description": "A small, lightweight utility to instantiate objects in PHP without invoking their constructors",
120
+ "homepage": "https://github.com/doctrine/instantiator",
121
+ "keywords": [
122
+ "constructor",
123
+ "instantiate"
124
+ ],
125
+ "time": "2015-06-14T21:17:01+00:00"
126
+ },
127
+ {
128
+ "name": "myclabs/deep-copy",
129
+ "version": "1.6.0",
130
+ "source": {
131
+ "type": "git",
132
+ "url": "https://github.com/myclabs/DeepCopy.git",
133
+ "reference": "5a5a9fc8025a08d8919be87d6884d5a92520cefe"
134
+ },
135
+ "dist": {
136
+ "type": "zip",
137
+ "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/5a5a9fc8025a08d8919be87d6884d5a92520cefe",
138
+ "reference": "5a5a9fc8025a08d8919be87d6884d5a92520cefe",
139
+ "shasum": ""
140
+ },
141
+ "require": {
142
+ "php": ">=5.4.0"
143
+ },
144
+ "require-dev": {
145
+ "doctrine/collections": "1.*",
146
+ "phpunit/phpunit": "~4.1"
147
+ },
148
+ "type": "library",
149
+ "autoload": {
150
+ "psr-4": {
151
+ "DeepCopy\\": "src/DeepCopy/"
152
+ }
153
+ },
154
+ "notification-url": "https://packagist.org/downloads/",
155
+ "license": [
156
+ "MIT"
157
+ ],
158
+ "description": "Create deep copies (clones) of your objects",
159
+ "homepage": "https://github.com/myclabs/DeepCopy",
160
+ "keywords": [
161
+ "clone",
162
+ "copy",
163
+ "duplicate",
164
+ "object",
165
+ "object graph"
166
+ ],
167
+ "time": "2017-01-26T22:05:40+00:00"
168
+ },
169
+ {
170
+ "name": "phpdocumentor/reflection-common",
171
+ "version": "1.0",
172
+ "source": {
173
+ "type": "git",
174
+ "url": "https://github.com/phpDocumentor/ReflectionCommon.git",
175
+ "reference": "144c307535e82c8fdcaacbcfc1d6d8eeb896687c"
176
+ },
177
+ "dist": {
178
+ "type": "zip",
179
+ "url": "https://api.github.com/repos/phpDocumentor/ReflectionCommon/zipball/144c307535e82c8fdcaacbcfc1d6d8eeb896687c",
180
+ "reference": "144c307535e82c8fdcaacbcfc1d6d8eeb896687c",
181
+ "shasum": ""
182
+ },
183
+ "require": {
184
+ "php": ">=5.5"
185
+ },
186
+ "require-dev": {
187
+ "phpunit/phpunit": "^4.6"
188
+ },
189
+ "type": "library",
190
+ "extra": {
191
+ "branch-alias": {
192
+ "dev-master": "1.0.x-dev"
193
+ }
194
+ },
195
+ "autoload": {
196
+ "psr-4": {
197
+ "phpDocumentor\\Reflection\\": [
198
+ "src"
199
+ ]
200
+ }
201
+ },
202
+ "notification-url": "https://packagist.org/downloads/",
203
+ "license": [
204
+ "MIT"
205
+ ],
206
+ "authors": [
207
+ {
208
+ "name": "Jaap van Otterdijk",
209
+ "email": "opensource@ijaap.nl"
210
+ }
211
+ ],
212
+ "description": "Common reflection classes used by phpdocumentor to reflect the code structure",
213
+ "homepage": "http://www.phpdoc.org",
214
+ "keywords": [
215
+ "FQSEN",
216
+ "phpDocumentor",
217
+ "phpdoc",
218
+ "reflection",
219
+ "static analysis"
220
+ ],
221
+ "time": "2015-12-27T11:43:31+00:00"
222
+ },
223
+ {
224
+ "name": "phpdocumentor/reflection-docblock",
225
+ "version": "3.1.1",
226
+ "source": {
227
+ "type": "git",
228
+ "url": "https://github.com/phpDocumentor/ReflectionDocBlock.git",
229
+ "reference": "8331b5efe816ae05461b7ca1e721c01b46bafb3e"
230
+ },
231
+ "dist": {
232
+ "type": "zip",
233
+ "url": "https://api.github.com/repos/phpDocumentor/ReflectionDocBlock/zipball/8331b5efe816ae05461b7ca1e721c01b46bafb3e",
234
+ "reference": "8331b5efe816ae05461b7ca1e721c01b46bafb3e",
235
+ "shasum": ""
236
+ },
237
+ "require": {
238
+ "php": ">=5.5",
239
+ "phpdocumentor/reflection-common": "^1.0@dev",
240
+ "phpdocumentor/type-resolver": "^0.2.0",
241
+ "webmozart/assert": "^1.0"
242
+ },
243
+ "require-dev": {
244
+ "mockery/mockery": "^0.9.4",
245
+ "phpunit/phpunit": "^4.4"
246
+ },
247
+ "type": "library",
248
+ "autoload": {
249
+ "psr-4": {
250
+ "phpDocumentor\\Reflection\\": [
251
+ "src/"
252
+ ]
253
+ }
254
+ },
255
+ "notification-url": "https://packagist.org/downloads/",
256
+ "license": [
257
+ "MIT"
258
+ ],
259
+ "authors": [
260
+ {
261
+ "name": "Mike van Riel",
262
+ "email": "me@mikevanriel.com"
263
+ }
264
+ ],
265
+ "description": "With this component, a library can provide support for annotations via DocBlocks or otherwise retrieve information that is embedded in a DocBlock.",
266
+ "time": "2016-09-30T07:12:33+00:00"
267
+ },
268
+ {
269
+ "name": "phpdocumentor/type-resolver",
270
+ "version": "0.2.1",
271
+ "source": {
272
+ "type": "git",
273
+ "url": "https://github.com/phpDocumentor/TypeResolver.git",
274
+ "reference": "e224fb2ea2fba6d3ad6fdaef91cd09a172155ccb"
275
+ },
276
+ "dist": {
277
+ "type": "zip",
278
+ "url": "https://api.github.com/repos/phpDocumentor/TypeResolver/zipball/e224fb2ea2fba6d3ad6fdaef91cd09a172155ccb",
279
+ "reference": "e224fb2ea2fba6d3ad6fdaef91cd09a172155ccb",
280
+ "shasum": ""
281
+ },
282
+ "require": {
283
+ "php": ">=5.5",
284
+ "phpdocumentor/reflection-common": "^1.0"
285
+ },
286
+ "require-dev": {
287
+ "mockery/mockery": "^0.9.4",
288
+ "phpunit/phpunit": "^5.2||^4.8.24"
289
+ },
290
+ "type": "library",
291
+ "extra": {
292
+ "branch-alias": {
293
+ "dev-master": "1.0.x-dev"
294
+ }
295
+ },
296
+ "autoload": {
297
+ "psr-4": {
298
+ "phpDocumentor\\Reflection\\": [
299
+ "src/"
300
+ ]
301
+ }
302
+ },
303
+ "notification-url": "https://packagist.org/downloads/",
304
+ "license": [
305
+ "MIT"
306
+ ],
307
+ "authors": [
308
+ {
309
+ "name": "Mike van Riel",
310
+ "email": "me@mikevanriel.com"
311
+ }
312
+ ],
313
+ "time": "2016-11-25T06:54:22+00:00"
314
+ },
315
+ {
316
+ "name": "phpspec/prophecy",
317
+ "version": "v1.7.0",
318
+ "source": {
319
+ "type": "git",
320
+ "url": "https://github.com/phpspec/prophecy.git",
321
+ "reference": "93d39f1f7f9326d746203c7c056f300f7f126073"
322
+ },
323
+ "dist": {
324
+ "type": "zip",
325
+ "url": "https://api.github.com/repos/phpspec/prophecy/zipball/93d39f1f7f9326d746203c7c056f300f7f126073",
326
+ "reference": "93d39f1f7f9326d746203c7c056f300f7f126073",
327
+ "shasum": ""
328
+ },
329
+ "require": {
330
+ "doctrine/instantiator": "^1.0.2",
331
+ "php": "^5.3|^7.0",
332
+ "phpdocumentor/reflection-docblock": "^2.0|^3.0.2",
333
+ "sebastian/comparator": "^1.1|^2.0",
334
+ "sebastian/recursion-context": "^1.0|^2.0|^3.0"
335
+ },
336
+ "require-dev": {
337
+ "phpspec/phpspec": "^2.5|^3.2",
338
+ "phpunit/phpunit": "^4.8 || ^5.6.5"
339
+ },
340
+ "type": "library",
341
+ "extra": {
342
+ "branch-alias": {
343
+ "dev-master": "1.6.x-dev"
344
+ }
345
+ },
346
+ "autoload": {
347
+ "psr-0": {
348
+ "Prophecy\\": "src/"
349
+ }
350
+ },
351
+ "notification-url": "https://packagist.org/downloads/",
352
+ "license": [
353
+ "MIT"
354
+ ],
355
+ "authors": [
356
+ {
357
+ "name": "Konstantin Kudryashov",
358
+ "email": "ever.zet@gmail.com",
359
+ "homepage": "http://everzet.com"
360
+ },
361
+ {
362
+ "name": "Marcello Duarte",
363
+ "email": "marcello.duarte@gmail.com"
364
+ }
365
+ ],
366
+ "description": "Highly opinionated mocking framework for PHP 5.3+",
367
+ "homepage": "https://github.com/phpspec/prophecy",
368
+ "keywords": [
369
+ "Double",
370
+ "Dummy",
371
+ "fake",
372
+ "mock",
373
+ "spy",
374
+ "stub"
375
+ ],
376
+ "time": "2017-03-02T20:05:34+00:00"
377
+ },
378
+ {
379
+ "name": "phpunit/php-code-coverage",
380
+ "version": "4.0.7",
381
+ "source": {
382
+ "type": "git",
383
+ "url": "https://github.com/sebastianbergmann/php-code-coverage.git",
384
+ "reference": "09e2277d14ea467e5a984010f501343ef29ffc69"
385
+ },
386
+ "dist": {
387
+ "type": "zip",
388
+ "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/09e2277d14ea467e5a984010f501343ef29ffc69",
389
+ "reference": "09e2277d14ea467e5a984010f501343ef29ffc69",
390
+ "shasum": ""
391
+ },
392
+ "require": {
393
+ "ext-dom": "*",
394
+ "ext-xmlwriter": "*",
395
+ "php": "^5.6 || ^7.0",
396
+ "phpunit/php-file-iterator": "^1.3",
397
+ "phpunit/php-text-template": "^1.2",
398
+ "phpunit/php-token-stream": "^1.4.2 || ^2.0",
399
+ "sebastian/code-unit-reverse-lookup": "^1.0",
400
+ "sebastian/environment": "^1.3.2 || ^2.0",
401
+ "sebastian/version": "^1.0 || ^2.0"
402
+ },
403
+ "require-dev": {
404
+ "ext-xdebug": "^2.1.4",
405
+ "phpunit/phpunit": "^5.7"
406
+ },
407
+ "suggest": {
408
+ "ext-xdebug": "^2.5.1"
409
+ },
410
+ "type": "library",
411
+ "extra": {
412
+ "branch-alias": {
413
+ "dev-master": "4.0.x-dev"
414
+ }
415
+ },
416
+ "autoload": {
417
+ "classmap": [
418
+ "src/"
419
+ ]
420
+ },
421
+ "notification-url": "https://packagist.org/downloads/",
422
+ "license": [
423
+ "BSD-3-Clause"
424
+ ],
425
+ "authors": [
426
+ {
427
+ "name": "Sebastian Bergmann",
428
+ "email": "sb@sebastian-bergmann.de",
429
+ "role": "lead"
430
+ }
431
+ ],
432
+ "description": "Library that provides collection, processing, and rendering functionality for PHP code coverage information.",
433
+ "homepage": "https://github.com/sebastianbergmann/php-code-coverage",
434
+ "keywords": [
435
+ "coverage",
436
+ "testing",
437
+ "xunit"
438
+ ],
439
+ "time": "2017-03-01T09:12:17+00:00"
440
+ },
441
+ {
442
+ "name": "phpunit/php-file-iterator",
443
+ "version": "1.4.2",
444
+ "source": {
445
+ "type": "git",
446
+ "url": "https://github.com/sebastianbergmann/php-file-iterator.git",
447
+ "reference": "3cc8f69b3028d0f96a9078e6295d86e9bf019be5"
448
+ },
449
+ "dist": {
450
+ "type": "zip",
451
+ "url": "https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/3cc8f69b3028d0f96a9078e6295d86e9bf019be5",
452
+ "reference": "3cc8f69b3028d0f96a9078e6295d86e9bf019be5",
453
+ "shasum": ""
454
+ },
455
+ "require": {
456
+ "php": ">=5.3.3"
457
+ },
458
+ "type": "library",
459
+ "extra": {
460
+ "branch-alias": {
461
+ "dev-master": "1.4.x-dev"
462
+ }
463
+ },
464
+ "autoload": {
465
+ "classmap": [
466
+ "src/"
467
+ ]
468
+ },
469
+ "notification-url": "https://packagist.org/downloads/",
470
+ "license": [
471
+ "BSD-3-Clause"
472
+ ],
473
+ "authors": [
474
+ {
475
+ "name": "Sebastian Bergmann",
476
+ "email": "sb@sebastian-bergmann.de",
477
+ "role": "lead"
478
+ }
479
+ ],
480
+ "description": "FilterIterator implementation that filters files based on a list of suffixes.",
481
+ "homepage": "https://github.com/sebastianbergmann/php-file-iterator/",
482
+ "keywords": [
483
+ "filesystem",
484
+ "iterator"
485
+ ],
486
+ "time": "2016-10-03T07:40:28+00:00"
487
+ },
488
+ {
489
+ "name": "phpunit/php-text-template",
490
+ "version": "1.2.1",
491
+ "source": {
492
+ "type": "git",
493
+ "url": "https://github.com/sebastianbergmann/php-text-template.git",
494
+ "reference": "31f8b717e51d9a2afca6c9f046f5d69fc27c8686"
495
+ },
496
+ "dist": {
497
+ "type": "zip",
498
+ "url": "https://api.github.com/repos/sebastianbergmann/php-text-template/zipball/31f8b717e51d9a2afca6c9f046f5d69fc27c8686",
499
+ "reference": "31f8b717e51d9a2afca6c9f046f5d69fc27c8686",
500
+ "shasum": ""
501
+ },
502
+ "require": {
503
+ "php": ">=5.3.3"
504
+ },
505
+ "type": "library",
506
+ "autoload": {
507
+ "classmap": [
508
+ "src/"
509
+ ]
510
+ },
511
+ "notification-url": "https://packagist.org/downloads/",
512
+ "license": [
513
+ "BSD-3-Clause"
514
+ ],
515
+ "authors": [
516
+ {
517
+ "name": "Sebastian Bergmann",
518
+ "email": "sebastian@phpunit.de",
519
+ "role": "lead"
520
+ }
521
+ ],
522
+ "description": "Simple template engine.",
523
+ "homepage": "https://github.com/sebastianbergmann/php-text-template/",
524
+ "keywords": [
525
+ "template"
526
+ ],
527
+ "time": "2015-06-21T13:50:34+00:00"
528
+ },
529
+ {
530
+ "name": "phpunit/php-timer",
531
+ "version": "1.0.9",
532
+ "source": {
533
+ "type": "git",
534
+ "url": "https://github.com/sebastianbergmann/php-timer.git",
535
+ "reference": "3dcf38ca72b158baf0bc245e9184d3fdffa9c46f"
536
+ },
537
+ "dist": {
538
+ "type": "zip",
539
+ "url": "https://api.github.com/repos/sebastianbergmann/php-timer/zipball/3dcf38ca72b158baf0bc245e9184d3fdffa9c46f",
540
+ "reference": "3dcf38ca72b158baf0bc245e9184d3fdffa9c46f",
541
+ "shasum": ""
542
+ },
543
+ "require": {
544
+ "php": "^5.3.3 || ^7.0"
545
+ },
546
+ "require-dev": {
547
+ "phpunit/phpunit": "^4.8.35 || ^5.7 || ^6.0"
548
+ },
549
+ "type": "library",
550
+ "extra": {
551
+ "branch-alias": {
552
+ "dev-master": "1.0-dev"
553
+ }
554
+ },
555
+ "autoload": {
556
+ "classmap": [
557
+ "src/"
558
+ ]
559
+ },
560
+ "notification-url": "https://packagist.org/downloads/",
561
+ "license": [
562
+ "BSD-3-Clause"
563
+ ],
564
+ "authors": [
565
+ {
566
+ "name": "Sebastian Bergmann",
567
+ "email": "sb@sebastian-bergmann.de",
568
+ "role": "lead"
569
+ }
570
+ ],
571
+ "description": "Utility class for timing",
572
+ "homepage": "https://github.com/sebastianbergmann/php-timer/",
573
+ "keywords": [
574
+ "timer"
575
+ ],
576
+ "time": "2017-02-26T11:10:40+00:00"
577
+ },
578
+ {
579
+ "name": "phpunit/php-token-stream",
580
+ "version": "1.4.11",
581
+ "source": {
582
+ "type": "git",
583
+ "url": "https://github.com/sebastianbergmann/php-token-stream.git",
584
+ "reference": "e03f8f67534427a787e21a385a67ec3ca6978ea7"
585
+ },
586
+ "dist": {
587
+ "type": "zip",
588
+ "url": "https://api.github.com/repos/sebastianbergmann/php-token-stream/zipball/e03f8f67534427a787e21a385a67ec3ca6978ea7",
589
+ "reference": "e03f8f67534427a787e21a385a67ec3ca6978ea7",
590
+ "shasum": ""
591
+ },
592
+ "require": {
593
+ "ext-tokenizer": "*",
594
+ "php": ">=5.3.3"
595
+ },
596
+ "require-dev": {
597
+ "phpunit/phpunit": "~4.2"
598
+ },
599
+ "type": "library",
600
+ "extra": {
601
+ "branch-alias": {
602
+ "dev-master": "1.4-dev"
603
+ }
604
+ },
605
+ "autoload": {
606
+ "classmap": [
607
+ "src/"
608
+ ]
609
+ },
610
+ "notification-url": "https://packagist.org/downloads/",
611
+ "license": [
612
+ "BSD-3-Clause"
613
+ ],
614
+ "authors": [
615
+ {
616
+ "name": "Sebastian Bergmann",
617
+ "email": "sebastian@phpunit.de"
618
+ }
619
+ ],
620
+ "description": "Wrapper around PHP's tokenizer extension.",
621
+ "homepage": "https://github.com/sebastianbergmann/php-token-stream/",
622
+ "keywords": [
623
+ "tokenizer"
624
+ ],
625
+ "time": "2017-02-27T10:12:30+00:00"
626
+ },
627
+ {
628
+ "name": "phpunit/phpunit",
629
+ "version": "5.7.15",
630
+ "source": {
631
+ "type": "git",
632
+ "url": "https://github.com/sebastianbergmann/phpunit.git",
633
+ "reference": "b99112aecc01f62acf3d81a3f59646700a1849e5"
634
+ },
635
+ "dist": {
636
+ "type": "zip",
637
+ "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/b99112aecc01f62acf3d81a3f59646700a1849e5",
638
+ "reference": "b99112aecc01f62acf3d81a3f59646700a1849e5",
639
+ "shasum": ""
640
+ },
641
+ "require": {
642
+ "ext-dom": "*",
643
+ "ext-json": "*",
644
+ "ext-libxml": "*",
645
+ "ext-mbstring": "*",
646
+ "ext-xml": "*",
647
+ "myclabs/deep-copy": "~1.3",
648
+ "php": "^5.6 || ^7.0",
649
+ "phpspec/prophecy": "^1.6.2",
650
+ "phpunit/php-code-coverage": "^4.0.4",
651
+ "phpunit/php-file-iterator": "~1.4",
652
+ "phpunit/php-text-template": "~1.2",
653
+ "phpunit/php-timer": "^1.0.6",
654
+ "phpunit/phpunit-mock-objects": "^3.2",
655
+ "sebastian/comparator": "^1.2.4",
656
+ "sebastian/diff": "~1.2",
657
+ "sebastian/environment": "^1.3.4 || ^2.0",
658
+ "sebastian/exporter": "~2.0",
659
+ "sebastian/global-state": "^1.1",
660
+ "sebastian/object-enumerator": "~2.0",
661
+ "sebastian/resource-operations": "~1.0",
662
+ "sebastian/version": "~1.0.3|~2.0",
663
+ "symfony/yaml": "~2.1|~3.0"
664
+ },
665
+ "conflict": {
666
+ "phpdocumentor/reflection-docblock": "3.0.2"
667
+ },
668
+ "require-dev": {
669
+ "ext-pdo": "*"
670
+ },
671
+ "suggest": {
672
+ "ext-xdebug": "*",
673
+ "phpunit/php-invoker": "~1.1"
674
+ },
675
+ "bin": [
676
+ "phpunit"
677
+ ],
678
+ "type": "library",
679
+ "extra": {
680
+ "branch-alias": {
681
+ "dev-master": "5.7.x-dev"
682
+ }
683
+ },
684
+ "autoload": {
685
+ "classmap": [
686
+ "src/"
687
+ ]
688
+ },
689
+ "notification-url": "https://packagist.org/downloads/",
690
+ "license": [
691
+ "BSD-3-Clause"
692
+ ],
693
+ "authors": [
694
+ {
695
+ "name": "Sebastian Bergmann",
696
+ "email": "sebastian@phpunit.de",
697
+ "role": "lead"
698
+ }
699
+ ],
700
+ "description": "The PHP Unit Testing framework.",
701
+ "homepage": "https://phpunit.de/",
702
+ "keywords": [
703
+ "phpunit",
704
+ "testing",
705
+ "xunit"
706
+ ],
707
+ "time": "2017-03-02T15:22:43+00:00"
708
+ },
709
+ {
710
+ "name": "phpunit/phpunit-mock-objects",
711
+ "version": "3.4.3",
712
+ "source": {
713
+ "type": "git",
714
+ "url": "https://github.com/sebastianbergmann/phpunit-mock-objects.git",
715
+ "reference": "3ab72b65b39b491e0c011e2e09bb2206c2aa8e24"
716
+ },
717
+ "dist": {
718
+ "type": "zip",
719
+ "url": "https://api.github.com/repos/sebastianbergmann/phpunit-mock-objects/zipball/3ab72b65b39b491e0c011e2e09bb2206c2aa8e24",
720
+ "reference": "3ab72b65b39b491e0c011e2e09bb2206c2aa8e24",
721
+ "shasum": ""
722
+ },
723
+ "require": {
724
+ "doctrine/instantiator": "^1.0.2",
725
+ "php": "^5.6 || ^7.0",
726
+ "phpunit/php-text-template": "^1.2",
727
+ "sebastian/exporter": "^1.2 || ^2.0"
728
+ },
729
+ "conflict": {
730
+ "phpunit/phpunit": "<5.4.0"
731
+ },
732
+ "require-dev": {
733
+ "phpunit/phpunit": "^5.4"
734
+ },
735
+ "suggest": {
736
+ "ext-soap": "*"
737
+ },
738
+ "type": "library",
739
+ "extra": {
740
+ "branch-alias": {
741
+ "dev-master": "3.2.x-dev"
742
+ }
743
+ },
744
+ "autoload": {
745
+ "classmap": [
746
+ "src/"
747
+ ]
748
+ },
749
+ "notification-url": "https://packagist.org/downloads/",
750
+ "license": [
751
+ "BSD-3-Clause"
752
+ ],
753
+ "authors": [
754
+ {
755
+ "name": "Sebastian Bergmann",
756
+ "email": "sb@sebastian-bergmann.de",
757
+ "role": "lead"
758
+ }
759
+ ],
760
+ "description": "Mock Object library for PHPUnit",
761
+ "homepage": "https://github.com/sebastianbergmann/phpunit-mock-objects/",
762
+ "keywords": [
763
+ "mock",
764
+ "xunit"
765
+ ],
766
+ "time": "2016-12-08T20:27:08+00:00"
767
+ },
768
+ {
769
+ "name": "sebastian/code-unit-reverse-lookup",
770
+ "version": "1.0.1",
771
+ "source": {
772
+ "type": "git",
773
+ "url": "https://github.com/sebastianbergmann/code-unit-reverse-lookup.git",
774
+ "reference": "4419fcdb5eabb9caa61a27c7a1db532a6b55dd18"
775
+ },
776
+ "dist": {
777
+ "type": "zip",
778
+ "url": "https://api.github.com/repos/sebastianbergmann/code-unit-reverse-lookup/zipball/4419fcdb5eabb9caa61a27c7a1db532a6b55dd18",
779
+ "reference": "4419fcdb5eabb9caa61a27c7a1db532a6b55dd18",
780
+ "shasum": ""
781
+ },
782
+ "require": {
783
+ "php": "^5.6 || ^7.0"
784
+ },
785
+ "require-dev": {
786
+ "phpunit/phpunit": "^5.7 || ^6.0"
787
+ },
788
+ "type": "library",
789
+ "extra": {
790
+ "branch-alias": {
791
+ "dev-master": "1.0.x-dev"
792
+ }
793
+ },
794
+ "autoload": {
795
+ "classmap": [
796
+ "src/"
797
+ ]
798
+ },
799
+ "notification-url": "https://packagist.org/downloads/",
800
+ "license": [
801
+ "BSD-3-Clause"
802
+ ],
803
+ "authors": [
804
+ {
805
+ "name": "Sebastian Bergmann",
806
+ "email": "sebastian@phpunit.de"
807
+ }
808
+ ],
809
+ "description": "Looks up which function or method a line of code belongs to",
810
+ "homepage": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/",
811
+ "time": "2017-03-04T06:30:41+00:00"
812
+ },
813
+ {
814
+ "name": "sebastian/comparator",
815
+ "version": "1.2.4",
816
+ "source": {
817
+ "type": "git",
818
+ "url": "https://github.com/sebastianbergmann/comparator.git",
819
+ "reference": "2b7424b55f5047b47ac6e5ccb20b2aea4011d9be"
820
+ },
821
+ "dist": {
822
+ "type": "zip",
823
+ "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/2b7424b55f5047b47ac6e5ccb20b2aea4011d9be",
824
+ "reference": "2b7424b55f5047b47ac6e5ccb20b2aea4011d9be",
825
+ "shasum": ""
826
+ },
827
+ "require": {
828
+ "php": ">=5.3.3",
829
+ "sebastian/diff": "~1.2",
830
+ "sebastian/exporter": "~1.2 || ~2.0"
831
+ },
832
+ "require-dev": {
833
+ "phpunit/phpunit": "~4.4"
834
+ },
835
+ "type": "library",
836
+ "extra": {
837
+ "branch-alias": {
838
+ "dev-master": "1.2.x-dev"
839
+ }
840
+ },
841
+ "autoload": {
842
+ "classmap": [
843
+ "src/"
844
+ ]
845
+ },
846
+ "notification-url": "https://packagist.org/downloads/",
847
+ "license": [
848
+ "BSD-3-Clause"
849
+ ],
850
+ "authors": [
851
+ {
852
+ "name": "Jeff Welch",
853
+ "email": "whatthejeff@gmail.com"
854
+ },
855
+ {
856
+ "name": "Volker Dusch",
857
+ "email": "github@wallbash.com"
858
+ },
859
+ {
860
+ "name": "Bernhard Schussek",
861
+ "email": "bschussek@2bepublished.at"
862
+ },
863
+ {
864
+ "name": "Sebastian Bergmann",
865
+ "email": "sebastian@phpunit.de"
866
+ }
867
+ ],
868
+ "description": "Provides the functionality to compare PHP values for equality",
869
+ "homepage": "http://www.github.com/sebastianbergmann/comparator",
870
+ "keywords": [
871
+ "comparator",
872
+ "compare",
873
+ "equality"
874
+ ],
875
+ "time": "2017-01-29T09:50:25+00:00"
876
+ },
877
+ {
878
+ "name": "sebastian/diff",
879
+ "version": "1.4.1",
880
+ "source": {
881
+ "type": "git",
882
+ "url": "https://github.com/sebastianbergmann/diff.git",
883
+ "reference": "13edfd8706462032c2f52b4b862974dd46b71c9e"
884
+ },
885
+ "dist": {
886
+ "type": "zip",
887
+ "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/13edfd8706462032c2f52b4b862974dd46b71c9e",
888
+ "reference": "13edfd8706462032c2f52b4b862974dd46b71c9e",
889
+ "shasum": ""
890
+ },
891
+ "require": {
892
+ "php": ">=5.3.3"
893
+ },
894
+ "require-dev": {
895
+ "phpunit/phpunit": "~4.8"
896
+ },
897
+ "type": "library",
898
+ "extra": {
899
+ "branch-alias": {
900
+ "dev-master": "1.4-dev"
901
+ }
902
+ },
903
+ "autoload": {
904
+ "classmap": [
905
+ "src/"
906
+ ]
907
+ },
908
+ "notification-url": "https://packagist.org/downloads/",
909
+ "license": [
910
+ "BSD-3-Clause"
911
+ ],
912
+ "authors": [
913
+ {
914
+ "name": "Kore Nordmann",
915
+ "email": "mail@kore-nordmann.de"
916
+ },
917
+ {
918
+ "name": "Sebastian Bergmann",
919
+ "email": "sebastian@phpunit.de"
920
+ }
921
+ ],
922
+ "description": "Diff implementation",
923
+ "homepage": "https://github.com/sebastianbergmann/diff",
924
+ "keywords": [
925
+ "diff"
926
+ ],
927
+ "time": "2015-12-08T07:14:41+00:00"
928
+ },
929
+ {
930
+ "name": "sebastian/environment",
931
+ "version": "2.0.0",
932
+ "source": {
933
+ "type": "git",
934
+ "url": "https://github.com/sebastianbergmann/environment.git",
935
+ "reference": "5795ffe5dc5b02460c3e34222fee8cbe245d8fac"
936
+ },
937
+ "dist": {
938
+ "type": "zip",
939
+ "url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/5795ffe5dc5b02460c3e34222fee8cbe245d8fac",
940
+ "reference": "5795ffe5dc5b02460c3e34222fee8cbe245d8fac",
941
+ "shasum": ""
942
+ },
943
+ "require": {
944
+ "php": "^5.6 || ^7.0"
945
+ },
946
+ "require-dev": {
947
+ "phpunit/phpunit": "^5.0"
948
+ },
949
+ "type": "library",
950
+ "extra": {
951
+ "branch-alias": {
952
+ "dev-master": "2.0.x-dev"
953
+ }
954
+ },
955
+ "autoload": {
956
+ "classmap": [
957
+ "src/"
958
+ ]
959
+ },
960
+ "notification-url": "https://packagist.org/downloads/",
961
+ "license": [
962
+ "BSD-3-Clause"
963
+ ],
964
+ "authors": [
965
+ {
966
+ "name": "Sebastian Bergmann",
967
+ "email": "sebastian@phpunit.de"
968
+ }
969
+ ],
970
+ "description": "Provides functionality to handle HHVM/PHP environments",
971
+ "homepage": "http://www.github.com/sebastianbergmann/environment",
972
+ "keywords": [
973
+ "Xdebug",
974
+ "environment",
975
+ "hhvm"
976
+ ],
977
+ "time": "2016-11-26T07:53:53+00:00"
978
+ },
979
+ {
980
+ "name": "sebastian/exporter",
981
+ "version": "2.0.0",
982
+ "source": {
983
+ "type": "git",
984
+ "url": "https://github.com/sebastianbergmann/exporter.git",
985
+ "reference": "ce474bdd1a34744d7ac5d6aad3a46d48d9bac4c4"
986
+ },
987
+ "dist": {
988
+ "type": "zip",
989
+ "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/ce474bdd1a34744d7ac5d6aad3a46d48d9bac4c4",
990
+ "reference": "ce474bdd1a34744d7ac5d6aad3a46d48d9bac4c4",
991
+ "shasum": ""
992
+ },
993
+ "require": {
994
+ "php": ">=5.3.3",
995
+ "sebastian/recursion-context": "~2.0"
996
+ },
997
+ "require-dev": {
998
+ "ext-mbstring": "*",
999
+ "phpunit/phpunit": "~4.4"
1000
+ },
1001
+ "type": "library",
1002
+ "extra": {
1003
+ "branch-alias": {
1004
+ "dev-master": "2.0.x-dev"
1005
+ }
1006
+ },
1007
+ "autoload": {
1008
+ "classmap": [
1009
+ "src/"
1010
+ ]
1011
+ },
1012
+ "notification-url": "https://packagist.org/downloads/",
1013
+ "license": [
1014
+ "BSD-3-Clause"
1015
+ ],
1016
+ "authors": [
1017
+ {
1018
+ "name": "Jeff Welch",
1019
+ "email": "whatthejeff@gmail.com"
1020
+ },
1021
+ {
1022
+ "name": "Volker Dusch",
1023
+ "email": "github@wallbash.com"
1024
+ },
1025
+ {
1026
+ "name": "Bernhard Schussek",
1027
+ "email": "bschussek@2bepublished.at"
1028
+ },
1029
+ {
1030
+ "name": "Sebastian Bergmann",
1031
+ "email": "sebastian@phpunit.de"
1032
+ },
1033
+ {
1034
+ "name": "Adam Harvey",
1035
+ "email": "aharvey@php.net"
1036
+ }
1037
+ ],
1038
+ "description": "Provides the functionality to export PHP variables for visualization",
1039
+ "homepage": "http://www.github.com/sebastianbergmann/exporter",
1040
+ "keywords": [
1041
+ "export",
1042
+ "exporter"
1043
+ ],
1044
+ "time": "2016-11-19T08:54:04+00:00"
1045
+ },
1046
+ {
1047
+ "name": "sebastian/global-state",
1048
+ "version": "1.1.1",
1049
+ "source": {
1050
+ "type": "git",
1051
+ "url": "https://github.com/sebastianbergmann/global-state.git",
1052
+ "reference": "bc37d50fea7d017d3d340f230811c9f1d7280af4"
1053
+ },
1054
+ "dist": {
1055
+ "type": "zip",
1056
+ "url": "https://api.github.com/repos/sebastianbergmann/global-state/zipball/bc37d50fea7d017d3d340f230811c9f1d7280af4",
1057
+ "reference": "bc37d50fea7d017d3d340f230811c9f1d7280af4",
1058
+ "shasum": ""
1059
+ },
1060
+ "require": {
1061
+ "php": ">=5.3.3"
1062
+ },
1063
+ "require-dev": {
1064
+ "phpunit/phpunit": "~4.2"
1065
+ },
1066
+ "suggest": {
1067
+ "ext-uopz": "*"
1068
+ },
1069
+ "type": "library",
1070
+ "extra": {
1071
+ "branch-alias": {
1072
+ "dev-master": "1.0-dev"
1073
+ }
1074
+ },
1075
+ "autoload": {
1076
+ "classmap": [
1077
+ "src/"
1078
+ ]
1079
+ },
1080
+ "notification-url": "https://packagist.org/downloads/",
1081
+ "license": [
1082
+ "BSD-3-Clause"
1083
+ ],
1084
+ "authors": [
1085
+ {
1086
+ "name": "Sebastian Bergmann",
1087
+ "email": "sebastian@phpunit.de"
1088
+ }
1089
+ ],
1090
+ "description": "Snapshotting of global state",
1091
+ "homepage": "http://www.github.com/sebastianbergmann/global-state",
1092
+ "keywords": [
1093
+ "global state"
1094
+ ],
1095
+ "time": "2015-10-12T03:26:01+00:00"
1096
+ },
1097
+ {
1098
+ "name": "sebastian/object-enumerator",
1099
+ "version": "2.0.1",
1100
+ "source": {
1101
+ "type": "git",
1102
+ "url": "https://github.com/sebastianbergmann/object-enumerator.git",
1103
+ "reference": "1311872ac850040a79c3c058bea3e22d0f09cbb7"
1104
+ },
1105
+ "dist": {
1106
+ "type": "zip",
1107
+ "url": "https://api.github.com/repos/sebastianbergmann/object-enumerator/zipball/1311872ac850040a79c3c058bea3e22d0f09cbb7",
1108
+ "reference": "1311872ac850040a79c3c058bea3e22d0f09cbb7",
1109
+ "shasum": ""
1110
+ },
1111
+ "require": {
1112
+ "php": ">=5.6",
1113
+ "sebastian/recursion-context": "~2.0"
1114
+ },
1115
+ "require-dev": {
1116
+ "phpunit/phpunit": "~5"
1117
+ },
1118
+ "type": "library",
1119
+ "extra": {
1120
+ "branch-alias": {
1121
+ "dev-master": "2.0.x-dev"
1122
+ }
1123
+ },
1124
+ "autoload": {
1125
+ "classmap": [
1126
+ "src/"
1127
+ ]
1128
+ },
1129
+ "notification-url": "https://packagist.org/downloads/",
1130
+ "license": [
1131
+ "BSD-3-Clause"
1132
+ ],
1133
+ "authors": [
1134
+ {
1135
+ "name": "Sebastian Bergmann",
1136
+ "email": "sebastian@phpunit.de"
1137
+ }
1138
+ ],
1139
+ "description": "Traverses array structures and object graphs to enumerate all referenced objects",
1140
+ "homepage": "https://github.com/sebastianbergmann/object-enumerator/",
1141
+ "time": "2017-02-18T15:18:39+00:00"
1142
+ },
1143
+ {
1144
+ "name": "sebastian/recursion-context",
1145
+ "version": "2.0.0",
1146
+ "source": {
1147
+ "type": "git",
1148
+ "url": "https://github.com/sebastianbergmann/recursion-context.git",
1149
+ "reference": "2c3ba150cbec723aa057506e73a8d33bdb286c9a"
1150
+ },
1151
+ "dist": {
1152
+ "type": "zip",
1153
+ "url": "https://api.github.com/repos/sebastianbergmann/recursion-context/zipball/2c3ba150cbec723aa057506e73a8d33bdb286c9a",
1154
+ "reference": "2c3ba150cbec723aa057506e73a8d33bdb286c9a",
1155
+ "shasum": ""
1156
+ },
1157
+ "require": {
1158
+ "php": ">=5.3.3"
1159
+ },
1160
+ "require-dev": {
1161
+ "phpunit/phpunit": "~4.4"
1162
+ },
1163
+ "type": "library",
1164
+ "extra": {
1165
+ "branch-alias": {
1166
+ "dev-master": "2.0.x-dev"
1167
+ }
1168
+ },
1169
+ "autoload": {
1170
+ "classmap": [
1171
+ "src/"
1172
+ ]
1173
+ },
1174
+ "notification-url": "https://packagist.org/downloads/",
1175
+ "license": [
1176
+ "BSD-3-Clause"
1177
+ ],
1178
+ "authors": [
1179
+ {
1180
+ "name": "Jeff Welch",
1181
+ "email": "whatthejeff@gmail.com"
1182
+ },
1183
+ {
1184
+ "name": "Sebastian Bergmann",
1185
+ "email": "sebastian@phpunit.de"
1186
+ },
1187
+ {
1188
+ "name": "Adam Harvey",
1189
+ "email": "aharvey@php.net"
1190
+ }
1191
+ ],
1192
+ "description": "Provides functionality to recursively process PHP variables",
1193
+ "homepage": "http://www.github.com/sebastianbergmann/recursion-context",
1194
+ "time": "2016-11-19T07:33:16+00:00"
1195
+ },
1196
+ {
1197
+ "name": "sebastian/resource-operations",
1198
+ "version": "1.0.0",
1199
+ "source": {
1200
+ "type": "git",
1201
+ "url": "https://github.com/sebastianbergmann/resource-operations.git",
1202
+ "reference": "ce990bb21759f94aeafd30209e8cfcdfa8bc3f52"
1203
+ },
1204
+ "dist": {
1205
+ "type": "zip",
1206
+ "url": "https://api.github.com/repos/sebastianbergmann/resource-operations/zipball/ce990bb21759f94aeafd30209e8cfcdfa8bc3f52",
1207
+ "reference": "ce990bb21759f94aeafd30209e8cfcdfa8bc3f52",
1208
+ "shasum": ""
1209
+ },
1210
+ "require": {
1211
+ "php": ">=5.6.0"
1212
+ },
1213
+ "type": "library",
1214
+ "extra": {
1215
+ "branch-alias": {
1216
+ "dev-master": "1.0.x-dev"
1217
+ }
1218
+ },
1219
+ "autoload": {
1220
+ "classmap": [
1221
+ "src/"
1222
+ ]
1223
+ },
1224
+ "notification-url": "https://packagist.org/downloads/",
1225
+ "license": [
1226
+ "BSD-3-Clause"
1227
+ ],
1228
+ "authors": [
1229
+ {
1230
+ "name": "Sebastian Bergmann",
1231
+ "email": "sebastian@phpunit.de"
1232
+ }
1233
+ ],
1234
+ "description": "Provides a list of PHP built-in functions that operate on resources",
1235
+ "homepage": "https://www.github.com/sebastianbergmann/resource-operations",
1236
+ "time": "2015-07-28T20:34:47+00:00"
1237
+ },
1238
+ {
1239
+ "name": "sebastian/version",
1240
+ "version": "2.0.1",
1241
+ "source": {
1242
+ "type": "git",
1243
+ "url": "https://github.com/sebastianbergmann/version.git",
1244
+ "reference": "99732be0ddb3361e16ad77b68ba41efc8e979019"
1245
+ },
1246
+ "dist": {
1247
+ "type": "zip",
1248
+ "url": "https://api.github.com/repos/sebastianbergmann/version/zipball/99732be0ddb3361e16ad77b68ba41efc8e979019",
1249
+ "reference": "99732be0ddb3361e16ad77b68ba41efc8e979019",
1250
+ "shasum": ""
1251
+ },
1252
+ "require": {
1253
+ "php": ">=5.6"
1254
+ },
1255
+ "type": "library",
1256
+ "extra": {
1257
+ "branch-alias": {
1258
+ "dev-master": "2.0.x-dev"
1259
+ }
1260
+ },
1261
+ "autoload": {
1262
+ "classmap": [
1263
+ "src/"
1264
+ ]
1265
+ },
1266
+ "notification-url": "https://packagist.org/downloads/",
1267
+ "license": [
1268
+ "BSD-3-Clause"
1269
+ ],
1270
+ "authors": [
1271
+ {
1272
+ "name": "Sebastian Bergmann",
1273
+ "email": "sebastian@phpunit.de",
1274
+ "role": "lead"
1275
+ }
1276
+ ],
1277
+ "description": "Library that helps with managing the version number of Git-hosted PHP projects",
1278
+ "homepage": "https://github.com/sebastianbergmann/version",
1279
+ "time": "2016-10-03T07:35:21+00:00"
1280
+ },
1281
+ {
1282
+ "name": "symfony/yaml",
1283
+ "version": "v3.2.4",
1284
+ "source": {
1285
+ "type": "git",
1286
+ "url": "https://github.com/symfony/yaml.git",
1287
+ "reference": "9724c684646fcb5387d579b4bfaa63ee0b0c64c8"
1288
+ },
1289
+ "dist": {
1290
+ "type": "zip",
1291
+ "url": "https://api.github.com/repos/symfony/yaml/zipball/9724c684646fcb5387d579b4bfaa63ee0b0c64c8",
1292
+ "reference": "9724c684646fcb5387d579b4bfaa63ee0b0c64c8",
1293
+ "shasum": ""
1294
+ },
1295
+ "require": {
1296
+ "php": ">=5.5.9"
1297
+ },
1298
+ "require-dev": {
1299
+ "symfony/console": "~2.8|~3.0"
1300
+ },
1301
+ "suggest": {
1302
+ "symfony/console": "For validating YAML files using the lint command"
1303
+ },
1304
+ "type": "library",
1305
+ "extra": {
1306
+ "branch-alias": {
1307
+ "dev-master": "3.2-dev"
1308
+ }
1309
+ },
1310
+ "autoload": {
1311
+ "psr-4": {
1312
+ "Symfony\\Component\\Yaml\\": ""
1313
+ },
1314
+ "exclude-from-classmap": [
1315
+ "/Tests/"
1316
+ ]
1317
+ },
1318
+ "notification-url": "https://packagist.org/downloads/",
1319
+ "license": [
1320
+ "MIT"
1321
+ ],
1322
+ "authors": [
1323
+ {
1324
+ "name": "Fabien Potencier",
1325
+ "email": "fabien@symfony.com"
1326
+ },
1327
+ {
1328
+ "name": "Symfony Community",
1329
+ "homepage": "https://symfony.com/contributors"
1330
+ }
1331
+ ],
1332
+ "description": "Symfony Yaml Component",
1333
+ "homepage": "https://symfony.com",
1334
+ "time": "2017-02-16T22:46:52+00:00"
1335
+ },
1336
+ {
1337
+ "name": "webmozart/assert",
1338
+ "version": "1.2.0",
1339
+ "source": {
1340
+ "type": "git",
1341
+ "url": "https://github.com/webmozart/assert.git",
1342
+ "reference": "2db61e59ff05fe5126d152bd0655c9ea113e550f"
1343
+ },
1344
+ "dist": {
1345
+ "type": "zip",
1346
+ "url": "https://api.github.com/repos/webmozart/assert/zipball/2db61e59ff05fe5126d152bd0655c9ea113e550f",
1347
+ "reference": "2db61e59ff05fe5126d152bd0655c9ea113e550f",
1348
+ "shasum": ""
1349
+ },
1350
+ "require": {
1351
+ "php": "^5.3.3 || ^7.0"
1352
+ },
1353
+ "require-dev": {
1354
+ "phpunit/phpunit": "^4.6",
1355
+ "sebastian/version": "^1.0.1"
1356
+ },
1357
+ "type": "library",
1358
+ "extra": {
1359
+ "branch-alias": {
1360
+ "dev-master": "1.3-dev"
1361
+ }
1362
+ },
1363
+ "autoload": {
1364
+ "psr-4": {
1365
+ "Webmozart\\Assert\\": "src/"
1366
+ }
1367
+ },
1368
+ "notification-url": "https://packagist.org/downloads/",
1369
+ "license": [
1370
+ "MIT"
1371
+ ],
1372
+ "authors": [
1373
+ {
1374
+ "name": "Bernhard Schussek",
1375
+ "email": "bschussek@gmail.com"
1376
+ }
1377
+ ],
1378
+ "description": "Assertions to validate method input/output with nice error messages.",
1379
+ "keywords": [
1380
+ "assert",
1381
+ "check",
1382
+ "validate"
1383
+ ],
1384
+ "time": "2016-11-23T20:04:58+00:00"
1385
+ }
1386
+ ],
1387
+ "aliases": [],
1388
+ "minimum-stability": "stable",
1389
+ "stability-flags": [],
1390
+ "prefer-stable": false,
1391
+ "prefer-lowest": false,
1392
+ "platform": [],
1393
+ "platform-dev": []
1394
+ }
images/buzzurl.gif DELETED
Binary file
images/choix.gif DELETED
Binary file
images/evernote.png DELETED
Binary file
images/fc2.gif DELETED
Binary file
images/friendfeed.png DELETED
Binary file
images/google-buzz.png DELETED
Binary file
images/livedoor.gif DELETED
Binary file
images/newsing.gif DELETED
Binary file
images/nifty.gif DELETED
Binary file
images/twib.gif DELETED
Binary file
images/yahoo.gif DELETED
Binary file
images/yahoo_buzz.png DELETED
Binary file
modules/admin.php DELETED
@@ -1,1018 +0,0 @@
1
- <?php
2
- /*
3
- Copyright 2010 utahta (email : labs.ninxit@gmail.com)
4
-
5
- This program is free software; you can redistribute it and/or modify
6
- it under the terms of the GNU General Public License as published by
7
- the Free Software Foundation; either version 2 of the License, or
8
- (at your option) any later version.
9
-
10
- This program is distributed in the hope that it will be useful,
11
- but WITHOUT ANY WARRANTY; without even the implied warranty of
12
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
- GNU General Public License for more details.
14
-
15
- You should have received a copy of the GNU General Public License
16
- along with this program; if not, write to the Free Software
17
- Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
18
- */
19
-
20
- /**
21
- * use jquery in admin page
22
- */
23
- function wp_social_bookmarking_light_admin_print_scripts()
24
- {
25
- wp_enqueue_script('jquery');
26
- wp_enqueue_script('jquery-ui-core');
27
- wp_enqueue_script('jquery-ui-tabs');
28
- wp_enqueue_script('jquery-ui-sortable');
29
- wp_enqueue_script('jquery-ui-draggable');
30
- }
31
-
32
- /**
33
- * use jquery-ui in admin page
34
- */
35
- function wp_social_bookmarking_light_admin_print_styles()
36
- {
37
- wp_enqueue_style('jquery-ui-tabs', wp_social_bookmarking_light_url("libs/jquery/css/pepper-grinder/jquery-ui-1.8.6.custom.css"));
38
- }
39
-
40
- /**
41
- * admin header
42
- */
43
- function wp_social_bookmarking_light_admin_head()
44
- {
45
- ?>
46
- <style type="text/css">
47
- .wsbl_options{
48
- border: 1px solid #CCCCCC;
49
- background-color: #F8F8EB;
50
- vertical-align: top;
51
- margin: 0px 10px 10px 0px;
52
- padding: 0px;
53
- }
54
- .wsbl_options th{
55
- background-color: #E8E8DB;
56
- text-align: center;
57
- margin: 0px;
58
- padding: 3px;
59
- }
60
- .wsbl_options td{
61
- text-align: left;
62
- margin: 0px;
63
- padding: 3px;
64
- }
65
-
66
- #wsbl_sortable, #wsbl_draggable {
67
- list-style-type: none;
68
- margin: 0;
69
- padding: 5px;
70
- overflow: auto;
71
- width: 165px;
72
- height: 240px;
73
- float: left;
74
- border: 1px solid #999;
75
- background-color: #FFF;
76
- }
77
- #wsbl_sortable li, #wsbl_draggable li{
78
- width: 135px;
79
- height: 20px;
80
- font-size: 12px;
81
- margin: 0px auto;
82
- padding: 3px;
83
- border: 1px solid #999;
84
- background-color: #F8F8EB;
85
- cursor: pointer;
86
- }
87
- .wsbl_sortable_highlight {
88
- border: 1px dashed #333 !important;
89
- background-color: transparent !important;
90
- }
91
- .wsbl_txt_draggable{
92
- float:left;
93
- }
94
- .wsbl_img_draggable{
95
- margin-left: auto;
96
- margin-right: 0;
97
- text-align: right;
98
- display: none;
99
- }
100
- .wsbl_point_left{
101
- float: left;
102
- height : 240px ;
103
- margin: 0 20px;
104
- }
105
- .wsbl_point_left img{
106
- margin-top: 90px;
107
- }
108
- </style>
109
-
110
- <script type="text/javascript" charset="utf-8">
111
- //<![CDATA[
112
-
113
- /**
114
- * get services
115
- */
116
- function wsbl_get_service_codes()
117
- {
118
- var val = jQuery("#services_id").val();
119
- return jQuery.map(val.split(","), function(n, i){
120
- return jQuery.trim(n);
121
- });
122
- }
123
-
124
- /**
125
- * get tab id.
126
- */
127
- function wsbl_get_tab_ids(service_id)
128
- {
129
- if(service_id == 'facebook_general'){
130
- return ['facebook_like', 'facebook_share', 'facebook_send'];
131
- }
132
- if(service_id == 'mixi'){
133
- return ['mixi', 'mixi_like'];
134
- }
135
- return [service_id];
136
- }
137
-
138
- /**
139
- * has option
140
- */
141
- function wsbl_has_option(service_id)
142
- {
143
- var services = wsbl_get_service_codes();
144
- var ids = wsbl_get_tab_ids(service_id);
145
- for(var i in ids){
146
- if(jQuery.inArray(ids[i], services) >= 0){
147
- return true;
148
- }
149
- }
150
- return false;
151
- }
152
-
153
- /**
154
- * tab toggle
155
- */
156
- function wsbl_tab_toggle(service_id, is_simply)
157
- {
158
- var has_option = wsbl_has_option(service_id);
159
- var tab_id = service_id;
160
-
161
- var tab_id_settings = "#" + tab_id + "_settings";
162
- if(is_simply){
163
- has_option ? jQuery(tab_id_settings).show() : jQuery(tab_id_settings).hide();
164
- }
165
- else{
166
- has_option ? jQuery(tab_id_settings).slideDown() : jQuery(tab_id_settings).slideUp();
167
- }
168
- }
169
-
170
- /**
171
- * update services
172
- */
173
- function wsbl_update_services(is_simply)
174
- {
175
- var vals = "";
176
- var service = jQuery("#wsbl_sortable .wsbl_txt_draggable");
177
- service.each(function(){
178
- vals += vals == "" ? "" : ",";
179
- vals += jQuery(this).text();
180
- });
181
- jQuery("#services_id").val(vals);
182
-
183
- is_simply = is_simply || false;
184
- var services = ['mixi', 'twitter', 'hatena_button', 'facebook_general', 'facebook_like', 'facebook_share', 'facebook_send',
185
- 'gree', 'evernote', 'tumblr', 'atode', 'google_plus_one', 'line', 'pocket', 'pinterest'];
186
- for(var i in services){
187
- wsbl_tab_toggle(services[i], is_simply);
188
- }
189
- }
190
-
191
- /**
192
- * set sortable
193
- */
194
- function wsbl_update_sortable()
195
- {
196
- jQuery("#wsbl_sortable .wsbl_img_draggable").each(function(){
197
- var button = jQuery(this);
198
- button.css("display", "block"); // show delete button.
199
- var img = jQuery("img", button);
200
- img.mousedown(function(){
201
- var p = jQuery(this).parents("li");
202
- p.slideUp("fast", function(){
203
- p.remove();
204
- wsbl_update_services();
205
- });
206
- });
207
- img.attr('src', '<?php echo wp_social_bookmarking_light_images_url("close_button.png")?>');
208
- img.hover(
209
- function(){
210
- jQuery(this).attr('src', '<?php echo wp_social_bookmarking_light_images_url("close_button2.png")?>');
211
- },
212
- function(){
213
- jQuery(this).attr('src', '<?php echo wp_social_bookmarking_light_images_url("close_button.png")?>');
214
- }
215
- );
216
- });
217
- }
218
-
219
- // main
220
- jQuery(document).ready(function(){
221
- jQuery("#wsbl_sortable").sortable({
222
- placeholder: "wsbl_sortable_highlight",
223
- update:function(e, ui){
224
- wsbl_update_sortable();
225
- wsbl_update_services();
226
- }
227
- });
228
-
229
- jQuery("#wsbl_draggable li").draggable({
230
- connectToSortable:"#wsbl_sortable",
231
- helper:'clone',
232
- revert:"invalid"
233
- });
234
- jQuery("#wsbl_draggable, #wsbl_sortable").disableSelection();
235
-
236
- wsbl_update_sortable();
237
- wsbl_update_services(true);
238
-
239
- jQuery("#tabs").tabs();
240
- });
241
- //]]>
242
- </script>
243
-
244
- <?php
245
- }
246
-
247
- /**
248
- * admin page
249
- */
250
- function wp_social_bookmarking_light_options_page()
251
- {
252
- if( isset( $_POST['save'] ) ){
253
- $options = wp_social_bookmarking_light_save_options($_POST);
254
- echo '<div class="updated"><p><strong>'.__( 'Options saved.', WP_SOCIAL_BOOKMARKING_LIGHT_DOMAIN ).'</strong></p></div>';
255
- }
256
- else if( isset( $_POST['restore'] ) ){
257
- $options = wp_social_bookmarking_light_restore_default_options();
258
- echo '<div class="updated"><p><strong>'.__( 'Restore defaults.', WP_SOCIAL_BOOKMARKING_LIGHT_DOMAIN ).'</strong></p></div>';
259
- }
260
- else{
261
- $options = wp_social_bookmarking_light_options();
262
- }
263
- $class_methods = wp_social_bookmarking_light_get_class_methods();
264
- ?>
265
-
266
- <div class="wrap">
267
- <h2>WP Social Bookmarking Light</h2>
268
-
269
- <form method='POST' action="<?php echo $_SERVER['REQUEST_URI'] ?>">
270
-
271
- <div id="tabs">
272
- <ul>
273
- <li><a href="#tabs-1"><span><?php _e("General Settings") ?></span></a></li>
274
- <li><a href="#tabs-1_2"><span><?php _e("Styles") ?></span></a></li>
275
- <li><a href="#tabs-1_3"><span><?php _e("Donate") ?></span></a></li>
276
- <li id='mixi_settings'><a href="#tabs-2"><span><?php _el("Mixi") ?></span></a></li>
277
- <li id='twitter_settings'><a href="#tabs-3"><span><?php _el("Twitter") ?></span></a></li>
278
- <li id='hatena_button_settings'><a href="#tabs-4"><span><?php _el("Hatena") ?></span></a></li>
279
- <li id='facebook_general_settings'><a href="#tabs-15"><span><?php _el("FB") ?></span></a></li>
280
- <li id='facebook_like_settings'><a href="#tabs-5"><span><?php _el("FB Like") ?></span></a></li>
281
- <li id='facebook_share_settings'><a href="#tabs-6"><span><?php _el("FB Share") ?></span></a></li>
282
- <li id='facebook_send_settings'><a href="#tabs-14"><span><?php _el("FB Send") ?></span></a></li>
283
- <li id='gree_settings'><a href="#tabs-7"><span><?php _el("GREE") ?></span></a></li>
284
- <li id='evernote_settings'><a href="#tabs-8"><span><?php _el("Evernote") ?></span></a></li>
285
- <li id='tumblr_settings'><a href="#tabs-9"><span><?php _el("tumblr") ?></span></a></li>
286
- <li id='atode_settings'><a href="#tabs-10"><span><?php _el("atode") ?></span></a></li>
287
- <li id='google_plus_one_settings'><a href="#tabs-11"><span><?php _el("Google Plus One") ?></span></a></li>
288
- <li id='line_settings'><a href="#tabs-12"><span><?php _el("LINE") ?></span></a></li>
289
- <li id='pocket_settings'><a href="#tabs-13"><span><?php _el("Pocket") ?></span></a></li>
290
- <li id='pinterest_settings'><a href="#tabs-16"><span><?php _el("Pinterest") ?></span></a></li>
291
- </ul>
292
-
293
- <!-- General -->
294
- <div id="tabs-1">
295
- <table class='form-table'>
296
- <tr>
297
- <th scope="row"><?php _el('Position') ?>:</th>
298
- <td>
299
- <select name='position'>
300
- <option value='top' <?php if( $options['position'] == 'top' ) echo 'selected'; ?>>Top</option>
301
- <option value='bottom' <?php if( $options['position'] == 'bottom' ) echo 'selected'; ?>>Bottom</option>
302
- <option value='both' <?php if( $options['position'] == 'both' ) echo 'selected'; ?>>Both</option>
303
- <option value='none' <?php if( $options['position'] == 'none' ) echo 'selected'; ?>>None</option>
304
- </select>
305
- </td>
306
- </tr>
307
- <tr>
308
- <th scope="row"><?php _el('Singular') ?>:</th>
309
- <td>
310
- <select name='single_page'>
311
- <option value='true' <?php if( $options['single_page'] == true ) echo 'selected'; ?>>Yes</option>
312
- <option value='false' <?php if( $options['single_page'] == false ) echo 'selected'; ?>>No</option>
313
- </select>
314
- </td>
315
- </tr>
316
- <tr>
317
- <th scope="row"><?php _el('Page') ?>:</th>
318
- <td>
319
- <select name='is_page'>
320
- <option value='true' <?php if( $options['is_page'] == true ) echo 'selected'; ?>>Yes</option>
321
- <option value='false' <?php if( $options['is_page'] == false ) echo 'selected'; ?>>No</option>
322
- </select>
323
- </td>
324
- </tr>
325
- <tr>
326
- <th scope="row"><?php _el('Services') ?>: <br/> <span style="font-size:10px">(drag-and-drop)</span></th>
327
- <td>
328
- <input type="text" id='services_id' name='services' value="<?php echo $options['services'] ?>"size=120 style="font-size:12px;" onclick="this.select(0, this.value.length)" readonly/>
329
- <br />
330
- <br />
331
- <ul id="wsbl_sortable">
332
- <?php
333
- foreach( explode(",", $options['services']) as $service ){
334
- $service = trim($service);
335
- if($service != ''){
336
- if(in_array($service, $class_methods)){
337
- echo "<li>"
338
- ."<div class='wsbl_txt_draggable'>$service</div>"
339
- ."<div class='wsbl_img_draggable'><img src=''></div>"
340
- ."<br clear='both'>"
341
- ."</li>\n";
342
- }
343
- }
344
- }
345
- ?>
346
- </ul>
347
- <div class="wsbl_point_left"><img src='<?php echo wp_social_bookmarking_light_images_url("point_left.png")?>'></div>
348
- <ul id="wsbl_draggable">
349
- <?php
350
- foreach($class_methods as $method){
351
- echo "<li>"
352
- ."<div class='wsbl_txt_draggable'>$method</div>"
353
- ."<div class='wsbl_img_draggable'><img src=''></div>"
354
- ."<br clear='both'>"
355
- ."</li>\n";
356
- }
357
- ?>
358
- </ul>
359
- <br clear="both"/>
360
- </td>
361
- </tr>
362
- </table>
363
- </div>
364
-
365
- <!-- Styles -->
366
- <div id="tabs-1_2">
367
- <table class='form-table'>
368
- <tr>
369
- <th scope="row">Custom CSS:</th>
370
- <td>
371
- <textarea name="styles" rows="20" cols="80"><?php echo $options['styles'] ?></textarea>
372
- </td>
373
- </tr>
374
- </table>
375
- </div>
376
-
377
- <!-- Donate -->
378
- <div id="tabs-1_3">
379
- <table class='form-table'>
380
- <tr>
381
- <th></th>
382
- <td>If you want to support this project, please make a donation.</td>
383
- </tr>
384
- <tr>
385
- <th scope="row">Amazon:</th>
386
- <td>
387
- <p><b>To: labs.ninxit@gmail.com</b></p>
388
- <p>
389
- <a href="https://www.amazon.co.jp/gp/product/B005FOVUS2/" target="_blank">ギフト券 - Eメールタイプ</a>
390
- </p>
391
- <p>
392
- <a href="http://www.amazon.co.jp/registry/wishlist/234SVHP1HFGPR" target=_blank>Wishlist - 欲しいものリスト</a>
393
- </p>
394
- </td>
395
- </tr>
396
- <tr>
397
- <th scope="row">Bitcoin:</th>
398
- <td>
399
- <p>Please Donate To Bitcoin Address:</p>
400
- <p><b>16H1Q6ZaYC7jX7tBLK2Z3EV9HmQEk5QnBo</b></p>
401
- <img src="https://cloud.githubusercontent.com/assets/97572/16043856/b8e50f52-327c-11e6-8c0c-bc1f3603d9e0.png">
402
- </td>
403
- </tr>
404
- </table>
405
- </div>
406
-
407
- <!-- mixi -->
408
- <div id="tabs-2">
409
- <!-- General -->
410
- <strong>General</strong>
411
- <table class='form-table'>
412
- <tr>
413
- <th scope="row">Check Key:</th>
414
- <td>
415
- <input type="text" name='mixi_check_key' value="<?php echo $options['mixi']["check_key"] ?>" size=50 />
416
- </td>
417
- </tr>
418
- </table>
419
- <br/>
420
-
421
- <!-- mixi Check -->
422
- <strong>mixi Check</strong>
423
- <table class='form-table'>
424
- <tr>
425
- <th scope="row">Check Robots:</th>
426
- <td>
427
- <input type="text" name='mixi_check_robots' value="<?php echo $options['mixi']["check_robots"] ?>" size=50 />
428
- </td>
429
- </tr>
430
- <tr>
431
- <th scope="row">Layout:</th>
432
- <td>
433
- <select name='mixi_button'>
434
- <option value='button-1' <?php if( $options['mixi']['button'] == 'button-1' ) echo 'selected'; ?>>button-1</option>
435
- <option value='button-2' <?php if( $options['mixi']['button'] == 'button-2' ) echo 'selected'; ?>>button-2</option>
436
- <option value='button-3' <?php if( $options['mixi']['button'] == 'button-3' ) echo 'selected'; ?>>button-3</option>
437
- <option value='button-4' <?php if( $options['mixi']['button'] == 'button-4' ) echo 'selected'; ?>>button-4</option>
438
- </select>
439
- </td>
440
- </tr>
441
- </table>
442
- <br/>
443
-
444
- <!-- mixi Like -->
445
- <strong>mixi Like</strong>
446
- <table class='form-table'>
447
- <tr>
448
- <th scope="row">Width:</th>
449
- <td>
450
- <input type="text" name='mixi_like_width' value="<?php echo $options['mixi_like']["width"] ?>"/>
451
- </td>
452
- </tr>
453
- </table>
454
- </div>
455
-
456
- <!-- Twitter -->
457
- <div id="tabs-3">
458
- <table class='form-table'>
459
- <tr>
460
- <th scope="row">Via: <br> <span style="font-size:10px">(your twitter account)</span></th>
461
- <td>
462
- <input type="text" name='twitter_via' value="<?php echo $options['twitter']['via'] ?>" size=50 />
463
- </td>
464
- </tr>
465
- <tr>
466
- <th scope="row">Related: <br> <span style="font-size:10px">(related twitter account)</span></th>
467
- <td>
468
- <input type="text" name='twitter_related' value="<?php echo $options['twitter']['related'] ?>" size=50 />
469
- </td>
470
- </tr>
471
- <tr>
472
- <th scope="row">Hashtags: <br> <span style="font-size:10px"></span></th>
473
- <td>
474
- <input type="text" name='twitter_hashtags' value="<?php echo $options['twitter']['hashtags'] ?>" size=50 />
475
- </td>
476
- </tr>
477
- <tr>
478
- <th scope="row">Dnt: <br> <span style="font-size:10px">(Opt-out of tailoring Twitter)</span></th>
479
- <td>
480
- <select name='twitter_dnt'>
481
- <option value='true' <?php if( $options['twitter']['dnt'] == true ) echo 'selected'; ?>>Yes</option>
482
- <option value='false' <?php if( $options['twitter']['dnt'] == false ) echo 'selected'; ?>>No</option>
483
- </select>
484
- </td>
485
- </tr>
486
- <tr>
487
- <th scope="row">Language:</th>
488
- <td>
489
- <select name='twitter_lang'>
490
- <option value="" <?php if( $options['twitter']['lang'] == '' ) echo 'selected'; ?>>---</option>
491
- <option value="fr" <?php if( $options['twitter']['lang'] == 'fr' ) echo 'selected'; ?>>French - français</option>
492
- <option value="en" <?php if( $options['twitter']['lang'] == 'en' ) echo 'selected'; ?>>English</option>
493
- <option value="ar" <?php if( $options['twitter']['lang'] == 'ar' ) echo 'selected'; ?>>Arabic - العربية</option>
494
- <option value="ja" <?php if( $options['twitter']['lang'] == 'ja' ) echo 'selected'; ?>>Japanese - 日本語</option>
495
- <option value="es" <?php if( $options['twitter']['lang'] == 'es' ) echo 'selected'; ?>>Spanish - Español</option>
496
- <option value="de" <?php if( $options['twitter']['lang'] == 'de' ) echo 'selected'; ?>>German - Deutsch</option>
497
- <option value="it" <?php if( $options['twitter']['lang'] == 'it' ) echo 'selected'; ?>>Italian - Italiano</option>
498
- <option value="id" <?php if( $options['twitter']['lang'] == 'id' ) echo 'selected'; ?>>Indonesian - Bahasa Indonesia</option>
499
- <option value="pt" <?php if( $options['twitter']['lang'] == 'pt' ) echo 'selected'; ?>>Portuguese - Português</option>
500
- <option value="ko" <?php if( $options['twitter']['lang'] == 'ko' ) echo 'selected'; ?>>Korean - 한국어</option>
501
- <option value="tr" <?php if( $options['twitter']['lang'] == 'tr' ) echo 'selected'; ?>>Turkish - Türkçe</option>
502
- <option value="ru" <?php if( $options['twitter']['lang'] == 'ru' ) echo 'selected'; ?>>Russian - Русский</option>
503
- <option value="nl" <?php if( $options['twitter']['lang'] == 'nl' ) echo 'selected'; ?>>Dutch - Nederlands</option>
504
- <option value="fil" <?php if( $options['twitter']['lang'] == 'fil' ) echo 'selected'; ?>>Filipino - Filipino</option>
505
- <option value="msa" <?php if( $options['twitter']['lang'] == 'msa' ) echo 'selected'; ?>>Malay - Bahasa Melayu</option>
506
- <option value="zh-tw" <?php if( $options['twitter']['lang'] == 'zh-tw' ) echo 'selected'; ?>>Traditional Chinese - 繁體中文</option>
507
- <option value="zh-cn" <?php if( $options['twitter']['lang'] == 'zh-cn' ) echo 'selected'; ?>>Simplified Chinese - 简体中文</option>
508
- <option value="hi" <?php if( $options['twitter']['lang'] == 'hi' ) echo 'selected'; ?>>Hindi - हिन्दी</option>
509
- <option value="no" <?php if( $options['twitter']['lang'] == 'no' ) echo 'selected'; ?>>Norwegian - Norsk</option>
510
- <option value="sv" <?php if( $options['twitter']['lang'] == 'sv' ) echo 'selected'; ?>>Swedish - Svenska</option>
511
- <option value="fi" <?php if( $options['twitter']['lang'] == 'fi' ) echo 'selected'; ?>>Finnish - Suomi</option>
512
- <option value="da" <?php if( $options['twitter']['lang'] == 'da' ) echo 'selected'; ?>>Danish - Dansk</option>
513
- <option value="pl" <?php if( $options['twitter']['lang'] == 'pl' ) echo 'selected'; ?>>Polish - Polski</option>
514
- <option value="hu" <?php if( $options['twitter']['lang'] == 'hu' ) echo 'selected'; ?>>Hungarian - Magyar</option>
515
- <option value="fa" <?php if( $options['twitter']['lang'] == 'fa' ) echo 'selected'; ?>>Farsi - فارسی</option>
516
- <option value="he" <?php if( $options['twitter']['lang'] == 'he' ) echo 'selected'; ?>>Hebrew - עִבְרִית</option>
517
- <option value="ur" <?php if( $options['twitter']['lang'] == 'ur' ) echo 'selected'; ?>>Urdu - اردو</option>
518
- <option value="th" <?php if( $options['twitter']['lang'] == 'th' ) echo 'selected'; ?>>Thai - ภาษาไทย</option>
519
- </select>
520
- </td>
521
- </tr>
522
- <tr>
523
- <th scope="row">Size:</th>
524
- <td>
525
- <select name='twitter_size'>
526
- <option value='' <?php if( $options['twitter']['size'] === '' ) echo 'selected'; ?>>normal</option>
527
- <option value='large' <?php if( $options['twitter']['size'] === 'large' ) echo 'selected'; ?>>large</option>
528
- </select>
529
- </td>
530
- </tr>
531
- </table>
532
- </div>
533
-
534
- <!-- hatena button -->
535
- <div id="tabs-4">
536
- <table class='form-table'>
537
- <tr>
538
- <th scope="row">Layout:</th>
539
- <td>
540
- <select name='hatena_button_layout'>
541
- <option value='standard-balloon' <?php if( $options['hatena_button']['layout'] == 'standard-balloon' ) echo 'selected'; ?>>standard-balloon</option>
542
- <option value='standard-noballoon' <?php if( $options['hatena_button']['layout'] == 'standard-noballoon' ) echo 'selected'; ?>>standard-noballoon</option>
543
- <option value='standard' <?php if( $options['hatena_button']['layout'] == 'standard' ) echo 'selected'; ?>>standard</option>
544
- <option value='simple' <?php if( $options['hatena_button']['layout'] == 'simple' ) echo 'selected'; ?>>simple</option>
545
- <option value='simple-balloon' <?php if( $options['hatena_button']['layout'] == 'simple-balloon' ) echo 'selected'; ?>>simple-balloon</option>
546
- </select>
547
- </td>
548
- </tr>
549
- </table>
550
- </div>
551
-
552
- <!-- Facebook General -->
553
- <div id="tabs-15">
554
- <table class='form-table'>
555
- <tr>
556
- <th scope="row">Locale:</th>
557
- <td>
558
- <input type="text" name='facebook_locale' value="<?php echo $options['facebook']["locale"] ?>" /><br/>
559
- <span>en_US, ja_JP, fr_FR ...</span> see more <a href='http://developers.facebook.com/docs/internationalization/' target=_blank>facebook docs</a>
560
- </td>
561
- </tr>
562
- <tr>
563
- <th scope="row">Version:</th>
564
- <td>
565
- <select name='facebook_version'>
566
- <option value='html5' <?php if( $options['facebook']['version'] == 'html5' ) echo 'selected'; ?>>html5</option>
567
- <option value='xfbml' <?php if( $options['facebook']['version'] == 'xfbml' ) echo 'selected'; ?>>xfbml</option>
568
- <option value='iframe' <?php if( $options['facebook']['version'] == 'iframe' ) echo 'selected'; ?>>iframe</option>
569
- </select>
570
- </td>
571
- </tr>
572
- <tr>
573
- <th scope="row">Add fb-root:</th>
574
- <td>
575
- <select name='facebook_fb_root'>
576
- <option value='true' <?php if( $options['facebook']['fb_root'] == true ) echo 'selected'; ?>>Yes</option>
577
- <option value='false' <?php if( $options['facebook']['fb_root'] == false ) echo 'selected'; ?>>No</option>
578
- </select>
579
- </td>
580
- </tr>
581
- </table>
582
- </div>
583
-
584
- <!-- Facebook Like Button -->
585
- <div id="tabs-5">
586
- <!-- Like Button -->
587
- <table class='form-table'>
588
- <tr>
589
- <th scope="row">Layout:</th>
590
- <td>
591
- <select name='facebook_like_layout'>
592
- <option value='button' <?php if( $options['facebook_like']['layout'] == 'button' ) echo 'selected'; ?>>button</option>
593
- <option value='button_count' <?php if( $options['facebook_like']['layout'] == 'button_count' ) echo 'selected'; ?>>button_count</option>
594
- </select>
595
- </td>
596
- </tr>
597
- <tr>
598
- <th scope="row">Action:</th>
599
- <td>
600
- <select name='facebook_like_action'>
601
- <option value='like' <?php if( $options['facebook_like']['action'] == 'like' ) echo 'selected'; ?>>like</option>
602
- <option value='recommend' <?php if( $options['facebook_like']['action'] == 'recommend' ) echo 'selected'; ?>>recommend</option>
603
- </select>
604
- </td>
605
- </tr>
606
- <tr>
607
- <th scope="row">Share:</th>
608
- <td>
609
- <select name='facebook_like_share'>
610
- <option value='true' <?php if( $options['facebook_like']['share'] == true ) echo 'selected'; ?>>Yes</option>
611
- <option value='false' <?php if( $options['facebook_like']['share'] == false ) echo 'selected'; ?>>No</option>
612
- </select>
613
- </td>
614
- </tr>
615
- <tr>
616
- <th scope="row">Width:</th>
617
- <td>
618
- <input type="text" name='facebook_like_width' value="<?php echo $options['facebook_like']['width'] ?>" size=20 />
619
- </td>
620
- </tr>
621
- </table>
622
- </div>
623
-
624
- <!-- Facebook Share Button -->
625
- <div id="tabs-6">
626
- <table class='form-table'>
627
- <tr>
628
- <th scope="row">Layout:</th>
629
- <td>
630
- <select name='facebook_share_type'>
631
- <option value='button' <?php if( $options['facebook_share']['type'] == 'button' ) echo 'selected'; ?>>button</option>
632
- <option value='button_count' <?php if( $options['facebook_share']['type'] == 'button_count' ) echo 'selected'; ?>>button_count</option>
633
- </select>
634
- </td>
635
- </tr>
636
- <tr>
637
- <th scope="row">Width:</th>
638
- <td>
639
- <input type="text" name='facebook_share_width' value="<?php echo $options['facebook_share']['width'] ?>" size=20 />
640
- </td>
641
- </tr>
642
- </table>
643
- </div>
644
-
645
- <!-- Facebook Send Button -->
646
- <div id="tabs-14">
647
- <table class='form-table'>
648
- <tr>
649
- <th scope="row">Color Scheme:</th>
650
- <td>
651
- <select name='facebook_send_colorscheme'>
652
- <option value='light' <?php if( $options['facebook_send']['colorscheme'] == 'light' ) echo 'selected'; ?>>light</option>
653
- <option value='dark' <?php if( $options['facebook_send']['colorscheme'] == 'dark' ) echo 'selected'; ?>>dark</option>
654
- </select>
655
- </td>
656
- </tr>
657
- <tr>
658
- <th scope="row">Width:</th>
659
- <td>
660
- <input type="text" name='facebook_send_width' value="<?php echo $options['facebook_send']['width'] ?>" size=20 />
661
- </td>
662
- </tr>
663
- <tr>
664
- <th scope="row">Height:</th>
665
- <td>
666
- <input type="text" name='facebook_send_height' value="<?php echo $options['facebook_send']['height'] ?>" size=20 />
667
- </td>
668
- </tr>
669
- </table>
670
- </div>
671
-
672
- <!-- gree -->
673
- <div id="tabs-7">
674
- <table class='form-table'>
675
- <tr>
676
- <th scope="row">Button type:</th>
677
- <td>
678
- <select name='gree_button_type'>
679
- <option value='0' <?php if( $options['gree']['button_type'] == '0' ) echo 'selected'; ?>><?php _el("iine") ?></option>
680
- <option value='1' <?php if( $options['gree']['button_type'] == '1' ) echo 'selected'; ?>><?php _el("kininaru") ?></option>
681
- <option value='2' <?php if( $options['gree']['button_type'] == '2' ) echo 'selected'; ?>><?php _el("osusume") ?></option>
682
- <option value='3' <?php if( $options['gree']['button_type'] == '3' ) echo 'selected'; ?>><?php _el("share") ?></option>
683
- <option value='4' <?php if( $options['gree']['button_type'] == '4' ) echo 'selected'; ?>><?php _el("logo") ?></option>
684
- </select>
685
- </td>
686
- </tr>
687
- <tr>
688
- <th scope="row">Button size:</th>
689
- <td>
690
- <select name='gree_button_size'>
691
- <option value='16' <?php if( $options['gree']['button_size'] == '16' ) echo 'selected'; ?>>16</option>
692
- <option value='20' <?php if( $options['gree']['button_size'] == '20' ) echo 'selected'; ?>>20</option>
693
- </select>
694
- </td>
695
- </tr>
696
- </table>
697
- </div>
698
-
699
- <!-- evernote -->
700
- <div id="tabs-8">
701
- <table class='form-table'>
702
- <tr>
703
- <th scope="row">Button type:</th>
704
- <td>
705
- <select name='evernote_button_type' onchange='jQuery("#evernote_img").attr("src", "http://static.evernote.com/"+this.form.evernote_button_type.value+".png")'>
706
- <?php
707
- $button_types = array('article-clipper', 'article-clipper-remember', 'article-clipper-fr', 'article-clipper-es', 'article-clipper-jp', 'article-clipper-rus', 'site-mem-16');
708
- foreach($button_types as $button_type){
709
- ?><option value='<?php echo $button_type ?>' <?php if( $options['evernote']['button_type'] == $button_type ) echo 'selected'; ?>><?php echo $button_type?></option><?php
710
- }
711
- ?>
712
- </select>
713
- <img id='evernote_img' style="vertical-align:middle" src='http://static.evernote.com/<?php echo $options['evernote']['button_type'] ?>.png'>
714
- </td>
715
- </tr>
716
- </table>
717
- </div>
718
-
719
- <!-- tumblr -->
720
- <div id="tabs-9">
721
- <table class='form-table'>
722
- <tr>
723
- <th scope="row">Button type:</th>
724
- <td>
725
- <select name='tumblr_button_type' onchange='jQuery("#tumblr_img").attr("src", "http://platform.tumblr.com/v1/share_"+this.form.tumblr_button_type.value+".png")'>
726
- <?php
727
- $button_types = array('1', '2', '3', '4');
728
- foreach($button_types as $button_type){
729
- ?><option value='<?php echo $button_type ?>' <?php if( $options['tumblr']['button_type'] == $button_type ) echo 'selected'; ?>>share_<?php echo $button_type?></option><?php
730
- }
731
- ?>
732
- </select>
733
- <img id='tumblr_img' style="vertical-align:middle" src='http://platform.tumblr.com/v1/share_<?php echo $options['tumblr']['button_type'] ?>.png'>
734
- </td>
735
- </tr>
736
- </table>
737
- </div>
738
-
739
- <!-- atode -->
740
- <div id="tabs-10">
741
- <table class='form-table'>
742
- <tr>
743
- <th scope="row">Button type:</th>
744
- <td>
745
- <select name='atode_button_type' onchange='jQuery("#atode_img").attr("src", "http://atode.cc/img/"+this.form.atode_button_type.value+".gif")'>
746
- <?php
747
- $button_types = array('iconsja', 'iconnja', 'iconnen');
748
- foreach($button_types as $button_type){
749
- ?><option value='<?php echo $button_type ?>' <?php if( $options['atode']['button_type'] == $button_type ) echo 'selected'; ?>><?php echo $button_type?></option><?php
750
- }
751
- ?>
752
- </select>
753
- <img id='atode_img' style="vertical-align:middle" src='http://atode.cc/img/<?php echo $options['atode']['button_type'] ?>.gif'>
754
- </td>
755
- </tr>
756
- </table>
757
- </div>
758
-
759
- <!-- google +1 -->
760
- <div id="tabs-11">
761
- <table class='form-table'>
762
- <tr>
763
- <th scope="row">Button size:</th>
764
- <td>
765
- <select name='google_plus_one_button_size'>
766
- <option value='small' <?php if( $options['google_plus_one']['button_size'] == 'small' ) echo 'selected'; ?>>small</option>
767
- <option value='medium' <?php if( $options['google_plus_one']['button_size'] == 'medium' ) echo 'selected'; ?>>medium</option>
768
- <option value='standard' <?php if( $options['google_plus_one']['button_size'] == 'standard' ) echo 'selected'; ?>>standard</option>
769
- <option value='tall' <?php if( $options['google_plus_one']['button_size'] == 'tall' ) echo 'selected'; ?>>tall</option>
770
- </select>
771
- </td>
772
- </tr>
773
- <tr>
774
- <th scope="row">Language:</th>
775
- <td>
776
- <select name='google_plus_one_lang'>
777
- <?php
778
- $langs = array(
779
- "ar" => "Arabic",
780
- "ar" => "Arabic",
781
- "bg" => "Bulgarian",
782
- "ca" => "Catalan",
783
- "zh-CN" => "Chinese (Simplified)",
784
- "zh-TW" => "Chinese (Traditional)",
785
- "hr" => "Croatian",
786
- "cs" => "Czech",
787
- "da" => "Danish",
788
- "nl" => "Dutch",
789
- "en-US" => "English (US)",
790
- "en-GB" => "English (UK)",
791
- "et" => "Estonian",
792
- "fil" => "Filipino",
793
- "fi" => "Finnish",
794
- "fr" => "French",
795
- "de" => "German",
796
- "el" => "Greek",
797
- "iw" => "Hebrew",
798
- "hi" => "Hindi",
799
- "hu" => "Hungarian",
800
- "id" => "Indonesian",
801
- "it" => "Italian",
802
- "ja" => "Japanese",
803
- "ko" => "Korean",
804
- "lv" => "Latvian",
805
- "lt" => "Lithuanian",
806
- "ms" => "Malay",
807
- "no" => "Norwegian",
808
- "fa" => "Persian",
809
- "pl" => "Polish",
810
- "pt-BR" => "Portuguese (Brazil)",
811
- "pt-PT" => "Portuguese (Portugal)",
812
- "ro" => "Romanian",
813
- "ru" => "Russian",
814
- "sr" => "Serbian",
815
- "sv" => "Swedish",
816
- "sk" => "Slovak",
817
- "sl" => "Slovenian",
818
- "es" => "Spanish",
819
- "es-419" => "Spanish (Latin America)",
820
- "th" => "Thai",
821
- "tr" => "Turkish",
822
- "uk" => "Ukrainian",
823
- "vi" => "Vietnamese",
824
- );
825
- foreach($langs as $key => $val){
826
- $selected = $options['google_plus_one']['lang'] == $key ? "selected" : "";
827
- echo "<option $selected value='$key'>$val</option>\n";
828
- }
829
- ?>
830
- </select>
831
- </td>
832
- </tr>
833
- <tr>
834
- <th scope="row">Annotation:</th>
835
- <td>
836
- <select name='google_plus_one_annotation'>
837
- <option value='none' <?php if( $options['google_plus_one']['annotation'] == "none" ) echo 'selected'; ?>>none</option>
838
- <option value='bubble' <?php if( $options['google_plus_one']['annotation'] == "bubble" ) echo 'selected'; ?>>bubble</option>
839
- <option value='inline' <?php if( $options['google_plus_one']['annotation'] == "inline" ) echo 'selected'; ?>>inline</option>
840
- </select>
841
- </td>
842
- </tr>
843
- <tr>
844
- <th scope="row">Inline size:</th>
845
- <td>
846
- <input type="text" name='google_plus_one_inline_size' value="<?php echo $options['google_plus_one']["inline_size"] ?>" />
847
- </td>
848
- </tr>
849
- </table>
850
- </div>
851
-
852
- <!-- line -->
853
- <div id="tabs-12">
854
- <table class='form-table'>
855
- <tr>
856
- <th scope="row">Button type:</th>
857
- <td>
858
- <select name='line_button_type' onchange='jQuery("#line_img").attr("src", "<?php echo wp_social_bookmarking_light_images_url() ?>/"+this.form.line_button_type.value+".png")'>
859
- <?php
860
- $button_types = array('line20x20', 'line88x20');
861
- foreach($button_types as $button_type){
862
- ?><option value='<?php echo $button_type ?>' <?php if( $options['line']['button_type'] == $button_type ) echo 'selected'; ?>><?php echo $button_type?></option><?php
863
- }
864
- ?>
865
- </select>
866
- <img id='line_img' style="vertical-align:middle" src='<?php echo wp_social_bookmarking_light_images_url($options['line']['button_type']) ?>.png'>
867
- </td>
868
- </tr>
869
- <tr>
870
- <th scope="row">Protocol:</th>
871
- <td>
872
- <select name='line_protocol'>
873
- <option value='http' <?php if( $options['line']['protocol'] == 'http' ) echo 'selected'; ?>>http://</option>
874
- <option value='line' <?php if( $options['line']['protocol'] == 'line' ) echo 'selected'; ?>>line://</option>
875
- </select>
876
- </td>
877
- </tr>
878
- </table>
879
- </div>
880
-
881
- <!-- pocket -->
882
- <div id="tabs-13">
883
- <table class='form-table'>
884
- <tr>
885
- <th scope="row">Button type:</th>
886
- <td>
887
- <select name='pocket_button_type'>
888
- <option value='none' <?php if( $options['pocket']['button_type'] == 'none' ) echo 'selected'; ?>>none</option>
889
- <option value='horizontal' <?php if( $options['pocket']['button_type'] == 'horizontal' ) echo 'selected'; ?>>horizontal</option>
890
- <option value='vertical' <?php if( $options['pocket']['button_type'] == 'vertical' ) echo 'selected'; ?>>vertical</option>
891
- </select>
892
- </td>
893
- </tr>
894
- </table>
895
- </div>
896
-
897
- <!-- Pinterest -->
898
- <div id="tabs-16">
899
- <table class='form-table'>
900
- <tr>
901
- <th scope="row">Type: <br> <span style="font-size:10px"></span></th>
902
- <td>
903
- <select name='pinterest_type'>
904
- <option value='all' <?php if( $options['pinterest']['type'] === 'all' ) echo 'selected'; ?>>All</option>
905
- <option value='hover' <?php if( $options['pinterest']['type'] === 'hover' ) echo 'selected'; ?>>Hover</option>
906
- </select>
907
- </td>
908
- </tr>
909
- <tr>
910
- <th scope="row">Shape: <br> <span style="font-size:10px"></span></th>
911
- <td>
912
- <select name='pinterest_shape'>
913
- <option value='rect' <?php if( $options['pinterest']['shape'] === 'rect' ) echo 'selected'; ?>>Rectangle</option>
914
- <option value='round' <?php if( $options['pinterest']['shape'] === 'round' ) echo 'selected'; ?>>Round</option>
915
- </select>
916
- </td>
917
- </tr>
918
- <tr>
919
- <th scope="row">Size: <br> <span style="font-size:10px"></span></th>
920
- <td>
921
- <select name='pinterest_size'>
922
- <option value='large' <?php if( $options['pinterest']['size'] === 'large' ) echo 'selected'; ?>>Large</option>
923
- <option value='small' <?php if( $options['pinterest']['size'] === 'small' ) echo 'selected'; ?>>Small</option>
924
- </select>
925
- </td>
926
- </tr>
927
- <tr>
928
- <th scope="row">Color: <br> <span style="font-size:10px"></span></th>
929
- <td>
930
- <select name='pinterest_color'>
931
- <option value='red' <?php if( $options['pinterest']['color'] === 'red' ) echo 'selected'; ?>>Red</option>
932
- <option value='gray' <?php if( $options['pinterest']['color'] === 'gray' ) echo 'selected'; ?>>Gray</option>
933
- <option value='white' <?php if( $options['pinterest']['color'] === 'white' ) echo 'selected'; ?>>White</option>
934
- </select>
935
- </td>
936
- </tr>
937
- <tr>
938
- <th scope="row">Language:</th>
939
- <td>
940
- <select name='pinterest_lang'>
941
- <option value="en" <?php if( $options['pinterest']['lang'] == 'en' ) echo 'selected'; ?>>English</option>
942
- <option value="ja" <?php if( $options['pinterest']['lang'] == 'ja' ) echo 'selected'; ?>>Japanese - 日本語</option>
943
- </select>
944
- </td>
945
- </tr>
946
- </table>
947
- </div>
948
-
949
- </div>
950
- <p class="submit">
951
- <input class="button-primary" type="submit" name='save' value='<?php _e('Save Changes') ?>' />
952
- <input type="submit" name='restore' value='<?php _e('Restore defaults') ?>' />
953
- </p>
954
- </form>
955
-
956
- <table class='wsbl_options'>
957
- <tr><th><?php _el("Service Code") ?></th><th><?php _el("Explain") ?></th></tr>
958
- <tr><td>hatena</td><td>Hatena Bookmark</td></tr>
959
- <tr><td>hatena_users</td><td>Hatena Bookmark Users</td></tr>
960
- <tr><td>hatena_button</td><td>Hatena Bookmark Button</td></tr>
961
- <tr><td>twitter</td><td>Tweet Button - Twitter</td></tr>
962
- <tr><td>livedoor</td><td>Livedoor Clip</td></tr>
963
- <tr><td>livedoor_users</td><td>Livedoor Clip Users</td></tr>
964
- <tr><td>yahoo</td><td>Yahoo!JAPAN Bookmark</td></tr>
965
- <tr><td>yahoo_users</td><td>Yahoo!JAPAN Bookmark Users</td></tr>
966
- <tr><td>yahoo_buzz</td><td>Yahoo!Buzz</td></tr>
967
- <tr><td>nifty</td><td>@nifty Clip</td></tr>
968
- <tr><td>nifty_users</td><td>@nifty Clip Users</td></tr>
969
- <tr><td>tumblr</td><td>Tumblr</td></tr>
970
- <tr><td>fc2</td><td>FC2 Bookmark</td></tr>
971
- <tr><td>fc2_users</td><td>FC2 Bookmark Users</td></tr>
972
- <tr><td>newsing</td><td>newsing</td></tr>
973
- <tr><td>choix</td><td>Choix</td></tr>
974
- <tr><td>google</td><td>Google Bookmarks</td></tr>
975
- <tr><td>google_buzz</td><td>Google Buzz</td></tr>
976
- <tr><td>google_plus_one</td><td>Google +1</td></tr>
977
- <tr><td>delicious</td><td>Delicious</td></tr>
978
- <tr><td>digg</td><td>Digg</td></tr>
979
- <tr><td>friendfeed</td><td>FriendFeed</td></tr>
980
- <tr><td>facebook</td><td>Facebook Share</td></tr>
981
- <tr><td>facebook_like</td><td>Facebook Like Button</td></tr>
982
- <tr><td>facebook_share</td><td>Facebook Share Button</td></tr>
983
- <tr><td>facebook_send</td><td>Facebook Send Button</td></tr>
984
- <tr><td>reddit</td><td>reddit</td></tr>
985
- <tr><td>linkedin</td><td>LinkedIn</td></tr>
986
- <tr><td>evernote</td><td>Evernote</td></tr>
987
- <tr><td>instapaper</td><td>Instapaper</td></tr>
988
- <tr><td>stumbleupon</td><td>StumbleUpon</td></tr>
989
- <tr><td>mixi</td><td>mixi Check (require <a href="http://developer.mixi.co.jp/connect/mixi_plugin/mixi_check/mixicheck" onclick="window.open('http://developer.mixi.co.jp/connect/mixi_plugin/mixi_check/mixicheck'); return false;" >mixi check key</a>)</td></tr>
990
- <tr><td>mixi_like</td><td>mixi Like (require <a href="http://developer.mixi.co.jp/connect/mixi_plugin/mixi_check/mixicheck" onclick="window.open('http://developer.mixi.co.jp/connect/mixi_plugin/mixi_check/mixicheck'); return false;" >mixi check key</a>)</td></tr>
991
- <tr><td>gree</td><td>GREE Social Feedback</td></tr>
992
- <tr><td>atode</td><td>atode (toread)</td></tr>
993
- <tr><td>line</td><td>LINE Button</td></tr>
994
- <tr><td>pocket</td><td>Pocket Button</td></tr>
995
- <tr><td>pinterest</td><td>Pinterest Button</td></tr>
996
- </table>
997
- </div>
998
-
999
- <?php
1000
- }
1001
-
1002
- /**
1003
- * admin menu
1004
- */
1005
- function wp_social_bookmarking_light_admin_menu()
1006
- {
1007
- if( function_exists('add_options_page') ){
1008
- $page = add_options_page( 'WP Social Bookmarking Light',
1009
- 'WP Social Bookmarking Light',
1010
- 'manage_options',
1011
- __FILE__,
1012
- 'wp_social_bookmarking_light_options_page' );
1013
-
1014
- add_action('admin_print_styles-'.$page, 'wp_social_bookmarking_light_admin_print_styles');
1015
- add_action('admin_print_scripts-'.$page, 'wp_social_bookmarking_light_admin_print_scripts');
1016
- add_action('admin_head-'.$page, 'wp_social_bookmarking_light_admin_head');
1017
- }
1018
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
modules/content.php CHANGED
@@ -20,216 +20,69 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
20
  /**
21
  * html format
22
  *
 
 
23
  * @param string $services
24
  * @param string $link
25
  * @param string $title
26
  * @return string
27
  */
28
- function wp_social_bookmarking_light_output( $services, $link, $title )
29
  {
30
- $wp = new WpSocialBookmarkingLight( $link, $title, get_bloginfo('name') );
31
- $class_methods = wp_social_bookmarking_light_get_class_methods();
32
- $out = '';
33
- foreach( explode(",", $services) as $service ){
34
- $service = trim($service);
35
- if($service != ''){
36
- if(in_array($service, $class_methods)){
37
- $out .= '<div class="wsbl_'.$service.'">'.call_user_func( array( $wp, $service ) ).'</div>'; // WpSocialBookmarkingLight method
38
- }
39
- else{
40
- $out .= "<div>[`$service` not found]</div>";
41
- }
42
- }
43
- }
44
- if( $out == '' ){
45
- return $out;
46
- }
47
- return "<div class='wp_social_bookmarking_light'>{$out}</div><br class='wp_social_bookmarking_light_clear' />";
48
  }
49
 
50
  /**
51
  * echo html format
52
  *
 
 
53
  * @param string $services
54
  * @param string $link
55
  * @param string $title
56
  */
57
- function wp_social_bookmarking_light_output_e( $services=null, $link=null, $title=null )
58
  {
59
- if($services == null){
60
- $options = wp_social_bookmarking_light_options();
 
61
  $services = $options['services'];
62
  }
63
- echo wp_social_bookmarking_light_output( $services, $link, $title );
64
  }
65
 
66
  /**
67
  * add_action wp_head
 
68
  */
69
  function wp_social_bookmarking_light_wp_head()
70
  {
71
- ?>
72
- <!-- BEGIN: WP Social Bookmarking Light -->
73
- <?php
74
- // load options
75
- $options = wp_social_bookmarking_light_options();
76
- $services = explode(",", $options['services']);
77
-
78
- // mixi-check-robots
79
- if(in_array('mixi', $services)){
80
- ?>
81
- <meta name="mixi-check-robots" content="<?php echo $options['mixi']['check_robots'] ?>" />
82
- <?php
83
- }
84
-
85
- // load javascript
86
- // tumblr
87
- if(in_array('tumblr', $services)){
88
- ?><script type="text/javascript" src="http://platform.tumblr.com/v1/share.js"></script><?php
89
- }
90
- // facebook
91
- if(in_array('facebook_like', $services) ||
92
- in_array('facebook_share', $services) ||
93
- in_array('facebook_send', $services)){
94
- $version = $options['facebook']['version'];
95
- if($version == "html5" || $version == "xfbml"){
96
- $locale = $options['facebook']['locale'];
97
- $locale = ($locale == '' ? 'en_US' : $locale);
98
- ?>
99
- <script>(function(d, s, id) {
100
- var js, fjs = d.getElementsByTagName(s)[0];
101
- if (d.getElementById(id)) return;
102
- js = d.createElement(s); js.id = id;
103
- js.src = "//connect.facebook.net/<?php echo $locale ?>/sdk.js#xfbml=1&version=v2.7";
104
- fjs.parentNode.insertBefore(js, fjs);
105
- }(document, 'script', 'facebook-jssdk'));</script>
106
- <?php
107
- }
108
- }
109
-
110
- // css
111
- ?>
112
- <style type="text/css">
113
- <?php echo $options['styles'] ?>
114
- </style>
115
- <!-- END: WP Social Bookmarking Light -->
116
- <?php
117
- }
118
-
119
- /**
120
- * true if can be displayed, false if not
121
- *
122
- * @return bool
123
- */
124
- function wp_social_bookmarking_light_is_enabled()
125
- {
126
- if (is_feed() || is_404() || is_robots() || (function_exists( 'is_ktai' ) && is_ktai())) {
127
- return false;
128
- }
129
-
130
- $options = wp_social_bookmarking_light_options();
131
- if ($options['single_page'] && !is_singular()) {
132
- return false;
133
- }
134
- if (!$options['is_page'] && is_page()) {
135
- return false;
136
- }
137
-
138
- global $wp_current_filter;
139
- if (in_array('get_the_excerpt', (array)$wp_current_filter)) {
140
- return false;
141
- }
142
-
143
- if (get_query_var('amp', false) !== false) {
144
- return false;
145
- }
146
-
147
- return true;
148
  }
149
 
150
  /**
151
  * Add the Share Buttons to the content.
152
  * add_filter "the_content"
153
  *
 
 
154
  * @param string $content
155
  * @return string
156
  */
157
  function wp_social_bookmarking_light_the_content($content)
158
  {
159
- if (!wp_social_bookmarking_light_is_enabled()) {
160
- return $content;
161
- }
162
-
163
- $options = wp_social_bookmarking_light_options();
164
- $out = wp_social_bookmarking_light_output( $options['services'], get_permalink(), get_the_title() );
165
- if( $out == '' ){
166
- return $content;
167
- }
168
- if( $options['position'] == 'top' ){
169
- return "{$out}{$content}";
170
- }
171
- else if( $options['position'] == 'bottom' ){
172
- return "{$content}{$out}";
173
- }
174
- else if( $options['position'] == 'both'){
175
- return "{$out}{$content}{$out}";
176
- }
177
- return $content;
178
  }
179
 
180
  /**
181
  * wp_footer function
 
182
  */
183
  function wp_social_bookmarking_light_wp_footer()
184
  {
185
- ?>
186
- <!-- BEGIN: WP Social Bookmarking Light -->
187
- <?php
188
- // load options
189
- $options = wp_social_bookmarking_light_options();
190
- $services = explode(",", $options['services']);
191
-
192
- /*
193
- * load javascript
194
- */
195
- // twitter
196
- if (in_array('twitter', $services)) {
197
- echo "<script>!function(d,s,id){var js,fjs=d.getElementsByTagName(s)[0],p=/^http:/.test(d.location)?'http':'https';if(!d.getElementById(id)){js=d.createElement(s);js.id=id;js.src=p+'://platform.twitter.com/widgets.js';fjs.parentNode.insertBefore(js,fjs);}}(document, 'script', 'twitter-wjs');</script>\n";
198
- }
199
- // evernote
200
- if (in_array('evernote', $services)) {
201
- echo '<script type="text/javascript" src="http://static.evernote.com/noteit.js"></script>'."\n";
202
- }
203
- // Google +1
204
- if (in_array('google_plus_one', $services)) {
205
- $lang = $options['google_plus_one']['lang'];
206
- echo '<script src="https://apis.google.com/js/platform.js" async defer>{lang: "'.$lang.'"}</script>'."\n";
207
- }
208
- // pinterest
209
- if (in_array('pinterest', $services)) {
210
- if ($options['pinterest']['type'] === 'all') {
211
- $data_pin_hover = $data_pin_shape = $data_pin_color = $data_pin_lang = $data_pin_height = '';
212
- } else {
213
- $data_pin_hover = 'data-pin-hover="true"';
214
- $shape = $options['pinterest']['shape'];
215
- $data_pin_shape = $shape === 'round' ? 'data-pin-shape="round"' : '';
216
- $data_pin_color = 'data-pin-color="'.$options['pinterest']['color'];
217
- $data_pin_lang = 'data-pin-lang="'.$options['pinterest']['lang'];
218
- $data_pin_height = '';
219
- if ($options['pinterest']['size'] === 'large') {
220
- $data_pin_height = $shape === 'round' ? 'data-pin-height="32"' : 'data-pin-height="28"';
221
- }
222
- }
223
- echo '<script type="text/javascript" async defer '
224
- .$data_pin_shape.' '
225
- .$data_pin_color.' '
226
- .$data_pin_lang.' '
227
- .$data_pin_height.' '
228
- .$data_pin_hover.' '
229
- .'src="//assets.pinterest.com/js/pinit.js"></script>';
230
- }
231
-
232
- ?>
233
- <!-- END: WP Social Bookmarking Light -->
234
- <?php
235
  }
20
  /**
21
  * html format
22
  *
23
+ * @deprecated
24
+ *
25
  * @param string $services
26
  * @param string $link
27
  * @param string $title
28
  * @return string
29
  */
30
+ function wp_social_bookmarking_light_output($services, $link, $title)
31
  {
32
+ $plugin = new \WpSocialBookmarkingLight\Plugin();
33
+ return $plugin->getBuilder()->content($services, $link, $title);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
34
  }
35
 
36
  /**
37
  * echo html format
38
  *
39
+ * @deprecated
40
+ *
41
  * @param string $services
42
  * @param string $link
43
  * @param string $title
44
  */
45
+ function wp_social_bookmarking_light_output_e($services = null, $link = null, $title = null)
46
  {
47
+ if ($services == null) {
48
+ $o = new \WpSocialBookmarkingLight\Option();
49
+ $options = $o->getAll();
50
  $services = $options['services'];
51
  }
52
+ echo wp_social_bookmarking_light_output($services, $link, $title);
53
  }
54
 
55
  /**
56
  * add_action wp_head
57
+ * @deprecated
58
  */
59
  function wp_social_bookmarking_light_wp_head()
60
  {
61
+ $plugin = new \WpSocialBookmarkingLight\Plugin();
62
+ $plugin->head();
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
63
  }
64
 
65
  /**
66
  * Add the Share Buttons to the content.
67
  * add_filter "the_content"
68
  *
69
+ * @deprecated
70
+ *
71
  * @param string $content
72
  * @return string
73
  */
74
  function wp_social_bookmarking_light_the_content($content)
75
  {
76
+ $plugin = new \WpSocialBookmarkingLight\Plugin();
77
+ return $plugin->theContent($content);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
78
  }
79
 
80
  /**
81
  * wp_footer function
82
+ * @deprecated
83
  */
84
  function wp_social_bookmarking_light_wp_footer()
85
  {
86
+ $plugin = new \WpSocialBookmarkingLight\Plugin();
87
+ $plugin->footer();
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
88
  }
modules/options.php CHANGED
@@ -18,229 +18,39 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
18
  */
19
 
20
  /**
21
- * default option
22
- */
23
- function wp_social_bookmarking_light_default_options()
24
- {
25
- $styles = <<<EOT
26
- .wp_social_bookmarking_light{
27
- border: 0 !important;
28
- padding: 10px 0 20px 0 !important;
29
- margin: 0 !important;
30
- }
31
- .wp_social_bookmarking_light div{
32
- float: left !important;
33
- border: 0 !important;
34
- padding: 0 !important;
35
- margin: 0 5px 0px 0 !important;
36
- min-height: 30px !important;
37
- line-height: 18px !important;
38
- text-indent: 0 !important;
39
- }
40
- .wp_social_bookmarking_light img{
41
- border: 0 !important;
42
- padding: 0;
43
- margin: 0;
44
- vertical-align: top !important;
45
- }
46
- .wp_social_bookmarking_light_clear{
47
- clear: both !important;
48
- }
49
- #fb-root{
50
- display: none;
51
- }
52
- .wsbl_twitter{
53
- width: 100px;
54
- }
55
- .wsbl_facebook_like iframe{
56
- max-width: none !important;
57
- }
58
- .wsbl_pinterest a{
59
- border: 0px !important;
60
- }
61
- EOT;
62
-
63
- return array(
64
- "services" => "hatena_button,facebook_like,twitter",
65
- "styles" => $styles,
66
- "position" => "top",
67
- "single_page" => true,
68
- "is_page" => true,
69
- "mixi" => array(
70
- 'check_key' => '',
71
- 'check_robots' => 'noimage',
72
- 'button' => 'button-3'
73
- ),
74
- 'mixi_like' => array('width' => '65'),
75
- "twitter" => array(
76
- 'via' => "",
77
- 'lang' => "",
78
- 'size' => "",
79
- 'related' => "",
80
- 'hashtags' => "",
81
- "dnt" => false
82
- ),
83
- "hatena_button" => array('layout' => 'simple-balloon'),
84
- 'facebook' => array(
85
- 'locale' => 'en_US',
86
- 'version' => 'xfbml',
87
- 'fb_root' => true
88
- ),
89
- 'facebook_like' => array(
90
- 'layout' => 'button_count',
91
- 'action' => 'like',
92
- 'share' => false,
93
- 'width' => '100'
94
- ),
95
- 'facebook_share' => array(
96
- 'type' => 'button_count',
97
- 'width' => ''
98
- ),
99
- 'facebook_send' => array(
100
- 'colorscheme' => 'light',
101
- 'width' => '',
102
- 'height' => ''
103
- ),
104
- 'gree' => array(
105
- 'button_type' => '4',
106
- 'button_size' => '16'
107
- ),
108
- 'evernote' => array('button_type' => 'article-clipper'),
109
- 'tumblr' => array('button_type' => '1'),
110
- 'atode' => array('button_type' => 'iconsja'),
111
- 'google_plus_one' => array(
112
- 'button_size' => 'medium',
113
- 'lang' => 'en-US',
114
- 'annotation' => 'none',
115
- 'inline_size' => '250'
116
- ),
117
- 'line' => array(
118
- 'button_type' => 'line88x20',
119
- 'protocol' => 'http'
120
- ),
121
- 'pocket' => array('button_type' => 'none'),
122
- 'pinterest' => array(
123
- 'type' => 'all',
124
- 'shape' => 'rect',
125
- 'size' => 'large',
126
- 'color' => 'gray',
127
- 'lang' => 'en',
128
- ),
129
- );
130
- }
131
-
132
- /**
133
- * option
134
  */
135
  function wp_social_bookmarking_light_options()
136
  {
137
- $options = get_option("wp_social_bookmarking_light_options", array());
138
-
139
- // array merge recursive overwrite (1 depth)
140
- $default_options = wp_social_bookmarking_light_default_options();
141
- foreach( $default_options as $key => $val ){
142
- if(is_array($default_options[$key])){
143
- if(!array_key_exists($key, $options) || !is_array($options[$key])){
144
- $options[$key] = array();
145
- }
146
- $options[$key] = array_merge($default_options[$key], $options[$key]);
147
- }
148
- }
149
- return array_merge( wp_social_bookmarking_light_default_options(), $options );
150
  }
151
 
152
  /**
153
  * save options
154
  *
 
 
155
  * @param array $data ($_POST)
156
  * @return array
157
  */
158
  function wp_social_bookmarking_light_save_options($data)
159
  {
160
- foreach ($data as $key => $value) {
161
- $data[$key] = htmlspecialchars($value, ENT_QUOTES, 'UTF-8');
162
- }
163
- $options = array(
164
- "services" => $data["services"],
165
- "styles" => $data["styles"],
166
- "position" => $data["position"],
167
- "single_page" => $data["single_page"] == 'true',
168
- "is_page" => $data["is_page"] == 'true',
169
- "mixi" => array(
170
- 'check_key' => $data["mixi_check_key"],
171
- 'check_robots' => $data["mixi_check_robots"],
172
- 'button' => $data['mixi_button']
173
- ),
174
- 'mixi_like' => array(
175
- 'width' => $data["mixi_like_width"]
176
- ),
177
- "twitter" => array(
178
- 'via' => $data['twitter_via'],
179
- 'lang' => $data['twitter_lang'],
180
- 'size' => $data['twitter_size'],
181
- 'related' => $data['twitter_related'],
182
- 'hashtags' => $data['twitter_hashtags'],
183
- 'dnt' => $data['twitter_dnt'] == 'true',
184
- ),
185
- 'hatena_button' => array(
186
- 'layout' => $data['hatena_button_layout']
187
- ),
188
- 'facebook' => array(
189
- 'locale' => trim($data['facebook_locale']),
190
- 'version' => $data['facebook_version'],
191
- 'fb_root' => $data['facebook_fb_root'] == 'true'
192
- ),
193
- 'facebook_like' => array(
194
- 'layout' => $data['facebook_like_layout'],
195
- 'action' => $data['facebook_like_action'],
196
- 'share' => $data['facebook_like_share'] == 'true',
197
- 'width' => $data['facebook_like_width']
198
- ),
199
- 'facebook_share' => array(
200
- 'type' => $data['facebook_share_type'],
201
- 'width' => $data['facebook_share_width']
202
- ),
203
- 'facebook_send' => array(
204
- 'colorscheme' => $data['facebook_send_colorscheme'],
205
- 'width' => $data['facebook_send_width'],
206
- 'height' => $data['facebook_send_height']
207
- ),
208
- 'gree' => array(
209
- 'button_type' => $data['gree_button_type'],
210
- 'button_size' => $data['gree_button_size']
211
- ),
212
- 'evernote' => array('button_type' => $data['evernote_button_type']),
213
- 'tumblr' => array('button_type' => $data['tumblr_button_type']),
214
- 'atode' => array('button_type' => $data['atode_button_type']),
215
- 'google_plus_one' => array(
216
- 'button_size' => $data['google_plus_one_button_size'],
217
- 'lang' => $data['google_plus_one_lang'],
218
- 'annotation' => $data['google_plus_one_annotation'],
219
- 'inline_size' => $data['google_plus_one_inline_size']
220
- ),
221
- 'line' => array(
222
- 'button_type' => $data['line_button_type'],
223
- 'protocol' => $data['line_protocol']
224
- ),
225
- 'pocket' => array('button_type' => $data['pocket_button_type']),
226
- 'pinterest' => array(
227
- 'type' => $data['pinterest_type'],
228
- 'shape' => $data['pinterest_shape'],
229
- 'size' => $data['pinterest_size'],
230
- 'color' => $data['pinterest_color'],
231
- 'lang' => $data['pinterest_lang'],
232
- ),
233
- );
234
- update_option( 'wp_social_bookmarking_light_options', $options );
235
- return $options;
236
  }
237
 
238
  /**
 
 
239
  * restore default options
240
  */
241
  function wp_social_bookmarking_light_restore_default_options()
242
  {
243
- $options = wp_social_bookmarking_light_default_options();
244
- update_option( 'wp_social_bookmarking_light_options', $options );
245
- return $options;
246
  }
18
  */
19
 
20
  /**
21
+ * current options
22
+ *
23
+ * @deprecated
24
+ *
25
+ * @return array
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
26
  */
27
  function wp_social_bookmarking_light_options()
28
  {
29
+ $o = new \WpSocialBookmarkingLight\Option();
30
+ return $o->getAll();
 
 
 
 
 
 
 
 
 
 
 
31
  }
32
 
33
  /**
34
  * save options
35
  *
36
+ * @deprecated
37
+ *
38
  * @param array $data ($_POST)
39
  * @return array
40
  */
41
  function wp_social_bookmarking_light_save_options($data)
42
  {
43
+ $o = new \WpSocialBookmarkingLight\Option();
44
+ return $o->save($data);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
45
  }
46
 
47
  /**
48
+ * @deprecated
49
+ *
50
  * restore default options
51
  */
52
  function wp_social_bookmarking_light_restore_default_options()
53
  {
54
+ $o = new \WpSocialBookmarkingLight\Option();
55
+ return $o->restoreDefaultOption();
 
56
  }
modules/services.php DELETED
@@ -1,681 +0,0 @@
1
- <?php
2
- /*
3
- Copyright 2010 utahta (email : labs.ninxit@gmail.com)
4
-
5
- This program is free software; you can redistribute it and/or modify
6
- it under the terms of the GNU General Public License as published by
7
- the Free Software Foundation; either version 2 of the License, or
8
- (at your option) any later version.
9
-
10
- This program is distributed in the hope that it will be useful,
11
- but WITHOUT ANY WARRANTY; without even the implied warranty of
12
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
- GNU General Public License for more details.
14
-
15
- You should have received a copy of the GNU General Public License
16
- along with this program; if not, write to the Free Software
17
- Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
18
- */
19
-
20
- /**
21
- * Services
22
- */
23
- class WpSocialBookmarkingLight
24
- {
25
- var $url;
26
- var $title;
27
- var $encode_url;
28
- var $encode_title;
29
- var $encode_blogname;
30
-
31
- function __construct( $url, $title, $blogname )
32
- {
33
- $title = $this->to_utf8( $title );
34
- $this->blogname = $this->to_utf8( $blogname );
35
- $this->url = $url;
36
- $this->title = $title;
37
- $this->encode_url = rawurlencode( $url );
38
- $this->encode_title = rawurlencode( $title );
39
- $this->encode_blogname = rawurlencode( $this->blogname );
40
- }
41
-
42
- function to_utf8( $str )
43
- {
44
- $charset = get_option( 'blog_charset' );
45
- if( strcasecmp( $charset, 'UTF-8' ) != 0 && function_exists('mb_convert_encoding') ){
46
- $str = mb_convert_encoding( $str, 'UTF-8', $charset );
47
- }
48
- return $str;
49
- }
50
-
51
- function link_raw( $url ){
52
- return $url;
53
- }
54
- function link( $url, $alt, $icon, $width, $height, $blank=true ){
55
- $width = $width ? "width='$width'" : "";
56
- $height = $height ? "height='$height'" : "";
57
- $blank = $blank ? "target=_blank" : "";
58
- return "<a href='{$url}' title='{$alt}' rel=nofollow class='wp_social_bookmarking_light_a' $blank>"
59
- ."<img src='{$icon}' alt='{$alt}' title='{$alt}' $width $height class='wp_social_bookmarking_light_img' />"
60
- ."</a>";
61
- }
62
-
63
- /**
64
- * @brief Hatena Bookmark
65
- */
66
- function hatena()
67
- {
68
- $url = "//b.hatena.ne.jp/add?mode=confirm&url={$this->encode_url}&title={$this->encode_title}";
69
- $alt = __( "Bookmark this on Hatena Bookmark", WP_SOCIAL_BOOKMARKING_LIGHT_DOMAIN );
70
- $icon = wp_social_bookmarking_light_images_url("hatena.gif");
71
- return $this->link( $url, $alt, $icon, 16, 12 );
72
- }
73
- function hatena_users()
74
- {
75
- $url = "//b.hatena.ne.jp/entry/{$this->url}";
76
- $alt = sprintf( __("Hatena Bookmark - %s", WP_SOCIAL_BOOKMARKING_LIGHT_DOMAIN), $this->title );
77
- $icon = "//b.hatena.ne.jp/entry/image/{$this->url}";
78
- return $this->link( $url, $alt, $icon, null, null );
79
- }
80
- function hatena_button()
81
- {
82
- $options = wp_social_bookmarking_light_options();
83
- $url = "//b.hatena.ne.jp/entry/{$this->url}";
84
- $title = $this->title;
85
- $alt = __( "Bookmark this on Hatena Bookmark", WP_SOCIAL_BOOKMARKING_LIGHT_DOMAIN );
86
- return $this->link_raw('<a href="'.$url.'"'
87
- .' class="hatena-bookmark-button"'
88
- .' data-hatena-bookmark-title="'.$title.'"'
89
- .' data-hatena-bookmark-layout="'.$options['hatena_button']['layout'].'"'
90
- .' title="'.$alt.'">'
91
- .' <img src="//b.hatena.ne.jp/images/entry-button/button-only@2x.png"'
92
- .' alt="'.$alt.'" width="20" height="20" style="border: none;" /></a>'
93
- .'<script type="text/javascript" src="//b.hatena.ne.jp/js/bookmark_button.js" charset="utf-8" async="async"></script>');
94
- }
95
-
96
- /**
97
- * @brief twitter
98
- */
99
- function twitter()
100
- {
101
- $options = wp_social_bookmarking_light_options();
102
- $twitter = $options['twitter'];
103
- $data_url = $this->url;
104
- $data_text = $this->title;
105
- $data_via = $twitter['via'] !== '' ? ' data-via="'.$twitter['via'].'"' : '';
106
- $data_size = $twitter['size'] === 'large' ? ' data-size="large"' : '';
107
- $data_related = $twitter['related'] !== '' ? ' data-related="'.$twitter['related'].'"' : '';
108
- $data_hashtags = $twitter['hashtags'] !== '' ? ' data-hashtags="'.$twitter['hashtags'].'"' : '';
109
- $data_dnt = $twitter['dnt'] ? ' data-dnt="true"' : '';
110
- $data_lang = $twitter['lang'] !== '' ? ' data-lang="'.$twitter['lang'].'"' : '';
111
-
112
- return $this->link_raw(
113
- '<a href="https://twitter.com/share" class="twitter-share-button"'
114
- .' data-url="'.$data_url.'"'
115
- .' data-text="'.$data_text.'"'
116
- .$data_via
117
- .$data_size
118
- .$data_related
119
- .$data_hashtags
120
- .$data_dnt
121
- .$data_lang
122
- .'>Tweet</a>'
123
- );
124
- }
125
-
126
- /**
127
- * @brief Livedoor Clip
128
- */
129
- function livedoor()
130
- {
131
- $url = "http://clip.livedoor.com/redirect?link={$this->encode_url}&title={$this->encode_blogname}%20-%20{$this->encode_title}&ie=utf-8";
132
- $alt = __( "Bookmark this on Livedoor Clip", WP_SOCIAL_BOOKMARKING_LIGHT_DOMAIN );
133
- $icon = wp_social_bookmarking_light_images_url("livedoor.gif");
134
- return $this->link( $url, $alt, $icon, 16, 16 );
135
- }
136
- function livedoor_users()
137
- {
138
- $url = "http://clip.livedoor.com/page/{$this->url}";
139
- $alt = sprintf( __("Livedoor Clip - %s", WP_SOCIAL_BOOKMARKING_LIGHT_DOMAIN), $this->title );
140
- $icon = "http://image.clip.livedoor.com/counter/{$this->url}";
141
- return $this->link( $url, $alt, $icon, null, null );
142
- }
143
-
144
- /**
145
- * @brief Yahoo!JAPAN Bookmark
146
- */
147
- function yahoo()
148
- {
149
- $url = "http://bookmarks.yahoo.co.jp/bookmarklet/showpopup?t={$this->encode_title}&u={$this->encode_url}&ei=UTF-8";
150
- $alt = __( "Bookmark this on Yahoo Bookmark", WP_SOCIAL_BOOKMARKING_LIGHT_DOMAIN );
151
- $icon = wp_social_bookmarking_light_images_url("yahoo.gif");
152
- return $this->link( $url, $alt, $icon, 16, 16 );
153
- }
154
- function yahoo_users()
155
- {
156
- return $this->link_raw( "<script src='http://num.bookmarks.yahoo.co.jp/numimage.js?disptype=small'></script>" );
157
- }
158
-
159
- /**
160
- * @brief Yahoo Buzz
161
- */
162
- function yahoo_buzz()
163
- {
164
- $url = "http://buzz.yahoo.com/buzz?targetUrl={$this->encode_url}&headline={$this->encode_title}";
165
- $alt = __( "Buzz This", WP_SOCIAL_BOOKMARKING_LIGHT_DOMAIN );
166
- $icon = wp_social_bookmarking_light_images_url("yahoo_buzz.png");
167
- return $this->link( $url, $alt, $icon, 16, 16 );
168
- }
169
-
170
- /**
171
- * @brief nifty clip
172
- */
173
- function nifty()
174
- {
175
- $url = "http://clip.nifty.com/create?url={$this->encode_url}&title={$this->encode_title}";
176
- $alt = __( "Bookmark this on @nifty clip", WP_SOCIAL_BOOKMARKING_LIGHT_DOMAIN );
177
- $icon = wp_social_bookmarking_light_images_url("nifty.gif");
178
- return $this->link( $url, $alt, $icon, 16, 16 );
179
- }
180
- function nifty_users()
181
- {
182
- $url = '#';
183
- $alt = sprintf( __("@nifty clip - %s", WP_SOCIAL_BOOKMARKING_LIGHT_DOMAIN), $this->title );
184
- $icon = "http://api.clip.nifty.com/api/v1/image/counter/{$this->url}";
185
- return $this->link( $url, $alt, $icon, null, null );
186
- }
187
-
188
- /**
189
- * @brief Tumblr
190
- */
191
- function tumblr()
192
- {
193
- $options = wp_social_bookmarking_light_options();
194
- $type = $options['tumblr']['button_type'];
195
- $width = 'width:81px;';
196
- switch($type){
197
- case '1' : $width = 'width:81px;'; break;
198
- case '2' : $width = 'width:61px;'; break;
199
- case '3' : $width = 'width:129px;'; break;
200
- case '4' : $width = 'width:20px;'; break;
201
- }
202
- return $this->link_raw('<a href="http://www.tumblr.com/share?v=3&u='.$this->encode_url.'&t='.$this->encode_title.'" '
203
- .'title="'.__l("Share on Tumblr").'" '
204
- .'style="display:inline-block; text-indent:-9999px; overflow:hidden; '
205
- .$width.' height:20px; '
206
- .'background:url(\'http://platform.tumblr.com/v1/share_'.$type.'.png\')'
207
- .' top left no-repeat transparent;">'
208
- .__l("Share on Tumblr")
209
- .'</a>');
210
- }
211
-
212
- /**
213
- * @brief FC2 Bookmark
214
- */
215
- function fc2()
216
- {
217
- $url = "http://bookmark.fc2.com/user/post?url={$this->encode_url}&title={$this->encode_title}";
218
- $alt = __( "Bookmark this on FC2 Bookmark", WP_SOCIAL_BOOKMARKING_LIGHT_DOMAIN );
219
- $icon = wp_social_bookmarking_light_images_url("fc2.gif");
220
- return $this->link( $url, $alt, $icon, 16, 16 );
221
- }
222
- function fc2_users()
223
- {
224
- $url = "http://bookmark.fc2.com/search/detail?url={$this->encode_url}";
225
- $alt = sprintf( __("FC2 Bookmark - %s", WP_SOCIAL_BOOKMARKING_LIGHT_DOMAIN), $this->title );
226
- $icon = "http://bookmark.fc2.com/image/users/{$this->url}";
227
- return $this->link( $url, $alt, $icon, null, null );
228
- }
229
-
230
- /**
231
- * @brief newsing
232
- */
233
- function newsing()
234
- {
235
- $url = "http://newsing.jp/nbutton?url={$this->encode_url}&title={$this->encode_title}";
236
- $alt = __( "Newsing it!", WP_SOCIAL_BOOKMARKING_LIGHT_DOMAIN );
237
- $icon = wp_social_bookmarking_light_images_url("newsing.gif");
238
- return $this->link( $url, $alt, $icon, 16, 16 );
239
- }
240
-
241
- /**
242
- * @brief Choix
243
- */
244
- function choix()
245
- {
246
- $url = "http://www.choix.jp/bloglink/{$this->url}";
247
- $alt = __( "Choix it!", WP_SOCIAL_BOOKMARKING_LIGHT_DOMAIN );
248
- $icon = wp_social_bookmarking_light_images_url("choix.gif");
249
- return $this->link( $url, $alt, $icon, 16, 16 );
250
- }
251
-
252
- /**
253
- * @brief Google Bookmarks
254
- */
255
- function google()
256
- {
257
- $url = "http://www.google.com/bookmarks/mark?op=add&bkmk={$this->encode_url}&title={$this->encode_title}";
258
- $alt = __( "Bookmark this on Google Bookmarks", WP_SOCIAL_BOOKMARKING_LIGHT_DOMAIN );
259
- $icon = wp_social_bookmarking_light_images_url("google.png");
260
- return $this->link( $url, $alt, $icon, 16, 16 );
261
- }
262
-
263
- /**
264
- * @brief Google Buzz
265
- */
266
- function google_buzz()
267
- {
268
- $url = "http://www.google.com/buzz/post?url={$this->encode_url}&message={$this->encode_title}";
269
- $alt = __( "Post to Google Buzz", WP_SOCIAL_BOOKMARKING_LIGHT_DOMAIN );
270
- $icon = wp_social_bookmarking_light_images_url("google-buzz.png");
271
- return $this->link( $url, $alt, $icon, 16, 16 );
272
- }
273
-
274
- /**
275
- * @brief Google +1
276
- */
277
- function google_plus_one()
278
- {
279
- $options = wp_social_bookmarking_light_options();
280
- $button_size = $options['google_plus_one']['button_size'];
281
- $annotation = $options['google_plus_one']['annotation'];
282
- $width = $annotation == 'inline' ? 'width="'.$options['google_plus_one']['inline_size'].'"' : "";
283
- $raw = '<g:plusone size="'.$button_size.'" annotation="'.$annotation.'" href="'.$this->url.'" '.$width.'></g:plusone>';
284
- return $this->link_raw($raw);
285
- }
286
-
287
- /**
288
- * @brief Delicious
289
- */
290
- function delicious()
291
- {
292
- $url = "http://delicious.com/save?url={$this->encode_url}&title={$this->encode_title}";
293
- $alt = __( "Bookmark this on Delicious", WP_SOCIAL_BOOKMARKING_LIGHT_DOMAIN );
294
- $icon = wp_social_bookmarking_light_images_url("delicious.png");
295
- return $this->link( $url, $alt, $icon, 16, 16 );
296
- }
297
-
298
- /**
299
- * @brief Digg
300
- */
301
- function digg()
302
- {
303
- $url = "http://digg.com/submit?url={$this->encode_url}&title={$this->encode_title}";
304
- $alt = __( "Bookmark this on Digg", WP_SOCIAL_BOOKMARKING_LIGHT_DOMAIN );
305
- $icon = wp_social_bookmarking_light_images_url("digg.png");
306
- return $this->link( $url, $alt, $icon, 16, 16 );
307
- }
308
-
309
- /**
310
- * @brief Friend feed
311
- */
312
- function friendfeed()
313
- {
314
- $url = "http://friendfeed.com/?url={$this->encode_url}&title={$this->encode_title}";
315
- $alt = __( "Share on FriendFeed", WP_SOCIAL_BOOKMARKING_LIGHT_DOMAIN );
316
- $icon = wp_social_bookmarking_light_images_url("friendfeed.png");
317
- return $this->link( $url, $alt, $icon, 16, 16 );
318
- }
319
-
320
- /**
321
- * @brief Facebook
322
- */
323
- function facebook()
324
- {
325
- $url = "http://www.facebook.com/share.php?u={$this->encode_url}&t={$this->encode_title}";
326
- $alt = __( "Share on Facebook", WP_SOCIAL_BOOKMARKING_LIGHT_DOMAIN );
327
- $icon = wp_social_bookmarking_light_images_url("facebook.png");
328
- return $this->link( $url, $alt, $icon, 16, 16 );
329
- }
330
-
331
- /**
332
- * @brief Facebook Like Button
333
- */
334
- function facebook_like()
335
- {
336
- $options = wp_social_bookmarking_light_options();
337
- $layout = $options['facebook_like']['layout'];
338
- $action = $options['facebook_like']['action'];
339
- $share = $options['facebook_like']['share'] ? 'true' : 'false';
340
- $width = $options['facebook_like']['width'];
341
- $locale = $options['facebook']['locale'];
342
- $version = $options['facebook']['version'];
343
- $fb_root = $options['facebook']['fb_root'] ? '<div id="fb-root"></div>' : '';
344
-
345
- if($version == "html5"){
346
- return $this->link_raw( $fb_root
347
- .'<div class="fb-like" '
348
- .'data-href="'.$this->url.'" '
349
- .'data-layout="'.$layout.'" '
350
- .'data-action="'.$action.'" '
351
- .($width != "" ? 'data-width="'.$width.'" ' : '')
352
- .'data-share="'.$share.'" '
353
- .'data-show_faces="false" >'
354
- .'</div>');
355
- }
356
- elseif($version == "xfbml"){
357
- return $this->link_raw( $fb_root
358
- .'<fb:like '
359
- .'href="'.$this->url.'" '
360
- .'layout="'.$layout.'" '
361
- .'action="'.$action.'" '
362
- .($width != "" ? 'width="'.$width.'" ' : '')
363
- .'share="'.$share.'" '
364
- .'show_faces="false" >'
365
- .'</fb:like>');
366
- }
367
- else{
368
- return $this->link_raw('<iframe src="//www.facebook.com/plugins/like.php?href='.$this->encode_url
369
- .'&amp;layout='.$layout
370
- .'&amp;show_faces=false'
371
- .'&amp;width='.$width
372
- .'&amp;action='.$action
373
- .'&amp;share='.$share
374
- .($locale == '' ? '' : '&amp;locale='.$locale)
375
- .'&amp;height=35"'
376
- .' scrolling="no" frameborder="0"'
377
- .' style="border:none; overflow:hidden; width:'.$width.'px; height:35px;"'
378
- .' allowTransparency="true"></iframe>');
379
- }
380
-
381
- }
382
-
383
- /**
384
- * @brief Facebook Share
385
- */
386
- function facebook_share()
387
- {
388
- $options = wp_social_bookmarking_light_options();
389
- $url = $this->url;
390
- $version = $options['facebook']['version'];
391
- $fb_root = $options['facebook']['fb_root'] ? '<div id="fb-root"></div>' : '';
392
- $width = $options['facebook_share']['width'];
393
- $type = $options['facebook_share']['type'];
394
-
395
- if($version == "html5"){
396
- return $this->link_raw( $fb_root
397
- .'<div class="fb-share-button" '
398
- .'data-href="'.$url.'" '
399
- .($width != "" ? 'data-width="'.$width.'" ' : '')
400
- .'data-type="'.$type.'">'
401
- .'</div>');
402
- }else{
403
- return $this->link_raw( $fb_root
404
- .'<fb:share-button '
405
- .'href="'.$url.'" '
406
- .($width != "" ? 'width="'.$width.'" ' : '')
407
- .'type="'.$type.'" >'
408
- .'</fb:share-button>');
409
- }
410
- }
411
-
412
- /**
413
- * @brief Facebook Send
414
- */
415
- function facebook_send()
416
- {
417
- $options = wp_social_bookmarking_light_options();
418
- $url = $this->url;
419
- $version = $options['facebook']['version'];
420
- $fb_root = $options['facebook']['fb_root'] ? '<div id="fb-root"></div>' : '';
421
- $colorscheme = $options['facebook_send']['colorscheme'];
422
- $width = $options['facebook_send']['width'];
423
- $height = $options['facebook_send']['height'];
424
-
425
- if($version == "html5"){
426
- return $this->link_raw( $fb_root
427
- .'<div class="fb-send" '
428
- .'data-href="'.$url.'" '
429
- .($width != "" ? 'data-width="'.$width.'" ' : '')
430
- .($height != "" ? 'data-height="'.$height.'" ' : '')
431
- .'data-colorscheme="'.$colorscheme.'">'
432
- .'</div>');
433
- }else{
434
- return $this->link_raw( $fb_root
435
- .'<fb:send '
436
- .'href="'.$url.'" '
437
- .($width != "" ? 'width="'.$width.'" ' : '')
438
- .($height != "" ? 'height="'.$height.'" ' : '')
439
- .'colorscheme="'.$colorscheme.'" >'
440
- .'</fb:send>');
441
- }
442
- }
443
-
444
- /**
445
- * @brief reddit
446
- */
447
- function reddit()
448
- {
449
- $url = "http://www.reddit.com/submit?url={$this->encode_url}&title={$this->encode_title}";
450
- $alt = __( "Share on reddit", WP_SOCIAL_BOOKMARKING_LIGHT_DOMAIN );
451
- $icon = wp_social_bookmarking_light_images_url("reddit.png");
452
- return $this->link( $url, $alt, $icon, 16, 16 );
453
- }
454
-
455
- /**
456
- * @brief LinkedIn
457
- */
458
- function linkedin()
459
- {
460
- $url = "http://www.linkedin.com/shareArticle?mini=true&url={$this->encode_url}&title={$this->encode_title}";
461
- $alt = __( "Share on LinkedIn", WP_SOCIAL_BOOKMARKING_LIGHT_DOMAIN );
462
- $icon = wp_social_bookmarking_light_images_url("linkedin.png");
463
- return $this->link( $url, $alt, $icon, 16, 16 );
464
- }
465
-
466
- /**
467
- * @brief Evernote
468
- */
469
- function evernote()
470
- {
471
- $options = wp_social_bookmarking_light_options();
472
- $type = $options['evernote']['button_type'];
473
-
474
- return $this->link_raw('<a href="#" onclick="Evernote.doClip({ title:\''.$this->title.'\', url:\''.$this->url.'\' });return false;">'
475
- .'<img src="http://static.evernote.com/'.$type.'.png" />'
476
- .'</a>');
477
- }
478
-
479
- /**
480
- * @brief Instapaper
481
- */
482
- function instapaper()
483
- {
484
- return $this->link_raw('<iframe border="0" scrolling="no" width="78" height="17" allowtransparency="true" frameborder="0" '
485
- .'style="margin-bottom: -3px; z-index: 1338; border: 0px; background-color: transparent; overflow: hidden;" '
486
- .'src="http://www.instapaper.com/e2?url='.$this->encode_url.'&title='.$this->encode_title.'&description="'
487
- .'></iframe>');
488
- }
489
-
490
- /**
491
- * @brief StumbleUpon
492
- */
493
- function stumbleupon()
494
- {
495
- $url = "http://www.stumbleupon.com/submit?url={$this->encode_url}&title={$this->encode_title}";
496
- $alt = __( "Share on StumbleUpon", WP_SOCIAL_BOOKMARKING_LIGHT_DOMAIN );
497
- $icon = wp_social_bookmarking_light_images_url("stumbleupon.png");
498
- return $this->link( $url, $alt, $icon, 16, 16 );
499
- }
500
-
501
- /**
502
- * @brief mixi Check
503
- */
504
- function mixi()
505
- {
506
- $options = wp_social_bookmarking_light_options();
507
- $data_button = $options['mixi']['button'];
508
- $data_key = $options['mixi']['check_key'];
509
-
510
- return $this->link_raw( '<a href="http://mixi.jp/share.pl" class="mixi-check-button"'
511
- ." data-url='{$this->url}'"
512
- ." data-button='{$data_button}'"
513
- ." data-key='{$data_key}'>Check</a>"
514
- .'<script type="text/javascript" src="//static.mixi.jp/js/share.js"></script>' );
515
- }
516
-
517
- /**
518
- * @brief mixi Like
519
- */
520
- function mixi_like()
521
- {
522
- $options = wp_social_bookmarking_light_options();
523
- $data_key = $options['mixi']['check_key'];
524
- $width = $options['mixi_like']['width'];
525
-
526
- return $this->link_raw('<iframe src="http://plugins.mixi.jp/favorite.pl?href='.$this->encode_url.'&service_key='.$data_key.'&show_faces=false" '
527
- .'scrolling="no" '
528
- .'frameborder="0" '
529
- .'allowTransparency="true" '
530
- .'style="border:0; overflow:hidden; width:'.$width.'px; height:20px;"></iframe>');
531
- }
532
-
533
- /**
534
- * @brief GREE Social Feedback
535
- */
536
- function gree()
537
- {
538
- $options = wp_social_bookmarking_light_options();
539
- $url = $this->encode_url;
540
- $type = $options['gree']['button_type'];
541
- $size = $options['gree']['button_size'];
542
- switch($type){
543
- case '0': $btn_type = 'btn_iine'; break;
544
- case '1': $btn_type = 'btn_kininaru'; break;
545
- case '2': $btn_type = 'btn_osusume'; break;
546
- case '3': $btn_type = 'btn_share'; break;
547
- case '4': $btn_type = 'btn_logo'; break;
548
- default: $btn_type = 'btn_logo';
549
- }
550
- $alt = __( "Share on GREE", WP_SOCIAL_BOOKMARKING_LIGHT_DOMAIN );
551
- return $this->link_raw('<a href="http://gree.jp/?mode=share&act=write'
552
- .'&url='.$url
553
- .'&button_type='.$type
554
- .'&button_size='.$size
555
- .'&guid=ON" '
556
- .'title="'.$alt.'" target=_blank>'
557
- .'<img alt="'.$alt.'" title="'.$alt.'" '
558
- .'src="http://i.share.gree.jp/img/share/button/'.$btn_type.'_'.$size.'.png">'
559
- .'</a>');
560
- }
561
-
562
- /**
563
- * @brief atode
564
- */
565
- function atode()
566
- {
567
- $options = wp_social_bookmarking_light_options();
568
- $type = $options['atode']['button_type'];
569
- switch($type){
570
- case 'iconsja': return $this->link_raw('<a href=\'http://atode.cc/\' onclick=\'javascript:(function(){var s=document.createElement("scr"+"ipt");s.charset="UTF-8";s.language="javascr"+"ipt";s.type="text/javascr"+"ipt";var d=new Date;s.src="http://atode.cc/bjs.php?d="+d.getMilliseconds();document.body.appendChild(s)})();return false;\'><img src="http://atode.cc/img/iconsja.gif" alt="email this" border="0" align="absmiddle" width="16" height="16"></a>');
571
- case 'iconnja': return $this->link_raw('<a href=\'http://atode.cc/\' onclick=\'javascript:(function(){var s=document.createElement("scr"+"ipt");s.charset="UTF-8";s.language="javascr"+"ipt";s.type="text/javascr"+"ipt";var d=new Date;s.src="http://atode.cc/bjs.php?d="+d.getMilliseconds();document.body.appendChild(s)})();return false;\'><img src="http://atode.cc/img/iconnja.gif" alt="email this" border="0" align="absmiddle" width="66" height="20"></a>');
572
- case 'iconnen': return $this->link_raw('<a href=\'http://atode.cc/\' onclick=\'javascript:(function(){var s=document.createElement("scr"+"ipt");s.charset="UTF-8";s.language="javascr"+"ipt";s.type="text/javascr"+"ipt";var d=new Date;s.src="http://atode.cc/bjs.php?d="+d.getMilliseconds();document.body.appendChild(s)})();return false;\'><img src="http://atode.cc/img/iconnen.gif" alt="email this" border="0" align="absmiddle" width="66" height="20"></a>');
573
- }
574
- return '';
575
- }
576
-
577
- /**
578
- * @brief LINE
579
- */
580
- function line()
581
- {
582
- $options = wp_social_bookmarking_light_options();
583
- if($options['line']['button_type'] == "line88x20"){
584
- $icon = wp_social_bookmarking_light_images_url("line88x20.png");
585
- $width = 88;
586
- $height = 20;
587
- }
588
- else{
589
- $icon = wp_social_bookmarking_light_images_url("line20x20.png");
590
- $width = 20;
591
- $height = 20;
592
- }
593
-
594
- if ($options['line']['protocol'] === 'line') {
595
- $url = "line://msg/text/{$this->encode_title}%0D%0A{$this->encode_url}";
596
- } else {
597
- $url = "http://line.me/R/msg/text/?{$this->encode_title}%0D%0A{$this->encode_url}";
598
- }
599
- return $this->link($url, "LINEで送る", $icon, $width, $height, false);
600
- }
601
-
602
- /**
603
- * @brief Pocket
604
- */
605
- function pocket()
606
- {
607
- $options = wp_social_bookmarking_light_options();
608
- return $this->link_raw('<a href="https://getpocket.com/save" class="pocket-btn" data-lang="en" data-save-url="' . $this->url . '" data-pocket-count="' . $options['pocket']['button_type'] . '" data-pocket-align="left" >Pocket</a><script type="text/javascript">!function(d,i){if(!d.getElementById(i)){var j=d.createElement("script");j.id=i;j.src="https://widgets.getpocket.com/v1/j/btn.js?v=1";var w=d.getElementById(i);d.body.appendChild(j);}}(document,"pocket-btn-js");</script>');
609
- }
610
-
611
-
612
- /**
613
- * @brief pinterest
614
- */
615
- function pinterest()
616
- {
617
- $options = wp_social_bookmarking_light_options();
618
- $pinterest = $options['pinterest'];
619
- if ($pinterest['type'] === 'hover') {
620
- return '';
621
- }
622
- $data_pin_shape = '';
623
- $data_pin_color = '';
624
- $data_pin_lang = '';
625
-
626
- if ($pinterest['shape'] === 'round') {
627
- // 円形ボタン
628
- $data_pin_shape = 'data-pin-shape="round"';
629
-
630
- if ($pinterest['size'] === 'large') {
631
- $data_pin_height = 'data-pin-height="32"';
632
- $img_src = "//assets.pinterest.com/images/pidgets/pinit_fg_en_round_red_32.png";
633
- } else {
634
- $data_pin_height = '';
635
- $img_src = '//assets.pinterest.com/images/pidgets/pinit_fg_en_round_red_16.png';
636
- }
637
- } else {
638
- // 長方形ボタン
639
- $color = $pinterest['color'];
640
- $lang = $pinterest['lang'];
641
- $data_pin_color = 'data-pin-color="'.$color.'"';
642
- $data_pin_lang = 'data-pin-lang="'.$lang.'"';
643
-
644
- if ($pinterest['size'] === 'large') {
645
- $data_pin_height = 'data-pin-height="28"';
646
- $img_src = "//assets.pinterest.com/images/pidgets/pinit_fg_${lang}_rect_${color}_28.png";
647
- } else {
648
- $data_pin_height = '';
649
- $img_src = "//assets.pinterest.com/images/pidgets/pinit_fg_${lang}_round_${$color}_16.png";
650
- }
651
- }
652
-
653
- return $this->link_raw(
654
- '<a href="//jp.pinterest.com/pin/create/button/"'
655
- .' data-pin-do="buttonBookmark"'
656
- .' '.$data_pin_color
657
- .' '.$data_pin_lang
658
- .' '.$data_pin_shape
659
- .' '.$data_pin_height
660
- .'>'
661
- .'<img src="'.$img_src.'" /></a>'
662
- );
663
- }
664
- }
665
-
666
- /**
667
- * class method
668
- * @return array
669
- */
670
- function wp_social_bookmarking_light_get_class_methods(){
671
- $all_methods = get_class_methods('WpSocialBookmarkingLight');
672
- $except_methods = array('WpSocialBookmarkingLight', 'wpsocialbookmarkinglight', 'to_utf8', 'link_raw', 'link', 'get_methods');
673
- $methods = array();
674
- foreach($all_methods as $method){
675
- if(in_array($method, $except_methods)){
676
- continue;
677
- }
678
- $methods[] = $method;
679
- }
680
- return $methods;
681
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
{libs/jquery/css → public/css/admin}/pepper-grinder/images/ui-bg_diagonal-maze_20_6e4f1c_10x10.png RENAMED
File without changes
{libs/jquery/css → public/css/admin}/pepper-grinder/images/ui-bg_diagonal-maze_40_000000_10x10.png RENAMED
File without changes
{libs/jquery/css → public/css/admin}/pepper-grinder/images/ui-bg_fine-grain_10_eceadf_60x60.png RENAMED
File without changes
{libs/jquery/css → public/css/admin}/pepper-grinder/images/ui-bg_fine-grain_10_f8f7f6_60x60.png RENAMED
File without changes
{libs/jquery/css → public/css/admin}/pepper-grinder/images/ui-bg_fine-grain_15_eceadf_60x60.png RENAMED
File without changes
{libs/jquery/css → public/css/admin}/pepper-grinder/images/ui-bg_fine-grain_15_f7f3de_60x60.png RENAMED
File without changes
{libs/jquery/css → public/css/admin}/pepper-grinder/images/ui-bg_fine-grain_15_ffffff_60x60.png RENAMED
File without changes
{libs/jquery/css → public/css/admin}/pepper-grinder/images/ui-bg_fine-grain_65_654b24_60x60.png RENAMED
File without changes
{libs/jquery/css → public/css/admin}/pepper-grinder/images/ui-bg_fine-grain_68_b83400_60x60.png RENAMED
File without changes
{libs/jquery/css → public/css/admin}/pepper-grinder/images/ui-icons_222222_256x240.png RENAMED
File without changes
{libs/jquery/css → public/css/admin}/pepper-grinder/images/ui-icons_3572ac_256x240.png RENAMED
File without changes
{libs/jquery/css → public/css/admin}/pepper-grinder/images/ui-icons_8c291d_256x240.png RENAMED
File without changes
{libs/jquery/css → public/css/admin}/pepper-grinder/images/ui-icons_b83400_256x240.png RENAMED
File without changes
{libs/jquery/css → public/css/admin}/pepper-grinder/images/ui-icons_fbdb93_256x240.png RENAMED
File without changes
{libs/jquery/css → public/css/admin}/pepper-grinder/images/ui-icons_ffffff_256x240.png RENAMED
File without changes
{libs/jquery/css → public/css/admin}/pepper-grinder/jquery-ui-1.8.6.custom.css RENAMED
File without changes
public/css/admin/style.css ADDED
@@ -0,0 +1,61 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ .wsbl_options{
2
+ border: 1px solid #CCCCCC;
3
+ background-color: #F8F8EB;
4
+ vertical-align: top;
5
+ margin: 0px 10px 10px 0px;
6
+ padding: 0px;
7
+ }
8
+ .wsbl_options th{
9
+ background-color: #E8E8DB;
10
+ text-align: center;
11
+ margin: 0px;
12
+ padding: 3px;
13
+ }
14
+ .wsbl_options td{
15
+ text-align: left;
16
+ margin: 0px;
17
+ padding: 3px;
18
+ }
19
+
20
+ #wsbl_sortable, #wsbl_draggable {
21
+ list-style-type: none;
22
+ margin: 0;
23
+ padding: 5px;
24
+ overflow: auto;
25
+ width: 165px;
26
+ height: 240px;
27
+ float: left;
28
+ border: 1px solid #999;
29
+ background-color: #FFF;
30
+ }
31
+ #wsbl_sortable li, #wsbl_draggable li{
32
+ width: 135px;
33
+ height: 20px;
34
+ font-size: 12px;
35
+ margin: 0px auto;
36
+ padding: 3px;
37
+ border: 1px solid #999;
38
+ background-color: #F8F8EB;
39
+ cursor: pointer;
40
+ }
41
+ .wsbl_sortable_highlight {
42
+ border: 1px dashed #333 !important;
43
+ background-color: transparent !important;
44
+ }
45
+ .wsbl_txt_draggable{
46
+ float:left;
47
+ }
48
+ .wsbl_img_draggable{
49
+ margin-left: auto;
50
+ margin-right: 0;
51
+ text-align: right;
52
+ display: none;
53
+ }
54
+ .wsbl_point_left{
55
+ float: left;
56
+ height : 240px ;
57
+ margin: 0 20px;
58
+ }
59
+ .wsbl_point_left img{
60
+ margin-top: 90px;
61
+ }
{images → public/images}/bitcoin_donate.jpg RENAMED
File without changes
{images → public/images}/close_button.png RENAMED
File without changes
{images → public/images}/close_button2.png RENAMED
File without changes
{images → public/images}/delicious.png RENAMED
File without changes
{images → public/images}/digg.png RENAMED
File without changes
{images → public/images}/facebook.png RENAMED
File without changes
{images → public/images}/google.png RENAMED
File without changes
{images → public/images}/gree.png RENAMED
File without changes
{images → public/images}/hatena.gif RENAMED
File without changes
{images → public/images}/line20x20.png RENAMED
File without changes
{images → public/images}/line88x20.png RENAMED
File without changes
{images → public/images}/linkedin.png RENAMED
File without changes
{images → public/images}/point_left.png RENAMED
File without changes
{images → public/images}/reddit.png RENAMED
File without changes
{images → public/images}/stumbleupon.png RENAMED
File without changes
{images → public/images}/tumblr.png RENAMED
File without changes
readme.txt CHANGED
@@ -1,10 +1,10 @@
1
  === WP Social Bookmarking Light ===
2
  Contributors: utahvich
3
- Donate link: https://github.com/utahta/WP-Social-Bookmarking-Light/wiki/Donate
4
- Tags: links, social, bookmarks, bookmarking, Hatena, Livedoor Clip, @nifty clip, Twitter, Tumblr, FC2 Bookmark, newsing, Choix, Yahoo!JAPAN Bookmark, Yahoo!Buzz, Google Bookmark, Delicious, Digg, FriendFeed, Google Buzz, Facebook, reddit, LinkedIn, Evernote, Instapaper, StumbleUpon, mixi, gree, atode, toread, line, pocket, Pinterest
5
- Requires at least: 2.9.0
6
  Tested up to: 4.7
7
- Stable tag: 1.9.2
8
 
9
  This plugin inserts social share links at the top or bottom of each post.
10
 
@@ -15,28 +15,17 @@ This plugin inserts social share links at the top or bottom of each post.
15
  This is the list of used social sites:
16
 
17
  * Hatena
18
- * Facebook
19
  * Facebook Like Button
20
  * Facebook Share Button
21
  * Facebook Send Button
22
  * Twitter
23
- * Livedoor Clip
24
- * @nifty clip
25
  * Tumblr
26
- * FC2 Bookmark
27
- * newsing
28
- * Choix
29
- * Yahoo!JAPAN Bookmark
30
- * Yahoo!Buzz
31
  * Google Bookmark
32
- * Google Buzz
33
  * Google +1
34
  * Delicious
35
  * Digg
36
- * FriendFeed
37
  * reddit
38
  * LinkedIn
39
- * Evernote
40
  * Instapaper
41
  * StumbleUpon
42
  * mixi Check
@@ -55,12 +44,16 @@ This is the list of used social sites:
55
 
56
  == Screenshots ==
57
 
58
- 1. This is how the plugin looks in your blog.
59
- 2. This is how the plugin looks in settings.
60
- 3. This is how the plugin looks in manually inside your template.
61
 
62
  == Changelog ==
63
 
 
 
 
 
 
64
  = 1.9.2 =
65
  * Workaround: disable social buttons on AMP [#47](https://github.com/utahta/WP-Social-Bookmarking-Light/pull/47)
66
 
1
  === WP Social Bookmarking Light ===
2
  Contributors: utahvich
3
+ Donate link: https://gumroad.com/l/rWLrL
4
+ Tags: social, bookmarks, bookmarking, Hatena, Twitter, Facebook, Tumblr, Google Bookmark, Delicious, Digg, reddit, LinkedIn, Instapaper, StumbleUpon, mixi, gree, atode, toread, line, pocket, Pinterest
5
+ Requires at least: 4.0.0
6
  Tested up to: 4.7
7
+ Stable tag: 2.0.0
8
 
9
  This plugin inserts social share links at the top or bottom of each post.
10
 
15
  This is the list of used social sites:
16
 
17
  * Hatena
 
18
  * Facebook Like Button
19
  * Facebook Share Button
20
  * Facebook Send Button
21
  * Twitter
 
 
22
  * Tumblr
 
 
 
 
 
23
  * Google Bookmark
 
24
  * Google +1
25
  * Delicious
26
  * Digg
 
27
  * reddit
28
  * LinkedIn
 
29
  * Instapaper
30
  * StumbleUpon
31
  * mixi Check
44
 
45
  == Screenshots ==
46
 
47
+ 1. Embed share buttons in your website.
48
+ 2. Admin settings page.
 
49
 
50
  == Changelog ==
51
 
52
+ = 2.0.0 =
53
+ * Breaking Changes: Drop support for PHP 5.5 or lower, Require PHP 5.6 or higher [#46](https://github.com/utahta/WP-Social-Bookmarking-Light/pull/46)
54
+ * Breaking Changes: Remove terminated some services
55
+ * Important: If your WordPress site working on PHP 5.5 or lower, please continue to use v1.9.2 version
56
+
57
  = 1.9.2 =
58
  * Workaround: disable social buttons on AMP [#47](https://github.com/utahta/WP-Social-Bookmarking-Light/pull/47)
59
 
screenshot-1.png CHANGED
Binary file
screenshot-3.png DELETED
Binary file
src/WpSocialBookmarkingLight/Admin.php ADDED
@@ -0,0 +1,84 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace WpSocialBookmarkingLight;
4
+
5
+ use WpSocialBookmarkingLight\Util\Url;
6
+
7
+ /**
8
+ * Class Admin
9
+ * @package WpSocialBookmarkingLight
10
+ */
11
+ class Admin
12
+ {
13
+ /** @var Renderer */
14
+ private $renderer;
15
+
16
+ /** @var OptionInterface */
17
+ private $option;
18
+
19
+ /**
20
+ * Admin constructor.
21
+ * @param OptionInterface $option
22
+ */
23
+ public function __construct(OptionInterface $option)
24
+ {
25
+ $this->renderer = new Renderer();
26
+ $this->option = $option;
27
+ }
28
+
29
+ /**
30
+ * enqueue admin styles
31
+ */
32
+ public function enqueueStyles()
33
+ {
34
+ wp_enqueue_style('wsbl-admin', Url::css("admin/style.css"));
35
+ wp_enqueue_style('wsbl-admin-jquery-ui-tabs', Url::css("admin/pepper-grinder/jquery-ui-1.8.6.custom.css"));
36
+ }
37
+
38
+ /**
39
+ * enqueue admin scripts
40
+ */
41
+ public function enqueueScripts()
42
+ {
43
+ wp_enqueue_script('jquery');
44
+ wp_enqueue_script('jquery-ui-core');
45
+ wp_enqueue_script('jquery-ui-tabs');
46
+ wp_enqueue_script('jquery-ui-sortable');
47
+ wp_enqueue_script('jquery-ui-draggable');
48
+ }
49
+
50
+ /**
51
+ * admin header
52
+ *
53
+ * @return string
54
+ */
55
+ public function head()
56
+ {
57
+ return $this->renderer->render("@admin/head.html.twig", array());
58
+ }
59
+
60
+ /**
61
+ * admin page content
62
+ *
63
+ * @return string
64
+ */
65
+ public function page()
66
+ {
67
+ $context = array(
68
+ 'saved' => isset($_POST['save']),
69
+ 'restored' => isset($_POST['restore']),
70
+ 'request_uri' => $_SERVER['REQUEST_URI'],
71
+ 'service_types' => Service::getServiceTypes(),
72
+ );
73
+
74
+ if ($context['saved']) {
75
+ $context['option'] = $this->option->save($_POST);
76
+ } elseif ($context['restored']) {
77
+ $context['option'] = $this->option->restoreDefaultOption();
78
+ } else {
79
+ $context['option'] = $this->option->getAll();
80
+ }
81
+
82
+ return $this->renderer->render("@admin/page.html.twig", $context);
83
+ }
84
+ }
src/WpSocialBookmarkingLight/Builder.php ADDED
@@ -0,0 +1,109 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace WpSocialBookmarkingLight;
4
+
5
+ /**
6
+ * Class Builder
7
+ *
8
+ * build up html content
9
+ *
10
+ * @package WpSocialBookmarkingLight
11
+ */
12
+ class Builder
13
+ {
14
+ /** @var Renderer */
15
+ private $renderer;
16
+
17
+ /** @var Option */
18
+ private $option;
19
+
20
+ /**
21
+ * Builder constructor.
22
+ * @param Renderer $renderer
23
+ * @param OptionInterface $option
24
+ */
25
+ public function __construct(Renderer $renderer, OptionInterface $option)
26
+ {
27
+ $this->renderer = $renderer;
28
+ $this->option = $option;
29
+ }
30
+
31
+ /**
32
+ * wp_head HTML
33
+ *
34
+ * @return string
35
+ */
36
+ public function head()
37
+ {
38
+ // Load options
39
+ $options = $this->option->getAll();
40
+ $services = explode(",", $options['services']);
41
+
42
+ $context = array();
43
+ $context['mixi'] = in_array('mixi', $services) ? $options['mixi'] : null;
44
+ $context['tumblr'] = in_array('tumblr', $services);
45
+
46
+ $context['facebook'] = null;
47
+ if (in_array('facebook_like', $services) ||
48
+ in_array('facebook_share', $services) ||
49
+ in_array('facebook_send', $services)
50
+ ) {
51
+ $version = $options['facebook']['version'];
52
+ if ($version == "html5" || $version == "xfbml") {
53
+ $context['facebook'] = $options['facebook'];
54
+ }
55
+ }
56
+
57
+ $context['styles'] = $options['styles'];
58
+
59
+ return $this->renderer->render("@builder/head.html.twig", $context);
60
+ }
61
+
62
+ /**
63
+ * wp_footer HTML
64
+ *
65
+ * @return string
66
+ */
67
+ public function footer()
68
+ {
69
+ // Load options
70
+ $options = $this->option->getAll();
71
+ $services = explode(",", $options['services']);
72
+
73
+ $context = array();
74
+ $context['twitter'] = in_array('twitter', $services);
75
+ $context['google_plus_one'] = in_array('google_plus_one', $services) ? $options['google_plus_one'] : null;
76
+ $context['pinterest'] = in_array('pinterest', $services) ? $options['pinterest'] : null;
77
+
78
+ return $this->renderer->render("@builder/footer.html.twig", $context);
79
+ }
80
+
81
+ /**
82
+ * the_content HTML
83
+ *
84
+ * @param $services string separated like `aaa,bbb,ccc`
85
+ * @param $link string
86
+ * @param $title string
87
+ * @return string
88
+ */
89
+ public function content($services, $link, $title)
90
+ {
91
+ $wp = new Service($this->option, $link, $title);
92
+ $service_types = Service::getServiceTypes();
93
+
94
+ $context = array();
95
+ foreach (explode(",", $services) as $service) {
96
+ $service = trim($service);
97
+ if ($service === '') {
98
+ continue;
99
+ }
100
+
101
+ $context['services'][] = array(
102
+ 'name' => $service,
103
+ 'content' => in_array($service, $service_types) ? $wp->invokeService($service) : "[`$service` not found]"
104
+ );
105
+ }
106
+
107
+ return $this->renderer->render("@builder/content.html.twig", $context);
108
+ }
109
+ }
src/WpSocialBookmarkingLight/Option.php ADDED
@@ -0,0 +1,254 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace WpSocialBookmarkingLight;
4
+
5
+ /**
6
+ * Class Option
7
+ * @package WpSocialBookmarkingLight
8
+ */
9
+ class Option implements OptionInterface
10
+ {
11
+ /** @var array */
12
+ private $cache;
13
+
14
+ /**
15
+ * Option constructor.
16
+ */
17
+ public function __construct()
18
+ {
19
+ $this->cache = null;
20
+ }
21
+
22
+ /**
23
+ * Returns all option
24
+ *
25
+ * @return array
26
+ */
27
+ public function getAll()
28
+ {
29
+ if (is_array($this->cache)) {
30
+ return $this->cache;
31
+ }
32
+ $options = get_option("wp_social_bookmarking_light_options", array());
33
+
34
+ // array merge recursive overwrite (1 depth)
35
+ $defaultOption = $this->defaultOption();
36
+ foreach ($defaultOption as $key => $val) {
37
+ if (!is_array($defaultOption[$key])) {
38
+ continue;
39
+ }
40
+
41
+ if (!array_key_exists($key, $options) || !is_array($options[$key])) {
42
+ $options[$key] = array();
43
+ }
44
+ $options[$key] = array_merge($defaultOption[$key], $options[$key]);
45
+ }
46
+ $this->cache = array_merge($defaultOption, $options);
47
+
48
+ return $this->cache;
49
+ }
50
+
51
+ /**
52
+ * @param array $data
53
+ * @return array
54
+ */
55
+ public function save(array $data)
56
+ {
57
+ foreach ($data as $key => $value) {
58
+ $data[$key] = htmlspecialchars($value, ENT_QUOTES, 'UTF-8');
59
+ }
60
+ $options = array(
61
+ "services" => $data["services"],
62
+ "styles" => $data["styles"],
63
+ "position" => $data["position"],
64
+ "single_page" => $data["single_page"] == 'true',
65
+ "is_page" => $data["is_page"] == 'true',
66
+ "mixi" => array(
67
+ 'check_key' => $data["mixi_check_key"],
68
+ 'check_robots' => $data["mixi_check_robots"],
69
+ 'button' => $data['mixi_button']
70
+ ),
71
+ 'mixi_like' => array(
72
+ 'width' => $data["mixi_like_width"]
73
+ ),
74
+ "twitter" => array(
75
+ 'via' => $data['twitter_via'],
76
+ 'lang' => $data['twitter_lang'],
77
+ 'size' => $data['twitter_size'],
78
+ 'related' => $data['twitter_related'],
79
+ 'hashtags' => $data['twitter_hashtags'],
80
+ 'dnt' => $data['twitter_dnt'] == 'true',
81
+ ),
82
+ 'hatena_button' => array(
83
+ 'layout' => $data['hatena_button_layout']
84
+ ),
85
+ 'facebook' => array(
86
+ 'locale' => trim($data['facebook_locale']),
87
+ 'version' => $data['facebook_version'],
88
+ 'fb_root' => $data['facebook_fb_root'] == 'true'
89
+ ),
90
+ 'facebook_like' => array(
91
+ 'layout' => $data['facebook_like_layout'],
92
+ 'action' => $data['facebook_like_action'],
93
+ 'share' => $data['facebook_like_share'] == 'true',
94
+ 'width' => $data['facebook_like_width']
95
+ ),
96
+ 'facebook_share' => array(
97
+ 'type' => $data['facebook_share_type'],
98
+ 'width' => $data['facebook_share_width']
99
+ ),
100
+ 'facebook_send' => array(
101
+ 'colorscheme' => $data['facebook_send_colorscheme'],
102
+ 'width' => $data['facebook_send_width'],
103
+ 'height' => $data['facebook_send_height']
104
+ ),
105
+ 'gree' => array(
106
+ 'button_type' => $data['gree_button_type'],
107
+ 'button_size' => $data['gree_button_size']
108
+ ),
109
+ 'evernote' => array('button_type' => $data['evernote_button_type']),
110
+ 'tumblr' => array('button_type' => $data['tumblr_button_type']),
111
+ 'atode' => array('button_type' => $data['atode_button_type']),
112
+ 'google_plus_one' => array(
113
+ 'button_size' => $data['google_plus_one_button_size'],
114
+ 'lang' => $data['google_plus_one_lang'],
115
+ 'annotation' => $data['google_plus_one_annotation'],
116
+ 'inline_size' => $data['google_plus_one_inline_size']
117
+ ),
118
+ 'line' => array(
119
+ 'button_type' => $data['line_button_type'],
120
+ 'protocol' => $data['line_protocol']
121
+ ),
122
+ 'pocket' => array('button_type' => $data['pocket_button_type']),
123
+ 'pinterest' => array(
124
+ 'type' => $data['pinterest_type'],
125
+ 'shape' => $data['pinterest_shape'],
126
+ 'size' => $data['pinterest_size'],
127
+ 'color' => $data['pinterest_color'],
128
+ 'lang' => $data['pinterest_lang'],
129
+ ),
130
+ );
131
+ update_option('wp_social_bookmarking_light_options', $options);
132
+ return $options;
133
+ }
134
+
135
+ /**
136
+ * @return array
137
+ */
138
+ public function restoreDefaultOption()
139
+ {
140
+ $options = $this->defaultOption();
141
+ update_option('wp_social_bookmarking_light_options', $options);
142
+ return $options;
143
+ }
144
+
145
+ /**
146
+ * Returns default option
147
+ *
148
+ * @return array
149
+ */
150
+ private function defaultOption()
151
+ {
152
+ $styles = <<<CSS
153
+ .wp_social_bookmarking_light{
154
+ border: 0 !important;
155
+ padding: 10px 0 20px 0 !important;
156
+ margin: 0 !important;
157
+ }
158
+ .wp_social_bookmarking_light div{
159
+ float: left !important;
160
+ border: 0 !important;
161
+ padding: 0 !important;
162
+ margin: 0 5px 0px 0 !important;
163
+ min-height: 30px !important;
164
+ line-height: 18px !important;
165
+ text-indent: 0 !important;
166
+ }
167
+ .wp_social_bookmarking_light img{
168
+ border: 0 !important;
169
+ padding: 0;
170
+ margin: 0;
171
+ vertical-align: top !important;
172
+ }
173
+ .wp_social_bookmarking_light_clear{
174
+ clear: both !important;
175
+ }
176
+ #fb-root{
177
+ display: none;
178
+ }
179
+ .wsbl_facebook_like iframe{
180
+ max-width: none !important;
181
+ }
182
+ .wsbl_pinterest a{
183
+ border: 0px !important;
184
+ }
185
+ CSS;
186
+ return array(
187
+ "services" => "hatena_button,facebook_like,twitter,pocket",
188
+ "styles" => $styles,
189
+ "position" => "top",
190
+ "single_page" => true,
191
+ "is_page" => true,
192
+ "mixi" => array(
193
+ 'check_key' => '',
194
+ 'check_robots' => 'noimage',
195
+ 'button' => 'button-3'
196
+ ),
197
+ 'mixi_like' => array('width' => '65'),
198
+ "twitter" => array(
199
+ 'via' => "",
200
+ 'lang' => "",
201
+ 'size' => "",
202
+ 'related' => "",
203
+ 'hashtags' => "",
204
+ "dnt" => false
205
+ ),
206
+ "hatena_button" => array('layout' => 'simple-balloon'),
207
+ 'facebook' => array(
208
+ 'locale' => 'en_US',
209
+ 'version' => 'xfbml',
210
+ 'fb_root' => true
211
+ ),
212
+ 'facebook_like' => array(
213
+ 'layout' => 'button_count',
214
+ 'action' => 'like',
215
+ 'share' => false,
216
+ 'width' => '100'
217
+ ),
218
+ 'facebook_share' => array(
219
+ 'type' => 'button_count',
220
+ 'width' => ''
221
+ ),
222
+ 'facebook_send' => array(
223
+ 'colorscheme' => 'light',
224
+ 'width' => '',
225
+ 'height' => ''
226
+ ),
227
+ 'gree' => array(
228
+ 'button_type' => '4',
229
+ 'button_size' => '16'
230
+ ),
231
+ 'evernote' => array('button_type' => 'article-clipper'),
232
+ 'tumblr' => array('button_type' => '1'),
233
+ 'atode' => array('button_type' => 'iconsja'),
234
+ 'google_plus_one' => array(
235
+ 'button_size' => 'medium',
236
+ 'lang' => 'en-US',
237
+ 'annotation' => 'none',
238
+ 'inline_size' => '250'
239
+ ),
240
+ 'line' => array(
241
+ 'button_type' => 'line88x20',
242
+ 'protocol' => 'http'
243
+ ),
244
+ 'pocket' => array('button_type' => 'none'),
245
+ 'pinterest' => array(
246
+ 'type' => 'all',
247
+ 'shape' => 'rect',
248
+ 'size' => 'small',
249
+ 'color' => 'gray',
250
+ 'lang' => 'en',
251
+ ),
252
+ );
253
+ }
254
+ }
src/WpSocialBookmarkingLight/OptionInterface.php ADDED
@@ -0,0 +1,26 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace WpSocialBookmarkingLight;
4
+
5
+ /**
6
+ * Interface OptionInterface
7
+ * @package WpSocialBookmarkingLight
8
+ */
9
+ interface OptionInterface
10
+ {
11
+ /**
12
+ * @return array
13
+ */
14
+ public function getAll();
15
+
16
+ /**
17
+ * @param array $data
18
+ * @return array
19
+ */
20
+ public function save(array $data);
21
+
22
+ /**
23
+ * @return array
24
+ */
25
+ public function restoreDefaultOption();
26
+ }
src/WpSocialBookmarkingLight/Plugin.php ADDED
@@ -0,0 +1,148 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace WpSocialBookmarkingLight;
4
+
5
+ /**
6
+ * Class Plugin
7
+ * WordPress Plugin
8
+ * @package WpSocialBookmarkingLight
9
+ */
10
+ class Plugin
11
+ {
12
+ /** @var Option */
13
+ private $option;
14
+
15
+ /** @var Builder */
16
+ private $builder;
17
+
18
+ /** @var Admin */
19
+ private $admin;
20
+
21
+ /**
22
+ * Plugin constructor.
23
+ */
24
+ public function __construct()
25
+ {
26
+ $renderer = new Renderer();
27
+ $this->option = new Option();
28
+ $this->builder = new Builder($renderer, $this->option);
29
+ $this->admin = new Admin($this->option);
30
+ }
31
+
32
+ /**
33
+ * @return Builder
34
+ */
35
+ public function getBuilder()
36
+ {
37
+ return $this->builder;
38
+ }
39
+
40
+ /**
41
+ * Initialize wp actions
42
+ */
43
+ public function init()
44
+ {
45
+ add_action('init', function() {
46
+ add_action('wp_head', array($this, 'head'));
47
+ add_action('wp_footer', array($this, 'footer'));
48
+ add_filter('the_content', array($this, 'theContent'));
49
+ add_action('admin_menu', array($this, 'adminMenu'));
50
+ });
51
+ }
52
+
53
+ /**
54
+ * called by wp_head action
55
+ */
56
+ public function head()
57
+ {
58
+ echo $this->builder->head();
59
+ }
60
+
61
+ /**
62
+ * called by wp_footer action
63
+ */
64
+ public function footer()
65
+ {
66
+ echo $this->builder->footer();
67
+ }
68
+
69
+ /**
70
+ * Embed the share buttons to the content.
71
+ * called by the_content filter
72
+ *
73
+ * @param string $content
74
+ * @return string
75
+ */
76
+ public function theContent($content)
77
+ {
78
+ if (!$this->isEnabled()) {
79
+ return $content;
80
+ }
81
+
82
+ $options = $this->option->getAll();
83
+ $out = $this->builder->content($options['services'], get_permalink(), get_the_title());
84
+ if ($out == '') {
85
+ return $content;
86
+ }
87
+ if ($options['position'] === 'top') {
88
+ return "{$out}{$content}";
89
+ } elseif ($options['position'] === 'bottom') {
90
+ return "{$content}{$out}";
91
+ } elseif ($options['position'] === 'both') {
92
+ return "{$out}{$content}{$out}";
93
+ }
94
+ return $content;
95
+ }
96
+
97
+ /**
98
+ * called by admin_menu action
99
+ */
100
+ public function adminMenu()
101
+ {
102
+ if( function_exists('add_options_page') ){
103
+ $page = add_options_page('WP Social Bookmarking Light',
104
+ 'WP Social Bookmarking Light',
105
+ 'manage_options',
106
+ __FILE__,
107
+ function () {
108
+ echo $this->admin->page();
109
+ });
110
+ add_action('admin_print_styles-' . $page, array($this->admin, 'enqueueStyles'));
111
+ add_action('admin_print_scripts-' . $page, array($this->admin, 'enqueueScripts'));
112
+ add_action('admin_head-' . $page, function () {
113
+ echo $this->admin->head();
114
+ });
115
+ }
116
+ }
117
+
118
+ /**
119
+ * true if can be displayed, false if not
120
+ *
121
+ * @return bool
122
+ */
123
+ private function isEnabled()
124
+ {
125
+ if (is_feed() || is_404() || is_robots() || (function_exists('is_ktai') && is_ktai())) {
126
+ return false;
127
+ }
128
+
129
+ $options = $this->option->getAll();
130
+ if ($options['single_page'] && !is_singular()) {
131
+ return false;
132
+ }
133
+ if (!$options['is_page'] && is_page()) {
134
+ return false;
135
+ }
136
+
137
+ global $wp_current_filter;
138
+ if (in_array('get_the_excerpt', (array)$wp_current_filter)) {
139
+ return false;
140
+ }
141
+
142
+ if (get_query_var('amp', false) !== false) {
143
+ return false;
144
+ }
145
+
146
+ return true;
147
+ }
148
+ }
src/WpSocialBookmarkingLight/Renderer.php ADDED
@@ -0,0 +1,48 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace WpSocialBookmarkingLight;
4
+
5
+ use WpSocialBookmarkingLight\Util\Text;
6
+ use WpSocialBookmarkingLight\Util\Url;
7
+
8
+ /**
9
+ * Class Renderer
10
+ * @package WpSocialBookmarkingLight
11
+ */
12
+ class Renderer
13
+ {
14
+ /** @var \Twig_Environment */
15
+ private $twig;
16
+
17
+ /**
18
+ * Renderer constructor.
19
+ */
20
+ public function __construct()
21
+ {
22
+ $loader = new \Twig_Loader_Filesystem();
23
+ $loader->addPath(WP_SOCIAL_BOOKMARKING_LIGHT_DIR . "/src/WpSocialBookmarkingLight/Resources/views/Builder", "builder");
24
+ $loader->addPath(WP_SOCIAL_BOOKMARKING_LIGHT_DIR . "/src/WpSocialBookmarkingLight/Resources/views/Admin", "admin");
25
+ $this->twig = new \Twig_Environment($loader, array('debug' => false));
26
+
27
+ $this->twig->addFilter(new \Twig_SimpleFilter("__", function ($val) {
28
+ return Text::locale($val);
29
+ }));
30
+
31
+ $this->twig->addFilter(new \Twig_SimpleFilter("images", function ($path) {
32
+ return Url::images($path);
33
+ }));
34
+ }
35
+
36
+ /**
37
+ * Render template
38
+ *
39
+ * @param $name string
40
+ * @param array $context
41
+ * @return string
42
+ */
43
+ public function render($name, array $context)
44
+ {
45
+ $template = $this->twig->load($name);
46
+ return $template->render($context);
47
+ }
48
+ }
src/WpSocialBookmarkingLight/Resources/views/Admin/head.html.twig ADDED
@@ -0,0 +1,132 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <script type="text/javascript" charset="utf-8">
2
+ //<![CDATA[
3
+
4
+ /**
5
+ * get services
6
+ */
7
+ function wsbl_get_service_codes()
8
+ {
9
+ var val = jQuery("#services_id").val();
10
+ return jQuery.map(val.split(","), function(n, i){
11
+ return jQuery.trim(n);
12
+ });
13
+ }
14
+
15
+ /**
16
+ * get tab id.
17
+ */
18
+ function wsbl_get_tab_ids(service_id)
19
+ {
20
+ if(service_id === 'facebook_general'){
21
+ return ['facebook_like', 'facebook_share', 'facebook_send'];
22
+ }
23
+ if(service_id === 'mixi'){
24
+ return ['mixi', 'mixi_like'];
25
+ }
26
+ return [service_id];
27
+ }
28
+
29
+ /**
30
+ * has option
31
+ */
32
+ function wsbl_has_option(service_id)
33
+ {
34
+ var services = wsbl_get_service_codes();
35
+ var ids = wsbl_get_tab_ids(service_id);
36
+ for(var i in ids){
37
+ if(jQuery.inArray(ids[i], services) >= 0){
38
+ return true;
39
+ }
40
+ }
41
+ return false;
42
+ }
43
+
44
+ /**
45
+ * tab toggle
46
+ */
47
+ function wsbl_tab_toggle(service_id, is_simply)
48
+ {
49
+ var has_option = wsbl_has_option(service_id);
50
+ var tab_id_settings = "#" + service_id + "_settings";
51
+
52
+ if(is_simply){
53
+ has_option ? jQuery(tab_id_settings).show() : jQuery(tab_id_settings).hide();
54
+ }
55
+ else{
56
+ has_option ? jQuery(tab_id_settings).slideDown() : jQuery(tab_id_settings).slideUp();
57
+ }
58
+ }
59
+
60
+ /**
61
+ * update services
62
+ */
63
+ function wsbl_update_services(is_simply)
64
+ {
65
+ var vals = "";
66
+ var service = jQuery("#wsbl_sortable .wsbl_txt_draggable");
67
+ service.each(function(){
68
+ vals += vals === "" ? "" : ",";
69
+ vals += jQuery(this).text();
70
+ });
71
+ jQuery("#services_id").val(vals);
72
+
73
+ is_simply = is_simply || false;
74
+ var services = ['mixi', 'twitter', 'hatena_button', 'facebook_general', 'facebook_like', 'facebook_share', 'facebook_send',
75
+ 'gree', 'tumblr', 'atode', 'google_plus_one', 'line', 'pocket', 'pinterest'];
76
+ for(var i in services){
77
+ wsbl_tab_toggle(services[i], is_simply);
78
+ }
79
+ }
80
+
81
+ /**
82
+ * set sortable
83
+ */
84
+ function wsbl_update_sortable()
85
+ {
86
+ jQuery("#wsbl_sortable .wsbl_img_draggable").each(function(){
87
+ var button = jQuery(this);
88
+ button.css("display", "block"); // show delete button.
89
+ var img = jQuery("img", button);
90
+ img.mousedown(function(){
91
+ var p = jQuery(this).parents("li");
92
+ p.slideUp("fast", function(){
93
+ p.remove();
94
+ wsbl_update_services();
95
+ });
96
+ });
97
+ img.attr('src', '{{ 'close_button.png' | images }}');
98
+ img.hover(
99
+ function(){
100
+ jQuery(this).attr('src', '{{ 'close_button2.png' | images }}');
101
+ },
102
+ function(){
103
+ jQuery(this).attr('src', '{{ 'close_button.png' | images }}');
104
+ }
105
+ );
106
+ });
107
+ }
108
+
109
+ // main
110
+ jQuery(document).ready(function(){
111
+ jQuery("#wsbl_sortable").sortable({
112
+ placeholder: "wsbl_sortable_highlight",
113
+ update:function(e, ui){
114
+ wsbl_update_sortable();
115
+ wsbl_update_services();
116
+ }
117
+ });
118
+
119
+ jQuery("#wsbl_draggable li").draggable({
120
+ connectToSortable:"#wsbl_sortable",
121
+ helper:'clone',
122
+ revert:"invalid"
123
+ });
124
+ jQuery("#wsbl_draggable, #wsbl_sortable").disableSelection();
125
+
126
+ wsbl_update_sortable();
127
+ wsbl_update_services(true);
128
+
129
+ jQuery("#tabs").tabs();
130
+ });
131
+ //]]>
132
+ </script>
src/WpSocialBookmarkingLight/Resources/views/Admin/page.html.twig ADDED
@@ -0,0 +1,676 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {% if saved %}
2
+ <div class="updated">
3
+ <p><strong>{{ 'Options saved.' | __ }}</strong></p>
4
+ </div>
5
+ {% endif %}
6
+
7
+ {% if restored %}
8
+ <div class="updated">
9
+ <p><strong>{{ 'Restore defaults.' | __ }}</strong></p>
10
+ </div>
11
+ {% endif %}
12
+
13
+
14
+ <div class="wrap">
15
+ <h2>WP Social Bookmarking Light</h2>
16
+
17
+ <form method='POST' action="{{ request_uri }}">
18
+
19
+ <div id="tabs">
20
+ <ul>
21
+ <li><a href="#tabs-1"><span>{{ 'General Settings' | __ }}</span></a></li>
22
+ <li><a href="#tabs-1_2"><span>{{ 'CSS' | __ }}</span></a></li>
23
+ <li><a href="#tabs-1_3"><span>{{ 'Donate' | __ }}</span></a></li>
24
+ <li id='mixi_settings'><a href="#tabs-2"><span>{{ 'Mixi' | __ }}</span></a></li>
25
+ <li id='twitter_settings'><a href="#tabs-3"><span>{{ 'Twitter' | __ }}</span></a></li>
26
+ <li id='hatena_button_settings'><a href="#tabs-4"><span>{{ 'Hatena' | __ }}</span></a></li>
27
+ <li id='facebook_general_settings'><a href="#tabs-15"><span>{{ 'FB' | __ }}</span></a></li>
28
+ <li id='facebook_like_settings'><a href="#tabs-5"><span>{{ 'FB Like' | __ }}</span></a></li>
29
+ <li id='facebook_share_settings'><a href="#tabs-6"><span>{{ 'FB Share' | __ }}</span></a></li>
30
+ <li id='facebook_send_settings'><a href="#tabs-14"><span>{{ 'FB Send' | __ }}</span></a></li>
31
+ <li id='gree_settings'><a href="#tabs-7"><span>{{ 'GREE' | __ }}</span></a></li>
32
+ <li id='tumblr_settings'><a href="#tabs-9"><span>{{ 'tumblr' | __ }}</span></a></li>
33
+ <li id='atode_settings'><a href="#tabs-10"><span>{{ 'atode' | __ }}</span></a></li>
34
+ <li id='google_plus_one_settings'><a href="#tabs-11"><span>{{ 'Google Plus One' | __ }}</span></a></li>
35
+ <li id='line_settings'><a href="#tabs-12"><span>{{ 'LINE' | __ }}</span></a></li>
36
+ <li id='pocket_settings'><a href="#tabs-13"><span>{{ 'Pocket' | __ }}</span></a></li>
37
+ <li id='pinterest_settings'><a href="#tabs-16"><span>{{ 'Pinterest' | __ }}</span></a></li>
38
+ </ul>
39
+
40
+ <!-- General -->
41
+ <div id="tabs-1">
42
+ <table class='form-table'>
43
+ <tr>
44
+ <th scope="row">{{ 'Position' | __ }}:</th>
45
+ <td>
46
+ <select name='position'>
47
+ <option value='top' {{ option.position == 'top' ? 'selected' : '' }}>Top</option>
48
+ <option value='bottom' {{ option.position == 'bottom' ? 'selected' : '' }}>Bottom</option>
49
+ <option value='both' {{ option.position == 'both' ? 'selected' : '' }}>Both</option>
50
+ <option value='none' {{ option.position == 'none' ? 'selected' : '' }}>None</option>
51
+ </select>
52
+ </td>
53
+ </tr>
54
+ <tr>
55
+ <th scope="row">{{ 'Singular' | __ }}:</th>
56
+ <td>
57
+ <select name='single_page'>
58
+ <option value='true' {{ option.single_page == true ? 'selected' : '' }}>Yes</option>
59
+ <option value='false' {{ option.single_page == false ? 'selected' : '' }}>No</option>
60
+ </select>
61
+ </td>
62
+ </tr>
63
+ <tr>
64
+ <th scope="row">{{ 'Page' | __ }}:</th>
65
+ <td>
66
+ <select name='is_page'>
67
+ <option value='true' {{ option.is_page == true ? 'selected' : '' }}>Yes</option>
68
+ <option value='false' {{ option.is_page == false ? 'selected' : '' }}>No</option>
69
+ </select>
70
+ </td>
71
+ </tr>
72
+ <tr>
73
+ <th scope="row">{{ 'Services' | __ }}: <br/> <span style="font-size:10px">(drag-and-drop)</span></th>
74
+ <td>
75
+ <input type="text" id='services_id' name='services' value="{{ option.services }}"size=120 style="font-size:12px;" onclick="this.select(0, this.value.length)" readonly/>
76
+ <br />
77
+ <br />
78
+ <ul id="wsbl_sortable">
79
+ {% for service in option.services | split(',') %}
80
+ {% if service in service_types %}
81
+ <li>
82
+ <div class='wsbl_txt_draggable'>{{ service }}</div>
83
+ <div class='wsbl_img_draggable'><img src=''></div>
84
+ <br clear='both'>
85
+ </li>
86
+ {% endif %}
87
+ {% endfor %}
88
+ </ul>
89
+ <div class="wsbl_point_left"><img src='{{ 'point_left.png' | images }}'></div>
90
+ <ul id="wsbl_draggable">
91
+ {% for service in service_types %}
92
+ <li>
93
+ <div class='wsbl_txt_draggable'>{{ service }}</div>
94
+ <div class='wsbl_img_draggable'><img src=''></div>
95
+ <br clear='both'>
96
+ </li>
97
+ {% endfor %}
98
+ </ul>
99
+ <br clear="both"/>
100
+ </td>
101
+ </tr>
102
+ </table>
103
+ </div>
104
+
105
+ <!-- CSS -->
106
+ <div id="tabs-1_2">
107
+ <table class='form-table'>
108
+ <tr>
109
+ <th scope="row">Custom CSS:</th>
110
+ <td>
111
+ <textarea name="styles" rows="20" cols="80">{{ option.styles }}</textarea>
112
+ </td>
113
+ </tr>
114
+ </table>
115
+ </div>
116
+
117
+ <!-- Donate -->
118
+ <div id="tabs-1_3">
119
+ <table class='form-table'>
120
+ <tr>
121
+ <td>
122
+ <p>If you want to support this project, please make a <a href="https://gumroad.com/l/rWLrL" target="_blank">donation</a>!</p>
123
+ <p>Thank you for your supporting.</p>
124
+ </td>
125
+ </tr>
126
+ </table>
127
+ </div>
128
+
129
+ <!-- mixi -->
130
+ <div id="tabs-2">
131
+ <!-- General -->
132
+ <strong>General</strong>
133
+ <table class='form-table'>
134
+ <tr>
135
+ <th scope="row">Check Key:</th>
136
+ <td>
137
+ <input type="text" name='mixi_check_key' value="{{ option.mixi.check_key }}" size=50 />
138
+ </td>
139
+ </tr>
140
+ </table>
141
+ <br/>
142
+
143
+ <!-- mixi Check -->
144
+ <strong>mixi Check</strong>
145
+ <table class='form-table'>
146
+ <tr>
147
+ <th scope="row">Check Robots:</th>
148
+ <td>
149
+ <input type="text" name='mixi_check_robots' value="{{ option.mixi.check_robots }}" size=50 />
150
+ </td>
151
+ </tr>
152
+ <tr>
153
+ <th scope="row">Layout:</th>
154
+ <td>
155
+ <select name='mixi_button'>
156
+ <option value='button-1' {{ option.mixi.button == 'button-1' ? 'selected' : '' }}>button-1</option>
157
+ <option value='button-2' {{ option.mixi.button == 'button-2' ? 'selected' : '' }}>button-2</option>
158
+ <option value='button-3' {{ option.mixi.button == 'button-3' ? 'selected' : '' }}>button-3</option>
159
+ <option value='button-4' {{ option.mixi.button == 'button-4' ? 'selected' : '' }}>button-4</option>
160
+ </select>
161
+ </td>
162
+ </tr>
163
+ </table>
164
+ <br/>
165
+
166
+ <!-- mixi Like -->
167
+ <strong>mixi Like</strong>
168
+ <table class='form-table'>
169
+ <tr>
170
+ <th scope="row">Width:</th>
171
+ <td>
172
+ <input type="text" name='mixi_like_width' value="{{ option.mixi_like.width }}"/>
173
+ </td>
174
+ </tr>
175
+ </table>
176
+ </div>
177
+
178
+ <!-- Twitter -->
179
+ <div id="tabs-3">
180
+ <table class='form-table'>
181
+ <tr>
182
+ <th scope="row">Via: <br> <span style="font-size:10px">(your twitter account)</span></th>
183
+ <td>
184
+ <input type="text" name='twitter_via' value="{{ option.twitter.via }}" size=50 />
185
+ </td>
186
+ </tr>
187
+ <tr>
188
+ <th scope="row">Related: <br> <span style="font-size:10px">(related twitter account)</span></th>
189
+ <td>
190
+ <input type="text" name='twitter_related' value="{{ option.twitter.related }}" size=50 />
191
+ </td>
192
+ </tr>
193
+ <tr>
194
+ <th scope="row">Hashtags: <br> <span style="font-size:10px"></span></th>
195
+ <td>
196
+ <input type="text" name='twitter_hashtags' value="{{ option.twitter.hashtags }}" size=50 />
197
+ </td>
198
+ </tr>
199
+ <tr>
200
+ <th scope="row">Dnt: <br> <span style="font-size:10px">(Opt-out of tailoring Twitter)</span></th>
201
+ <td>
202
+ <select name='twitter_dnt'>
203
+ <option value='true' {{ option.twitter.dnt == true ? 'selected' : '' }}>Yes</option>
204
+ <option value='false' {{ option.twitter.dnt == false ? 'selected' : '' }}>No</option>
205
+ </select>
206
+ </td>
207
+ </tr>
208
+ <tr>
209
+ <th scope="row">Language:</th>
210
+ <td>
211
+ <select name='twitter_lang'>
212
+ <option value="" {{ option.twitter.lang == '' ? 'selected' : '' }}>---</option>
213
+ <option value="fr" {{ option.twitter.lang == 'fr' ? 'selected' : '' }}>French - français</option>
214
+ <option value="en" {{ option.twitter.lang == 'en' ? 'selected' : '' }}>English</option>
215
+ <option value="ar" {{ option.twitter.lang == 'ar' ? 'selected' : '' }}>Arabic - العربية</option>
216
+ <option value="ja" {{ option.twitter.lang == 'ja' ? 'selected' : '' }}>Japanese - 日本語</option>
217
+ <option value="es" {{ option.twitter.lang == 'es' ? 'selected' : '' }}>Spanish - Español</option>
218
+ <option value="de" {{ option.twitter.lang == 'de' ? 'selected' : '' }}>German - Deutsch</option>
219
+ <option value="it" {{ option.twitter.lang == 'it' ? 'selected' : '' }}>Italian - Italiano</option>
220
+ <option value="id" {{ option.twitter.lang == 'id' ? 'selected' : '' }}>Indonesian - Bahasa Indonesia</option>
221
+ <option value="pt" {{ option.twitter.lang == 'pt' ? 'selected' : '' }}>Portuguese - Português</option>
222
+ <option value="ko" {{ option.twitter.lang == 'ko' ? 'selected' : '' }}>Korean - 한국어</option>
223
+ <option value="tr" {{ option.twitter.lang == 'tr' ? 'selected' : '' }}>Turkish - Türkçe</option>
224
+ <option value="ru" {{ option.twitter.lang == 'ru' ? 'selected' : '' }}>Russian - Русский</option>
225
+ <option value="nl" {{ option.twitter.lang == 'nl' ? 'selected' : '' }}>Dutch - Nederlands</option>
226
+ <option value="fil" {{ option.twitter.lang == 'fil' ? 'selected' : '' }}>Filipino - Filipino</option>
227
+ <option value="msa" {{ option.twitter.lang == 'msa' ? 'selected' : '' }}>Malay - Bahasa Melayu</option>
228
+ <option value="zh-tw" {{ option.twitter.lang == 'zh-tw' ? 'selected' : '' }}>Traditional Chinese - 繁體中文</option>
229
+ <option value="zh-cn" {{ option.twitter.lang == 'zh-cn' ? 'selected' : '' }}>Simplified Chinese - 简体中文</option>
230
+ <option value="hi" {{ option.twitter.lang == 'hi' ? 'selected' : '' }}>Hindi - हिन्दी</option>
231
+ <option value="no" {{ option.twitter.lang == 'no' ? 'selected' : '' }}>Norwegian - Norsk</option>
232
+ <option value="sv" {{ option.twitter.lang == 'sv' ? 'selected' : '' }}>Swedish - Svenska</option>
233
+ <option value="fi" {{ option.twitter.lang == 'fi' ? 'selected' : '' }}>Finnish - Suomi</option>
234
+ <option value="da" {{ option.twitter.lang == 'da' ? 'selected' : '' }}>Danish - Dansk</option>
235
+ <option value="pl" {{ option.twitter.lang == 'pl' ? 'selected' : '' }}>Polish - Polski</option>
236
+ <option value="hu" {{ option.twitter.lang == 'hu' ? 'selected' : '' }}>Hungarian - Magyar</option>
237
+ <option value="fa" {{ option.twitter.lang == 'fa' ? 'selected' : '' }}>Farsi - فارسی</option>
238
+ <option value="he" {{ option.twitter.lang == 'he' ? 'selected' : '' }}>Hebrew - עִבְרִית</option>
239
+ <option value="ur" {{ option.twitter.lang == 'ur' ? 'selected' : '' }}>Urdu - اردو</option>
240
+ <option value="th" {{ option.twitter.lang == 'th' ? 'selected' : '' }}>Thai - ภาษาไทย</option>
241
+ </select>
242
+ </td>
243
+ </tr>
244
+ <tr>
245
+ <th scope="row">Size:</th>
246
+ <td>
247
+ <select name='twitter_size'>
248
+ <option value='' {{ option.twitter.size == '' ? 'selected' : '' }}>normal</option>
249
+ <option value='large' {{ option.twitter.size == 'large' ? 'selected' : '' }}>large</option>
250
+ </select>
251
+ </td>
252
+ </tr>
253
+ </table>
254
+ </div>
255
+
256
+ <!-- hatena button -->
257
+ <div id="tabs-4">
258
+ <table class='form-table'>
259
+ <tr>
260
+ <th scope="row">Layout:</th>
261
+ <td>
262
+ <select name='hatena_button_layout'>
263
+ <option value='standard-balloon' {{ option.hatena_button.layout == 'standard-balloon' ? 'selected' : '' }}>standard-balloon</option>
264
+ <option value='standard-noballoon' {{ option.hatena_button.layout == 'standard-noballoon' ? 'selected' : '' }}>standard-noballoon</option>
265
+ <option value='standard' {{ option.hatena_button.layout == 'standard' ? 'selected' : '' }}>standard</option>
266
+ <option value='simple' {{ option.hatena_button.layout == 'simple' ? 'selected' : '' }}>simple</option>
267
+ <option value='simple-balloon' {{ option.hatena_button.layout == 'simple-balloon' ? 'selected' : '' }}>simple-balloon</option>
268
+ </select>
269
+ </td>
270
+ </tr>
271
+ </table>
272
+ </div>
273
+
274
+ <!-- Facebook General -->
275
+ <div id="tabs-15">
276
+ <table class='form-table'>
277
+ <tr>
278
+ <th scope="row">Locale:</th>
279
+ <td>
280
+ <input type="text" name='facebook_locale' value="{{ option.facebook.locale }}" /><br/>
281
+ <span>en_US, ja_JP, fr_FR ...</span> see more <a href='http://developers.facebook.com/docs/internationalization/' target=_blank>facebook docs</a>
282
+ </td>
283
+ </tr>
284
+ <tr>
285
+ <th scope="row">Version:</th>
286
+ <td>
287
+ <select name='facebook_version'>
288
+ <option value='html5' {{ option.facebook.version == 'html5' ? 'selected' : '' }}>html5</option>
289
+ <option value='xfbml' {{ option.facebook.version == 'xfbml' ? 'selected' : '' }}>xfbml</option>
290
+ <option value='iframe' {{ option.facebook.version == 'iframe' ? 'selected' : '' }}>iframe</option>
291
+ </select>
292
+ </td>
293
+ </tr>
294
+ <tr>
295
+ <th scope="row">Add fb-root:</th>
296
+ <td>
297
+ <select name='facebook_fb_root'>
298
+ <option value='true' {{ option.facebook.fb_root == true ? 'selected' : '' }}>Yes</option>
299
+ <option value='false' {{ option.facebook.fb_root == false ? 'selected' : '' }}>No</option>
300
+ </select>
301
+ </td>
302
+ </tr>
303
+ </table>
304
+ </div>
305
+
306
+ <!-- Facebook Like Button -->
307
+ <div id="tabs-5">
308
+ <!-- Like Button -->
309
+ <table class='form-table'>
310
+ <tr>
311
+ <th scope="row">Layout:</th>
312
+ <td>
313
+ <select name='facebook_like_layout'>
314
+ <option value='button' {{ option.facebook_like.layout == 'button' ? 'selected' : '' }}>button</option>
315
+ <option value='button_count' {{ option.facebook_like.layout == 'button_count' ? 'selected' : '' }}>button_count</option>
316
+ </select>
317
+ </td>
318
+ </tr>
319
+ <tr>
320
+ <th scope="row">Action:</th>
321
+ <td>
322
+ <select name='facebook_like_action'>
323
+ <option value='like' {{ option.facebook_like.action == 'like' ? 'selected' : '' }}>like</option>
324
+ <option value='recommend' {{ option.facebook_like.action == 'recommend' ? 'selected' : '' }}>recommend</option>
325
+ </select>
326
+ </td>
327
+ </tr>
328
+ <tr>
329
+ <th scope="row">Share:</th>
330
+ <td>
331
+ <select name='facebook_like_share'>
332
+ <option value='true' {{ option.facebook_like.share == true ? 'selected' : '' }}>Yes</option>
333
+ <option value='false' {{ option.facebook_like.share == false ? 'selected' : '' }}>No</option>
334
+ </select>
335
+ </td>
336
+ </tr>
337
+ <tr>
338
+ <th scope="row">Width:</th>
339
+ <td>
340
+ <input type="text" name='facebook_like_width' value="{{ option.facebook_like.width }}" size=20 />
341
+ </td>
342
+ </tr>
343
+ </table>
344
+ </div>
345
+
346
+ <!-- Facebook Share Button -->
347
+ <div id="tabs-6">
348
+ <table class='form-table'>
349
+ <tr>
350
+ <th scope="row">Layout:</th>
351
+ <td>
352
+ <select name='facebook_share_type'>
353
+ <option value='button' {{ option.facebook_share.type == 'button' ? 'selected' : '' }}>button</option>
354
+ <option value='button_count' {{ option.facebook_share.type == 'button_count' ? 'selected' : '' }}>button_count</option>
355
+ </select>
356
+ </td>
357
+ </tr>
358
+ <tr>
359
+ <th scope="row">Width:</th>
360
+ <td>
361
+ <input type="text" name='facebook_share_width' value="{{ option.facebook_share.width }}" size=20 />
362
+ </td>
363
+ </tr>
364
+ </table>
365
+ </div>
366
+
367
+ <!-- Facebook Send Button -->
368
+ <div id="tabs-14">
369
+ <table class='form-table'>
370
+ <tr>
371
+ <th scope="row">Color Scheme:</th>
372
+ <td>
373
+ <select name='facebook_send_colorscheme'>
374
+ <option value='light' {{ option.facebook_send.colorscheme == 'light' ? 'selected' : '' }}>light</option>
375
+ <option value='dark' {{ option.facebook_send.colorscheme == 'dark' ? 'selected' : '' }}>dark</option>
376
+ </select>
377
+ </td>
378
+ </tr>
379
+ <tr>
380
+ <th scope="row">Width:</th>
381
+ <td>
382
+ <input type="text" name='facebook_send_width' value="{{ option.facebook_send.width }}" size=20 />
383
+ </td>
384
+ </tr>
385
+ <tr>
386
+ <th scope="row">Height:</th>
387
+ <td>
388
+ <input type="text" name='facebook_send_height' value="{{ option.facebook_send.height }}" size=20 />
389
+ </td>
390
+ </tr>
391
+ </table>
392
+ </div>
393
+
394
+ <!-- gree -->
395
+ <div id="tabs-7">
396
+ <table class='form-table'>
397
+ <tr>
398
+ <th scope="row">Button type:</th>
399
+ <td>
400
+ <select name='gree_button_type'>
401
+ <option value='0' {{ option.gree.button_type == '0' ? 'selected' : '' }}>{{ 'iine' | __ }}</option>
402
+ <option value='1' {{ option.gree.button_type == '1' ? 'selected' : '' }}>{{ 'kininaru' | __ }}</option>
403
+ <option value='2' {{ option.gree.button_type == '2' ? 'selected' : '' }}>{{ 'osusume' | __ }}</option>
404
+ <option value='3' {{ option.gree.button_type == '3' ? 'selected' : '' }}>{{ 'share' | __ }}</option>
405
+ <option value='4' {{ option.gree.button_type == '4' ? 'selected' : '' }}>{{ 'logo' | __ }}</option>
406
+ </select>
407
+ </td>
408
+ </tr>
409
+ <tr>
410
+ <th scope="row">Button size:</th>
411
+ <td>
412
+ <select name='gree_button_size'>
413
+ <option value='16' {{ option.gree.button_size == '16' ? 'selected' : '' }}>16</option>
414
+ <option value='20' {{ option.gree.button_size == '20' ? 'selected' : '' }}>20</option>
415
+ </select>
416
+ </td>
417
+ </tr>
418
+ </table>
419
+ </div>
420
+
421
+ <!-- tumblr -->
422
+ <div id="tabs-9">
423
+ <table class='form-table'>
424
+ <tr>
425
+ <th scope="row">Button type:</th>
426
+ <td>
427
+ <select name='tumblr_button_type' onchange='jQuery("#tumblr_img").attr("src", "http://platform.tumblr.com/v1/share_"+this.form.tumblr_button_type.value+".png")'>
428
+ {% set tumblr_button_types = ['1', '2', '3', '4'] %}
429
+ {% for button_type in tumblr_button_types %}
430
+ <option value='{{ button_type }}' {{ option.tumblr.button_type == button_type ? 'selected' : '' }}>share_{{ button_type }}</option>
431
+ {% endfor %}
432
+ </select>
433
+ <img id='tumblr_img' style="vertical-align:middle" src='http://platform.tumblr.com/v1/share_{{ option.tumblr.button_type }}.png'>
434
+ </td>
435
+ </tr>
436
+ </table>
437
+ </div>
438
+
439
+ <!-- atode -->
440
+ <div id="tabs-10">
441
+ <table class='form-table'>
442
+ <tr>
443
+ <th scope="row">Button type:</th>
444
+ <td>
445
+ <select name='atode_button_type' onchange='jQuery("#atode_img").attr("src", "http://atode.cc/img/"+this.form.atode_button_type.value+".gif")'>
446
+ {% set atode_button_types = ['iconsja', 'iconnja', 'iconnen'] %}
447
+ {% for button_type in atode_button_types %}
448
+ <option value='{{ button_type }}' {{ option.atode.button_type == button_type ? 'selected' : '' }}>{{ button_type }}</option>
449
+ {% endfor %}
450
+ </select>
451
+ <img id='atode_img' style="vertical-align:middle" src='http://atode.cc/img/{{ option.atode.button_type }}.gif'>
452
+ </td>
453
+ </tr>
454
+ </table>
455
+ </div>
456
+
457
+ <!-- google +1 -->
458
+ <div id="tabs-11">
459
+ <table class='form-table'>
460
+ <tr>
461
+ <th scope="row">Button size:</th>
462
+ <td>
463
+ <select name='google_plus_one_button_size'>
464
+ <option value='small' {{ option.google_plus_one.button_size == 'small' ? 'selected' : '' }}>small</option>
465
+ <option value='medium' {{ option.google_plus_one.button_size == 'medium' ? 'selected' : '' }}>medium</option>
466
+ <option value='standard' {{ option.google_plus_one.button_size == 'standard' ? 'selected' : '' }}>standard</option>
467
+ <option value='tall' {{ option.google_plus_one.button_size == 'tall' ? 'selected' : '' }}>tall</option>
468
+ </select>
469
+ </td>
470
+ </tr>
471
+ <tr>
472
+ <th scope="row">Language:</th>
473
+ <td>
474
+ <select name='google_plus_one_lang'>
475
+ {% set google_langs = {
476
+ "ar": "Arabic",
477
+ "ar": "Arabic",
478
+ "bg": "Bulgarian",
479
+ "ca": "Catalan",
480
+ "zh-CN": "Chinese (Simplified)",
481
+ "zh-TW": "Chinese (Traditional)",
482
+ "hr": "Croatian",
483
+ "cs": "Czech",
484
+ "da": "Danish",
485
+ "nl": "Dutch",
486
+ "en-US": "English (US)",
487
+ "en-GB": "English (UK)",
488
+ "et": "Estonian",
489
+ "fil": "Filipino",
490
+ "fi": "Finnish",
491
+ "fr": "French",
492
+ "de": "German",
493
+ "el": "Greek",
494
+ "iw": "Hebrew",
495
+ "hi": "Hindi",
496
+ "hu": "Hungarian",
497
+ "id": "Indonesian",
498
+ "it": "Italian",
499
+ "ja": "Japanese",
500
+ "ko": "Korean",
501
+ "lv": "Latvian",
502
+ "lt": "Lithuanian",
503
+ "ms": "Malay",
504
+ "no": "Norwegian",
505
+ "fa": "Persian",
506
+ "pl": "Polish",
507
+ "pt-BR": "Portuguese (Brazil)",
508
+ "pt-PT": "Portuguese (Portugal)",
509
+ "ro": "Romanian",
510
+ "ru": "Russian",
511
+ "sr": "Serbian",
512
+ "sv": "Swedish",
513
+ "sk": "Slovak",
514
+ "sl": "Slovenian",
515
+ "es": "Spanish",
516
+ "es-419": "Spanish (Latin America)",
517
+ "th": "Thai",
518
+ "tr": "Turkish",
519
+ "uk": "Ukrainian",
520
+ "vi": "Vietnamese",
521
+ } %}
522
+ {% for key, value in google_langs %}
523
+ <option {{ option.google_plus_one.lang == key ? "selected" : "" }} value='{{ key }}'>{{ val }}</option>
524
+ {% endfor %}
525
+ </select>
526
+ </td>
527
+ </tr>
528
+ <tr>
529
+ <th scope="row">Annotation:</th>
530
+ <td>
531
+ <select name='google_plus_one_annotation'>
532
+ <option value='none' {{ option.google_plus_one.annotation == "none" ? 'selected' : '' }}>none</option>
533
+ <option value='bubble' {{ option.google_plus_one.annotation == "bubble" ? 'selected' : '' }}>bubble</option>
534
+ <option value='inline' {{ option.google_plus_one.annotation == "inline" ? 'selected' : '' }}>inline</option>
535
+ </select>
536
+ </td>
537
+ </tr>
538
+ <tr>
539
+ <th scope="row">Inline size:</th>
540
+ <td>
541
+ <input type="text" name='google_plus_one_inline_size' value="{{ option.google_plus_one.inline_size }}" />
542
+ </td>
543
+ </tr>
544
+ </table>
545
+ </div>
546
+
547
+ <!-- line -->
548
+ <div id="tabs-12">
549
+ <table class='form-table'>
550
+ <tr>
551
+ <th scope="row">Button type:</th>
552
+ <td>
553
+ <select name='line_button_type' onchange='jQuery("#line_img").attr("src", "{{ "" | images }}/"+this.form.line_button_type.value+".png")'>
554
+ {% set line_button_types = ['line20x20', 'line88x20'] %}
555
+ {% for button_type in line_button_types %}
556
+ <option value='{{ button_type }}' {{ option.line.button_type == button_type ? 'selected' : '' }}>{{ button_type }}</option>
557
+ {% endfor %}
558
+ </select>
559
+ <img id='line_img' style="vertical-align:middle" src='{{ option.line.button_type | images }}.png'>
560
+ </td>
561
+ </tr>
562
+ <tr>
563
+ <th scope="row">Protocol:</th>
564
+ <td>
565
+ <select name='line_protocol'>
566
+ <option value='http' {{ option.line.protocol == 'http' ? 'selected' : '' }}>http://</option>
567
+ <option value='line' {{ option.line.protocol == 'line' ? 'selected' : '' }}>line://</option>
568
+ </select>
569
+ </td>
570
+ </tr>
571
+ </table>
572
+ </div>
573
+
574
+ <!-- pocket -->
575
+ <div id="tabs-13">
576
+ <table class='form-table'>
577
+ <tr>
578
+ <th scope="row">Button type:</th>
579
+ <td>
580
+ <select name='pocket_button_type'>
581
+ <option value='none' {{ option.pocket.button_type == 'none' ? 'selected' : '' }}>none</option>
582
+ <option value='horizontal' {{ option.pocket.button_type == 'horizontal' ? 'selected' : '' }}>horizontal</option>
583
+ <option value='vertical' {{ option.pocket.button_type == 'vertical' ? 'selected' : '' }}>vertical</option>
584
+ </select>
585
+ </td>
586
+ </tr>
587
+ </table>
588
+ </div>
589
+
590
+ <!-- Pinterest -->
591
+ <div id="tabs-16">
592
+ <table class='form-table'>
593
+ <tr>
594
+ <th scope="row">Type: <br> <span style="font-size:10px"></span></th>
595
+ <td>
596
+ <select name='pinterest_type'>
597
+ <option value='all' {{ option.pinterest.type == 'all' ? 'selected' : '' }}>All</option>
598
+ <option value='hover' {{ option.pinterest.type == 'hover' ? 'selected' : '' }}>Hover</option>
599
+ </select>
600
+ </td>
601
+ </tr>
602
+ <tr>
603
+ <th scope="row">Shape: <br> <span style="font-size:10px"></span></th>
604
+ <td>
605
+ <select name='pinterest_shape'>
606
+ <option value='rect' {{ option.pinterest.shape == 'rect' ? 'selected' : '' }}>Rectangle</option>
607
+ <option value='round' {{ option.pinterest.shape == 'round' ? 'selected' : '' }}>Round</option>
608
+ </select>
609
+ </td>
610
+ </tr>
611
+ <tr>
612
+ <th scope="row">Size: <br> <span style="font-size:10px"></span></th>
613
+ <td>
614
+ <select name='pinterest_size'>
615
+ <option value='large' {{ option.pinterest.size == 'large' ? 'selected' : '' }}>Large</option>
616
+ <option value='small' {{ option.pinterest.size == 'small' ? 'selected' : '' }}>Small</option>
617
+ </select>
618
+ </td>
619
+ </tr>
620
+ <tr>
621
+ <th scope="row">Color: <br> <span style="font-size:10px"></span></th>
622
+ <td>
623
+ <select name='pinterest_color'>
624
+ <option value='red' {{ option.pinterest.color == 'red' ? 'selected' : '' }}>Red</option>
625
+ <option value='gray' {{ option.pinterest.color == 'gray' ? 'selected' : '' }}>Gray</option>
626
+ <option value='white' {{ option.pinterest.color == 'white' ? 'selected' : '' }}>White</option>
627
+ </select>
628
+ </td>
629
+ </tr>
630
+ <tr>
631
+ <th scope="row">Language:</th>
632
+ <td>
633
+ <select name='pinterest_lang'>
634
+ <option value="en" {{ option.pinterest.lang == 'en' ? 'selected' : '' }}>English</option>
635
+ <option value="ja" {{ option.pinterest.lang == 'ja' ? 'selected' : '' }}>Japanese - 日本語</option>
636
+ </select>
637
+ </td>
638
+ </tr>
639
+ </table>
640
+ </div>
641
+
642
+ </div>
643
+ <p class="submit">
644
+ <input class="button-primary" type="submit" name='save' value='{{ 'Save Changes' | __ }}' />
645
+ <input type="submit" name='restore' value='{{ 'Restore defaults' | __ }}' />
646
+ </p>
647
+ </form>
648
+
649
+ <table class='wsbl_options'>
650
+ <tr><th>{{ 'Service Code' | __ }}</th><th>{{ 'Explain' | __ }}</th></tr>
651
+ <tr><td>hatena</td><td>Hatena Bookmark</td></tr>
652
+ <tr><td>hatena_users</td><td>Hatena Bookmark Users</td></tr>
653
+ <tr><td>hatena_button</td><td>Hatena Bookmark Button</td></tr>
654
+ <tr><td>twitter</td><td>Tweet Button - Twitter</td></tr>
655
+ <tr><td>tumblr</td><td>Tumblr</td></tr>
656
+ <tr><td>google</td><td>Google Bookmarks</td></tr>
657
+ <tr><td>google_plus_one</td><td>Google +1</td></tr>
658
+ <tr><td>delicious</td><td>Delicious</td></tr>
659
+ <tr><td>digg</td><td>Digg</td></tr>
660
+ <tr><td>facebook</td><td>Facebook Share</td></tr>
661
+ <tr><td>facebook_like</td><td>Facebook Like Button</td></tr>
662
+ <tr><td>facebook_share</td><td>Facebook Share Button</td></tr>
663
+ <tr><td>facebook_send</td><td>Facebook Send Button</td></tr>
664
+ <tr><td>reddit</td><td>reddit</td></tr>
665
+ <tr><td>linkedin</td><td>LinkedIn</td></tr>
666
+ <tr><td>instapaper</td><td>Instapaper</td></tr>
667
+ <tr><td>stumbleupon</td><td>StumbleUpon</td></tr>
668
+ <tr><td>mixi</td><td>mixi Check (require <a href="http://developer.mixi.co.jp/connect/mixi_plugin/mixi_check/mixicheck" onclick="window.open('http://developer.mixi.co.jp/connect/mixi_plugin/mixi_check/mixicheck'); return false;" >mixi check key</a>)</td></tr>
669
+ <tr><td>mixi_like</td><td>mixi Like (require <a href="http://developer.mixi.co.jp/connect/mixi_plugin/mixi_check/mixicheck" onclick="window.open('http://developer.mixi.co.jp/connect/mixi_plugin/mixi_check/mixicheck'); return false;" >mixi check key</a>)</td></tr>
670
+ <tr><td>gree</td><td>GREE Social Feedback</td></tr>
671
+ <tr><td>atode</td><td>atode (toread)</td></tr>
672
+ <tr><td>line</td><td>LINE Button</td></tr>
673
+ <tr><td>pocket</td><td>Pocket Button</td></tr>
674
+ <tr><td>pinterest</td><td>Pinterest Button</td></tr>
675
+ </table>
676
+ </div>
src/WpSocialBookmarkingLight/Resources/views/Builder/content.html.twig ADDED
@@ -0,0 +1,6 @@
 
 
 
 
 
 
1
+ <div class='wp_social_bookmarking_light'>
2
+ {% for service in services %}
3
+ <div class="wsbl_{{ service.name }}">{{ service.content | raw }}</div>
4
+ {% endfor %}
5
+ </div>
6
+ <br class='wp_social_bookmarking_light_clear' />
src/WpSocialBookmarkingLight/Resources/views/Builder/footer.html.twig ADDED
@@ -0,0 +1,30 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+
2
+ <!-- BEGIN: WP Social Bookmarking Light FOOTER -->
3
+ {% if twitter %}
4
+ <script>!function(d,s,id){var js,fjs=d.getElementsByTagName(s)[0],p=/^http:/.test(d.location)?'http':'https';if(!d.getElementById(id)){js=d.createElement(s);js.id=id;js.src=p+'://platform.twitter.com/widgets.js';fjs.parentNode.insertBefore(js,fjs);}}(document, 'script', 'twitter-wjs');</script>
5
+ {% endif %}
6
+
7
+ {% if google_plus_one %}
8
+ <script src="https://apis.google.com/js/platform.js" async defer>
9
+ {lang: "{{ google_plus_one.lang }}"}
10
+ </script>
11
+ {% endif %}
12
+
13
+ {% if pinterest %}
14
+ {% set data_pin_shape = pinterest.shape == 'round' ? 'data-pin-shape=true' : '' %}
15
+
16
+ {% if pinterest.size == 'large' %}
17
+ {% set data_pin_height = pinterest.shape == 'round' ? 'data-pin-height="32"' : 'data-pin-height="28"' %}
18
+ {% else %}
19
+ {% set data_pin_height = '' %}
20
+ {% endif %}
21
+
22
+ <script type="text/javascript" async defer
23
+ data-pin-hover=true
24
+ {{ data_pin_shape }}
25
+ data-pin-color="{{ pinterest.color }}"
26
+ data-pin-lang="{{ pinterest.lang }}"
27
+ {{ data_pin_height | raw }}
28
+ src="//assets.pinterest.com/js/pinit.js"></script>
29
+ {% endif %}
30
+ <!-- END: WP Social Bookmarking Light FOOTER -->
src/WpSocialBookmarkingLight/Resources/views/Builder/head.html.twig ADDED
@@ -0,0 +1,27 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+
2
+ <!-- BEGIN: WP Social Bookmarking Light HEAD -->
3
+ {% if mixi %}
4
+ <meta name="mixi-check-robots" content="{{ mixi.check_robots }}"/>
5
+ {% endif %}
6
+
7
+ {% if tumblr %}
8
+ <script type="text/javascript" src="//platform.tumblr.com/v1/share.js"></script>
9
+ {% endif %}
10
+
11
+ {% if facebook %}
12
+ <script>
13
+ (function (d, s, id) {
14
+ var js, fjs = d.getElementsByTagName(s)[0];
15
+ if (d.getElementById(id)) return;
16
+ js = d.createElement(s);
17
+ js.id = id;
18
+ js.src = "//connect.facebook.net/{{ facebook.locale }}/sdk.js#xfbml=1&version=v2.7";
19
+ fjs.parentNode.insertBefore(js, fjs);
20
+ }(document, 'script', 'facebook-jssdk'));
21
+ </script>
22
+ {% endif %}
23
+
24
+ <style type="text/css">
25
+ {{ styles }}
26
+ </style>
27
+ <!-- END: WP Social Bookmarking Light HEAD -->
src/WpSocialBookmarkingLight/Service.php ADDED
@@ -0,0 +1,668 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace WpSocialBookmarkingLight;
4
+
5
+ use WpSocialBookmarkingLight\Util\Text;
6
+ use WpSocialBookmarkingLight\Util\Url;
7
+
8
+ /**
9
+ * Class Service
10
+ *
11
+ * public method name is service type
12
+ * e.g.
13
+ * facebookLike() -> facebook_like
14
+ *
15
+ * @package WpSocialBookmarkingLight
16
+ */
17
+ class Service
18
+ {
19
+ /** @var OptionInterface */
20
+ private $option;
21
+
22
+ /** @var string */
23
+ private $url;
24
+
25
+ /** @var string */
26
+ private $title;
27
+
28
+ /** @var string */
29
+ private $encode_url;
30
+
31
+ /** @var string */
32
+ private $encode_title;
33
+
34
+ /** @var string */
35
+ private $encode_blogname;
36
+
37
+ /**
38
+ * Service constructor.
39
+ * @param OptionInterface $option
40
+ * @param $url string
41
+ * @param $title string
42
+ */
43
+ public function __construct(OptionInterface $option, $url, $title)
44
+ {
45
+ $this->option = $option;
46
+ $this->blogname = $this->toUTF8(get_bloginfo('name'));
47
+ $this->url = $url;
48
+ $this->title = $this->toUTF8($title);
49
+ $this->encode_url = rawurlencode($url);
50
+ $this->encode_title = rawurlencode($this->title);
51
+ $this->encode_blogname = rawurlencode($this->blogname);
52
+ }
53
+
54
+ /**
55
+ * @param $str
56
+ * @return string
57
+ */
58
+ private function toUTF8($str)
59
+ {
60
+ $charset = get_option('blog_charset');
61
+ if (strcasecmp($charset, 'UTF-8') != 0 && function_exists('mb_convert_encoding')) {
62
+ $str = mb_convert_encoding($str, 'UTF-8', $charset);
63
+ }
64
+ return $str;
65
+ }
66
+
67
+ /**
68
+ * @param $url string
69
+ * @return string
70
+ */
71
+ private function linkRaw($url)
72
+ {
73
+ return $url;
74
+ }
75
+
76
+ /**
77
+ * build <a> and <img> tag
78
+ *
79
+ * @param $url
80
+ * @param $alt
81
+ * @param $icon
82
+ * @param $width
83
+ * @param $height
84
+ * @param bool $blank
85
+ * @return string
86
+ */
87
+ private function link($url, $alt, $icon, $width, $height, $blank = true)
88
+ {
89
+ $width = $width ? "width='$width'" : "";
90
+ $height = $height ? "height='$height'" : "";
91
+ $blank = $blank ? "target=_blank" : "";
92
+ return "<a href='{$url}' title='{$alt}' rel=nofollow class='wp_social_bookmarking_light_a' $blank>"
93
+ . "<img src='{$icon}' alt='{$alt}' title='{$alt}' $width $height class='wp_social_bookmarking_light_img' />"
94
+ . "</a>";
95
+ }
96
+
97
+ /**
98
+ * Invoke service method
99
+ *
100
+ * @param $service string
101
+ * @return string
102
+ */
103
+ public function invokeService($service)
104
+ {
105
+ $method = str_replace('_', '', ucwords($service, '_')); // snake_case to camelCase
106
+ return $this->$method();
107
+ }
108
+
109
+ /**
110
+ * Returns service types
111
+ *
112
+ * @return array
113
+ */
114
+ public static function getServiceTypes()
115
+ {
116
+ $class_methods = get_class_methods(__CLASS__);
117
+ $excepts = array('__construct', 'toUTF8', 'linkRaw', 'link', 'invokeService', 'getServiceTypes'); // except not service type function.
118
+
119
+ $methods = array();
120
+ foreach ($class_methods as $method) {
121
+ if (in_array($method, $excepts)) {
122
+ continue;
123
+ }
124
+ $methods[] = strtolower(preg_replace('/[a-z]+(?=[A-Z])|[A-Z]+(?=[A-Z][a-z])/', '\0_', $method)); // camelCase to snake_case.
125
+ }
126
+ return $methods;
127
+ }
128
+
129
+ /**
130
+ * Hatena bookmark button (old type)
131
+ *
132
+ * @return string
133
+ */
134
+ public function hatena()
135
+ {
136
+ $url = "//b.hatena.ne.jp/add?mode=confirm&url={$this->encode_url}&title={$this->encode_title}";
137
+ $alt = Text::locale("Bookmark this on Hatena Bookmark");
138
+ $icon = Url::images("hatena.gif");
139
+ return $this->link($url, $alt, $icon, 16, 12);
140
+ }
141
+
142
+ /**
143
+ * Hatena bookmark users (old type)
144
+ *
145
+ * @return string
146
+ */
147
+ public function hatenaUsers()
148
+ {
149
+ $url = "//b.hatena.ne.jp/entry/{$this->url}";
150
+ $alt = sprintf(Text::locale("Hatena Bookmark - %s"), $this->title);
151
+ $icon = "//b.hatena.ne.jp/entry/image/{$this->url}";
152
+ return $this->link($url, $alt, $icon, null, null);
153
+ }
154
+
155
+ /**
156
+ * Hatena bookmark button
157
+ *
158
+ * @return string
159
+ */
160
+ public function hatenaButton()
161
+ {
162
+ $options = $this->option->getAll();
163
+ $url = "//b.hatena.ne.jp/entry/{$this->url}";
164
+ $title = $this->title;
165
+ $alt = Text::locale("Bookmark this on Hatena Bookmark");
166
+ return $this->linkRaw('<a href="' . $url . '"'
167
+ . ' class="hatena-bookmark-button"'
168
+ . ' data-hatena-bookmark-title="' . $title . '"'
169
+ . ' data-hatena-bookmark-layout="' . $options['hatena_button']['layout'] . '"'
170
+ . ' title="' . $alt . '">'
171
+ . ' <img src="//b.hatena.ne.jp/images/entry-button/button-only@2x.png"'
172
+ . ' alt="' . $alt . '" width="20" height="20" style="border: none;" /></a>'
173
+ . '<script type="text/javascript" src="//b.hatena.ne.jp/js/bookmark_button.js" charset="utf-8" async="async"></script>');
174
+ }
175
+
176
+ /**
177
+ * Twitter button
178
+ *
179
+ * @return string
180
+ */
181
+ public function twitter()
182
+ {
183
+ $options = $this->option->getAll();;
184
+ $twitter = $options['twitter'];
185
+ $data_url = $this->url;
186
+ $data_text = $this->title;
187
+ $data_via = $twitter['via'] !== '' ? ' data-via="' . $twitter['via'] . '"' : '';
188
+ $data_size = $twitter['size'] === 'large' ? ' data-size="large"' : '';
189
+ $data_related = $twitter['related'] !== '' ? ' data-related="' . $twitter['related'] . '"' : '';
190
+ $data_hashtags = $twitter['hashtags'] !== '' ? ' data-hashtags="' . $twitter['hashtags'] . '"' : '';
191
+ $data_dnt = $twitter['dnt'] ? ' data-dnt="true"' : '';
192
+ $data_lang = $twitter['lang'] !== '' ? ' data-lang="' . $twitter['lang'] . '"' : '';
193
+
194
+ return $this->linkRaw(
195
+ '<a href="https://twitter.com/share" class="twitter-share-button"'
196
+ . ' data-url="' . $data_url . '"'
197
+ . ' data-text="' . $data_text . '"'
198
+ . $data_via
199
+ . $data_size
200
+ . $data_related
201
+ . $data_hashtags
202
+ . $data_dnt
203
+ . $data_lang
204
+ . '>Tweet</a>'
205
+ );
206
+ }
207
+
208
+ /**
209
+ * Tumblr
210
+ *
211
+ * @deprecated
212
+ */
213
+ public function tumblr()
214
+ {
215
+ $options = $this->option->getAll();
216
+ $type = $options['tumblr']['button_type'];
217
+ $width = 'width:81px;';
218
+ switch ($type) {
219
+ case '1':
220
+ $width = 'width:81px;';
221
+ break;
222
+ case '2':
223
+ $width = 'width:61px;';
224
+ break;
225
+ case '3':
226
+ $width = 'width:129px;';
227
+ break;
228
+ case '4':
229
+ $width = 'width:20px;';
230
+ break;
231
+ }
232
+ return $this->linkRaw('<a href="//www.tumblr.com/share?v=3&u=' . $this->encode_url . '&t=' . $this->encode_title . '" '
233
+ . 'title="' . Text::locale("Share on Tumblr") . '" '
234
+ . 'style="display:inline-block; text-indent:-9999px; overflow:hidden; '
235
+ . $width . ' height:20px; '
236
+ . 'background:url(\'//platform.tumblr.com/v1/share_' . $type . '.png\')'
237
+ . ' top left no-repeat transparent;">'
238
+ . Text::locale("Share on Tumblr")
239
+ . '</a>');
240
+ }
241
+
242
+ /**
243
+ * Google Bookmarks
244
+ *
245
+ * @return string
246
+ */
247
+ public function google()
248
+ {
249
+ $url = "http://www.google.com/bookmarks/mark?op=add&bkmk={$this->encode_url}&title={$this->encode_title}";
250
+ $alt = Text::locale("Bookmark this on Google Bookmarks");
251
+ $icon = Url::images("google.png");
252
+ return $this->link($url, $alt, $icon, 16, 16);
253
+ }
254
+
255
+ /**
256
+ * Google +1
257
+ *
258
+ * @return string
259
+ */
260
+ public function googlePlusOne()
261
+ {
262
+ $options = $this->option->getAll();
263
+ $button_size = $options['google_plus_one']['button_size'];
264
+ $annotation = $options['google_plus_one']['annotation'];
265
+ $width = $annotation == 'inline' ? 'width="' . $options['google_plus_one']['inline_size'] . '"' : "";
266
+ $raw = '<g:plusone size="' . $button_size . '" annotation="' . $annotation . '" href="' . $this->url . '" ' . $width . '></g:plusone>';
267
+ return $this->linkRaw($raw);
268
+ }
269
+
270
+ /**
271
+ * Delicious
272
+ *
273
+ * @return string
274
+ */
275
+ public function delicious()
276
+ {
277
+ $url = "//del.icio.us/save/get_bookmarklet_save??url={$this->encode_url}&title={$this->encode_title}";
278
+ $alt = Text::locale("Bookmark this on Delicious");
279
+ $icon = Url::images("delicious.png");
280
+ return $this->link($url, $alt, $icon, 16, 16);
281
+ }
282
+
283
+ /**
284
+ * Digg
285
+ *
286
+ * @return string
287
+ */
288
+ public function digg()
289
+ {
290
+ $url = "//digg.com/submit?url={$this->encode_url}&title={$this->encode_title}";
291
+ $alt = Text::locale("Bookmark this on Digg");
292
+ $icon = Url::images("digg.png");
293
+ return $this->link($url, $alt, $icon, 16, 16);
294
+ }
295
+
296
+ /**
297
+ * Facebook
298
+ *
299
+ * @return string
300
+ */
301
+ public function facebook()
302
+ {
303
+ $url = "http://www.facebook.com/share.php?u={$this->encode_url}&t={$this->encode_title}";
304
+ $alt = Text::locale("Share on Facebook");
305
+ $icon = Url::images("facebook.png");
306
+ return $this->link($url, $alt, $icon, 16, 16);
307
+ }
308
+
309
+ /**
310
+ * Facebook Like Button
311
+ *
312
+ * @return string
313
+ */
314
+ public function facebookLike()
315
+ {
316
+ $options = $this->option->getAll();
317
+ $layout = $options['facebook_like']['layout'];
318
+ $action = $options['facebook_like']['action'];
319
+ $share = $options['facebook_like']['share'] ? 'true' : 'false';
320
+ $width = $options['facebook_like']['width'];
321
+ $locale = $options['facebook']['locale'];
322
+ $version = $options['facebook']['version'];
323
+ $fb_root = $options['facebook']['fb_root'] ? '<div id="fb-root"></div>' : '';
324
+
325
+ if ($version == "html5") {
326
+ return $this->linkRaw($fb_root
327
+ . '<div class="fb-like" '
328
+ . 'data-href="' . $this->url . '" '
329
+ . 'data-layout="' . $layout . '" '
330
+ . 'data-action="' . $action . '" '
331
+ . ($width != "" ? 'data-width="' . $width . '" ' : '')
332
+ . 'data-share="' . $share . '" '
333
+ . 'data-show_faces="false" >'
334
+ . '</div>');
335
+ } elseif ($version == "xfbml") {
336
+ return $this->linkRaw($fb_root
337
+ . '<fb:like '
338
+ . 'href="' . $this->url . '" '
339
+ . 'layout="' . $layout . '" '
340
+ . 'action="' . $action . '" '
341
+ . ($width != "" ? 'width="' . $width . '" ' : '')
342
+ . 'share="' . $share . '" '
343
+ . 'show_faces="false" >'
344
+ . '</fb:like>');
345
+ } else {
346
+ return $this->linkRaw('<iframe src="//www.facebook.com/plugins/like.php?href=' . $this->encode_url
347
+ . '&amp;layout=' . $layout
348
+ . '&amp;show_faces=false'
349
+ . '&amp;width=' . $width
350
+ . '&amp;action=' . $action
351
+ . '&amp;share=' . $share
352
+ . ($locale == '' ? '' : '&amp;locale=' . $locale)
353
+ . '&amp;height=35"'
354
+ . ' scrolling="no" frameborder="0"'
355
+ . ' style="border:none; overflow:hidden; width:' . $width . 'px; height:35px;"'
356
+ . ' allowTransparency="true"></iframe>');
357
+ }
358
+
359
+ }
360
+
361
+ /**
362
+ * Facebook Share
363
+ *
364
+ * @return string
365
+ */
366
+ public function facebookShare()
367
+ {
368
+ $options = $this->option->getAll();
369
+ $url = $this->url;
370
+ $version = $options['facebook']['version'];
371
+ $fb_root = $options['facebook']['fb_root'] ? '<div id="fb-root"></div>' : '';
372
+ $width = $options['facebook_share']['width'];
373
+ $type = $options['facebook_share']['type'];
374
+
375
+ if ($version == "html5") {
376
+ return $this->linkRaw($fb_root
377
+ . '<div class="fb-share-button" '
378
+ . 'data-href="' . $url . '" '
379
+ . ($width != "" ? 'data-width="' . $width . '" ' : '')
380
+ . 'data-type="' . $type . '">'
381
+ . '</div>');
382
+ } else {
383
+ return $this->linkRaw($fb_root
384
+ . '<fb:share-button '
385
+ . 'href="' . $url . '" '
386
+ . ($width != "" ? 'width="' . $width . '" ' : '')
387
+ . 'type="' . $type . '" >'
388
+ . '</fb:share-button>');
389
+ }
390
+ }
391
+
392
+ /**
393
+ * Facebook Send
394
+ *
395
+ * @return string
396
+ */
397
+ public function facebookSend()
398
+ {
399
+ $options = $this->option->getAll();
400
+ $url = $this->url;
401
+ $version = $options['facebook']['version'];
402
+ $fb_root = $options['facebook']['fb_root'] ? '<div id="fb-root"></div>' : '';
403
+ $colorscheme = $options['facebook_send']['colorscheme'];
404
+ $width = $options['facebook_send']['width'];
405
+ $height = $options['facebook_send']['height'];
406
+
407
+ if ($version == "html5") {
408
+ return $this->linkRaw($fb_root
409
+ . '<div class="fb-send" '
410
+ . 'data-href="' . $url . '" '
411
+ . ($width != "" ? 'data-width="' . $width . '" ' : '')
412
+ . ($height != "" ? 'data-height="' . $height . '" ' : '')
413
+ . 'data-colorscheme="' . $colorscheme . '">'
414
+ . '</div>');
415
+ } else {
416
+ return $this->linkRaw($fb_root
417
+ . '<fb:send '
418
+ . 'href="' . $url . '" '
419
+ . ($width != "" ? 'width="' . $width . '" ' : '')
420
+ . ($height != "" ? 'height="' . $height . '" ' : '')
421
+ . 'colorscheme="' . $colorscheme . '" >'
422
+ . '</fb:send>');
423
+ }
424
+ }
425
+
426
+ /**
427
+ * reddit
428
+ *
429
+ * @return string
430
+ */
431
+ public function reddit()
432
+ {
433
+ $url = "//www.reddit.com/submit?url={$this->encode_url}&title={$this->encode_title}";
434
+ $alt = Text::locale("Share on reddit");
435
+ $icon = Url::images("reddit.png");
436
+ return $this->link($url, $alt, $icon, 16, 16);
437
+ }
438
+
439
+ /**
440
+ * LinkedIn
441
+ *
442
+ * @return string
443
+ */
444
+ public function linkedin()
445
+ {
446
+ $url = "//www.linkedin.com/shareArticle?mini=true&url={$this->encode_url}&title={$this->encode_title}";
447
+ $alt = Text::locale("Share on LinkedIn");
448
+ $icon = Url::images("linkedin.png");
449
+ return $this->link($url, $alt, $icon, 16, 16);
450
+ }
451
+
452
+ /**
453
+ * Instapaper
454
+ *
455
+ * @return string
456
+ */
457
+ public function instapaper()
458
+ {
459
+ return $this->linkRaw('<iframe border="0" scrolling="no" width="78" height="17" allowtransparency="true" frameborder="0" '
460
+ . 'style="margin-bottom: -3px; z-index: 1338; border: 0px; background-color: transparent; overflow: hidden;" '
461
+ . 'src="//www.instapaper.com/e2?url=' . $this->encode_url . '&title=' . $this->encode_title . '&description="'
462
+ . '></iframe>');
463
+ }
464
+
465
+ /**
466
+ * StumbleUpon
467
+ *
468
+ * @return string
469
+ */
470
+ public function stumbleupon()
471
+ {
472
+ $url = "//www.stumbleupon.com/submit?url={$this->encode_url}&title={$this->encode_title}";
473
+ $alt = Text::locale("Share on StumbleUpon");
474
+ $icon = Url::images("stumbleupon.png");
475
+ return $this->link($url, $alt, $icon, 16, 16);
476
+ }
477
+
478
+ /**
479
+ * mixi Check
480
+ *
481
+ * @return string
482
+ */
483
+ public function mixi()
484
+ {
485
+ $options = $this->option->getAll();
486
+ $data_button = $options['mixi']['button'];
487
+ $data_key = $options['mixi']['check_key'];
488
+
489
+ return $this->linkRaw('<a href="http://mixi.jp/share.pl" class="mixi-check-button"'
490
+ . " data-url='{$this->url}'"
491
+ . " data-button='{$data_button}'"
492
+ . " data-key='{$data_key}'>Check</a>"
493
+ . '<script type="text/javascript" src="//static.mixi.jp/js/share.js"></script>');
494
+ }
495
+
496
+ /**
497
+ * mixi Like
498
+ *
499
+ * @return string
500
+ */
501
+ public function mixiLike()
502
+ {
503
+ $options = $this->option->getAll();
504
+ $data_key = $options['mixi']['check_key'];
505
+ $width = $options['mixi_like']['width'];
506
+
507
+ return $this->linkRaw('<iframe src="http://plugins.mixi.jp/favorite.pl?href=' . $this->encode_url . '&service_key=' . $data_key . '&show_faces=false" '
508
+ . 'scrolling="no" '
509
+ . 'frameborder="0" '
510
+ . 'allowTransparency="true" '
511
+ . 'style="border:0; overflow:hidden; width:' . $width . 'px; height:20px;"></iframe>');
512
+ }
513
+
514
+ /**
515
+ * GREE Social Feedback
516
+ *
517
+ * @return string
518
+ */
519
+ public function gree()
520
+ {
521
+ $options = $this->option->getAll();
522
+ $url = $this->encode_url;
523
+ $type = $options['gree']['button_type'];
524
+ $size = $options['gree']['button_size'];
525
+ switch ($type) {
526
+ case '0':
527
+ $btn_type = 'btn_iine';
528
+ break;
529
+ case '1':
530
+ $btn_type = 'btn_kininaru';
531
+ break;
532
+ case '2':
533
+ $btn_type = 'btn_osusume';
534
+ break;
535
+ case '3':
536
+ $btn_type = 'btn_share';
537
+ break;
538
+ case '4':
539
+ $btn_type = 'btn_logo';
540
+ break;
541
+ default:
542
+ $btn_type = 'btn_logo';
543
+ }
544
+ $alt = Text::locale("Share on GREE");
545
+ return $this->linkRaw('<a href="http://gree.jp/?mode=share&act=write'
546
+ . '&url=' . $url
547
+ . '&button_type=' . $type
548
+ . '&button_size=' . $size
549
+ . '&guid=ON" '
550
+ . 'title="' . $alt . '" target=_blank>'
551
+ . '<img alt="' . $alt . '" title="' . $alt . '" '
552
+ . 'src="http://i.share.gree.jp/img/share/button/' . $btn_type . '_' . $size . '.png">'
553
+ . '</a>');
554
+ }
555
+
556
+ /**
557
+ * atode
558
+ *
559
+ * @return string
560
+ */
561
+ public function atode()
562
+ {
563
+ $options = $this->option->getAll();
564
+ $type = $options['atode']['button_type'];
565
+ switch ($type) {
566
+ case 'iconsja':
567
+ return $this->linkRaw('<a href=\'http://atode.cc/\' onclick=\'javascript:(function(){var s=document.createElement("scr"+"ipt");s.charset="UTF-8";s.language="javascr"+"ipt";s.type="text/javascr"+"ipt";var d=new Date;s.src="http://atode.cc/bjs.php?d="+d.getMilliseconds();document.body.appendChild(s)})();return false;\'><img src="http://atode.cc/img/iconsja.gif" alt="email this" border="0" align="absmiddle" width="16" height="16"></a>');
568
+ case 'iconnja':
569
+ return $this->linkRaw('<a href=\'http://atode.cc/\' onclick=\'javascript:(function(){var s=document.createElement("scr"+"ipt");s.charset="UTF-8";s.language="javascr"+"ipt";s.type="text/javascr"+"ipt";var d=new Date;s.src="http://atode.cc/bjs.php?d="+d.getMilliseconds();document.body.appendChild(s)})();return false;\'><img src="http://atode.cc/img/iconnja.gif" alt="email this" border="0" align="absmiddle" width="66" height="20"></a>');
570
+ case 'iconnen':
571
+ return $this->linkRaw('<a href=\'http://atode.cc/\' onclick=\'javascript:(function(){var s=document.createElement("scr"+"ipt");s.charset="UTF-8";s.language="javascr"+"ipt";s.type="text/javascr"+"ipt";var d=new Date;s.src="http://atode.cc/bjs.php?d="+d.getMilliseconds();document.body.appendChild(s)})();return false;\'><img src="http://atode.cc/img/iconnen.gif" alt="email this" border="0" align="absmiddle" width="66" height="20"></a>');
572
+ }
573
+ return '';
574
+ }
575
+
576
+ /**
577
+ * LINE
578
+ *
579
+ * @return string
580
+ */
581
+ public function line()
582
+ {
583
+ $options = $this->option->getAll();
584
+ if ($options['line']['button_type'] == "line88x20") {
585
+ $icon = Url::images("line88x20.png");
586
+ $width = 88;
587
+ $height = 20;
588
+ } else {
589
+ $icon = Url::images("line20x20.png");
590
+ $width = 20;
591
+ $height = 20;
592
+ }
593
+
594
+ if ($options['line']['protocol'] === 'line') {
595
+ $url = "line://msg/text/{$this->encode_title}%0D%0A{$this->encode_url}";
596
+ } else {
597
+ $url = "http://line.me/R/msg/text/?{$this->encode_title}%0D%0A{$this->encode_url}";
598
+ }
599
+ return $this->link($url, "LINEで送る", $icon, $width, $height, false);
600
+ }
601
+
602
+ /**
603
+ * Pocket
604
+ *
605
+ * @return string
606
+ */
607
+ public function pocket()
608
+ {
609
+ $options = $this->option->getAll();
610
+ return $this->linkRaw('<a href="https://getpocket.com/save" class="pocket-btn" data-lang="en" data-save-url="' . $this->url . '" data-pocket-count="' . $options['pocket']['button_type'] . '" data-pocket-align="left" >Pocket</a><script type="text/javascript">!function(d,i){if(!d.getElementById(i)){var j=d.createElement("script");j.id=i;j.src="https://widgets.getpocket.com/v1/j/btn.js?v=1";var w=d.getElementById(i);d.body.appendChild(j);}}(document,"pocket-btn-js");</script>');
611
+ }
612
+
613
+
614
+ /**
615
+ * pinterest
616
+ *
617
+ * @return string
618
+ */
619
+ public function pinterest()
620
+ {
621
+ $options = $this->option->getAll();
622
+ $pinterest = $options['pinterest'];
623
+ if ($pinterest['type'] === 'hover') {
624
+ return '';
625
+ }
626
+ $data_pin_shape = '';
627
+ $data_pin_color = '';
628
+ $data_pin_lang = '';
629
+
630
+ if ($pinterest['shape'] === 'round') {
631
+ // 円形ボタン
632
+ $data_pin_shape = 'data-pin-shape="round"';
633
+
634
+ if ($pinterest['size'] === 'large') {
635
+ $data_pin_height = 'data-pin-height="32"';
636
+ $img_src = "//assets.pinterest.com/images/pidgets/pinit_fg_en_round_red_32.png";
637
+ } else {
638
+ $data_pin_height = '';
639
+ $img_src = '//assets.pinterest.com/images/pidgets/pinit_fg_en_round_red_16.png';
640
+ }
641
+ } else {
642
+ // 長方形ボタン
643
+ $color = $pinterest['color'];
644
+ $lang = $pinterest['lang'];
645
+ $data_pin_color = 'data-pin-color="' . $color . '"';
646
+ $data_pin_lang = 'data-pin-lang="' . $lang . '"';
647
+
648
+ if ($pinterest['size'] === 'large') {
649
+ $data_pin_height = 'data-pin-height="28"';
650
+ $img_src = "//assets.pinterest.com/images/pidgets/pinit_fg_${lang}_rect_${color}_28.png";
651
+ } else {
652
+ $data_pin_height = '';
653
+ $img_src = "//assets.pinterest.com/images/pidgets/pinit_fg_${lang}_round_${$color}_16.png";
654
+ }
655
+ }
656
+
657
+ return $this->linkRaw(
658
+ '<a href="//jp.pinterest.com/pin/create/button/"'
659
+ . ' data-pin-do="buttonBookmark"'
660
+ . ' ' . $data_pin_color
661
+ . ' ' . $data_pin_lang
662
+ . ' ' . $data_pin_shape
663
+ . ' ' . $data_pin_height
664
+ . '>'
665
+ . '<img src="' . $img_src . '" /></a>'
666
+ );
667
+ }
668
+ }
src/WpSocialBookmarkingLight/Tests/BuilderTest.php ADDED
@@ -0,0 +1,143 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace WpSocialBookmarkingLight\Tests;
4
+
5
+ use PHPUnit\Framework\TestCase;
6
+ use WpSocialBookmarkingLight\Builder;
7
+ use WpSocialBookmarkingLight\OptionInterface;
8
+ use WpSocialBookmarkingLight\Renderer;
9
+
10
+ require_once __DIR__.'/Helper/setting.php';
11
+ require_once __DIR__.'/Helper/Builder/global.php';
12
+
13
+ /**
14
+ * Class BuilderTest
15
+ * @package WpSocialBookmarkingLight\Tests
16
+ */
17
+ class BuilderTest extends TestCase
18
+ {
19
+ /**
20
+ * @test
21
+ * @dataProvider headContainsTestCase
22
+ *
23
+ * @param array $option
24
+ * @param $expectHtml
25
+ */
26
+ public function headContains(array $option, $expectHtml)
27
+ {
28
+ $stub = $this->createMock(OptionInterface::class);
29
+ $stub->method('getAll')->willReturn($option);
30
+
31
+ $builder = new Builder(new Renderer(), $stub);
32
+ $this->assertContains($expectHtml, $builder->head());
33
+ }
34
+
35
+ /**
36
+ * @return array
37
+ */
38
+ public function headContainsTestCase()
39
+ {
40
+ return [
41
+ [
42
+ ['services' => 'mixi', 'mixi' => ['check_robots' => 'test'], 'styles' => ''],
43
+ '<meta name="mixi-check-robots" content="test"/>'
44
+ ],
45
+ [
46
+ ['services' => 'tumblr', 'styles' => ''],
47
+ '<script type="text/javascript" src="//platform.tumblr.com/v1/share.js"></script>'
48
+ ],
49
+ [
50
+ ['services' => 'facebook_like', 'facebook' => ['version' => 'html5', 'locale' => 'en'], 'styles' => ''],
51
+ '//connect.facebook.net/en/sdk.js#xfbml=1&version=v2.7'
52
+ ],
53
+ [
54
+ ['services' => 'facebook_share', 'facebook' => ['version' => 'html5', 'locale' => 'ja'], 'styles' => ''],
55
+ '//connect.facebook.net/ja/sdk.js#xfbml=1&version=v2.7'
56
+ ],
57
+ [
58
+ ['services' => 'facebook_send', 'facebook' => ['version' => 'html5', 'locale' => 'de'], 'styles' => ''],
59
+ '//connect.facebook.net/de/sdk.js#xfbml=1&version=v2.7'
60
+ ],
61
+ [
62
+ ['services' => '', 'styles' => 'dummy css'],
63
+ 'dummy css'
64
+ ]
65
+ ];
66
+ }
67
+
68
+ /**
69
+ * @test
70
+ * @dataProvider footerContainsTestCase
71
+ *
72
+ * @param array $option
73
+ * @param $expectHtml
74
+ */
75
+ public function footerContains(array $option, $expectHtml)
76
+ {
77
+ $stub = $this->createMock(OptionInterface::class);
78
+ $stub->method('getAll')->willReturn($option);
79
+
80
+ $builder = new Builder(new Renderer(), $stub);
81
+ $this->assertContains($expectHtml, $builder->footer());
82
+ }
83
+
84
+ /**
85
+ * @return array
86
+ */
87
+ public function footerContainsTestCase()
88
+ {
89
+ return [
90
+ [
91
+ ['services' => 'twitter'],
92
+ '//platform.twitter.com/widgets.js'
93
+ ],
94
+ [
95
+ ['services' => 'google_plus_one', 'google_plus_one' => ['lang' => 'ja']],
96
+ 'https://apis.google.com/js/platform.js'
97
+ ],
98
+ [
99
+ ['services' => 'pinterest', 'pinterest' => ['type' => 'all']],
100
+ '//assets.pinterest.com/js/pinit.js'
101
+ ]
102
+ ];
103
+ }
104
+
105
+ /**
106
+ * @test
107
+ */
108
+ public function contentContains()
109
+ {
110
+ $stub = $this->createMock(OptionInterface::class);
111
+ $stub->method('getAll')->willReturn([
112
+ "hatena_button" => ['layout' => 'simple-balloon'],
113
+ "twitter" => [
114
+ 'via' => "",
115
+ 'lang' => "",
116
+ 'size' => "",
117
+ 'related' => "",
118
+ 'hashtags' => "",
119
+ "dnt" => false
120
+ ],
121
+ 'facebook' => [
122
+ 'locale' => 'en_US',
123
+ 'version' => 'xfbml',
124
+ 'fb_root' => true
125
+ ],
126
+ 'facebook_like' => [
127
+ 'layout' => 'button_count',
128
+ 'action' => 'like',
129
+ 'share' => false,
130
+ 'width' => '100'
131
+ ],
132
+ 'pocket' => ['button_type' => 'none']
133
+ ]);
134
+ $builder = new Builder(new Renderer(), $stub);
135
+ $services = 'hatena_button,twitter,facebook_like,pocket';
136
+
137
+ $this->assertContains('wsbl_hatena_button', $builder->content($services, '', ''));
138
+ $this->assertContains('wsbl_twitter', $builder->content($services, '', ''));
139
+ $this->assertContains('wsbl_facebook_like', $builder->content($services, '', ''));
140
+ $this->assertContains('wsbl_pocket', $builder->content($services, '', ''));
141
+ $this->assertContains('[`dummy` not found]', $builder->content('dummy', '', ''));
142
+ }
143
+ }
src/WpSocialBookmarkingLight/Tests/Helper/Builder/global.php ADDED
@@ -0,0 +1,38 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /**
4
+ * stub get_bloginfo
5
+ *
6
+ * @param $key
7
+ * @return string
8
+ */
9
+ function get_bloginfo($key)
10
+ {
11
+ return 'blog';
12
+ }
13
+
14
+ /**
15
+ * stub get_option
16
+ *
17
+ * @param $key
18
+ * @return mixed
19
+ */
20
+ function get_option($key)
21
+ {
22
+ $o = [
23
+ 'blog_charset' => 'UTF-8'
24
+ ];
25
+ return $o[$key];
26
+ }
27
+
28
+ /**
29
+ * stub __
30
+ *
31
+ * @param $val
32
+ * @param $domain
33
+ * @return mixed
34
+ */
35
+ function __($val, $domain)
36
+ {
37
+ return $val;
38
+ }
src/WpSocialBookmarkingLight/Tests/Helper/setting.php ADDED
@@ -0,0 +1,4 @@
 
 
 
 
1
+ <?php
2
+
3
+ define("WP_SOCIAL_BOOKMARKING_LIGHT_DIR", dirname(__FILE__)."/../../../..");
4
+ define("WP_SOCIAL_BOOKMARKING_LIGHT_DOMAIN", "wp-social-bookmarking-light");
src/WpSocialBookmarkingLight/Tests/ServiceTest.php ADDED
@@ -0,0 +1,62 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace WpSocialBookmarkingLight\Tests;
4
+
5
+ use PHPUnit\Framework\TestCase;
6
+ use WpSocialBookmarkingLight\Service;
7
+
8
+ require_once __DIR__.'/Helper/setting.php';
9
+
10
+ /**
11
+ * Class ServiceTest
12
+ * @package WpSocialBookmarkingLight\Tests
13
+ */
14
+ class ServiceTest extends TestCase
15
+ {
16
+ /**
17
+ * @test
18
+ * @dataProvider testCaseServiceType
19
+ *
20
+ * @param $service
21
+ * @param $expectedFlag
22
+ */
23
+ public function testGetServiceTypes($service, $expectedFlag)
24
+ {
25
+ $serviceTypes = Service::getServiceTypes();
26
+ $this->assertEquals($expectedFlag, in_array($service, $serviceTypes));
27
+ }
28
+
29
+ /**
30
+ * @return array
31
+ */
32
+ public function testCaseServiceType()
33
+ {
34
+ return [
35
+ ['hatena', true],
36
+ ['hatena_users', true],
37
+ ['hatena_button', true],
38
+ ['twitter', true],
39
+ ['tumblr', true],
40
+ ['google', true],
41
+ ['google_plus_one', true],
42
+ ['delicious', true],
43
+ ['digg', true],
44
+ ['facebook', true],
45
+ ['facebook_like', true],
46
+ ['facebook_share', true],
47
+ ['facebook_send', true],
48
+ ['reddit', true],
49
+ ['linkedin', true],
50
+ ['instapaper', true],
51
+ ['stumbleupon', true],
52
+ ['mixi', true],
53
+ ['mixi_like', true],
54
+ ['gree', true],
55
+ ['atode', true],
56
+ ['line', true],
57
+ ['pocket', true],
58
+ ['pinterest', true],
59
+ ['dummy', false],
60
+ ];
61
+ }
62
+ }
src/WpSocialBookmarkingLight/Util/Text.php ADDED
@@ -0,0 +1,21 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace WpSocialBookmarkingLight\Util;
4
+
5
+ /**
6
+ * Class Text
7
+ * @package WpSocialBookmarkingLight\Util
8
+ */
9
+ class Text
10
+ {
11
+ /**
12
+ * text localize
13
+ *
14
+ * @param $val
15
+ * @return string
16
+ */
17
+ public static function locale($val)
18
+ {
19
+ return __($val, WP_SOCIAL_BOOKMARKING_LIGHT_DOMAIN);
20
+ }
21
+ }
src/WpSocialBookmarkingLight/Util/Url.php ADDED
@@ -0,0 +1,56 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace WpSocialBookmarkingLight\Util;
4
+
5
+ /**
6
+ * Class Url
7
+ * @package WpSocialBookmarkingLight\Util
8
+ */
9
+ class Url
10
+ {
11
+ /**
12
+ * Returns base url
13
+ * (in public directory)
14
+ *
15
+ * @param string $path
16
+ * @return string
17
+ */
18
+ public static function base($path = "")
19
+ {
20
+ return plugins_url("wp-social-bookmarking-light/public" . self::lslash($path));
21
+ }
22
+
23
+ /**
24
+ * Returns images url
25
+ *
26
+ * @param string $path
27
+ * @return string
28
+ */
29
+ public static function images($path = "")
30
+ {
31
+ return self::base("images" . self::lslash($path));
32
+ }
33
+
34
+ /**
35
+ * Returns css url
36
+ *
37
+ * @param string $path
38
+ * @return string
39
+ */
40
+ public static function css($path = "")
41
+ {
42
+ return self::base("css" . self::lslash($path));
43
+ }
44
+
45
+ /**
46
+ * @param $path
47
+ * @return string
48
+ */
49
+ private static function lslash($path)
50
+ {
51
+ if ($path && is_string($path)) {
52
+ $path = "/" . ltrim($path, "/");
53
+ }
54
+ return $path;
55
+ }
56
+ }
vendor/autoload.php ADDED
@@ -0,0 +1,7 @@
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ // autoload.php @generated by Composer
4
+
5
+ require_once __DIR__ . '/composer/autoload_real.php';
6
+
7
+ return ComposerAutoloaderInit4fe4c6e52fdaf3237998a04b24e6983b::getLoader();
vendor/composer/ClassLoader.php ADDED
@@ -0,0 +1,441 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * This file is part of Composer.
5
+ *
6
+ * (c) Nils Adermann <naderman@naderman.de>
7
+ * Jordi Boggiano <j.boggiano@seld.be>
8
+ *
9
+ * For the full copyright and license information, please view the LICENSE
10
+ * file that was distributed with this source code.
11
+ */
12
+
13
+ namespace Composer\Autoload;
14
+
15
+ /**
16
+ * ClassLoader implements a PSR-0, PSR-4 and classmap class loader.
17
+ *
18
+ * $loader = new \Composer\Autoload\ClassLoader();
19
+ *
20
+ * // register classes with namespaces
21
+ * $loader->add('Symfony\Component', __DIR__.'/component');
22
+ * $loader->add('Symfony', __DIR__.'/framework');
23
+ *
24
+ * // activate the autoloader
25
+ * $loader->register();
26
+ *
27
+ * // to enable searching the include path (eg. for PEAR packages)
28
+ * $loader->setUseIncludePath(true);
29
+ *
30
+ * In this example, if you try to use a class in the Symfony\Component
31
+ * namespace or one of its children (Symfony\Component\Console for instance),
32
+ * the autoloader will first look for the class under the component/
33
+ * directory, and it will then fallback to the framework/ directory if not
34
+ * found before giving up.
35
+ *
36
+ * This class is loosely based on the Symfony UniversalClassLoader.
37
+ *
38
+ * @author Fabien Potencier <fabien@symfony.com>
39
+ * @author Jordi Boggiano <j.boggiano@seld.be>
40
+ * @see http://www.php-fig.org/psr/psr-0/
41
+ * @see http://www.php-fig.org/psr/psr-4/
42
+ */
43
+ class ClassLoader
44
+ {
45
+ // PSR-4
46
+ private $prefixLengthsPsr4 = array();
47
+ private $prefixDirsPsr4 = array();
48
+ private $fallbackDirsPsr4 = array();
49
+
50
+ // PSR-0
51
+ private $prefixesPsr0 = array();
52
+ private $fallbackDirsPsr0 = array();
53
+
54
+ private $useIncludePath = false;
55
+ private $classMap = array();
56
+ private $classMapAuthoritative = false;
57
+ private $missingClasses = array();
58
+ private $apcuPrefix;
59
+
60
+ public function getPrefixes()
61
+ {
62
+ if (!empty($this->prefixesPsr0)) {
63
+ return call_user_func_array('array_merge', $this->prefixesPsr0);
64
+ }
65
+
66
+ return array();
67
+ }
68
+
69
+ public function getPrefixesPsr4()
70
+ {
71
+ return $this->prefixDirsPsr4;
72
+ }
73
+
74
+ public function getFallbackDirs()
75
+ {
76
+ return $this->fallbackDirsPsr0;
77
+ }
78
+
79
+ public function getFallbackDirsPsr4()
80
+ {
81
+ return $this->fallbackDirsPsr4;
82
+ }
83
+
84
+ public function getClassMap()
85
+ {
86
+ return $this->classMap;
87
+ }
88
+
89
+ /**
90
+ * @param array $classMap Class to filename map
91
+ */
92
+ public function addClassMap(array $classMap)
93
+ {
94
+ if ($this->classMap) {
95
+ $this->classMap = array_merge($this->classMap, $classMap);
96
+ } else {
97
+ $this->classMap = $classMap;
98
+ }
99
+ }
100
+
101
+ /**
102
+ * Registers a set of PSR-0 directories for a given prefix, either
103
+ * appending or prepending to the ones previously set for this prefix.
104
+ *
105
+ * @param string $prefix The prefix
106
+ * @param array|string $paths The PSR-0 root directories
107
+ * @param bool $prepend Whether to prepend the directories
108
+ */
109
+ public function add($prefix, $paths, $prepend = false)
110
+ {
111
+ if (!$prefix) {
112
+ if ($prepend) {
113
+ $this->fallbackDirsPsr0 = array_merge(
114
+ (array) $paths,
115
+ $this->fallbackDirsPsr0
116
+ );
117
+ } else {
118
+ $this->fallbackDirsPsr0 = array_merge(
119
+ $this->fallbackDirsPsr0,
120
+ (array) $paths
121
+ );
122
+ }
123
+
124
+ return;
125
+ }
126
+
127
+ $first = $prefix[0];
128
+ if (!isset($this->prefixesPsr0[$first][$prefix])) {
129
+ $this->prefixesPsr0[$first][$prefix] = (array) $paths;
130
+
131
+ return;
132
+ }
133
+ if ($prepend) {
134
+ $this->prefixesPsr0[$first][$prefix] = array_merge(
135
+ (array) $paths,
136
+ $this->prefixesPsr0[$first][$prefix]
137
+ );
138
+ } else {
139
+ $this->prefixesPsr0[$first][$prefix] = array_merge(
140
+ $this->prefixesPsr0[$first][$prefix],
141
+ (array) $paths
142
+ );
143
+ }
144
+ }
145
+
146
+ /**
147
+ * Registers a set of PSR-4 directories for a given namespace, either
148
+ * appending or prepending to the ones previously set for this namespace.
149
+ *
150
+ * @param string $prefix The prefix/namespace, with trailing '\\'
151
+ * @param array|string $paths The PSR-4 base directories
152
+ * @param bool $prepend Whether to prepend the directories
153
+ *
154
+ * @throws \InvalidArgumentException
155
+ */
156
+ public function addPsr4($prefix, $paths, $prepend = false)
157
+ {
158
+ if (!$prefix) {
159
+ // Register directories for the root namespace.
160
+ if ($prepend) {
161
+ $this->fallbackDirsPsr4 = array_merge(
162
+ (array) $paths,
163
+ $this->fallbackDirsPsr4
164
+ );
165
+ } else {
166
+ $this->fallbackDirsPsr4 = array_merge(
167
+ $this->fallbackDirsPsr4,
168
+ (array) $paths
169
+ );
170
+ }
171
+ } elseif (!isset($this->prefixDirsPsr4[$prefix])) {
172
+ // Register directories for a new namespace.
173
+ $length = strlen($prefix);
174
+ if ('\\' !== $prefix[$length - 1]) {
175
+ throw new \InvalidArgumentException("A non-empty PSR-4 prefix must end with a namespace separator.");
176
+ }
177
+ $this->prefixLengthsPsr4[$prefix[0]][$prefix] = $length;
178
+ $this->prefixDirsPsr4[$prefix] = (array) $paths;
179
+ } elseif ($prepend) {
180
+ // Prepend directories for an already registered namespace.
181
+ $this->prefixDirsPsr4[$prefix] = array_merge(
182
+ (array) $paths,
183
+ $this->prefixDirsPsr4[$prefix]
184
+ );
185
+ } else {
186
+ // Append directories for an already registered namespace.
187
+ $this->prefixDirsPsr4[$prefix] = array_merge(
188
+ $this->prefixDirsPsr4[$prefix],
189
+ (array) $paths
190
+ );
191
+ }
192
+ }
193
+
194
+ /**
195
+ * Registers a set of PSR-0 directories for a given prefix,
196
+ * replacing any others previously set for this prefix.
197
+ *
198
+ * @param string $prefix The prefix
199
+ * @param array|string $paths The PSR-0 base directories
200
+ */
201
+ public function set($prefix, $paths)
202
+ {
203
+ if (!$prefix) {
204
+ $this->fallbackDirsPsr0 = (array) $paths;
205
+ } else {
206
+ $this->prefixesPsr0[$prefix[0]][$prefix] = (array) $paths;
207
+ }
208
+ }
209
+
210
+ /**
211
+ * Registers a set of PSR-4 directories for a given namespace,
212
+ * replacing any others previously set for this namespace.
213
+ *
214
+ * @param string $prefix The prefix/namespace, with trailing '\\'
215
+ * @param array|string $paths The PSR-4 base directories
216
+ *
217
+ * @throws \InvalidArgumentException
218
+ */
219
+ public function setPsr4($prefix, $paths)
220
+ {
221
+ if (!$prefix) {
222
+ $this->fallbackDirsPsr4 = (array) $paths;
223
+ } else {
224
+ $length = strlen($prefix);
225
+ if ('\\' !== $prefix[$length - 1]) {
226
+ throw new \InvalidArgumentException("A non-empty PSR-4 prefix must end with a namespace separator.");
227
+ }
228
+ $this->prefixLengthsPsr4[$prefix[0]][$prefix] = $length;
229
+ $this->prefixDirsPsr4[$prefix] = (array) $paths;
230
+ }
231
+ }
232
+
233
+ /**
234
+ * Turns on searching the include path for class files.
235
+ *
236
+ * @param bool $useIncludePath
237
+ */
238
+ public function setUseIncludePath($useIncludePath)
239
+ {
240
+ $this->useIncludePath = $useIncludePath;
241
+ }
242
+
243
+ /**
244
+ * Can be used to check if the autoloader uses the include path to check
245
+ * for classes.
246
+ *
247
+ * @return bool
248
+ */
249
+ public function getUseIncludePath()
250
+ {
251
+ return $this->useIncludePath;
252
+ }
253
+
254
+ /**
255
+ * Turns off searching the prefix and fallback directories for classes
256
+ * that have not been registered with the class map.
257
+ *
258
+ * @param bool $classMapAuthoritative
259
+ */
260
+ public function setClassMapAuthoritative($classMapAuthoritative)
261
+ {
262
+ $this->classMapAuthoritative = $classMapAuthoritative;
263
+ }
264
+
265
+ /**
266
+ * Should class lookup fail if not found in the current class map?
267
+ *
268
+ * @return bool
269
+ */
270
+ public function isClassMapAuthoritative()
271
+ {
272
+ return $this->classMapAuthoritative;
273
+ }
274
+
275
+ /**
276
+ * APCu prefix to use to cache found/not-found classes, if the extension is enabled.
277
+ *
278
+ * @param string|null $apcuPrefix
279
+ */
280
+ public function setApcuPrefix($apcuPrefix)
281
+ {
282
+ $this->apcuPrefix = function_exists('apcu_fetch') && ini_get('apc.enabled') ? $apcuPrefix : null;
283
+ }
284
+
285
+ /**
286
+ * The APCu prefix in use, or null if APCu caching is not enabled.
287
+ *
288
+ * @return string|null
289
+ */
290
+ public function getApcuPrefix()
291
+ {
292
+ return $this->apcuPrefix;
293
+ }
294
+
295
+ /**
296
+ * Registers this instance as an autoloader.
297
+ *
298
+ * @param bool $prepend Whether to prepend the autoloader or not
299
+ */
300
+ public function register($prepend = false)
301
+ {
302
+ spl_autoload_register(array($this, 'loadClass'), true, $prepend);
303
+ }
304
+
305
+ /**
306
+ * Unregisters this instance as an autoloader.
307
+ */
308
+ public function unregister()
309
+ {
310
+ spl_autoload_unregister(array($this, 'loadClass'));
311
+ }
312
+
313
+ /**
314
+ * Loads the given class or interface.
315
+ *
316
+ * @param string $class The name of the class
317
+ * @return bool|null True if loaded, null otherwise
318
+ */
319
+ public function loadClass($class)
320
+ {
321
+ if ($file = $this->findFile($class)) {
322
+ includeFile($file);
323
+
324
+ return true;
325
+ }
326
+ }
327
+
328
+ /**
329
+ * Finds the path to the file where the class is defined.
330
+ *
331
+ * @param string $class The name of the class
332
+ *
333
+ * @return string|false The path if found, false otherwise
334
+ */
335
+ public function findFile($class)
336
+ {
337
+ // class map lookup
338
+ if (isset($this->classMap[$class])) {
339
+ return $this->classMap[$class];
340
+ }
341
+ if ($this->classMapAuthoritative || isset($this->missingClasses[$class])) {
342
+ return false;
343
+ }
344
+ if (null !== $this->apcuPrefix) {
345
+ $file = apcu_fetch($this->apcuPrefix.$class, $hit);
346
+ if ($hit) {
347
+ return $file;
348
+ }
349
+ }
350
+
351
+ $file = $this->findFileWithExtension($class, '.php');
352
+
353
+ // Search for Hack files if we are running on HHVM
354
+ if (false === $file && defined('HHVM_VERSION')) {
355
+ $file = $this->findFileWithExtension($class, '.hh');
356
+ }
357
+
358
+ if (null !== $this->apcuPrefix) {
359
+ apcu_add($this->apcuPrefix.$class, $file);
360
+ }
361
+
362
+ if (false === $file) {
363
+ // Remember that this class does not exist.
364
+ $this->missingClasses[$class] = true;
365
+ }
366
+
367
+ return $file;
368
+ }
369
+
370
+ private function findFileWithExtension($class, $ext)
371
+ {
372
+ // PSR-4 lookup
373
+ $logicalPathPsr4 = strtr($class, '\\', DIRECTORY_SEPARATOR) . $ext;
374
+
375
+ $first = $class[0];
376
+ if (isset($this->prefixLengthsPsr4[$first])) {
377
+ foreach ($this->prefixLengthsPsr4[$first] as $prefix => $length) {
378
+ if (0 === strpos($class, $prefix)) {
379
+ foreach ($this->prefixDirsPsr4[$prefix] as $dir) {
380
+ if (file_exists($file = $dir . DIRECTORY_SEPARATOR . substr($logicalPathPsr4, $length))) {
381
+ return $file;
382
+ }
383
+ }
384
+ }
385
+ }
386
+ }
387
+
388
+ // PSR-4 fallback dirs
389
+ foreach ($this->fallbackDirsPsr4 as $dir) {
390
+ if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr4)) {
391
+ return $file;
392
+ }
393
+ }
394
+
395
+ // PSR-0 lookup
396
+ if (false !== $pos = strrpos($class, '\\')) {
397
+ // namespaced class name
398
+ $logicalPathPsr0 = substr($logicalPathPsr4, 0, $pos + 1)
399
+ . strtr(substr($logicalPathPsr4, $pos + 1), '_', DIRECTORY_SEPARATOR);
400
+ } else {
401
+ // PEAR-like class name
402
+ $logicalPathPsr0 = strtr($class, '_', DIRECTORY_SEPARATOR) . $ext;
403
+ }
404
+
405
+ if (isset($this->prefixesPsr0[$first])) {
406
+ foreach ($this->prefixesPsr0[$first] as $prefix => $dirs) {
407
+ if (0 === strpos($class, $prefix)) {
408
+ foreach ($dirs as $dir) {
409
+ if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr0)) {
410
+ return $file;
411
+ }
412
+ }
413
+ }
414
+ }
415
+ }
416
+
417
+ // PSR-0 fallback dirs
418
+ foreach ($this->fallbackDirsPsr0 as $dir) {
419
+ if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr0)) {
420
+ return $file;
421
+ }
422
+ }
423
+
424
+ // PSR-0 include paths.
425
+ if ($this->useIncludePath && $file = stream_resolve_include_path($logicalPathPsr0)) {
426
+ return $file;
427
+ }
428
+
429
+ return false;
430
+ }
431
+ }
432
+
433
+ /**
434
+ * Scope isolated include.
435
+ *
436
+ * Prevents access to $this/self from included files.
437
+ */
438
+ function includeFile($file)
439
+ {
440
+ include $file;
441
+ }
vendor/composer/LICENSE ADDED
@@ -0,0 +1,21 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+
2
+ Copyright (c) 2016 Nils Adermann, Jordi Boggiano
3
+
4
+ Permission is hereby granted, free of charge, to any person obtaining a copy
5
+ of this software and associated documentation files (the "Software"), to deal
6
+ in the Software without restriction, including without limitation the rights
7
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8
+ copies of the Software, and to permit persons to whom the Software is furnished
9
+ to do so, subject to the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be included in all
12
+ copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
20
+ THE SOFTWARE.
21
+
vendor/composer/autoload_classmap.php ADDED
@@ -0,0 +1,9 @@
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ // autoload_classmap.php @generated by Composer
4
+
5
+ $vendorDir = dirname(dirname(__FILE__));
6
+ $baseDir = dirname($vendorDir);
7
+
8
+ return array(
9
+ );
vendor/composer/autoload_namespaces.php ADDED
@@ -0,0 +1,10 @@
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ // autoload_namespaces.php @generated by Composer
4
+
5
+ $vendorDir = dirname(dirname(__FILE__));
6
+ $baseDir = dirname($vendorDir);
7
+
8
+ return array(
9
+ 'Twig_' => array($vendorDir . '/twig/twig/lib'),
10
+ );
vendor/composer/autoload_psr4.php ADDED
@@ -0,0 +1,10 @@
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ // autoload_psr4.php @generated by Composer
4
+
5
+ $vendorDir = dirname(dirname(__FILE__));
6
+ $baseDir = dirname($vendorDir);
7
+
8
+ return array(
9
+ '' => array($baseDir . '/src'),
10
+ );
vendor/composer/autoload_real.php ADDED
@@ -0,0 +1,52 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ // autoload_real.php @generated by Composer
4
+
5
+ class ComposerAutoloaderInit4fe4c6e52fdaf3237998a04b24e6983b
6
+ {
7
+ private static $loader;
8
+
9
+ public static function loadClassLoader($class)
10
+ {
11
+ if ('Composer\Autoload\ClassLoader' === $class) {
12
+ require __DIR__ . '/ClassLoader.php';
13
+ }
14
+ }
15
+
16
+ public static function getLoader()
17
+ {
18
+ if (null !== self::$loader) {
19
+ return self::$loader;
20
+ }
21
+
22
+ spl_autoload_register(array('ComposerAutoloaderInit4fe4c6e52fdaf3237998a04b24e6983b', 'loadClassLoader'), true, true);
23
+ self::$loader = $loader = new \Composer\Autoload\ClassLoader();
24
+ spl_autoload_unregister(array('ComposerAutoloaderInit4fe4c6e52fdaf3237998a04b24e6983b', 'loadClassLoader'));
25
+
26
+ $useStaticLoader = PHP_VERSION_ID >= 50600 && !defined('HHVM_VERSION') && (!function_exists('zend_loader_file_encoded') || !zend_loader_file_encoded());
27
+ if ($useStaticLoader) {
28
+ require_once __DIR__ . '/autoload_static.php';
29
+
30
+ call_user_func(\Composer\Autoload\ComposerStaticInit4fe4c6e52fdaf3237998a04b24e6983b::getInitializer($loader));
31
+ } else {
32
+ $map = require __DIR__ . '/autoload_namespaces.php';
33
+ foreach ($map as $namespace => $path) {
34
+ $loader->set($namespace, $path);
35
+ }
36
+
37
+ $map = require __DIR__ . '/autoload_psr4.php';
38
+ foreach ($map as $namespace => $path) {
39
+ $loader->setPsr4($namespace, $path);
40
+ }
41
+
42
+ $classMap = require __DIR__ . '/autoload_classmap.php';
43
+ if ($classMap) {
44
+ $loader->addClassMap($classMap);
45
+ }
46
+ }
47
+
48
+ $loader->register(true);
49
+
50
+ return $loader;
51
+ }
52
+ }
vendor/composer/autoload_static.php ADDED
@@ -0,0 +1,31 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ // autoload_static.php @generated by Composer
4
+
5
+ namespace Composer\Autoload;
6
+
7
+ class ComposerStaticInit4fe4c6e52fdaf3237998a04b24e6983b
8
+ {
9
+ public static $fallbackDirsPsr4 = array (
10
+ 0 => __DIR__ . '/../..' . '/src',
11
+ );
12
+
13
+ public static $prefixesPsr0 = array (
14
+ 'T' =>
15
+ array (
16
+ 'Twig_' =>
17
+ array (
18
+ 0 => __DIR__ . '/..' . '/twig/twig/lib',
19
+ ),
20
+ ),
21
+ );
22
+
23
+ public static function getInitializer(ClassLoader $loader)
24
+ {
25
+ return \Closure::bind(function () use ($loader) {
26
+ $loader->fallbackDirsPsr4 = ComposerStaticInit4fe4c6e52fdaf3237998a04b24e6983b::$fallbackDirsPsr4;
27
+ $loader->prefixesPsr0 = ComposerStaticInit4fe4c6e52fdaf3237998a04b24e6983b::$prefixesPsr0;
28
+
29
+ }, null, ClassLoader::class);
30
+ }
31
+ }
vendor/composer/installed.json ADDED
@@ -0,0 +1,66 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ [
2
+ {
3
+ "name": "twig/twig",
4
+ "version": "v1.32.0",
5
+ "version_normalized": "1.32.0.0",
6
+ "source": {
7
+ "type": "git",
8
+ "url": "https://github.com/twigphp/Twig.git",
9
+ "reference": "9935b662e24d6e634da88901ab534cc12e8c728f"
10
+ },
11
+ "dist": {
12
+ "type": "zip",
13
+ "url": "https://api.github.com/repos/twigphp/Twig/zipball/9935b662e24d6e634da88901ab534cc12e8c728f",
14
+ "reference": "9935b662e24d6e634da88901ab534cc12e8c728f",
15
+ "shasum": ""
16
+ },
17
+ "require": {
18
+ "php": ">=5.2.7"
19
+ },
20
+ "require-dev": {
21
+ "psr/container": "^1.0",
22
+ "symfony/debug": "~2.7",
23
+ "symfony/phpunit-bridge": "~3.2"
24
+ },
25
+ "time": "2017-02-27T00:07:03+00:00",
26
+ "type": "library",
27
+ "extra": {
28
+ "branch-alias": {
29
+ "dev-master": "1.32-dev"
30
+ }
31
+ },
32
+ "installation-source": "dist",
33
+ "autoload": {
34
+ "psr-0": {
35
+ "Twig_": "lib/"
36
+ }
37
+ },
38
+ "notification-url": "https://packagist.org/downloads/",
39
+ "license": [
40
+ "BSD-3-Clause"
41
+ ],
42
+ "authors": [
43
+ {
44
+ "name": "Fabien Potencier",
45
+ "email": "fabien@symfony.com",
46
+ "homepage": "http://fabien.potencier.org",
47
+ "role": "Lead Developer"
48
+ },
49
+ {
50
+ "name": "Armin Ronacher",
51
+ "email": "armin.ronacher@active-4.com",
52
+ "role": "Project Founder"
53
+ },
54
+ {
55
+ "name": "Twig Team",
56
+ "homepage": "http://twig.sensiolabs.org/contributors",
57
+ "role": "Contributors"
58
+ }
59
+ ],
60
+ "description": "Twig, the flexible, fast, and secure template language for PHP",
61
+ "homepage": "http://twig.sensiolabs.org",
62
+ "keywords": [
63
+ "templating"
64
+ ]
65
+ }
66
+ ]
vendor/twig/twig/.editorconfig ADDED
@@ -0,0 +1,18 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ; top-most EditorConfig file
2
+ root = true
3
+
4
+ ; Unix-style newlines
5
+ [*]
6
+ end_of_line = LF
7
+
8
+ [*.php]
9
+ indent_style = space
10
+ indent_size = 4
11
+
12
+ [*.test]
13
+ indent_style = space
14
+ indent_size = 4
15
+
16
+ [*.rst]
17
+ indent_style = space
18
+ indent_size = 4
vendor/twig/twig/.gitignore ADDED
@@ -0,0 +1,5 @@
 
 
 
 
 
1
+ /build
2
+ /composer.lock
3
+ /ext/twig/autom4te.cache/
4
+ /phpunit.xml
5
+ /vendor
vendor/twig/twig/.php_cs.dist ADDED
@@ -0,0 +1,15 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ return PhpCsFixer\Config::create()
4
+ ->setRules(array(
5
+ '@Symfony' => true,
6
+ '@Symfony:risky' => true,
7
+ 'array_syntax' => array('syntax' => 'long'),
8
+ 'php_unit_fqcn_annotation' => false,
9
+ 'no_unreachable_default_argument_value' => false,
10
+ 'braces' => array('allow_single_line_closure' => true),
11
+ 'heredoc_to_nowdoc' => false,
12
+ ))
13
+ ->setRiskyAllowed(true)
14
+ ->setFinder(PhpCsFixer\Finder::create()->in(__DIR__))
15
+ ;
vendor/twig/twig/.travis.yml ADDED
@@ -0,0 +1,44 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ language: php
2
+
3
+ sudo: false
4
+
5
+ cache:
6
+ directories:
7
+ - vendor
8
+ - $HOME/.composer/cache/files
9
+
10
+ php:
11
+ - 5.2
12
+ - 5.3
13
+ - 5.4
14
+ - 5.5
15
+ - 5.6
16
+ - 7.0
17
+ - 7.1
18
+ - hhvm
19
+
20
+ env:
21
+ - TWIG_EXT=no
22
+ - TWIG_EXT=yes
23
+
24
+ before_install:
25
+ - if [[ ! $TRAVIS_PHP_VERSION = hhvm* ]]; then phpenv config-rm xdebug.ini || echo "xdebug not available"; fi
26
+
27
+ install:
28
+ # Composer is not available on PHP 5.2
29
+ - if [ ${TRAVIS_PHP_VERSION:0:3} != "5.2" ]; then travis_retry composer install; fi
30
+
31
+ before_script:
32
+ - if [ "$TWIG_EXT" == "yes" ]; then sh -c "cd ext/twig && phpize && ./configure --enable-twig && make && make install"; fi
33
+ - if [ "$TWIG_EXT" == "yes" ]; then echo "extension=twig.so" >> `php --ini | grep "Loaded Configuration" | sed -e "s|.*:\s*||"`; fi
34
+ - if [ ${TRAVIS_PHP_VERSION:0:3} == "5.2" ]; then sed -i.bak "s|vendor/autoload.php|test/bootstrap.php|" phpunit.xml.dist; fi
35
+
36
+ matrix:
37
+ fast_finish: true
38
+ exclude:
39
+ - php: hhvm
40
+ env: TWIG_EXT=yes
41
+ - php: 7.0
42
+ env: TWIG_EXT=yes
43
+ - php: 7.1
44
+ env: TWIG_EXT=yes
vendor/twig/twig/CHANGELOG ADDED
@@ -0,0 +1,933 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ * 1.32.0 (2017-02-26)
2
+
3
+ * fixed deprecation notice in Twig_Util_DeprecationCollector
4
+ * added a PSR-11 compatible runtime loader
5
+ * added `side` argument to `trim` to allow left or right trimming only.
6
+
7
+ * 1.31.0 (2017-01-11)
8
+
9
+ * added Twig_NodeCaptureInterface for nodes that capture all output
10
+ * fixed marking the environment as initialized too early
11
+ * fixed C89 compat for the C extension
12
+ * turned fatal error into exception when a previously generated cache is corrupted
13
+ * fixed offline cache warm-ups for embedded templates
14
+
15
+ * 1.30.0 (2016-12-23)
16
+
17
+ * added Twig_FactoryRuntimeLoader
18
+ * deprecated function/test/filter/tag overriding
19
+ * deprecated the "disable_c_ext" attribute on Twig_Node_Expression_GetAttr
20
+
21
+ * 1.29.0 (2016-12-13)
22
+
23
+ * fixed sandbox being left enabled if an exception is thrown while rendering
24
+ * marked some classes as being final (via @final)
25
+ * made Twig_Error report real source path when possible
26
+ * added support for {{ _self }} to provide an upgrade path from 1.x to 2.0 (replaces {{ _self.templateName }})
27
+ * deprecated silent display of undefined blocks
28
+ * deprecated support for mbstring.func_overload != 0
29
+
30
+ * 1.28.2 (2016-11-23)
31
+
32
+ * fixed precedence between getFoo() and isFoo() in Twig_Template::getAttribute()
33
+ * improved a deprecation message
34
+
35
+ * 1.28.1 (2016-11-18)
36
+
37
+ * fixed block() function when used with a template argument
38
+
39
+ * 1.28.0 (2016-11-17)
40
+
41
+ * added support for the PHP 7 null coalescing operator for the ?? Twig implementation
42
+ * exposed a way to access template data and methods in a portable way
43
+ * changed context access to use the PHP 7 null coalescing operator when available
44
+ * added the "with" tag
45
+ * added support for a custom template on the block() function
46
+ * added "is defined" support for block() and constant()
47
+ * optimized the way attributes are fetched
48
+
49
+ * 1.27.0 (2016-10-25)
50
+
51
+ * deprecated Twig_Parser::getEnvironment()
52
+ * deprecated Twig_Parser::addHandler() and Twig_Parser::addNodeVisitor()
53
+ * deprecated Twig_Compiler::addIndentation()
54
+ * fixed regression when registering two extensions having the same class name
55
+ * deprecated Twig_LoaderInterface::getSource() (implement Twig_SourceContextLoaderInterface instead)
56
+ * fixed the filesystem loader with relative paths
57
+ * deprecated Twig_Node::getLine() in favor of Twig_Node::getTemplateLine()
58
+ * deprecated Twig_Template::getSource() in favor of Twig_Template::getSourceContext()
59
+ * deprecated Twig_Node::getFilename() in favor of Twig_Node::getTemplateName()
60
+ * deprecated the "filename" escaping strategy (use "name" instead)
61
+ * added Twig_Source to hold information about the original template
62
+ * deprecated Twig_Error::getTemplateFile() and Twig_Error::setTemplateFile() in favor of Twig_Error::getTemplateName() and Twig_Error::setTemplateName()
63
+ * deprecated Parser::getFilename()
64
+ * fixed template paths when a template name contains a protocol like vfs://
65
+ * improved debugging with Twig_Sandbox_SecurityError exceptions for disallowed methods and properties
66
+
67
+ * 1.26.1 (2016-10-05)
68
+
69
+ * removed template source code from generated template classes when debug is disabled
70
+ * fixed default implementation of Twig_Template::getDebugInfo() for better BC
71
+ * fixed regression on static calls for functions/filters/tests
72
+
73
+ * 1.26.0 (2016-10-02)
74
+
75
+ * added template cache invalidation based on more environment options
76
+ * added a missing deprecation notice
77
+ * fixed template paths when a template is stored in a PHAR file
78
+ * allowed filters/functions/tests implementation to use a different class than the extension they belong to
79
+ * deprecated Twig_ExtensionInterface::getName()
80
+
81
+ * 1.25.0 (2016-09-21)
82
+
83
+ * changed the way we store template source in template classes
84
+ * removed usage of realpath in cache keys
85
+ * fixed Twig cache sharing when used with different versions of PHP
86
+ * removed embed parent workaround for simple use cases
87
+ * deprecated the ability to store non Node instances in Node::$nodes
88
+ * deprecated Twig_Environment::getLexer(), Twig_Environment::getParser(), Twig_Environment::getCompiler()
89
+ * deprecated Twig_Compiler::getFilename()
90
+
91
+ * 1.24.2 (2016-09-01)
92
+
93
+ * fixed static callables
94
+ * fixed a potential PHP warning when loading the cache
95
+ * fixed a case where the autoescaping does not work as expected
96
+
97
+ * 1.24.1 (2016-05-30)
98
+
99
+ * fixed reserved keywords (forbids true, false, null and none keywords for variables names)
100
+ * fixed support for PHP7 (Throwable support)
101
+ * marked the following methods as being internals on Twig_Environment:
102
+ getFunctions(), getFilters(), getTests(), getFunction(), getFilter(), getTest(),
103
+ getTokenParsers(), getTags(), getNodeVisitors(), getUnaryOperators(), getBinaryOperators(),
104
+ getFunctions(), getFilters(), getGlobals(), initGlobals(), initExtensions(), and initExtension()
105
+
106
+ * 1.24.0 (2016-01-25)
107
+
108
+ * adding support for the ?? operator
109
+ * fixed the defined test when used on a constant, a map, or a sequence
110
+ * undeprecated _self (should only be used to get the template name, not the template instance)
111
+ * fixed parsing on PHP7
112
+
113
+ * 1.23.3 (2016-01-11)
114
+
115
+ * fixed typo
116
+
117
+ * 1.23.2 (2015-01-11)
118
+
119
+ * added versions in deprecated messages
120
+ * made file cache tolerant for trailing (back)slashes on directory configuration
121
+ * deprecated unused Twig_Node_Expression_ExtensionReference class
122
+
123
+ * 1.23.1 (2015-11-05)
124
+
125
+ * fixed some exception messages which triggered PHP warnings
126
+ * fixed BC on Twig_Test_NodeTestCase
127
+
128
+ * 1.23.0 (2015-10-29)
129
+
130
+ * deprecated the possibility to override an extension by registering another one with the same name
131
+ * deprecated Twig_ExtensionInterface::getGlobals() (added Twig_Extension_GlobalsInterface for BC)
132
+ * deprecated Twig_ExtensionInterface::initRuntime() (added Twig_Extension_InitRuntimeInterface for BC)
133
+ * deprecated Twig_Environment::computeAlternatives()
134
+
135
+ * 1.22.3 (2015-10-13)
136
+
137
+ * fixed regression when using null as a cache strategy
138
+ * improved performance when checking template freshness
139
+ * fixed warnings when loaded templates do not exist
140
+ * fixed template class name generation to prevent possible collisions
141
+ * fixed logic for custom escapers to call them even on integers and null values
142
+ * changed template cache names to take into account the Twig C extension
143
+
144
+ * 1.22.2 (2015-09-22)
145
+
146
+ * fixed a race condition in template loading
147
+
148
+ * 1.22.1 (2015-09-15)
149
+
150
+ * fixed regression in template_from_string
151
+
152
+ * 1.22.0 (2015-09-13)
153
+
154
+ * made Twig_Test_IntegrationTestCase more flexible
155
+ * added an option to force PHP bytecode invalidation when writing a compiled template into the cache
156
+ * fixed the profiler duration for the root node
157
+ * changed template cache names to take into account enabled extensions
158
+ * deprecated Twig_Environment::clearCacheFiles(), Twig_Environment::getCacheFilename(),
159
+ Twig_Environment::writeCacheFile(), and Twig_Environment::getTemplateClassPrefix()
160
+ * added a way to override the filesystem template cache system
161
+ * added a way to get the original template source from Twig_Template
162
+
163
+ * 1.21.2 (2015-09-09)
164
+
165
+ * fixed variable names for the deprecation triggering code
166
+ * fixed escaping strategy detection based on filename
167
+ * added Traversable support for replace, merge, and sort
168
+ * deprecated support for character by character replacement for the "replace" filter
169
+
170
+ * 1.21.1 (2015-08-26)
171
+
172
+ * fixed regression when using the deprecated Twig_Test_* classes
173
+
174
+ * 1.21.0 (2015-08-24)
175
+
176
+ * added deprecation notices for deprecated features
177
+ * added a deprecation "framework" for filters/functions/tests and test fixtures
178
+
179
+ * 1.20.0 (2015-08-12)
180
+
181
+ * forbid access to the Twig environment from templates and internal parts of Twig_Template
182
+ * fixed limited RCEs when in sandbox mode
183
+ * deprecated Twig_Template::getEnvironment()
184
+ * deprecated the _self variable for usage outside of the from and import tags
185
+ * added Twig_BaseNodeVisitor to ease the compatibility of node visitors
186
+ between 1.x and 2.x
187
+
188
+ * 1.19.0 (2015-07-31)
189
+
190
+ * fixed wrong error message when including an undefined template in a child template
191
+ * added support for variadic filters, functions, and tests
192
+ * added support for extra positional arguments in macros
193
+ * added ignore_missing flag to the source function
194
+ * fixed batch filter with zero items
195
+ * deprecated Twig_Environment::clearTemplateCache()
196
+ * fixed sandbox disabling when using the include function
197
+
198
+ * 1.18.2 (2015-06-06)
199
+
200
+ * fixed template/line guessing in exceptions for nested templates
201
+ * optimized the number of inodes and the size of realpath cache when using the cache
202
+
203
+ * 1.18.1 (2015-04-19)
204
+
205
+ * fixed memory leaks in the C extension
206
+ * deprecated Twig_Loader_String
207
+ * fixed the slice filter when used with a SimpleXMLElement object
208
+ * fixed filesystem loader when trying to load non-files (like directories)
209
+
210
+ * 1.18.0 (2015-01-25)
211
+
212
+ * fixed some error messages where the line was wrong (unknown variables or argument names)
213
+ * added a new way to customize the main Module node (via empty nodes)
214
+ * added Twig_Environment::createTemplate() to create a template from a string
215
+ * added a profiler
216
+ * fixed filesystem loader cache when different file paths are used for the same template
217
+
218
+ * 1.17.0 (2015-01-14)
219
+
220
+ * added a 'filename' autoescaping strategy, which dynamically chooses the
221
+ autoescaping strategy for a template based on template file extension.
222
+
223
+ * 1.16.3 (2014-12-25)
224
+
225
+ * fixed regression for dynamic parent templates
226
+ * fixed cache management with statcache
227
+ * fixed a regression in the slice filter
228
+
229
+ * 1.16.2 (2014-10-17)
230
+
231
+ * fixed timezone on dates as strings
232
+ * fixed 2-words test names when a custom node class is not used
233
+ * fixed macros when using an argument named like a PHP super global (like GET or POST)
234
+ * fixed date_modify when working with DateTimeImmutable
235
+ * optimized for loops
236
+ * fixed multi-byte characters handling in the split filter
237
+ * fixed a regression in the in operator
238
+ * fixed a regression in the slice filter
239
+
240
+ * 1.16.1 (2014-10-10)
241
+
242
+ * improved error reporting in a sandboxed template
243
+ * fixed missing error file/line information under certain circumstances
244
+ * fixed wrong error line number in some error messages
245
+ * fixed the in operator to use strict comparisons
246
+ * sped up the slice filter
247
+ * fixed for mb function overload mb_substr acting different
248
+ * fixed the attribute() function when passing a variable for the arguments
249
+
250
+ * 1.16.0 (2014-07-05)
251
+
252
+ * changed url_encode to always encode according to RFC 3986
253
+ * fixed inheritance in a 'use'-hierarchy
254
+ * removed the __toString policy check when the sandbox is disabled
255
+ * fixed recursively calling blocks in templates with inheritance
256
+
257
+ * 1.15.1 (2014-02-13)
258
+
259
+ * fixed the conversion of the special '0000-00-00 00:00' date
260
+ * added an error message when trying to import an undefined block from a trait
261
+ * fixed a C extension crash when accessing defined but uninitialized property.
262
+
263
+ * 1.15.0 (2013-12-06)
264
+
265
+ * made ignoreStrictCheck in Template::getAttribute() works with __call() methods throwing BadMethodCallException
266
+ * added min and max functions
267
+ * added the round filter
268
+ * fixed a bug that prevented the optimizers to be enabled/disabled selectively
269
+ * fixed first and last filters for UTF-8 strings
270
+ * added a source function to include the content of a template without rendering it
271
+ * fixed the C extension sandbox behavior when get or set is prepend to method name
272
+
273
+ * 1.14.2 (2013-10-30)
274
+
275
+ * fixed error filename/line when an error occurs in an included file
276
+ * allowed operators that contain whitespaces to have more than one whitespace
277
+ * allowed tests to be made of 1 or 2 words (like "same as" or "divisible by")
278
+
279
+ * 1.14.1 (2013-10-15)
280
+
281
+ * made it possible to use named operators as variables
282
+ * fixed the possibility to have a variable named 'matches'
283
+ * added support for PHP 5.5 DateTimeInterface
284
+
285
+ * 1.14.0 (2013-10-03)
286
+
287
+ * fixed usage of the html_attr escaping strategy to avoid double-escaping with the html strategy
288
+ * added new operators: ends with, starts with, and matches
289
+ * fixed some compatibility issues with HHVM
290
+ * added a way to add custom escaping strategies
291
+ * fixed the C extension compilation on Windows
292
+ * fixed the batch filter when using a fill argument with an exact match of elements to batch
293
+ * fixed the filesystem loader cache when a template name exists in several namespaces
294
+ * fixed template_from_string when the template includes or extends other ones
295
+ * fixed a crash of the C extension on an edge case
296
+
297
+ * 1.13.2 (2013-08-03)
298
+
299
+ * fixed the error line number for an error occurs in and embedded template
300
+ * fixed crashes of the C extension on some edge cases
301
+
302
+ * 1.13.1 (2013-06-06)
303
+
304
+ * added the possibility to ignore the filesystem constructor argument in Twig_Loader_Filesystem
305
+ * fixed Twig_Loader_Chain::exists() for a loader which implements Twig_ExistsLoaderInterface
306
+ * adjusted backtrace call to reduce memory usage when an error occurs
307
+ * added support for object instances as the second argument of the constant test
308
+ * fixed the include function when used in an assignment
309
+
310
+ * 1.13.0 (2013-05-10)
311
+
312
+ * fixed getting a numeric-like item on a variable ('09' for instance)
313
+ * fixed getting a boolean or float key on an array, so it is consistent with PHP's array access:
314
+ `{{ array[false] }}` behaves the same as `echo $array[false];` (equals `$array[0]`)
315
+ * made the escape filter 20% faster for happy path (escaping string for html with UTF-8)
316
+ * changed ☃ to § in tests
317
+ * enforced usage of named arguments after positional ones
318
+
319
+ * 1.12.3 (2013-04-08)
320
+
321
+ * fixed a security issue in the filesystem loader where it was possible to include a template one
322
+ level above the configured path
323
+ * fixed fatal error that should be an exception when adding a filter/function/test too late
324
+ * added a batch filter
325
+ * added support for encoding an array as query string in the url_encode filter
326
+
327
+ * 1.12.2 (2013-02-09)
328
+
329
+ * fixed the timezone used by the date filter and function when the given date contains a timezone (like 2010-01-28T15:00:00+02:00)
330
+ * fixed globals when getGlobals is called early on
331
+ * added the first and last filter
332
+
333
+ * 1.12.1 (2013-01-15)
334
+
335
+ * added support for object instances as the second argument of the constant function
336
+ * relaxed globals management to avoid a BC break
337
+ * added support for {{ some_string[:2] }}
338
+
339
+ * 1.12.0 (2013-01-08)
340
+
341
+ * added verbatim as an alias for the raw tag to avoid confusion with the raw filter
342
+ * fixed registration of tests and functions as anonymous functions
343
+ * fixed globals management
344
+
345
+ * 1.12.0-RC1 (2012-12-29)
346
+
347
+ * added an include function (does the same as the include tag but in a more flexible way)
348
+ * added the ability to use any PHP callable to define filters, functions, and tests
349
+ * added a syntax error when using a loop variable that is not defined
350
+ * added the ability to set default values for macro arguments
351
+ * added support for named arguments for filters, tests, and functions
352
+ * moved filters/functions/tests syntax errors to the parser
353
+ * added support for extended ternary operator syntaxes
354
+
355
+ * 1.11.1 (2012-11-11)
356
+
357
+ * fixed debug info line numbering (was off by 2)
358
+ * fixed escaping when calling a macro inside another one (regression introduced in 1.9.1)
359
+ * optimized variable access on PHP 5.4
360
+ * fixed a crash of the C extension when an exception was thrown from a macro called without being imported (using _self.XXX)
361
+
362
+ * 1.11.0 (2012-11-07)
363
+
364
+ * fixed macro compilation when a variable name is a PHP reserved keyword
365
+ * changed the date filter behavior to always apply the default timezone, except if false is passed as the timezone
366
+ * fixed bitwise operator precedences
367
+ * added the template_from_string function
368
+ * fixed default timezone usage for the date function
369
+ * optimized the way Twig exceptions are managed (to make them faster)
370
+ * added Twig_ExistsLoaderInterface (implementing this interface in your loader make the chain loader much faster)
371
+
372
+ * 1.10.3 (2012-10-19)
373
+
374
+ * fixed wrong template location in some error messages
375
+ * reverted a BC break introduced in 1.10.2
376
+ * added a split filter
377
+
378
+ * 1.10.2 (2012-10-15)
379
+
380
+ * fixed macro calls on PHP 5.4
381
+
382
+ * 1.10.1 (2012-10-15)
383
+
384
+ * made a speed optimization to macro calls when imported via the "import" tag
385
+ * fixed C extension compilation on Windows
386
+ * fixed a segfault in the C extension when using DateTime objects
387
+
388
+ * 1.10.0 (2012-09-28)
389
+
390
+ * extracted functional tests framework to make it reusable for third-party extensions
391
+ * added namespaced templates support in Twig_Loader_Filesystem
392
+ * added Twig_Loader_Filesystem::prependPath()
393
+ * fixed an error when a token parser pass a closure as a test to the subparse() method
394
+
395
+ * 1.9.2 (2012-08-25)
396
+
397
+ * fixed the in operator for objects that contain circular references
398
+ * fixed the C extension when accessing a public property of an object implementing the \ArrayAccess interface
399
+
400
+ * 1.9.1 (2012-07-22)
401
+
402
+ * optimized macro calls when auto-escaping is on
403
+ * fixed wrong parent class for Twig_Function_Node
404
+ * made Twig_Loader_Chain more explicit about problems
405
+
406
+ * 1.9.0 (2012-07-13)
407
+
408
+ * made the parsing independent of the template loaders
409
+ * fixed exception trace when an error occurs when rendering a child template
410
+ * added escaping strategies for CSS, URL, and HTML attributes
411
+ * fixed nested embed tag calls
412
+ * added the date_modify filter
413
+
414
+ * 1.8.3 (2012-06-17)
415
+
416
+ * fixed paths in the filesystem loader when passing a path that ends with a slash or a backslash
417
+ * fixed escaping when a project defines a function named html or js
418
+ * fixed chmod mode to apply the umask correctly
419
+
420
+ * 1.8.2 (2012-05-30)
421
+
422
+ * added the abs filter
423
+ * fixed a regression when using a number in template attributes
424
+ * fixed compiler when mbstring.func_overload is set to 2
425
+ * fixed DateTimeZone support in date filter
426
+
427
+ * 1.8.1 (2012-05-17)
428
+
429
+ * fixed a regression when dealing with SimpleXMLElement instances in templates
430
+ * fixed "is_safe" value for the "dump" function when "html_errors" is not defined in php.ini
431
+ * switched to use mbstring whenever possible instead of iconv (you might need to update your encoding as mbstring and iconv encoding names sometimes differ)
432
+
433
+ * 1.8.0 (2012-05-08)
434
+
435
+ * enforced interface when adding tests, filters, functions, and node visitors from extensions
436
+ * fixed a side-effect of the date filter where the timezone might be changed
437
+ * simplified usage of the autoescape tag; the only (optional) argument is now the escaping strategy or false (with a BC layer)
438
+ * added a way to dynamically change the auto-escaping strategy according to the template "filename"
439
+ * changed the autoescape option to also accept a supported escaping strategy (for BC, true is equivalent to html)
440
+ * added an embed tag
441
+
442
+ * 1.7.0 (2012-04-24)
443
+
444
+ * fixed a PHP warning when using CIFS
445
+ * fixed template line number in some exceptions
446
+ * added an iterable test
447
+ * added an error when defining two blocks with the same name in a template
448
+ * added the preserves_safety option for filters
449
+ * fixed a PHP notice when trying to access a key on a non-object/array variable
450
+ * enhanced error reporting when the template file is an instance of SplFileInfo
451
+ * added Twig_Environment::mergeGlobals()
452
+ * added compilation checks to avoid misuses of the sandbox tag
453
+ * fixed filesystem loader freshness logic for high traffic websites
454
+ * fixed random function when charset is null
455
+
456
+ * 1.6.5 (2012-04-11)
457
+
458
+ * fixed a regression when a template only extends another one without defining any blocks
459
+
460
+ * 1.6.4 (2012-04-02)
461
+
462
+ * fixed PHP notice in Twig_Error::guessTemplateLine() introduced in 1.6.3
463
+ * fixed performance when compiling large files
464
+ * optimized parent template creation when the template does not use dynamic inheritance
465
+
466
+ * 1.6.3 (2012-03-22)
467
+
468
+ * fixed usage of Z_ADDREF_P for PHP 5.2 in the C extension
469
+ * fixed compilation of numeric values used in templates when using a locale where the decimal separator is not a dot
470
+ * made the strategy used to guess the real template file name and line number in exception messages much faster and more accurate
471
+
472
+ * 1.6.2 (2012-03-18)
473
+
474
+ * fixed sandbox mode when used with inheritance
475
+ * added preserveKeys support for the slice filter
476
+ * fixed the date filter when a DateTime instance is passed with a specific timezone
477
+ * added a trim filter
478
+
479
+ * 1.6.1 (2012-02-29)
480
+
481
+ * fixed Twig C extension
482
+ * removed the creation of Twig_Markup instances when not needed
483
+ * added a way to set the default global timezone for dates
484
+ * fixed the slice filter on strings when the length is not specified
485
+ * fixed the creation of the cache directory in case of a race condition
486
+
487
+ * 1.6.0 (2012-02-04)
488
+
489
+ * fixed raw blocks when used with the whitespace trim option
490
+ * made a speed optimization to macro calls when imported via the "from" tag
491
+ * fixed globals, parsers, visitors, filters, tests, and functions management in Twig_Environment when a new one or new extension is added
492
+ * fixed the attribute function when passing arguments
493
+ * added slice notation support for the [] operator (syntactic sugar for the slice operator)
494
+ * added a slice filter
495
+ * added string support for the reverse filter
496
+ * fixed the empty test and the length filter for Twig_Markup instances
497
+ * added a date function to ease date comparison
498
+ * fixed unary operators precedence
499
+ * added recursive parsing support in the parser
500
+ * added string and integer handling for the random function
501
+
502
+ * 1.5.1 (2012-01-05)
503
+
504
+ * fixed a regression when parsing strings
505
+
506
+ * 1.5.0 (2012-01-04)
507
+
508
+ * added Traversable objects support for the join filter
509
+
510
+ * 1.5.0-RC2 (2011-12-30)
511
+
512
+ * added a way to set the default global date interval format
513
+ * fixed the date filter for DateInterval instances (setTimezone() does not exist for them)
514
+ * refactored Twig_Template::display() to ease its extension
515
+ * added a number_format filter
516
+
517
+ * 1.5.0-RC1 (2011-12-26)
518
+
519
+ * removed the need to quote hash keys
520
+ * allowed hash keys to be any expression
521
+ * added a do tag
522
+ * added a flush tag
523
+ * added support for dynamically named filters and functions
524
+ * added a dump function to help debugging templates
525
+ * added a nl2br filter
526
+ * added a random function
527
+ * added a way to change the default format for the date filter
528
+ * fixed the lexer when an operator ending with a letter ends a line
529
+ * added string interpolation support
530
+ * enhanced exceptions for unknown filters, functions, tests, and tags
531
+
532
+ * 1.4.0 (2011-12-07)
533
+
534
+ * fixed lexer when using big numbers (> PHP_INT_MAX)
535
+ * added missing preserveKeys argument to the reverse filter
536
+ * fixed macros containing filter tag calls
537
+
538
+ * 1.4.0-RC2 (2011-11-27)
539
+
540
+ * removed usage of Reflection in Twig_Template::getAttribute()
541
+ * added a C extension that can optionally replace Twig_Template::getAttribute()
542
+ * added negative timestamp support to the date filter
543
+
544
+ * 1.4.0-RC1 (2011-11-20)
545
+
546
+ * optimized variable access when using PHP 5.4
547
+ * changed the precedence of the .. operator to be more consistent with languages that implements such a feature like Ruby
548
+ * added an Exception to Twig_Loader_Array::isFresh() method when the template does not exist to be consistent with other loaders
549
+ * added Twig_Function_Node to allow more complex functions to have their own Node class
550
+ * added Twig_Filter_Node to allow more complex filters to have their own Node class
551
+ * added Twig_Test_Node to allow more complex tests to have their own Node class
552
+ * added a better error message when a template is empty but contain a BOM
553
+ * fixed "in" operator for empty strings
554
+ * fixed the "defined" test and the "default" filter (now works with more than one call (foo.bar.foo) and for both values of the strict_variables option)
555
+ * changed the way extensions are loaded (addFilter/addFunction/addGlobal/addTest/addNodeVisitor/addTokenParser/addExtension can now be called in any order)
556
+ * added Twig_Environment::display()
557
+ * made the escape filter smarter when the encoding is not supported by PHP
558
+ * added a convert_encoding filter
559
+ * moved all node manipulations outside the compile() Node method
560
+ * made several speed optimizations
561
+
562
+ * 1.3.0 (2011-10-08)
563
+
564
+ no changes
565
+
566
+ * 1.3.0-RC1 (2011-10-04)
567
+
568
+ * added an optimization for the parent() function
569
+ * added cache reloading when auto_reload is true and an extension has been modified
570
+ * added the possibility to force the escaping of a string already marked as safe (instance of Twig_Markup)
571
+ * allowed empty templates to be used as traits
572
+ * added traits support for the "parent" function
573
+
574
+ * 1.2.0 (2011-09-13)
575
+
576
+ no changes
577
+
578
+ * 1.2.0-RC1 (2011-09-10)
579
+
580
+ * enhanced the exception when a tag remains unclosed
581
+ * added support for empty Countable objects for the "empty" test
582
+ * fixed algorithm that determines if a template using inheritance is valid (no output between block definitions)
583
+ * added better support for encoding problems when escaping a string (available as of PHP 5.4)
584
+ * added a way to ignore a missing template when using the "include" tag ({% include "foo" ignore missing %})
585
+ * added support for an array of templates to the "include" and "extends" tags ({% include ['foo', 'bar'] %})
586
+ * added support for bitwise operators in expressions
587
+ * added the "attribute" function to allow getting dynamic attributes on variables
588
+ * added Twig_Loader_Chain
589
+ * added Twig_Loader_Array::setTemplate()
590
+ * added an optimization for the set tag when used to capture a large chunk of static text
591
+ * changed name regex to match PHP one "[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*" (works for blocks, tags, functions, filters, and macros)
592
+ * removed the possibility to use the "extends" tag from a block
593
+ * added "if" modifier support to "for" loops
594
+
595
+ * 1.1.2 (2011-07-30)
596
+
597
+ * fixed json_encode filter on PHP 5.2
598
+ * fixed regression introduced in 1.1.1 ({{ block(foo|lower) }})
599
+ * fixed inheritance when using conditional parents
600
+ * fixed compilation of templates when the body of a child template is not empty
601
+ * fixed output when a macro throws an exception
602
+ * fixed a parsing problem when a large chunk of text is enclosed in a comment tag
603
+ * added PHPDoc for all Token parsers and Core extension functions
604
+
605
+ * 1.1.1 (2011-07-17)
606
+
607
+ * added a performance optimization in the Optimizer (also helps to lower the number of nested level calls)
608
+ * made some performance improvement for some edge cases
609
+
610
+ * 1.1.0 (2011-06-28)
611
+
612
+ * fixed json_encode filter
613
+
614
+ * 1.1.0-RC3 (2011-06-24)
615
+
616
+ * fixed method case-sensitivity when using the sandbox mode
617
+ * added timezone support for the date filter
618
+ * fixed possible security problems with NUL bytes
619
+
620
+ * 1.1.0-RC2 (2011-06-16)
621
+
622
+ * added an exception when the template passed to "use" is not a string
623
+ * made 'a.b is defined' not throw an exception if a is not defined (in strict mode)
624
+ * added {% line \d+ %} directive
625
+
626
+ * 1.1.0-RC1 (2011-05-28)
627
+
628
+ Flush your cache after upgrading.
629
+
630
+ * fixed date filter when using a timestamp
631
+ * fixed the defined test for some cases
632
+ * fixed a parsing problem when a large chunk of text is enclosed in a raw tag
633
+ * added support for horizontal reuse of template blocks (see docs for more information)
634
+ * added whitespace control modifier to all tags (see docs for more information)
635
+ * added null as an alias for none (the null test is also an alias for the none test now)
636
+ * made TRUE, FALSE, NONE equivalent to their lowercase counterparts
637
+ * wrapped all compilation and runtime exceptions with Twig_Error_Runtime and added logic to guess the template name and line
638
+ * moved display() method to Twig_Template (generated templates should now use doDisplay() instead)
639
+
640
+ * 1.0.0 (2011-03-27)
641
+
642
+ * fixed output when using mbstring
643
+ * fixed duplicate call of methods when using the sandbox
644
+ * made the charset configurable for the escape filter
645
+
646
+ * 1.0.0-RC2 (2011-02-21)
647
+
648
+ * changed the way {% set %} works when capturing (the content is now marked as safe)
649
+ * added support for macro name in the endmacro tag
650
+ * make Twig_Error compatible with PHP 5.3.0 >
651
+ * fixed an infinite loop on some Windows configurations
652
+ * fixed the "length" filter for numbers
653
+ * fixed Template::getAttribute() as properties in PHP are case sensitive
654
+ * removed coupling between Twig_Node and Twig_Template
655
+ * fixed the ternary operator precedence rule
656
+
657
+ * 1.0.0-RC1 (2011-01-09)
658
+
659
+ Backward incompatibilities:
660
+
661
+ * the "items" filter, which has been deprecated for quite a long time now, has been removed
662
+ * the "range" filter has been converted to a function: 0|range(10) -> range(0, 10)
663
+ * the "constant" filter has been converted to a function: {{ some_date|date('DATE_W3C'|constant) }} -> {{ some_date|date(constant('DATE_W3C')) }}
664
+ * the "cycle" filter has been converted to a function: {{ ['odd', 'even']|cycle(i) }} -> {{ cycle(['odd', 'even'], i) }}
665
+ * the "for" tag does not support "joined by" anymore
666
+ * the "autoescape" first argument is now "true"/"false" (instead of "on"/"off")
667
+ * the "parent" tag has been replaced by a "parent" function ({{ parent() }} instead of {% parent %})
668
+ * the "display" tag has been replaced by a "block" function ({{ block('title') }} instead of {% display title %})
669
+ * removed the grammar and simple token parser (moved to the Twig Extensions repository)
670
+
671
+ Changes:
672
+
673
+ * added "needs_context" option for filters and functions (the context is then passed as a first argument)
674
+ * added global variables support
675
+ * made macros return their value instead of echoing directly (fixes calling a macro in sandbox mode)
676
+ * added the "from" tag to import macros as functions
677
+ * added support for functions (a function is just syntactic sugar for a getAttribute() call)
678
+ * made macros callable when sandbox mode is enabled
679
+ * added an exception when a macro uses a reserved name
680
+ * the "default" filter now uses the "empty" test instead of just checking for null
681
+ * added the "empty" test
682
+
683
+ * 0.9.10 (2010-12-16)
684
+
685
+ Backward incompatibilities:
686
+
687
+ * The Escaper extension is enabled by default, which means that all displayed
688
+ variables are now automatically escaped. You can revert to the previous
689
+ behavior by removing the extension via $env->removeExtension('escaper')
690
+ or just set the 'autoescape' option to 'false'.
691
+ * removed the "without loop" attribute for the "for" tag (not needed anymore
692
+ as the Optimizer take care of that for most cases)
693
+ * arrays and hashes have now a different syntax
694
+ * arrays keep the same syntax with square brackets: [1, 2]
695
+ * hashes now use curly braces (["a": "b"] should now be written as {"a": "b"})
696
+ * support for "arrays with keys" and "hashes without keys" is not supported anymore ([1, "foo": "bar"] or {"foo": "bar", 1})
697
+ * the i18n extension is now part of the Twig Extensions repository
698
+
699
+ Changes:
700
+
701
+ * added the merge filter
702
+ * removed 'is_escaper' option for filters (a left over from the previous version) -- you must use 'is_safe' now instead
703
+ * fixed usage of operators as method names (like is, in, and not)
704
+ * changed the order of execution for node visitors
705
+ * fixed default() filter behavior when used with strict_variables set to on
706
+ * fixed filesystem loader compatibility with PHAR files
707
+ * enhanced error messages when an unexpected token is parsed in an expression
708
+ * fixed filename not being added to syntax error messages
709
+ * added the autoescape option to enable/disable autoescaping
710
+ * removed the newline after a comment (mimics PHP behavior)
711
+ * added a syntax error exception when parent block is used on a template that does not extend another one
712
+ * made the Escaper extension enabled by default
713
+ * fixed sandbox extension when used with auto output escaping
714
+ * fixed escaper when wrapping a Twig_Node_Print (the original class must be preserved)
715
+ * added an Optimizer extension (enabled by default; optimizes "for" loops and "raw" filters)
716
+ * added priority to node visitors
717
+
718
+ * 0.9.9 (2010-11-28)
719
+
720
+ Backward incompatibilities:
721
+ * the self special variable has been renamed to _self
722
+ * the odd and even filters are now tests:
723
+ {{ foo|odd }} must now be written {{ foo is odd }}
724
+ * the "safe" filter has been renamed to "raw"
725
+ * in Node classes,
726
+ sub-nodes are now accessed via getNode() (instead of property access)
727
+ attributes via getAttribute() (instead of array access)
728
+ * the urlencode filter had been renamed to url_encode
729
+ * the include tag now merges the passed variables with the current context by default
730
+ (the old behavior is still possible by adding the "only" keyword)
731
+ * moved Exceptions to Twig_Error_* (Twig_SyntaxError/Twig_RuntimeError are now Twig_Error_Syntax/Twig_Error_Runtime)
732
+ * removed support for {{ 1 < i < 3 }} (use {{ i > 1 and i < 3 }} instead)
733
+ * the "in" filter has been removed ({{ a|in(b) }} should now be written {{ a in b }})
734
+
735
+ Changes:
736
+ * added file and line to Twig_Error_Runtime exceptions thrown from Twig_Template
737
+ * changed trans tag to accept any variable for the plural count
738
+ * fixed sandbox mode (__toString() method check was not enforced if called implicitly from complex statements)
739
+ * added the ** (power) operator
740
+ * changed the algorithm used for parsing expressions
741
+ * added the spaceless tag
742
+ * removed trim_blocks option
743
+ * added support for is*() methods for attributes (foo.bar now looks for foo->getBar() or foo->isBar())
744
+ * changed all exceptions to extend Twig_Error
745
+ * fixed unary expressions ({{ not(1 or 0) }})
746
+ * fixed child templates (with an extend tag) that uses one or more imports
747
+ * added support for {{ 1 not in [2, 3] }} (more readable than the current {{ not (1 in [2, 3]) }})
748
+ * escaping has been rewritten
749
+ * the implementation of template inheritance has been rewritten
750
+ (blocks can now be called individually and still work with inheritance)
751
+ * fixed error handling for if tag when a syntax error occurs within a subparse process
752
+ * added a way to implement custom logic for resolving token parsers given a tag name
753
+ * fixed js escaper to be stricter (now uses a whilelist-based js escaper)
754
+ * added the following filers: "constant", "trans", "replace", "json_encode"
755
+ * added a "constant" test
756
+ * fixed objects with __toString() not being autoescaped
757
+ * fixed subscript expressions when calling __call() (methods now keep the case)
758
+ * added "test" feature (accessible via the "is" operator)
759
+ * removed the debug tag (should be done in an extension)
760
+ * fixed trans tag when no vars are used in plural form
761
+ * fixed race condition when writing template cache
762
+ * added the special _charset variable to reference the current charset
763
+ * added the special _context variable to reference the current context
764
+ * renamed self to _self (to avoid conflict)
765
+ * fixed Twig_Template::getAttribute() for protected properties
766
+
767
+ * 0.9.8 (2010-06-28)
768
+
769
+ Backward incompatibilities:
770
+ * the trans tag plural count is now attached to the plural tag:
771
+ old: `{% trans count %}...{% plural %}...{% endtrans %}`
772
+ new: `{% trans %}...{% plural count %}...{% endtrans %}`
773
+
774
+ * added a way to translate strings coming from a variable ({% trans var %})
775
+ * fixed trans tag when used with the Escaper extension
776
+ * fixed default cache umask
777
+ * removed Twig_Template instances from the debug tag output
778
+ * fixed objects with __isset() defined
779
+ * fixed set tag when used with a capture
780
+ * fixed type hinting for Twig_Environment::addFilter() method
781
+
782
+ * 0.9.7 (2010-06-12)
783
+
784
+ Backward incompatibilities:
785
+ * changed 'as' to '=' for the set tag ({% set title as "Title" %} must now be {% set title = "Title" %})
786
+ * removed the sandboxed attribute of the include tag (use the new sandbox tag instead)
787
+ * refactored the Node system (if you have custom nodes, you will have to update them to use the new API)
788
+
789
+ * added self as a special variable that refers to the current template (useful for importing macros from the current template)
790
+ * added Twig_Template instance support to the include tag
791
+ * added support for dynamic and conditional inheritance ({% extends some_var %} and {% extends standalone ? "minimum" : "base" %})
792
+ * added a grammar sub-framework to ease the creation of custom tags
793
+ * fixed the for tag for large arrays (some loop variables are now only available for arrays and objects that implement the Countable interface)
794
+ * removed the Twig_Resource::resolveMissingFilter() method
795
+ * fixed the filter tag which did not apply filtering to included files
796
+ * added a bunch of unit tests
797
+ * added a bunch of phpdoc
798
+ * added a sandbox tag in the sandbox extension
799
+ * changed the date filter to support any date format supported by DateTime
800
+ * added strict_variable setting to throw an exception when an invalid variable is used in a template (disabled by default)
801
+ * added the lexer, parser, and compiler as arguments to the Twig_Environment constructor
802
+ * changed the cache option to only accepts an explicit path to a cache directory or false
803
+ * added a way to add token parsers, filters, and visitors without creating an extension
804
+ * added three interfaces: Twig_NodeInterface, Twig_TokenParserInterface, and Twig_FilterInterface
805
+ * changed the generated code to match the new coding standards
806
+ * fixed sandbox mode (__toString() method check was not enforced if called implicitly from a simple statement like {{ article }})
807
+ * added an exception when a child template has a non-empty body (as it is always ignored when rendering)
808
+
809
+ * 0.9.6 (2010-05-12)
810
+
811
+ * fixed variables defined outside a loop and for which the value changes in a for loop
812
+ * fixed the test suite for PHP 5.2 and older versions of PHPUnit
813
+ * added support for __call() in expression resolution
814
+ * fixed node visiting for macros (macros are now visited by visitors as any other node)
815
+ * fixed nested block definitions with a parent call (rarely useful but nonetheless supported now)
816
+ * added the cycle filter
817
+ * fixed the Lexer when mbstring.func_overload is used with an mbstring.internal_encoding different from ASCII
818
+ * added a long-syntax for the set tag ({% set foo %}...{% endset %})
819
+ * unit tests are now powered by PHPUnit
820
+ * added support for gettext via the `i18n` extension
821
+ * fixed twig_capitalize_string_filter() and fixed twig_length_filter() when used with UTF-8 values
822
+ * added a more useful exception if an if tag is not closed properly
823
+ * added support for escaping strategy in the autoescape tag
824
+ * fixed lexer when a template has a big chunk of text between/in a block
825
+
826
+ * 0.9.5 (2010-01-20)
827
+
828
+ As for any new release, don't forget to remove all cached templates after
829
+ upgrading.
830
+
831
+ If you have defined custom filters, you MUST upgrade them for this release. To
832
+ upgrade, replace "array" with "new Twig_Filter_Function", and replace the
833
+ environment constant by the "needs_environment" option:
834
+
835
+ // before
836
+ 'even' => array('twig_is_even_filter', false),
837
+ 'escape' => array('twig_escape_filter', true),
838
+
839
+ // after
840
+ 'even' => new Twig_Filter_Function('twig_is_even_filter'),
841
+ 'escape' => new Twig_Filter_Function('twig_escape_filter', array('needs_environment' => true)),
842
+
843
+ If you have created NodeTransformer classes, you will need to upgrade them to
844
+ the new interface (please note that the interface is not yet considered
845
+ stable).
846
+
847
+ * fixed list nodes that did not extend the Twig_NodeListInterface
848
+ * added the "without loop" option to the for tag (it disables the generation of the loop variable)
849
+ * refactored node transformers to node visitors
850
+ * fixed automatic-escaping for blocks
851
+ * added a way to specify variables to pass to an included template
852
+ * changed the automatic-escaping rules to be more sensible and more configurable in custom filters (the documentation lists all the rules)
853
+ * improved the filter system to allow object methods to be used as filters
854
+ * changed the Array and String loaders to actually make use of the cache mechanism
855
+ * included the default filter function definitions in the extension class files directly (Core, Escaper)
856
+ * added the // operator (like the floor() PHP function)
857
+ * added the .. operator (as a syntactic sugar for the range filter when the step is 1)
858
+ * added the in operator (as a syntactic sugar for the in filter)
859
+ * added the following filters in the Core extension: in, range
860
+ * added support for arrays (same behavior as in PHP, a mix between lists and dictionaries, arrays and hashes)
861
+ * enhanced some error messages to provide better feedback in case of parsing errors
862
+
863
+ * 0.9.4 (2009-12-02)
864
+
865
+ If you have custom loaders, you MUST upgrade them for this release: The
866
+ Twig_Loader base class has been removed, and the Twig_LoaderInterface has also
867
+ been changed (see the source code for more information or the documentation).
868
+
869
+ * added support for DateTime instances for the date filter
870
+ * fixed loop.last when the array only has one item
871
+ * made it possible to insert newlines in tag and variable blocks
872
+ * fixed a bug when a literal '\n' were present in a template text
873
+ * fixed bug when the filename of a template contains */
874
+ * refactored loaders
875
+
876
+ * 0.9.3 (2009-11-11)
877
+
878
+ This release is NOT backward compatible with the previous releases.
879
+
880
+ The loaders do not take the cache and autoReload arguments anymore. Instead,
881
+ the Twig_Environment class has two new options: cache and auto_reload.
882
+ Upgrading your code means changing this kind of code:
883
+
884
+ $loader = new Twig_Loader_Filesystem('/path/to/templates', '/path/to/compilation_cache', true);
885
+ $twig = new Twig_Environment($loader);
886
+
887
+ to something like this:
888
+
889
+ $loader = new Twig_Loader_Filesystem('/path/to/templates');
890
+ $twig = new Twig_Environment($loader, array(
891
+ 'cache' => '/path/to/compilation_cache',
892
+ 'auto_reload' => true,
893
+ ));
894
+
895
+ * deprecated the "items" filter as it is not needed anymore
896
+ * made cache and auto_reload options of Twig_Environment instead of arguments of Twig_Loader
897
+ * optimized template loading speed
898
+ * removed output when an error occurs in a template and render() is used
899
+ * made major speed improvements for loops (up to 300% on even the smallest loops)
900
+ * added properties as part of the sandbox mode
901
+ * added public properties support (obj.item can now be the item property on the obj object)
902
+ * extended set tag to support expression as value ({% set foo as 'foo' ~ 'bar' %} )
903
+ * fixed bug when \ was used in HTML
904
+
905
+ * 0.9.2 (2009-10-29)
906
+
907
+ * made some speed optimizations
908
+ * changed the cache extension to .php
909
+ * added a js escaping strategy
910
+ * added support for short block tag
911
+ * changed the filter tag to allow chained filters
912
+ * made lexer more flexible as you can now change the default delimiters
913
+ * added set tag
914
+ * changed default directory permission when cache dir does not exist (more secure)
915
+ * added macro support
916
+ * changed filters first optional argument to be a Twig_Environment instance instead of a Twig_Template instance
917
+ * made Twig_Autoloader::autoload() a static method
918
+ * avoid writing template file if an error occurs
919
+ * added $ escaping when outputting raw strings
920
+ * enhanced some error messages to ease debugging
921
+ * fixed empty cache files when the template contains an error
922
+
923
+ * 0.9.1 (2009-10-14)
924
+
925
+ * fixed a bug in PHP 5.2.6
926
+ * fixed numbers with one than one decimal
927
+ * added support for method calls with arguments ({{ foo.bar('a', 43) }})
928
+ * made small speed optimizations
929
+ * made minor tweaks to allow better extensibility and flexibility
930
+
931
+ * 0.9.0 (2009-10-12)
932
+
933
+ * Initial release
vendor/twig/twig/LICENSE ADDED
@@ -0,0 +1,31 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ Copyright (c) 2009-2017 by the Twig Team.
2
+
3
+ Some rights reserved.
4
+
5
+ Redistribution and use in source and binary forms, with or without
6
+ modification, are permitted provided that the following conditions are
7
+ met:
8
+
9
+ * Redistributions of source code must retain the above copyright
10
+ notice, this list of conditions and the following disclaimer.
11
+
12
+ * Redistributions in binary form must reproduce the above
13
+ copyright notice, this list of conditions and the following
14
+ disclaimer in the documentation and/or other materials provided
15
+ with the distribution.
16
+
17
+ * The names of the contributors may not be used to endorse or
18
+ promote products derived from this software without specific
19
+ prior written permission.
20
+
21
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22
+ "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23
+ LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
24
+ A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
25
+ OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
26
+ SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
27
+ LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
28
+ DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
29
+ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
30
+ (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
31
+ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
vendor/twig/twig/README.rst ADDED
@@ -0,0 +1,15 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ Twig, the flexible, fast, and secure template language for PHP
2
+ ==============================================================
3
+
4
+ Twig is a template language for PHP, released under the new BSD license (code
5
+ and documentation).
6
+
7
+ Twig uses a syntax similar to the Django and Jinja template languages which
8
+ inspired the Twig runtime environment.
9
+
10
+ More Information
11
+ ----------------
12
+
13
+ Read the `documentation`_ for more information.
14
+
15
+ .. _documentation: http://twig.sensiolabs.org/documentation
vendor/twig/twig/composer.json ADDED
@@ -0,0 +1,47 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "name": "twig/twig",
3
+ "type": "library",
4
+ "description": "Twig, the flexible, fast, and secure template language for PHP",
5
+ "keywords": ["templating"],
6
+ "homepage": "http://twig.sensiolabs.org",
7
+ "license": "BSD-3-Clause",
8
+ "authors": [
9
+ {
10
+ "name": "Fabien Potencier",
11
+ "email": "fabien@symfony.com",
12
+ "homepage": "http://fabien.potencier.org",
13
+ "role": "Lead Developer"
14
+ },
15
+ {
16
+ "name": "Twig Team",
17
+ "homepage": "http://twig.sensiolabs.org/contributors",
18
+ "role": "Contributors"
19
+ },
20
+ {
21
+ "name": "Armin Ronacher",
22
+ "email": "armin.ronacher@active-4.com",
23
+ "role": "Project Founder"
24
+ }
25
+ ],
26
+ "support": {
27
+ "forum": "https://groups.google.com/forum/#!forum/twig-users"
28
+ },
29
+ "require": {
30
+ "php": ">=5.2.7"
31
+ },
32
+ "require-dev": {
33
+ "symfony/phpunit-bridge": "~3.2",
34
+ "symfony/debug": "~2.7",
35
+ "psr/container": "^1.0"
36
+ },
37
+ "autoload": {
38
+ "psr-0" : {
39
+ "Twig_" : "lib/"
40
+ }
41
+ },
42
+ "extra": {
43
+ "branch-alias": {
44
+ "dev-master": "1.32-dev"
45
+ }
46
+ }
47
+ }
vendor/twig/twig/doc/advanced.rst ADDED
@@ -0,0 +1,962 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ Extending Twig
2
+ ==============
3
+
4
+ .. caution::
5
+
6
+ This section describes how to extend Twig as of **Twig 1.12**. If you are
7
+ using an older version, read the :doc:`legacy<advanced_legacy>` chapter
8
+ instead.
9
+
10
+ Twig can be extended in many ways; you can add extra tags, filters, tests,
11
+ operators, global variables, and functions. You can even extend the parser
12
+ itself with node visitors.
13
+
14
+ .. note::
15
+
16
+ The first section of this chapter describes how to extend Twig easily. If
17
+ you want to reuse your changes in different projects or if you want to
18
+ share them with others, you should then create an extension as described
19
+ in the following section.
20
+
21
+ .. caution::
22
+
23
+ When extending Twig without creating an extension, Twig won't be able to
24
+ recompile your templates when the PHP code is updated. To see your changes
25
+ in real-time, either disable template caching or package your code into an
26
+ extension (see the next section of this chapter).
27
+
28
+ Before extending Twig, you must understand the differences between all the
29
+ different possible extension points and when to use them.
30
+
31
+ First, remember that Twig has two main language constructs:
32
+
33
+ * ``{{ }}``: used to print the result of an expression evaluation;
34
+
35
+ * ``{% %}``: used to execute statements.
36
+
37
+ To understand why Twig exposes so many extension points, let's see how to
38
+ implement a *Lorem ipsum* generator (it needs to know the number of words to
39
+ generate).
40
+
41
+ You can use a ``lipsum`` *tag*:
42
+
43
+ .. code-block:: jinja
44
+
45
+ {% lipsum 40 %}
46
+
47
+ That works, but using a tag for ``lipsum`` is not a good idea for at least
48
+ three main reasons:
49
+
50
+ * ``lipsum`` is not a language construct;
51
+ * The tag outputs something;
52
+ * The tag is not flexible as you cannot use it in an expression:
53
+
54
+ .. code-block:: jinja
55
+
56
+ {{ 'some text' ~ {% lipsum 40 %} ~ 'some more text' }}
57
+
58
+ In fact, you rarely need to create tags; and that's good news because tags are
59
+ the most complex extension point of Twig.
60
+
61
+ Now, let's use a ``lipsum`` *filter*:
62
+
63
+ .. code-block:: jinja
64
+
65
+ {{ 40|lipsum }}
66
+
67
+ Again, it works, but it looks weird. A filter transforms the passed value to
68
+ something else but here we use the value to indicate the number of words to
69
+ generate (so, ``40`` is an argument of the filter, not the value we want to
70
+ transform).
71
+
72
+ Next, let's use a ``lipsum`` *function*:
73
+
74
+ .. code-block:: jinja
75
+
76
+ {{ lipsum(40) }}
77
+
78
+ Here we go. For this specific example, the creation of a function is the
79
+ extension point to use. And you can use it anywhere an expression is accepted:
80
+
81
+ .. code-block:: jinja
82
+
83
+ {{ 'some text' ~ lipsum(40) ~ 'some more text' }}
84
+
85
+ {% set lipsum = lipsum(40) %}
86
+
87
+ Last but not the least, you can also use a *global* object with a method able
88
+ to generate lorem ipsum text:
89
+
90
+ .. code-block:: jinja
91
+
92
+ {{ text.lipsum(40) }}
93
+
94
+ As a rule of thumb, use functions for frequently used features and global
95
+ objects for everything else.
96
+
97
+ Keep in mind the following when you want to extend Twig:
98
+
99
+ ========== ========================== ========== =========================
100
+ What? Implementation difficulty? How often? When?
101
+ ========== ========================== ========== =========================
102
+ *macro* trivial frequent Content generation
103
+ *global* trivial frequent Helper object
104
+ *function* trivial frequent Content generation
105
+ *filter* trivial frequent Value transformation
106
+ *tag* complex rare DSL language construct
107
+ *test* trivial rare Boolean decision
108
+ *operator* trivial rare Values transformation
109
+ ========== ========================== ========== =========================
110
+
111
+ Globals
112
+ -------
113
+
114
+ A global variable is like any other template variable, except that it's
115
+ available in all templates and macros::
116
+
117
+ $twig = new Twig_Environment($loader);
118
+ $twig->addGlobal('text', new Text());
119
+
120
+ You can then use the ``text`` variable anywhere in a template:
121
+
122
+ .. code-block:: jinja
123
+
124
+ {{ text.lipsum(40) }}
125
+
126
+ Filters
127
+ -------
128
+
129
+ Creating a filter is as simple as associating a name with a PHP callable::
130
+
131
+ // an anonymous function
132
+ $filter = new Twig_SimpleFilter('rot13', function ($string) {
133
+ return str_rot13($string);
134
+ });
135
+
136
+ // or a simple PHP function
137
+ $filter = new Twig_SimpleFilter('rot13', 'str_rot13');
138
+
139
+ // or a class static method
140
+ $filter = new Twig_SimpleFilter('rot13', array('SomeClass', 'rot13Filter'));
141
+ $filter = new Twig_SimpleFilter('rot13', 'SomeClass::rot13Filter');
142
+
143
+ // or a class method
144
+ $filter = new Twig_SimpleFilter('rot13', array($this, 'rot13Filter'));
145
+ // the one below needs a runtime implementation (see below for more information)
146
+ $filter = new Twig_SimpleFilter('rot13', array('SomeClass', 'rot13Filter'));
147
+
148
+ The first argument passed to the ``Twig_SimpleFilter`` constructor is the name
149
+ of the filter you will use in templates and the second one is the PHP callable
150
+ to associate with it.
151
+
152
+ Then, add the filter to your Twig environment::
153
+
154
+ $twig = new Twig_Environment($loader);
155
+ $twig->addFilter($filter);
156
+
157
+ And here is how to use it in a template:
158
+
159
+ .. code-block:: jinja
160
+
161
+ {{ 'Twig'|rot13 }}
162
+
163
+ {# will output Gjvt #}
164
+
165
+ When called by Twig, the PHP callable receives the left side of the filter
166
+ (before the pipe ``|``) as the first argument and the extra arguments passed
167
+ to the filter (within parentheses ``()``) as extra arguments.
168
+
169
+ For instance, the following code:
170
+
171
+ .. code-block:: jinja
172
+
173
+ {{ 'TWIG'|lower }}
174
+ {{ now|date('d/m/Y') }}
175
+
176
+ is compiled to something like the following::
177
+
178
+ <?php echo strtolower('TWIG') ?>
179
+ <?php echo twig_date_format_filter($now, 'd/m/Y') ?>
180
+
181
+ The ``Twig_SimpleFilter`` class takes an array of options as its last
182
+ argument::
183
+
184
+ $filter = new Twig_SimpleFilter('rot13', 'str_rot13', $options);
185
+
186
+ Environment-aware Filters
187
+ ~~~~~~~~~~~~~~~~~~~~~~~~~
188
+
189
+ If you want to access the current environment instance in your filter, set the
190
+ ``needs_environment`` option to ``true``; Twig will pass the current
191
+ environment as the first argument to the filter call::
192
+
193
+ $filter = new Twig_SimpleFilter('rot13', function (Twig_Environment $env, $string) {
194
+ // get the current charset for instance
195
+ $charset = $env->getCharset();
196
+
197
+ return str_rot13($string);
198
+ }, array('needs_environment' => true));
199
+
200
+ Context-aware Filters
201
+ ~~~~~~~~~~~~~~~~~~~~~
202
+
203
+ If you want to access the current context in your filter, set the
204
+ ``needs_context`` option to ``true``; Twig will pass the current context as
205
+ the first argument to the filter call (or the second one if
206
+ ``needs_environment`` is also set to ``true``)::
207
+
208
+ $filter = new Twig_SimpleFilter('rot13', function ($context, $string) {
209
+ // ...
210
+ }, array('needs_context' => true));
211
+
212
+ $filter = new Twig_SimpleFilter('rot13', function (Twig_Environment $env, $context, $string) {
213
+ // ...
214
+ }, array('needs_context' => true, 'needs_environment' => true));
215
+
216
+ Automatic Escaping
217
+ ~~~~~~~~~~~~~~~~~~
218
+
219
+ If automatic escaping is enabled, the output of the filter may be escaped
220
+ before printing. If your filter acts as an escaper (or explicitly outputs HTML
221
+ or JavaScript code), you will want the raw output to be printed. In such a
222
+ case, set the ``is_safe`` option::
223
+
224
+ $filter = new Twig_SimpleFilter('nl2br', 'nl2br', array('is_safe' => array('html')));
225
+
226
+ Some filters may need to work on input that is already escaped or safe, for
227
+ example when adding (safe) HTML tags to originally unsafe output. In such a
228
+ case, set the ``pre_escape`` option to escape the input data before it is run
229
+ through your filter::
230
+
231
+ $filter = new Twig_SimpleFilter('somefilter', 'somefilter', array('pre_escape' => 'html', 'is_safe' => array('html')));
232
+
233
+ Variadic Filters
234
+ ~~~~~~~~~~~~~~~~
235
+
236
+ .. versionadded:: 1.19
237
+ Support for variadic filters was added in Twig 1.19.
238
+
239
+ When a filter should accept an arbitrary number of arguments, set the
240
+ ``is_variadic`` option to ``true``; Twig will pass the extra arguments as the
241
+ last argument to the filter call as an array::
242
+
243
+ $filter = new Twig_SimpleFilter('thumbnail', function ($file, array $options = array()) {
244
+ // ...
245
+ }, array('is_variadic' => true));
246
+
247
+ Be warned that named arguments passed to a variadic filter cannot be checked
248
+ for validity as they will automatically end up in the option array.
249
+
250
+ Dynamic Filters
251
+ ~~~~~~~~~~~~~~~
252
+
253
+ A filter name containing the special ``*`` character is a dynamic filter as
254
+ the ``*`` can be any string::
255
+
256
+ $filter = new Twig_SimpleFilter('*_path', function ($name, $arguments) {
257
+ // ...
258
+ });
259
+
260
+ The following filters will be matched by the above defined dynamic filter:
261
+
262
+ * ``product_path``
263
+ * ``category_path``
264
+
265
+ A dynamic filter can define more than one dynamic parts::
266
+
267
+ $filter = new Twig_SimpleFilter('*_path_*', function ($name, $suffix, $arguments) {
268
+ // ...
269
+ });
270
+
271
+ The filter will receive all dynamic part values before the normal filter
272
+ arguments, but after the environment and the context. For instance, a call to
273
+ ``'foo'|a_path_b()`` will result in the following arguments to be passed to
274
+ the filter: ``('a', 'b', 'foo')``.
275
+
276
+ Deprecated Filters
277
+ ~~~~~~~~~~~~~~~~~~
278
+
279
+ .. versionadded:: 1.21
280
+ Support for deprecated filters was added in Twig 1.21.
281
+
282
+ You can mark a filter as being deprecated by setting the ``deprecated`` option
283
+ to ``true``. You can also give an alternative filter that replaces the
284
+ deprecated one when that makes sense::
285
+
286
+ $filter = new Twig_SimpleFilter('obsolete', function () {
287
+ // ...
288
+ }, array('deprecated' => true, 'alternative' => 'new_one'));
289
+
290
+ When a filter is deprecated, Twig emits a deprecation notice when compiling a
291
+ template using it. See :ref:`deprecation-notices` for more information.
292
+
293
+ Functions
294
+ ---------
295
+
296
+ Functions are defined in the exact same way as filters, but you need to create
297
+ an instance of ``Twig_SimpleFunction``::
298
+
299
+ $twig = new Twig_Environment($loader);
300
+ $function = new Twig_SimpleFunction('function_name', function () {
301
+ // ...
302
+ });
303
+ $twig->addFunction($function);
304
+
305
+ Functions support the same features as filters, except for the ``pre_escape``
306
+ and ``preserves_safety`` options.
307
+
308
+ Tests
309
+ -----
310
+
311
+ Tests are defined in the exact same way as filters and functions, but you need
312
+ to create an instance of ``Twig_SimpleTest``::
313
+
314
+ $twig = new Twig_Environment($loader);
315
+ $test = new Twig_SimpleTest('test_name', function () {
316
+ // ...
317
+ });
318
+ $twig->addTest($test);
319
+
320
+ Tests allow you to create custom application specific logic for evaluating
321
+ boolean conditions. As a simple example, let's create a Twig test that checks if
322
+ objects are 'red'::
323
+
324
+ $twig = new Twig_Environment($loader);
325
+ $test = new Twig_SimpleTest('red', function ($value) {
326
+ if (isset($value->color) && $value->color == 'red') {
327
+ return true;
328
+ }
329
+ if (isset($value->paint) && $value->paint == 'red') {
330
+ return true;
331
+ }
332
+ return false;
333
+ });
334
+ $twig->addTest($test);
335
+
336
+ Test functions should always return true/false.
337
+
338
+ When creating tests you can use the ``node_class`` option to provide custom test
339
+ compilation. This is useful if your test can be compiled into PHP primitives.
340
+ This is used by many of the tests built into Twig::
341
+
342
+ $twig = new Twig_Environment($loader);
343
+ $test = new Twig_SimpleTest(
344
+ 'odd',
345
+ null,
346
+ array('node_class' => 'Twig_Node_Expression_Test_Odd'));
347
+ $twig->addTest($test);
348
+
349
+ class Twig_Node_Expression_Test_Odd extends Twig_Node_Expression_Test
350
+ {
351
+ public function compile(Twig_Compiler $compiler)
352
+ {
353
+ $compiler
354
+ ->raw('(')
355
+ ->subcompile($this->getNode('node'))
356
+ ->raw(' % 2 == 1')
357
+ ->raw(')')
358
+ ;
359
+ }
360
+ }
361
+
362
+ The above example shows how you can create tests that use a node class. The
363
+ node class has access to one sub-node called 'node'. This sub-node contains the
364
+ value that is being tested. When the ``odd`` filter is used in code such as:
365
+
366
+ .. code-block:: jinja
367
+
368
+ {% if my_value is odd %}
369
+
370
+ The ``node`` sub-node will contain an expression of ``my_value``. Node-based
371
+ tests also have access to the ``arguments`` node. This node will contain the
372
+ various other arguments that have been provided to your test.
373
+
374
+ If you want to pass a variable number of positional or named arguments to the
375
+ test, set the ``is_variadic`` option to ``true``. Tests also support dynamic
376
+ name feature as filters and functions.
377
+
378
+ Tags
379
+ ----
380
+
381
+ One of the most exciting features of a template engine like Twig is the
382
+ possibility to define new language constructs. This is also the most complex
383
+ feature as you need to understand how Twig's internals work.
384
+
385
+ Let's create a simple ``set`` tag that allows the definition of simple
386
+ variables from within a template. The tag can be used like follows:
387
+
388
+ .. code-block:: jinja
389
+
390
+ {% set name = "value" %}
391
+
392
+ {{ name }}
393
+
394
+ {# should output value #}
395
+
396
+ .. note::
397
+
398
+ The ``set`` tag is part of the Core extension and as such is always
399
+ available. The built-in version is slightly more powerful and supports
400
+ multiple assignments by default (cf. the template designers chapter for
401
+ more information).
402
+
403
+ Three steps are needed to define a new tag:
404
+
405
+ * Defining a Token Parser class (responsible for parsing the template code);
406
+
407
+ * Defining a Node class (responsible for converting the parsed code to PHP);
408
+
409
+ * Registering the tag.
410
+
411
+ Registering a new tag
412
+ ~~~~~~~~~~~~~~~~~~~~~
413
+
414
+ Adding a tag is as simple as calling the ``addTokenParser`` method on the
415
+ ``Twig_Environment`` instance::
416
+
417
+ $twig = new Twig_Environment($loader);
418
+ $twig->addTokenParser(new Project_Set_TokenParser());
419
+
420
+ Defining a Token Parser
421
+ ~~~~~~~~~~~~~~~~~~~~~~~
422
+
423
+ Now, let's see the actual code of this class::
424
+
425
+ class Project_Set_TokenParser extends Twig_TokenParser
426
+ {
427
+ public function parse(Twig_Token $token)
428
+ {
429
+ $parser = $this->parser;
430
+ $stream = $parser->getStream();
431
+
432
+ $name = $stream->expect(Twig_Token::NAME_TYPE)->getValue();
433
+ $stream->expect(Twig_Token::OPERATOR_TYPE, '=');
434
+ $value = $parser->getExpressionParser()->parseExpression();
435
+ $stream->expect(Twig_Token::BLOCK_END_TYPE);
436
+
437
+ return new Project_Set_Node($name, $value, $token->getLine(), $this->getTag());
438
+ }
439
+
440
+ public function getTag()
441
+ {
442
+ return 'set';
443
+ }
444
+ }
445
+
446
+ The ``getTag()`` method must return the tag we want to parse, here ``set``.
447
+
448
+ The ``parse()`` method is invoked whenever the parser encounters a ``set``
449
+ tag. It should return a ``Twig_Node`` instance that represents the node (the
450
+ ``Project_Set_Node`` calls creating is explained in the next section).
451
+
452
+ The parsing process is simplified thanks to a bunch of methods you can call
453
+ from the token stream (``$this->parser->getStream()``):
454
+
455
+ * ``getCurrent()``: Gets the current token in the stream.
456
+
457
+ * ``next()``: Moves to the next token in the stream, *but returns the old one*.
458
+
459
+ * ``test($type)``, ``test($value)`` or ``test($type, $value)``: Determines whether
460
+ the current token is of a particular type or value (or both). The value may be an
461
+ array of several possible values.
462
+
463
+ * ``expect($type[, $value[, $message]])``: If the current token isn't of the given
464
+ type/value a syntax error is thrown. Otherwise, if the type and value are correct,
465
+ the token is returned and the stream moves to the next token.
466
+
467
+ * ``look()``: Looks a the next token without consuming it.
468
+
469
+ Parsing expressions is done by calling the ``parseExpression()`` like we did for
470
+ the ``set`` tag.
471
+
472
+ .. tip::
473
+
474
+ Reading the existing ``TokenParser`` classes is the best way to learn all
475
+ the nitty-gritty details of the parsing process.
476
+
477
+ Defining a Node
478
+ ~~~~~~~~~~~~~~~
479
+
480
+ The ``Project_Set_Node`` class itself is rather simple::
481
+
482
+ class Project_Set_Node extends Twig_Node
483
+ {
484
+ public function __construct($name, Twig_Node_Expression $value, $line, $tag = null)
485
+ {
486
+ parent::__construct(array('value' => $value), array('name' => $name), $line, $tag);
487
+ }
488
+
489
+ public function compile(Twig_Compiler $compiler)
490
+ {
491
+ $compiler
492
+ ->addDebugInfo($this)
493
+ ->write('$context[\''.$this->getAttribute('name').'\'] = ')
494
+ ->subcompile($this->getNode('value'))
495
+ ->raw(";\n")
496
+ ;
497
+ }
498
+ }
499
+
500
+ The compiler implements a fluid interface and provides methods that helps the
501
+ developer generate beautiful and readable PHP code:
502
+
503
+ * ``subcompile()``: Compiles a node.
504
+
505
+ * ``raw()``: Writes the given string as is.
506
+
507
+ * ``write()``: Writes the given string by adding indentation at the beginning
508
+ of each line.
509
+
510
+ * ``string()``: Writes a quoted string.
511
+
512
+ * ``repr()``: Writes a PHP representation of a given value (see
513
+ ``Twig_Node_For`` for a usage example).
514
+
515
+ * ``addDebugInfo()``: Adds the line of the original template file related to
516
+ the current node as a comment.
517
+
518
+ * ``indent()``: Indents the generated code (see ``Twig_Node_Block`` for a
519
+ usage example).
520
+
521
+ * ``outdent()``: Outdents the generated code (see ``Twig_Node_Block`` for a
522
+ usage example).
523
+
524
+ .. _creating_extensions:
525
+
526
+ Creating an Extension
527
+ ---------------------
528
+
529
+ The main motivation for writing an extension is to move often used code into a
530
+ reusable class like adding support for internationalization. An extension can
531
+ define tags, filters, tests, operators, global variables, functions, and node
532
+ visitors.
533
+
534
+ Most of the time, it is useful to create a single extension for your project,
535
+ to host all the specific tags and filters you want to add to Twig.
536
+
537
+ .. tip::
538
+
539
+ When packaging your code into an extension, Twig is smart enough to
540
+ recompile your templates whenever you make a change to it (when
541
+ ``auto_reload`` is enabled).
542
+
543
+ .. note::
544
+
545
+ Before writing your own extensions, have a look at the Twig official
546
+ extension repository: http://github.com/twigphp/Twig-extensions.
547
+
548
+ An extension is a class that implements the following interface::
549
+
550
+ interface Twig_ExtensionInterface
551
+ {
552
+ /**
553
+ * Initializes the runtime environment.
554
+ *
555
+ * This is where you can load some file that contains filter functions for instance.
556
+ *
557
+ * @deprecated since 1.23 (to be removed in 2.0), implement Twig_Extension_InitRuntimeInterface instead
558
+ */
559
+ function initRuntime(Twig_Environment $environment);
560
+
561
+ /**
562
+ * Returns the token parser instances to add to the existing list.
563
+ *
564
+ * @return (Twig_TokenParserInterface|Twig_TokenParserBrokerInterface)[]
565
+ */
566
+ function getTokenParsers();
567
+
568
+ /**
569
+ * Returns the node visitor instances to add to the existing list.
570
+ *
571
+ * @return Twig_NodeVisitorInterface[]
572
+ */
573
+ function getNodeVisitors();
574
+
575
+ /**
576
+ * Returns a list of filters to add to the existing list.
577
+ *
578
+ * @return Twig_SimpleFilter[]
579
+ */
580
+ function getFilters();
581
+
582
+ /**
583
+ * Returns a list of tests to add to the existing list.
584
+ *
585
+ * @return Twig_SimpleTest[]
586
+ */
587
+ function getTests();
588
+
589
+ /**
590
+ * Returns a list of functions to add to the existing list.
591
+ *
592
+ * @return Twig_SimpleFunction[]
593
+ */
594
+ function getFunctions();
595
+
596
+ /**
597
+ * Returns a list of operators to add to the existing list.
598
+ *
599
+ * @return array<array> First array of unary operators, second array of binary operators
600
+ */
601
+ function getOperators();
602
+
603
+ /**
604
+ * Returns a list of global variables to add to the existing list.
605
+ *
606
+ * @return array An array of global variables
607
+ *
608
+ * @deprecated since 1.23 (to be removed in 2.0), implement Twig_Extension_GlobalsInterface instead
609
+ */
610
+ function getGlobals();
611
+
612
+ /**
613
+ * Returns the name of the extension.
614
+ *
615
+ * @return string The extension name
616
+ *
617
+ * @deprecated since 1.26 (to be removed in 2.0), not used anymore internally
618
+ */
619
+ function getName();
620
+ }
621
+
622
+ To keep your extension class clean and lean, inherit from the built-in
623
+ ``Twig_Extension`` class instead of implementing the interface as it provides
624
+ empty implementations for all methods:
625
+
626
+ class Project_Twig_Extension extends Twig_Extension
627
+ {
628
+ }
629
+
630
+ Of course, this extension does nothing for now. We will customize it in the
631
+ next sections.
632
+
633
+ .. note::
634
+
635
+ Prior to Twig 1.26, you must implement the ``getName()`` method which must
636
+ return a unique identifier for the extension.
637
+
638
+ Twig does not care where you save your extension on the filesystem, as all
639
+ extensions must be registered explicitly to be available in your templates.
640
+
641
+ You can register an extension by using the ``addExtension()`` method on your
642
+ main ``Environment`` object::
643
+
644
+ $twig = new Twig_Environment($loader);
645
+ $twig->addExtension(new Project_Twig_Extension());
646
+
647
+ .. tip::
648
+
649
+ The Twig core extensions are great examples of how extensions work.
650
+
651
+ Globals
652
+ ~~~~~~~
653
+
654
+ Global variables can be registered in an extension via the ``getGlobals()``
655
+ method::
656
+
657
+ class Project_Twig_Extension extends Twig_Extension implements Twig_Extension_GlobalsInterface
658
+ {
659
+ public function getGlobals()
660
+ {
661
+ return array(
662
+ 'text' => new Text(),
663
+ );
664
+ }
665
+
666
+ // ...
667
+ }
668
+
669
+ Functions
670
+ ~~~~~~~~~
671
+
672
+ Functions can be registered in an extension via the ``getFunctions()``
673
+ method::
674
+
675
+ class Project_Twig_Extension extends Twig_Extension
676
+ {
677
+ public function getFunctions()
678
+ {
679
+ return array(
680
+ new Twig_SimpleFunction('lipsum', 'generate_lipsum'),
681
+ );
682
+ }
683
+
684
+ // ...
685
+ }
686
+
687
+ Filters
688
+ ~~~~~~~
689
+
690
+ To add a filter to an extension, you need to override the ``getFilters()``
691
+ method. This method must return an array of filters to add to the Twig
692
+ environment::
693
+
694
+ class Project_Twig_Extension extends Twig_Extension
695
+ {
696
+ public function getFilters()
697
+ {
698
+ return array(
699
+ new Twig_SimpleFilter('rot13', 'str_rot13'),
700
+ );
701
+ }
702
+
703
+ // ...
704
+ }
705
+
706
+ Tags
707
+ ~~~~
708
+
709
+ Adding a tag in an extension can be done by overriding the
710
+ ``getTokenParsers()`` method. This method must return an array of tags to add
711
+ to the Twig environment::
712
+
713
+ class Project_Twig_Extension extends Twig_Extension
714
+ {
715
+ public function getTokenParsers()
716
+ {
717
+ return array(new Project_Set_TokenParser());
718
+ }
719
+
720
+ // ...
721
+ }
722
+
723
+ In the above code, we have added a single new tag, defined by the
724
+ ``Project_Set_TokenParser`` class. The ``Project_Set_TokenParser`` class is
725
+ responsible for parsing the tag and compiling it to PHP.
726
+
727
+ Operators
728
+ ~~~~~~~~~
729
+
730
+ The ``getOperators()`` methods lets you add new operators. Here is how to add
731
+ ``!``, ``||``, and ``&&`` operators::
732
+
733
+ class Project_Twig_Extension extends Twig_Extension
734
+ {
735
+ public function getOperators()
736
+ {
737
+ return array(
738
+ array(
739
+ '!' => array('precedence' => 50, 'class' => 'Twig_Node_Expression_Unary_Not'),
740
+ ),
741
+ array(
742
+ '||' => array('precedence' => 10, 'class' => 'Twig_Node_Expression_Binary_Or', 'associativity' => Twig_ExpressionParser::OPERATOR_LEFT),
743
+ '&&' => array('precedence' => 15, 'class' => 'Twig_Node_Expression_Binary_And', 'associativity' => Twig_ExpressionParser::OPERATOR_LEFT),
744
+ ),
745
+ );
746
+ }
747
+
748
+ // ...
749
+ }
750
+
751
+ Tests
752
+ ~~~~~
753
+
754
+ The ``getTests()`` method lets you add new test functions::
755
+
756
+ class Project_Twig_Extension extends Twig_Extension
757
+ {
758
+ public function getTests()
759
+ {
760
+ return array(
761
+ new Twig_SimpleTest('even', 'twig_test_even'),
762
+ );
763
+ }
764
+
765
+ // ...
766
+ }
767
+
768
+ Definition vs Runtime
769
+ ~~~~~~~~~~~~~~~~~~~~~
770
+
771
+ Twig filters, functions, and tests runtime implementations can be defined as
772
+ any valid PHP callable:
773
+
774
+ * **functions/static methods**: Simple to implement and fast (used by all Twig
775
+ core extensions); but it is hard for the runtime to depend on external
776
+ objects;
777
+
778
+ * **closures**: Simple to implement;
779
+
780
+ * **object methods**: More flexible and required if your runtime code depends
781
+ on external objects.
782
+
783
+ The simplest way to use methods is to define them on the extension itself::
784
+
785
+ class Project_Twig_Extension extends Twig_Extension
786
+ {
787
+ private $rot13Provider;
788
+
789
+ public function __construct($rot13Provider)
790
+ {
791
+ $this->rot13Provider = $rot13Provider;
792
+ }
793
+
794
+ public function getFunctions()
795
+ {
796
+ return array(
797
+ new Twig_SimpleFunction('rot13', array($this, 'rot13')),
798
+ );
799
+ }
800
+
801
+ public function rot13($value)
802
+ {
803
+ return $rot13Provider->rot13($value);
804
+ }
805
+ }
806
+
807
+ This is very convenient but not recommended as it makes template compilation
808
+ depend on runtime dependencies even if they are not needed (think for instance
809
+ as a dependency that connects to a database engine).
810
+
811
+ As of Twig 1.26, you can easily decouple the extension definitions from their
812
+ runtime implementations by registering a ``Twig_RuntimeLoaderInterface``
813
+ instance on the environment that knows how to instantiate such runtime classes
814
+ (runtime classes must be autoload-able)::
815
+
816
+ class RuntimeLoader implements Twig_RuntimeLoaderInterface
817
+ {
818
+ public function load($class)
819
+ {
820
+ // implement the logic to create an instance of $class
821
+ // and inject its dependencies
822
+ // most of the time, it means using your dependency injection container
823
+ if ('Project_Twig_RuntimeExtension' === $class) {
824
+ return new $class(new Rot13Provider());
825
+ } else {
826
+ // ...
827
+ }
828
+ }
829
+ }
830
+
831
+ $twig->addRuntimeLoader(new RuntimeLoader());
832
+
833
+ .. note::
834
+
835
+ As of Twig 1.32, Twig comes with a PSR-11 compatible runtime loader
836
+ (``Twig_ContainerRuntimeLoader``) that works on PHP 5.3+.
837
+
838
+ It is now possible to move the runtime logic to a new
839
+ ``Project_Twig_RuntimeExtension`` class and use it directly in the extension::
840
+
841
+ class Project_Twig_RuntimeExtension extends Twig_Extension
842
+ {
843
+ private $rot13Provider;
844
+
845
+ public function __construct($rot13Provider)
846
+ {
847
+ $this->rot13Provider = $rot13Provider;
848
+ }
849
+
850
+ public function rot13($value)
851
+ {
852
+ return $rot13Provider->rot13($value);
853
+ }
854
+ }
855
+
856
+ class Project_Twig_Extension extends Twig_Extension
857
+ {
858
+ public function getFunctions()
859
+ {
860
+ return array(
861
+ new Twig_SimpleFunction('rot13', array('Project_Twig_RuntimeExtension', 'rot13')),
862
+ // or
863
+ new Twig_SimpleFunction('rot13', 'Project_Twig_RuntimeExtension::rot13'),
864
+ );
865
+ }
866
+ }
867
+
868
+ Overloading
869
+ -----------
870
+
871
+ To overload an already defined filter, test, operator, global variable, or
872
+ function, re-define it in an extension and register it **as late as
873
+ possible** (order matters)::
874
+
875
+ class MyCoreExtension extends Twig_Extension
876
+ {
877
+ public function getFilters()
878
+ {
879
+ return array(
880
+ new Twig_SimpleFilter('date', array($this, 'dateFilter')),
881
+ );
882
+ }
883
+
884
+ public function dateFilter($timestamp, $format = 'F j, Y H:i')
885
+ {
886
+ // do something different from the built-in date filter
887
+ }
888
+ }
889
+
890
+ $twig = new Twig_Environment($loader);
891
+ $twig->addExtension(new MyCoreExtension());
892
+
893
+ Here, we have overloaded the built-in ``date`` filter with a custom one.
894
+
895
+ If you do the same on the ``Twig_Environment`` itself, beware that it takes
896
+ precedence over any other registered extensions::
897
+
898
+ $twig = new Twig_Environment($loader);
899
+ $twig->addFilter(new Twig_SimpleFilter('date', function ($timestamp, $format = 'F j, Y H:i') {
900
+ // do something different from the built-in date filter
901
+ }));
902
+ // the date filter will come from the above registration, not
903
+ // from the registered extension below
904
+ $twig->addExtension(new MyCoreExtension());
905
+
906
+ .. caution::
907
+
908
+ Note that overloading the built-in Twig elements is not recommended as it
909
+ might be confusing.
910
+
911
+ Testing an Extension
912
+ --------------------
913
+
914
+ Functional Tests
915
+ ~~~~~~~~~~~~~~~~
916
+
917
+ You can create functional tests for extensions simply by creating the
918
+ following file structure in your test directory::
919
+
920
+ Fixtures/
921
+ filters/
922
+ foo.test
923
+ bar.test
924
+ functions/
925
+ foo.test
926
+ bar.test
927
+ tags/
928
+ foo.test
929
+ bar.test
930
+ IntegrationTest.php
931
+
932
+ The ``IntegrationTest.php`` file should look like this::
933
+
934
+ class Project_Tests_IntegrationTest extends Twig_Test_IntegrationTestCase
935
+ {
936
+ public function getExtensions()
937
+ {
938
+ return array(
939
+ new Project_Twig_Extension1(),
940
+ new Project_Twig_Extension2(),
941
+ );
942
+ }
943
+
944
+ public function getFixturesDir()
945
+ {
946
+ return dirname(__FILE__).'/Fixtures/';
947
+ }
948
+ }
949
+
950
+ Fixtures examples can be found within the Twig repository
951
+ `tests/Twig/Fixtures`_ directory.
952
+
953
+ Node Tests
954
+ ~~~~~~~~~~
955
+
956
+ Testing the node visitors can be complex, so extend your test cases from
957
+ ``Twig_Test_NodeTestCase``. Examples can be found in the Twig repository
958
+ `tests/Twig/Node`_ directory.
959
+
960
+ .. _`rot13`: http://www.php.net/manual/en/function.str-rot13.php
961
+ .. _`tests/Twig/Fixtures`: https://github.com/twigphp/Twig/tree/master/test/Twig/Tests/Fixtures
962
+ .. _`tests/Twig/Node`: https://github.com/twigphp/Twig/tree/master/test/Twig/Tests/Node
vendor/twig/twig/doc/advanced_legacy.rst ADDED
@@ -0,0 +1,885 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ Extending Twig
2
+ ==============
3
+
4
+ .. caution::
5
+
6
+ This section describes how to extends Twig for versions **older than
7
+ 1.12**. If you are using a newer version, read the :doc:`newer<advanced>`
8
+ chapter instead.
9
+
10
+ Twig can be extended in many ways; you can add extra tags, filters, tests,
11
+ operators, global variables, and functions. You can even extend the parser
12
+ itself with node visitors.
13
+
14
+ .. note::
15
+
16
+ The first section of this chapter describes how to extend Twig easily. If
17
+ you want to reuse your changes in different projects or if you want to
18
+ share them with others, you should then create an extension as described
19
+ in the following section.
20
+
21
+ .. caution::
22
+
23
+ When extending Twig by calling methods on the Twig environment instance,
24
+ Twig won't be able to recompile your templates when the PHP code is
25
+ updated. To see your changes in real-time, either disable template caching
26
+ or package your code into an extension (see the next section of this
27
+ chapter).
28
+
29
+ Before extending Twig, you must understand the differences between all the
30
+ different possible extension points and when to use them.
31
+
32
+ First, remember that Twig has two main language constructs:
33
+
34
+ * ``{{ }}``: used to print the result of an expression evaluation;
35
+
36
+ * ``{% %}``: used to execute statements.
37
+
38
+ To understand why Twig exposes so many extension points, let's see how to
39
+ implement a *Lorem ipsum* generator (it needs to know the number of words to
40
+ generate).
41
+
42
+ You can use a ``lipsum`` *tag*:
43
+
44
+ .. code-block:: jinja
45
+
46
+ {% lipsum 40 %}
47
+
48
+ That works, but using a tag for ``lipsum`` is not a good idea for at least
49
+ three main reasons:
50
+
51
+ * ``lipsum`` is not a language construct;
52
+ * The tag outputs something;
53
+ * The tag is not flexible as you cannot use it in an expression:
54
+
55
+ .. code-block:: jinja
56
+
57
+ {{ 'some text' ~ {% lipsum 40 %} ~ 'some more text' }}
58
+
59
+ In fact, you rarely need to create tags; and that's good news because tags are
60
+ the most complex extension point of Twig.
61
+
62
+ Now, let's use a ``lipsum`` *filter*:
63
+
64
+ .. code-block:: jinja
65
+
66
+ {{ 40|lipsum }}
67
+
68
+ Again, it works, but it looks weird. A filter transforms the passed value to
69
+ something else but here we use the value to indicate the number of words to
70
+ generate (so, ``40`` is an argument of the filter, not the value we want to
71
+ transform).
72
+
73
+ Next, let's use a ``lipsum`` *function*:
74
+
75
+ .. code-block:: jinja
76
+
77
+ {{ lipsum(40) }}
78
+
79
+ Here we go. For this specific example, the creation of a function is the
80
+ extension point to use. And you can use it anywhere an expression is accepted:
81
+
82
+ .. code-block:: jinja
83
+
84
+ {{ 'some text' ~ ipsum(40) ~ 'some more text' }}
85
+
86
+ {% set ipsum = ipsum(40) %}
87
+
88
+ Last but not the least, you can also use a *global* object with a method able
89
+ to generate lorem ipsum text:
90
+
91
+ .. code-block:: jinja
92
+
93
+ {{ text.lipsum(40) }}
94
+
95
+ As a rule of thumb, use functions for frequently used features and global
96
+ objects for everything else.
97
+
98
+ Keep in mind the following when you want to extend Twig:
99
+
100
+ ========== ========================== ========== =========================
101
+ What? Implementation difficulty? How often? When?
102
+ ========== ========================== ========== =========================
103
+ *macro* trivial frequent Content generation
104
+ *global* trivial frequent Helper object
105
+ *function* trivial frequent Content generation
106
+ *filter* trivial frequent Value transformation
107
+ *tag* complex rare DSL language construct
108
+ *test* trivial rare Boolean decision
109
+ *operator* trivial rare Values transformation
110
+ ========== ========================== ========== =========================
111
+
112
+ Globals
113
+ -------
114
+
115
+ A global variable is like any other template variable, except that it's
116
+ available in all templates and macros::
117
+
118
+ $twig = new Twig_Environment($loader);
119
+ $twig->addGlobal('text', new Text());
120
+
121
+ You can then use the ``text`` variable anywhere in a template:
122
+
123
+ .. code-block:: jinja
124
+
125
+ {{ text.lipsum(40) }}
126
+
127
+ Filters
128
+ -------
129
+
130
+ A filter is a regular PHP function or an object method that takes the left
131
+ side of the filter (before the pipe ``|``) as first argument and the extra
132
+ arguments passed to the filter (within parentheses ``()``) as extra arguments.
133
+
134
+ Defining a filter is as easy as associating the filter name with a PHP
135
+ callable. For instance, let's say you have the following code in a template:
136
+
137
+ .. code-block:: jinja
138
+
139
+ {{ 'TWIG'|lower }}
140
+
141
+ When compiling this template to PHP, Twig looks for the PHP callable
142
+ associated with the ``lower`` filter. The ``lower`` filter is a built-in Twig
143
+ filter, and it is simply mapped to the PHP ``strtolower()`` function. After
144
+ compilation, the generated PHP code is roughly equivalent to:
145
+
146
+ .. code-block:: html+php
147
+
148
+ <?php echo strtolower('TWIG') ?>
149
+
150
+ As you can see, the ``'TWIG'`` string is passed as a first argument to the PHP
151
+ function.
152
+
153
+ A filter can also take extra arguments like in the following example:
154
+
155
+ .. code-block:: jinja
156
+
157
+ {{ now|date('d/m/Y') }}
158
+
159
+ In this case, the extra arguments are passed to the function after the main
160
+ argument, and the compiled code is equivalent to:
161
+
162
+ .. code-block:: html+php
163
+
164
+ <?php echo twig_date_format_filter($now, 'd/m/Y') ?>
165
+
166
+ Let's see how to create a new filter.
167
+
168
+ In this section, we will create a ``rot13`` filter, which should return the
169
+ `rot13`_ transformation of a string. Here is an example of its usage and the
170
+ expected output:
171
+
172
+ .. code-block:: jinja
173
+
174
+ {{ "Twig"|rot13 }}
175
+
176
+ {# should displays Gjvt #}
177
+
178
+ Adding a filter is as simple as calling the ``addFilter()`` method on the
179
+ ``Twig_Environment`` instance::
180
+
181
+ $twig = new Twig_Environment($loader);
182
+ $twig->addFilter('rot13', new Twig_Filter_Function('str_rot13'));
183
+
184
+ The second argument of ``addFilter()`` is an instance of ``Twig_Filter``.
185
+ Here, we use ``Twig_Filter_Function`` as the filter is a PHP function. The
186
+ first argument passed to the ``Twig_Filter_Function`` constructor is the name
187
+ of the PHP function to call, here ``str_rot13``, a native PHP function.
188
+
189
+ Let's say I now want to be able to add a prefix before the converted string:
190
+
191
+ .. code-block:: jinja
192
+
193
+ {{ "Twig"|rot13('prefix_') }}
194
+
195
+ {# should displays prefix_Gjvt #}
196
+
197
+ As the PHP ``str_rot13()`` function does not support this requirement, let's
198
+ create a new PHP function::
199
+
200
+ function project_compute_rot13($string, $prefix = '')
201
+ {
202
+ return $prefix.str_rot13($string);
203
+ }
204
+
205
+ As you can see, the ``prefix`` argument of the filter is passed as an extra
206
+ argument to the ``project_compute_rot13()`` function.
207
+
208
+ Adding this filter is as easy as before::
209
+
210
+ $twig->addFilter('rot13', new Twig_Filter_Function('project_compute_rot13'));
211
+
212
+ For better encapsulation, a filter can also be defined as a static method of a
213
+ class. The ``Twig_Filter_Function`` class can also be used to register such
214
+ static methods as filters::
215
+
216
+ $twig->addFilter('rot13', new Twig_Filter_Function('SomeClass::rot13Filter'));
217
+
218
+ .. tip::
219
+
220
+ In an extension, you can also define a filter as a static method of the
221
+ extension class.
222
+
223
+ Environment aware Filters
224
+ ~~~~~~~~~~~~~~~~~~~~~~~~~
225
+
226
+ The ``Twig_Filter`` classes take options as their last argument. For instance,
227
+ if you want access to the current environment instance in your filter, set the
228
+ ``needs_environment`` option to ``true``::
229
+
230
+ $filter = new Twig_Filter_Function('str_rot13', array('needs_environment' => true));
231
+
232
+ Twig will then pass the current environment as the first argument to the
233
+ filter call::
234
+
235
+ function twig_compute_rot13(Twig_Environment $env, $string)
236
+ {
237
+ // get the current charset for instance
238
+ $charset = $env->getCharset();
239
+
240
+ return str_rot13($string);
241
+ }
242
+
243
+ Automatic Escaping
244
+ ~~~~~~~~~~~~~~~~~~
245
+
246
+ If automatic escaping is enabled, the output of the filter may be escaped
247
+ before printing. If your filter acts as an escaper (or explicitly outputs HTML
248
+ or JavaScript code), you will want the raw output to be printed. In such a
249
+ case, set the ``is_safe`` option::
250
+
251
+ $filter = new Twig_Filter_Function('nl2br', array('is_safe' => array('html')));
252
+
253
+ Some filters may need to work on input that is already escaped or safe, for
254
+ example when adding (safe) HTML tags to originally unsafe output. In such a
255
+ case, set the ``pre_escape`` option to escape the input data before it is run
256
+ through your filter::
257
+
258
+ $filter = new Twig_Filter_Function('somefilter', array('pre_escape' => 'html', 'is_safe' => array('html')));
259
+
260
+ Dynamic Filters
261
+ ~~~~~~~~~~~~~~~
262
+
263
+ .. versionadded:: 1.5
264
+ Dynamic filters support was added in Twig 1.5.
265
+
266
+ A filter name containing the special ``*`` character is a dynamic filter as
267
+ the ``*`` can be any string::
268
+
269
+ $twig->addFilter('*_path_*', new Twig_Filter_Function('twig_path'));
270
+
271
+ function twig_path($name, $arguments)
272
+ {
273
+ // ...
274
+ }
275
+
276
+ The following filters will be matched by the above defined dynamic filter:
277
+
278
+ * ``product_path``
279
+ * ``category_path``
280
+
281
+ A dynamic filter can define more than one dynamic parts::
282
+
283
+ $twig->addFilter('*_path_*', new Twig_Filter_Function('twig_path'));
284
+
285
+ function twig_path($name, $suffix, $arguments)
286
+ {
287
+ // ...
288
+ }
289
+
290
+ The filter will receive all dynamic part values before the normal filters
291
+ arguments. For instance, a call to ``'foo'|a_path_b()`` will result in the
292
+ following PHP call: ``twig_path('a', 'b', 'foo')``.
293
+
294
+ Functions
295
+ ---------
296
+
297
+ A function is a regular PHP function or an object method that can be called from
298
+ templates.
299
+
300
+ .. code-block:: jinja
301
+
302
+ {{ constant("DATE_W3C") }}
303
+
304
+ When compiling this template to PHP, Twig looks for the PHP callable
305
+ associated with the ``constant`` function. The ``constant`` function is a built-in Twig
306
+ function, and it is simply mapped to the PHP ``constant()`` function. After
307
+ compilation, the generated PHP code is roughly equivalent to:
308
+
309
+ .. code-block:: html+php
310
+
311
+ <?php echo constant('DATE_W3C') ?>
312
+
313
+ Adding a function is similar to adding a filter. This can be done by calling the
314
+ ``addFunction()`` method on the ``Twig_Environment`` instance::
315
+
316
+ $twig = new Twig_Environment($loader);
317
+ $twig->addFunction('functionName', new Twig_Function_Function('someFunction'));
318
+
319
+ You can also expose extension methods as functions in your templates::
320
+
321
+ // $this is an object that implements Twig_ExtensionInterface.
322
+ $twig = new Twig_Environment($loader);
323
+ $twig->addFunction('otherFunction', new Twig_Function_Method($this, 'someMethod'));
324
+
325
+ Functions also support ``needs_environment`` and ``is_safe`` parameters.
326
+
327
+ Dynamic Functions
328
+ ~~~~~~~~~~~~~~~~~
329
+
330
+ .. versionadded:: 1.5
331
+ Dynamic functions support was added in Twig 1.5.
332
+
333
+ A function name containing the special ``*`` character is a dynamic function
334
+ as the ``*`` can be any string::
335
+
336
+ $twig->addFunction('*_path', new Twig_Function_Function('twig_path'));
337
+
338
+ function twig_path($name, $arguments)
339
+ {
340
+ // ...
341
+ }
342
+
343
+ The following functions will be matched by the above defined dynamic function:
344
+
345
+ * ``product_path``
346
+ * ``category_path``
347
+
348
+ A dynamic function can define more than one dynamic parts::
349
+
350
+ $twig->addFilter('*_path_*', new Twig_Filter_Function('twig_path'));
351
+
352
+ function twig_path($name, $suffix, $arguments)
353
+ {
354
+ // ...
355
+ }
356
+
357
+ The function will receive all dynamic part values before the normal functions
358
+ arguments. For instance, a call to ``a_path_b('foo')`` will result in the
359
+ following PHP call: ``twig_path('a', 'b', 'foo')``.
360
+
361
+ Tags
362
+ ----
363
+
364
+ One of the most exciting feature of a template engine like Twig is the
365
+ possibility to define new language constructs. This is also the most complex
366
+ feature as you need to understand how Twig's internals work.
367
+
368
+ Let's create a simple ``set`` tag that allows the definition of simple
369
+ variables from within a template. The tag can be used like follows:
370
+
371
+ .. code-block:: jinja
372
+
373
+ {% set name = "value" %}
374
+
375
+ {{ name }}
376
+
377
+ {# should output value #}
378
+
379
+ .. note::
380
+
381
+ The ``set`` tag is part of the Core extension and as such is always
382
+ available. The built-in version is slightly more powerful and supports
383
+ multiple assignments by default (cf. the template designers chapter for
384
+ more information).
385
+
386
+ Three steps are needed to define a new tag:
387
+
388
+ * Defining a Token Parser class (responsible for parsing the template code);
389
+
390
+ * Defining a Node class (responsible for converting the parsed code to PHP);
391
+
392
+ * Registering the tag.
393
+
394
+ Registering a new tag
395
+ ~~~~~~~~~~~~~~~~~~~~~
396
+
397
+ Adding a tag is as simple as calling the ``addTokenParser`` method on the
398
+ ``Twig_Environment`` instance::
399
+
400
+ $twig = new Twig_Environment($loader);
401
+ $twig->addTokenParser(new Project_Set_TokenParser());
402
+
403
+ Defining a Token Parser
404
+ ~~~~~~~~~~~~~~~~~~~~~~~
405
+
406
+ Now, let's see the actual code of this class::
407
+
408
+ class Project_Set_TokenParser extends Twig_TokenParser
409
+ {
410
+ public function parse(Twig_Token $token)
411
+ {
412
+ $lineno = $token->getLine();
413
+ $name = $this->parser->getStream()->expect(Twig_Token::NAME_TYPE)->getValue();
414
+ $this->parser->getStream()->expect(Twig_Token::OPERATOR_TYPE, '=');
415
+ $value = $this->parser->getExpressionParser()->parseExpression();
416
+
417
+ $this->parser->getStream()->expect(Twig_Token::BLOCK_END_TYPE);
418
+
419
+ return new Project_Set_Node($name, $value, $lineno, $this->getTag());
420
+ }
421
+
422
+ public function getTag()
423
+ {
424
+ return 'set';
425
+ }
426
+ }
427
+
428
+ The ``getTag()`` method must return the tag we want to parse, here ``set``.
429
+
430
+ The ``parse()`` method is invoked whenever the parser encounters a ``set``
431
+ tag. It should return a ``Twig_Node`` instance that represents the node (the
432
+ ``Project_Set_Node`` calls creating is explained in the next section).
433
+
434
+ The parsing process is simplified thanks to a bunch of methods you can call
435
+ from the token stream (``$this->parser->getStream()``):
436
+
437
+ * ``getCurrent()``: Gets the current token in the stream.
438
+
439
+ * ``next()``: Moves to the next token in the stream, *but returns the old one*.
440
+
441
+ * ``test($type)``, ``test($value)`` or ``test($type, $value)``: Determines whether
442
+ the current token is of a particular type or value (or both). The value may be an
443
+ array of several possible values.
444
+
445
+ * ``expect($type[, $value[, $message]])``: If the current token isn't of the given
446
+ type/value a syntax error is thrown. Otherwise, if the type and value are correct,
447
+ the token is returned and the stream moves to the next token.
448
+
449
+ * ``look()``: Looks a the next token without consuming it.
450
+
451
+ Parsing expressions is done by calling the ``parseExpression()`` like we did for
452
+ the ``set`` tag.
453
+
454
+ .. tip::
455
+
456
+ Reading the existing ``TokenParser`` classes is the best way to learn all
457
+ the nitty-gritty details of the parsing process.
458
+
459
+ Defining a Node
460
+ ~~~~~~~~~~~~~~~
461
+
462
+ The ``Project_Set_Node`` class itself is rather simple::
463
+
464
+ class Project_Set_Node extends Twig_Node
465
+ {
466
+ public function __construct($name, Twig_Node_Expression $value, $lineno, $tag = null)
467
+ {
468
+ parent::__construct(array('value' => $value), array('name' => $name), $lineno, $tag);
469
+ }
470
+
471
+ public function compile(Twig_Compiler $compiler)
472
+ {
473
+ $compiler
474
+ ->addDebugInfo($this)
475
+ ->write('$context[\''.$this->getAttribute('name').'\'] = ')
476
+ ->subcompile($this->getNode('value'))
477
+ ->raw(";\n")
478
+ ;
479
+ }
480
+ }
481
+
482
+ The compiler implements a fluid interface and provides methods that helps the
483
+ developer generate beautiful and readable PHP code:
484
+
485
+ * ``subcompile()``: Compiles a node.
486
+
487
+ * ``raw()``: Writes the given string as is.
488
+
489
+ * ``write()``: Writes the given string by adding indentation at the beginning
490
+ of each line.
491
+
492
+ * ``string()``: Writes a quoted string.
493
+
494
+ * ``repr()``: Writes a PHP representation of a given value (see
495
+ ``Twig_Node_For`` for a usage example).
496
+
497
+ * ``addDebugInfo()``: Adds the line of the original template file related to
498
+ the current node as a comment.
499
+
500
+ * ``indent()``: Indents the generated code (see ``Twig_Node_Block`` for a
501
+ usage example).
502
+
503
+ * ``outdent()``: Outdents the generated code (see ``Twig_Node_Block`` for a
504
+ usage example).
505
+
506
+ .. _creating_extensions:
507
+
508
+ Creating an Extension
509
+ ---------------------
510
+
511
+ The main motivation for writing an extension is to move often used code into a
512
+ reusable class like adding support for internationalization. An extension can
513
+ define tags, filters, tests, operators, global variables, functions, and node
514
+ visitors.
515
+
516
+ Creating an extension also makes for a better separation of code that is
517
+ executed at compilation time and code needed at runtime. As such, it makes
518
+ your code faster.
519
+
520
+ Most of the time, it is useful to create a single extension for your project,
521
+ to host all the specific tags and filters you want to add to Twig.
522
+
523
+ .. tip::
524
+
525
+ When packaging your code into an extension, Twig is smart enough to
526
+ recompile your templates whenever you make a change to it (when the
527
+ ``auto_reload`` is enabled).
528
+
529
+ .. note::
530
+
531
+ Before writing your own extensions, have a look at the Twig official
532
+ extension repository: http://github.com/twigphp/Twig-extensions.
533
+
534
+ An extension is a class that implements the following interface::
535
+
536
+ interface Twig_ExtensionInterface
537
+ {
538
+ /**
539
+ * Initializes the runtime environment.
540
+ *
541
+ * This is where you can load some file that contains filter functions for instance.
542
+ */
543
+ function initRuntime(Twig_Environment $environment);
544
+
545
+ /**
546
+ * Returns the token parser instances to add to the existing list.
547
+ *
548
+ * @return (Twig_TokenParserInterface|Twig_TokenParserBrokerInterface)[]
549
+ */
550
+ function getTokenParsers();
551
+
552
+ /**
553
+ * Returns the node visitor instances to add to the existing list.
554
+ *
555
+ * @return Twig_NodeVisitorInterface[]
556
+ */
557
+ function getNodeVisitors();
558
+
559
+ /**
560
+ * Returns a list of filters to add to the existing list.
561
+ *
562
+ * @return Twig_SimpleFilter[]
563
+ */
564
+ function getFilters();
565
+
566
+ /**
567
+ * Returns a list of tests to add to the existing list.
568
+ *
569
+ * @return Twig_SimpleTest[]
570
+ */
571
+ function getTests();
572
+
573
+ /**
574
+ * Returns a list of functions to add to the existing list.
575
+ *
576
+ * @return Twig_SimpleFunction[]
577
+ */
578
+ function getFunctions();
579
+
580
+ /**
581
+ * Returns a list of operators to add to the existing list.
582
+ *
583
+ * @return array<array> First array of unary operators, second array of binary operators
584
+ */
585
+ function getOperators();
586
+
587
+ /**
588
+ * Returns a list of global variables to add to the existing list.
589
+ *
590
+ * @return array An array of global variables
591
+ */
592
+ function getGlobals();
593
+
594
+ /**
595
+ * Returns the name of the extension.
596
+ *
597
+ * @return string The extension name
598
+ */
599
+ function getName();
600
+ }
601
+
602
+ To keep your extension class clean and lean, it can inherit from the built-in
603
+ ``Twig_Extension`` class instead of implementing the whole interface. That
604
+ way, you just need to implement the ``getName()`` method as the
605
+ ``Twig_Extension`` provides empty implementations for all other methods.
606
+
607
+ The ``getName()`` method must return a unique identifier for your extension.
608
+
609
+ Now, with this information in mind, let's create the most basic extension
610
+ possible::
611
+
612
+ class Project_Twig_Extension extends Twig_Extension
613
+ {
614
+ public function getName()
615
+ {
616
+ return 'project';
617
+ }
618
+ }
619
+
620
+ .. note::
621
+
622
+ Of course, this extension does nothing for now. We will customize it in
623
+ the next sections.
624
+
625
+ Twig does not care where you save your extension on the filesystem, as all
626
+ extensions must be registered explicitly to be available in your templates.
627
+
628
+ You can register an extension by using the ``addExtension()`` method on your
629
+ main ``Environment`` object::
630
+
631
+ $twig = new Twig_Environment($loader);
632
+ $twig->addExtension(new Project_Twig_Extension());
633
+
634
+ Of course, you need to first load the extension file by either using
635
+ ``require_once()`` or by using an autoloader (see `spl_autoload_register()`_).
636
+
637
+ .. tip::
638
+
639
+ The bundled extensions are great examples of how extensions work.
640
+
641
+ Globals
642
+ ~~~~~~~
643
+
644
+ Global variables can be registered in an extension via the ``getGlobals()``
645
+ method::
646
+
647
+ class Project_Twig_Extension extends Twig_Extension
648
+ {
649
+ public function getGlobals()
650
+ {
651
+ return array(
652
+ 'text' => new Text(),
653
+ );
654
+ }
655
+
656
+ // ...
657
+ }
658
+
659
+ Functions
660
+ ~~~~~~~~~
661
+
662
+ Functions can be registered in an extension via the ``getFunctions()``
663
+ method::
664
+
665
+ class Project_Twig_Extension extends Twig_Extension
666
+ {
667
+ public function getFunctions()
668
+ {
669
+ return array(
670
+ 'lipsum' => new Twig_Function_Function('generate_lipsum'),
671
+ );
672
+ }
673
+
674
+ // ...
675
+ }
676
+
677
+ Filters
678
+ ~~~~~~~
679
+
680
+ To add a filter to an extension, you need to override the ``getFilters()``
681
+ method. This method must return an array of filters to add to the Twig
682
+ environment::
683
+
684
+ class Project_Twig_Extension extends Twig_Extension
685
+ {
686
+ public function getFilters()
687
+ {
688
+ return array(
689
+ 'rot13' => new Twig_Filter_Function('str_rot13'),
690
+ );
691
+ }
692
+
693
+ // ...
694
+ }
695
+
696
+ As you can see in the above code, the ``getFilters()`` method returns an array
697
+ where keys are the name of the filters (``rot13``) and the values the
698
+ definition of the filter (``new Twig_Filter_Function('str_rot13')``).
699
+
700
+ As seen in the previous chapter, you can also define filters as static methods
701
+ on the extension class::
702
+
703
+ $twig->addFilter('rot13', new Twig_Filter_Function('Project_Twig_Extension::rot13Filter'));
704
+
705
+ You can also use ``Twig_Filter_Method`` instead of ``Twig_Filter_Function``
706
+ when defining a filter to use a method::
707
+
708
+ class Project_Twig_Extension extends Twig_Extension
709
+ {
710
+ public function getFilters()
711
+ {
712
+ return array(
713
+ 'rot13' => new Twig_Filter_Method($this, 'rot13Filter'),
714
+ );
715
+ }
716
+
717
+ public function rot13Filter($string)
718
+ {
719
+ return str_rot13($string);
720
+ }
721
+
722
+ // ...
723
+ }
724
+
725
+ The first argument of the ``Twig_Filter_Method`` constructor is always
726
+ ``$this``, the current extension object. The second one is the name of the
727
+ method to call.
728
+
729
+ Using methods for filters is a great way to package your filter without
730
+ polluting the global namespace. This also gives the developer more flexibility
731
+ at the cost of a small overhead.
732
+
733
+ Overriding default Filters
734
+ ..........................
735
+
736
+ If some default core filters do not suit your needs, you can easily override
737
+ them by creating your own extension. Just use the same names as the one you
738
+ want to override::
739
+
740
+ class MyCoreExtension extends Twig_Extension
741
+ {
742
+ public function getFilters()
743
+ {
744
+ return array(
745
+ 'date' => new Twig_Filter_Method($this, 'dateFilter'),
746
+ // ...
747
+ );
748
+ }
749
+
750
+ public function dateFilter($timestamp, $format = 'F j, Y H:i')
751
+ {
752
+ return '...'.twig_date_format_filter($timestamp, $format);
753
+ }
754
+
755
+ public function getName()
756
+ {
757
+ return 'project';
758
+ }
759
+ }
760
+
761
+ Here, we override the ``date`` filter with a custom one. Using this extension
762
+ is as simple as registering the ``MyCoreExtension`` extension by calling the
763
+ ``addExtension()`` method on the environment instance::
764
+
765
+ $twig = new Twig_Environment($loader);
766
+ $twig->addExtension(new MyCoreExtension());
767
+
768
+ Tags
769
+ ~~~~
770
+
771
+ Adding a tag in an extension can be done by overriding the
772
+ ``getTokenParsers()`` method. This method must return an array of tags to add
773
+ to the Twig environment::
774
+
775
+ class Project_Twig_Extension extends Twig_Extension
776
+ {
777
+ public function getTokenParsers()
778
+ {
779
+ return array(new Project_Set_TokenParser());
780
+ }
781
+
782
+ // ...
783
+ }
784
+
785
+ In the above code, we have added a single new tag, defined by the
786
+ ``Project_Set_TokenParser`` class. The ``Project_Set_TokenParser`` class is
787
+ responsible for parsing the tag and compiling it to PHP.
788
+
789
+ Operators
790
+ ~~~~~~~~~
791
+
792
+ The ``getOperators()`` methods allows to add new operators. Here is how to add
793
+ ``!``, ``||``, and ``&&`` operators::
794
+
795
+ class Project_Twig_Extension extends Twig_Extension
796
+ {
797
+ public function getOperators()
798
+ {
799
+ return array(
800
+ array(
801
+ '!' => array('precedence' => 50, 'class' => 'Twig_Node_Expression_Unary_Not'),
802
+ ),
803
+ array(
804
+ '||' => array('precedence' => 10, 'class' => 'Twig_Node_Expression_Binary_Or', 'associativity' => Twig_ExpressionParser::OPERATOR_LEFT),
805
+ '&&' => array('precedence' => 15, 'class' => 'Twig_Node_Expression_Binary_And', 'associativity' => Twig_ExpressionParser::OPERATOR_LEFT),
806
+ ),
807
+ );
808
+ }
809
+
810
+ // ...
811
+ }
812
+
813
+ Tests
814
+ ~~~~~
815
+
816
+ The ``getTests()`` methods allows to add new test functions::
817
+
818
+ class Project_Twig_Extension extends Twig_Extension
819
+ {
820
+ public function getTests()
821
+ {
822
+ return array(
823
+ 'even' => new Twig_Test_Function('twig_test_even'),
824
+ );
825
+ }
826
+
827
+ // ...
828
+ }
829
+
830
+ Testing an Extension
831
+ --------------------
832
+
833
+ .. versionadded:: 1.10
834
+ Support for functional tests was added in Twig 1.10.
835
+
836
+ Functional Tests
837
+ ~~~~~~~~~~~~~~~~
838
+
839
+ You can create functional tests for extensions simply by creating the
840
+ following file structure in your test directory::
841
+
842
+ Fixtures/
843
+ filters/
844
+ foo.test
845
+ bar.test
846
+ functions/
847
+ foo.test
848
+ bar.test
849
+ tags/
850
+ foo.test
851
+ bar.test
852
+ IntegrationTest.php
853
+
854
+ The ``IntegrationTest.php`` file should look like this::
855
+
856
+ class Project_Tests_IntegrationTest extends Twig_Test_IntegrationTestCase
857
+ {
858
+ public function getExtensions()
859
+ {
860
+ return array(
861
+ new Project_Twig_Extension1(),
862
+ new Project_Twig_Extension2(),
863
+ );
864
+ }
865
+
866
+ public function getFixturesDir()
867
+ {
868
+ return dirname(__FILE__).'/Fixtures/';
869
+ }
870
+ }
871
+
872
+ Fixtures examples can be found within the Twig repository
873
+ `tests/Twig/Fixtures`_ directory.
874
+
875
+ Node Tests
876
+ ~~~~~~~~~~
877
+
878
+ Testing the node visitors can be complex, so extend your test cases from
879
+ ``Twig_Test_NodeTestCase``. Examples can be found in the Twig repository
880
+ `tests/Twig/Node`_ directory.
881
+
882
+ .. _`spl_autoload_register()`: http://www.php.net/spl_autoload_register
883
+ .. _`rot13`: http://www.php.net/manual/en/function.str-rot13.php
884
+ .. _`tests/Twig/Fixtures`: https://github.com/twigphp/Twig/tree/master/test/Twig/Tests/Fixtures
885
+ .. _`tests/Twig/Node`: https://github.com/twigphp/Twig/tree/master/test/Twig/Tests/Node
vendor/twig/twig/doc/api.rst ADDED
@@ -0,0 +1,590 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ Twig for Developers
2
+ ===================
3
+
4
+ This chapter describes the API to Twig and not the template language. It will
5
+ be most useful as reference to those implementing the template interface to
6
+ the application and not those who are creating Twig templates.
7
+
8
+ Basics
9
+ ------
10
+
11
+ Twig uses a central object called the **environment** (of class
12
+ ``Twig_Environment``). Instances of this class are used to store the
13
+ configuration and extensions, and are used to load templates from the file
14
+ system or other locations.
15
+
16
+ Most applications will create one ``Twig_Environment`` object on application
17
+ initialization and use that to load templates. In some cases it's however
18
+ useful to have multiple environments side by side, if different configurations
19
+ are in use.
20
+
21
+ The simplest way to configure Twig to load templates for your application
22
+ looks roughly like this::
23
+
24
+ require_once '/path/to/lib/Twig/Autoloader.php';
25
+ Twig_Autoloader::register();
26
+
27
+ $loader = new Twig_Loader_Filesystem('/path/to/templates');
28
+ $twig = new Twig_Environment($loader, array(
29
+ 'cache' => '/path/to/compilation_cache',
30
+ ));
31
+
32
+ This will create a template environment with the default settings and a loader
33
+ that looks up the templates in the ``/path/to/templates/`` folder. Different
34
+ loaders are available and you can also write your own if you want to load
35
+ templates from a database or other resources.
36
+
37
+ .. note::
38
+
39
+ Notice that the second argument of the environment is an array of options.
40
+ The ``cache`` option is a compilation cache directory, where Twig caches
41
+ the compiled templates to avoid the parsing phase for sub-sequent
42
+ requests. It is very different from the cache you might want to add for
43
+ the evaluated templates. For such a need, you can use any available PHP
44
+ cache library.
45
+
46
+ Rendering Templates
47
+ -------------------
48
+
49
+ To load a template from a Twig environment, call the ``load()`` method which
50
+ returns a ``Twig_TemplateWrapper`` instance::
51
+
52
+ $template = $twig->load('index.html');
53
+
54
+ .. note::
55
+
56
+ Before Twig 1.28, you should use ``loadTemplate()`` instead which returns a
57
+ ``Twig_Template`` instance.
58
+
59
+ To render the template with some variables, call the ``render()`` method::
60
+
61
+ echo $template->render(array('the' => 'variables', 'go' => 'here'));
62
+
63
+ .. note::
64
+
65
+ The ``display()`` method is a shortcut to output the template directly.
66
+
67
+ You can also load and render the template in one fell swoop::
68
+
69
+ echo $twig->render('index.html', array('the' => 'variables', 'go' => 'here'));
70
+
71
+ .. versionadded:: 1.28
72
+ The possibility to render blocks from the API was added in Twig 1.28.
73
+
74
+ If a template defines blocks, they can be rendered individually via the
75
+ ``renderBlock()`` call::
76
+
77
+ echo $template->renderBlock('block_name', array('the' => 'variables', 'go' => 'here'));
78
+
79
+ .. _environment_options:
80
+
81
+ Environment Options
82
+ -------------------
83
+
84
+ When creating a new ``Twig_Environment`` instance, you can pass an array of
85
+ options as the constructor second argument::
86
+
87
+ $twig = new Twig_Environment($loader, array('debug' => true));
88
+
89
+ The following options are available:
90
+
91
+ * ``debug`` *boolean*
92
+
93
+ When set to ``true``, the generated templates have a
94
+ ``__toString()`` method that you can use to display the generated nodes
95
+ (default to ``false``).
96
+
97
+ * ``charset`` *string* (defaults to ``utf-8``)
98
+
99
+ The charset used by the templates.
100
+
101
+ * ``base_template_class`` *string* (defaults to ``Twig_Template``)
102
+
103
+ The base template class to use for generated
104
+ templates.
105
+
106
+ * ``cache`` *string* or ``false``
107
+
108
+ An absolute path where to store the compiled templates, or
109
+ ``false`` to disable caching (which is the default).
110
+
111
+ * ``auto_reload`` *boolean*
112
+
113
+ When developing with Twig, it's useful to recompile the
114
+ template whenever the source code changes. If you don't provide a value for
115
+ the ``auto_reload`` option, it will be determined automatically based on the
116
+ ``debug`` value.
117
+
118
+ * ``strict_variables`` *boolean*
119
+
120
+ If set to ``false``, Twig will silently ignore invalid
121
+ variables (variables and or attributes/methods that do not exist) and
122
+ replace them with a ``null`` value. When set to ``true``, Twig throws an
123
+ exception instead (default to ``false``).
124
+
125
+ * ``autoescape`` *string* or *boolean*
126
+
127
+ If set to ``true``, HTML auto-escaping will be enabled by
128
+ default for all templates (default to ``true``).
129
+
130
+ As of Twig 1.8, you can set the escaping strategy to use (``html``, ``js``,
131
+ ``false`` to disable).
132
+
133
+ As of Twig 1.9, you can set the escaping strategy to use (``css``, ``url``,
134
+ ``html_attr``, or a PHP callback that takes the template name and must
135
+ return the escaping strategy to use -- the callback cannot be a function name
136
+ to avoid collision with built-in escaping strategies).
137
+
138
+ As of Twig 1.17, the ``filename`` escaping strategy (renamed to ``name`` as
139
+ of Twig 1.27) determines the escaping strategy to use for a template based on
140
+ the template filename extension (this strategy does not incur any overhead at
141
+ runtime as auto-escaping is done at compilation time.)
142
+
143
+ * ``optimizations`` *integer*
144
+
145
+ A flag that indicates which optimizations to apply
146
+ (default to ``-1`` -- all optimizations are enabled; set it to ``0`` to
147
+ disable).
148
+
149
+ Loaders
150
+ -------
151
+
152
+ Loaders are responsible for loading templates from a resource such as the file
153
+ system.
154
+
155
+ Compilation Cache
156
+ ~~~~~~~~~~~~~~~~~
157
+
158
+ All template loaders can cache the compiled templates on the filesystem for
159
+ future reuse. It speeds up Twig a lot as templates are only compiled once; and
160
+ the performance boost is even larger if you use a PHP accelerator such as APC.
161
+ See the ``cache`` and ``auto_reload`` options of ``Twig_Environment`` above
162
+ for more information.
163
+
164
+ Built-in Loaders
165
+ ~~~~~~~~~~~~~~~~
166
+
167
+ Here is a list of the built-in loaders Twig provides:
168
+
169
+ ``Twig_Loader_Filesystem``
170
+ ..........................
171
+
172
+ .. versionadded:: 1.10
173
+ The ``prependPath()`` and support for namespaces were added in Twig 1.10.
174
+
175
+ .. versionadded:: 1.27
176
+ Relative paths support was added in Twig 1.27.
177
+
178
+ ``Twig_Loader_Filesystem`` loads templates from the file system. This loader
179
+ can find templates in folders on the file system and is the preferred way to
180
+ load them::
181
+
182
+ $loader = new Twig_Loader_Filesystem($templateDir);
183
+
184
+ It can also look for templates in an array of directories::
185
+
186
+ $loader = new Twig_Loader_Filesystem(array($templateDir1, $templateDir2));
187
+
188
+ With such a configuration, Twig will first look for templates in
189
+ ``$templateDir1`` and if they do not exist, it will fallback to look for them
190
+ in the ``$templateDir2``.
191
+
192
+ You can add or prepend paths via the ``addPath()`` and ``prependPath()``
193
+ methods::
194
+
195
+ $loader->addPath($templateDir3);
196
+ $loader->prependPath($templateDir4);
197
+
198
+ The filesystem loader also supports namespaced templates. This allows to group
199
+ your templates under different namespaces which have their own template paths.
200
+
201
+ When using the ``setPaths()``, ``addPath()``, and ``prependPath()`` methods,
202
+ specify the namespace as the second argument (when not specified, these
203
+ methods act on the "main" namespace)::
204
+
205
+ $loader->addPath($templateDir, 'admin');
206
+
207
+ Namespaced templates can be accessed via the special
208
+ ``@namespace_name/template_path`` notation::
209
+
210
+ $twig->render('@admin/index.html', array());
211
+
212
+ ``Twig_Loader_Filesystem`` support absolute and relative paths. Using relative
213
+ paths is preferred as it makes the cache keys independent of the project root
214
+ directory (for instance, it allows warming the cache from a build server where
215
+ the directory might be different from the one used on production servers)::
216
+
217
+ $loader = new Twig_Loader_Filesystem('templates', getcwd().'/..');
218
+
219
+ .. note::
220
+
221
+ When not passing the root path as a second argument, Twig uses ``getcwd()``
222
+ for relative paths.
223
+
224
+ ``Twig_Loader_Array``
225
+ .....................
226
+
227
+ ``Twig_Loader_Array`` loads a template from a PHP array. It's passed an array
228
+ of strings bound to template names::
229
+
230
+ $loader = new Twig_Loader_Array(array(
231
+ 'index.html' => 'Hello {{ name }}!',
232
+ ));
233
+ $twig = new Twig_Environment($loader);
234
+
235
+ echo $twig->render('index.html', array('name' => 'Fabien'));
236
+
237
+ This loader is very useful for unit testing. It can also be used for small
238
+ projects where storing all templates in a single PHP file might make sense.
239
+
240
+ .. tip::
241
+
242
+ When using the ``Array`` or ``String`` loaders with a cache mechanism, you
243
+ should know that a new cache key is generated each time a template content
244
+ "changes" (the cache key being the source code of the template). If you
245
+ don't want to see your cache grows out of control, you need to take care
246
+ of clearing the old cache file by yourself.
247
+
248
+ ``Twig_Loader_Chain``
249
+ .....................
250
+
251
+ ``Twig_Loader_Chain`` delegates the loading of templates to other loaders::
252
+
253
+ $loader1 = new Twig_Loader_Array(array(
254
+ 'base.html' => '{% block content %}{% endblock %}',
255
+ ));
256
+ $loader2 = new Twig_Loader_Array(array(
257
+ 'index.html' => '{% extends "base.html" %}{% block content %}Hello {{ name }}{% endblock %}',
258
+ 'base.html' => 'Will never be loaded',
259
+ ));
260
+
261
+ $loader = new Twig_Loader_Chain(array($loader1, $loader2));
262
+
263
+ $twig = new Twig_Environment($loader);
264
+
265
+ When looking for a template, Twig will try each loader in turn and it will
266
+ return as soon as the template is found. When rendering the ``index.html``
267
+ template from the above example, Twig will load it with ``$loader2`` but the
268
+ ``base.html`` template will be loaded from ``$loader1``.
269
+
270
+ ``Twig_Loader_Chain`` accepts any loader that implements
271
+ ``Twig_LoaderInterface``.
272
+
273
+ .. note::
274
+
275
+ You can also add loaders via the ``addLoader()`` method.
276
+
277
+ Create your own Loader
278
+ ~~~~~~~~~~~~~~~~~~~~~~
279
+
280
+ All loaders implement the ``Twig_LoaderInterface``::
281
+
282
+ interface Twig_LoaderInterface
283
+ {
284
+ /**
285
+ * Gets the source code of a template, given its name.
286
+ *
287
+ * @param string $name string The name of the template to load
288
+ *
289
+ * @return string The template source code
290
+ *
291
+ * @deprecated since 1.27 (to be removed in 2.0), implement Twig_SourceContextLoaderInterface
292
+ */
293
+ function getSource($name);
294
+
295
+ /**
296
+ * Gets the cache key to use for the cache for a given template name.
297
+ *
298
+ * @param string $name string The name of the template to load
299
+ *
300
+ * @return string The cache key
301
+ */
302
+ function getCacheKey($name);
303
+
304
+ /**
305
+ * Returns true if the template is still fresh.
306
+ *
307
+ * @param string $name The template name
308
+ * @param timestamp $time The last modification time of the cached template
309
+ */
310
+ function isFresh($name, $time);
311
+ }
312
+
313
+ The ``isFresh()`` method must return ``true`` if the current cached template
314
+ is still fresh, given the last modification time, or ``false`` otherwise.
315
+
316
+ .. note::
317
+
318
+ As of Twig 1.27, you should also implement
319
+ ``Twig_SourceContextLoaderInterface`` to avoid deprecation notices.
320
+
321
+ .. tip::
322
+
323
+ As of Twig 1.11.0, you can also implement ``Twig_ExistsLoaderInterface``
324
+ to make your loader faster when used with the chain loader.
325
+
326
+ Using Extensions
327
+ ----------------
328
+
329
+ Twig extensions are packages that add new features to Twig. Using an
330
+ extension is as simple as using the ``addExtension()`` method::
331
+
332
+ $twig->addExtension(new Twig_Extension_Sandbox());
333
+
334
+ Twig comes bundled with the following extensions:
335
+
336
+ * *Twig_Extension_Core*: Defines all the core features of Twig.
337
+
338
+ * *Twig_Extension_Escaper*: Adds automatic output-escaping and the possibility
339
+ to escape/unescape blocks of code.
340
+
341
+ * *Twig_Extension_Sandbox*: Adds a sandbox mode to the default Twig
342
+ environment, making it safe to evaluate untrusted code.
343
+
344
+ * *Twig_Extension_Profiler*: Enabled the built-in Twig profiler (as of Twig
345
+ 1.18).
346
+
347
+ * *Twig_Extension_Optimizer*: Optimizes the node tree before compilation.
348
+
349
+ The core, escaper, and optimizer extensions do not need to be added to the
350
+ Twig environment, as they are registered by default.
351
+
352
+ Built-in Extensions
353
+ -------------------
354
+
355
+ This section describes the features added by the built-in extensions.
356
+
357
+ .. tip::
358
+
359
+ Read the chapter about extending Twig to learn how to create your own
360
+ extensions.
361
+
362
+ Core Extension
363
+ ~~~~~~~~~~~~~~
364
+
365
+ The ``core`` extension defines all the core features of Twig:
366
+
367
+ * :doc:`Tags <tags/index>`;
368
+ * :doc:`Filters <filters/index>`;
369
+ * :doc:`Functions <functions/index>`;
370
+ * :doc:`Tests <tests/index>`.
371
+
372
+ Escaper Extension
373
+ ~~~~~~~~~~~~~~~~~
374
+
375
+ The ``escaper`` extension adds automatic output escaping to Twig. It defines a
376
+ tag, ``autoescape``, and a filter, ``raw``.
377
+
378
+ When creating the escaper extension, you can switch on or off the global
379
+ output escaping strategy::
380
+
381
+ $escaper = new Twig_Extension_Escaper('html');
382
+ $twig->addExtension($escaper);
383
+
384
+ If set to ``html``, all variables in templates are escaped (using the ``html``
385
+ escaping strategy), except those using the ``raw`` filter:
386
+
387
+ .. code-block:: jinja
388
+
389
+ {{ article.to_html|raw }}
390
+
391
+ You can also change the escaping mode locally by using the ``autoescape`` tag
392
+ (see the :doc:`autoescape<tags/autoescape>` doc for the syntax used before
393
+ Twig 1.8):
394
+
395
+ .. code-block:: jinja
396
+
397
+ {% autoescape 'html' %}
398
+ {{ var }}
399
+ {{ var|raw }} {# var won't be escaped #}
400
+ {{ var|escape }} {# var won't be double-escaped #}
401
+ {% endautoescape %}
402
+
403
+ .. warning::
404
+
405
+ The ``autoescape`` tag has no effect on included files.
406
+
407
+ The escaping rules are implemented as follows:
408
+
409
+ * Literals (integers, booleans, arrays, ...) used in the template directly as
410
+ variables or filter arguments are never automatically escaped:
411
+
412
+ .. code-block:: jinja
413
+
414
+ {{ "Twig<br />" }} {# won't be escaped #}
415
+
416
+ {% set text = "Twig<br />" %}
417
+ {{ text }} {# will be escaped #}
418
+
419
+ * Expressions which the result is always a literal or a variable marked safe
420
+ are never automatically escaped:
421
+
422
+ .. code-block:: jinja
423
+
424
+ {{ foo ? "Twig<br />" : "<br />Twig" }} {# won't be escaped #}
425
+
426
+ {% set text = "Twig<br />" %}
427
+ {{ foo ? text : "<br />Twig" }} {# will be escaped #}
428
+
429
+ {% set text = "Twig<br />" %}
430
+ {{ foo ? text|raw : "<br />Twig" }} {# won't be escaped #}
431
+
432
+ {% set text = "Twig<br />" %}
433
+ {{ foo ? text|escape : "<br />Twig" }} {# the result of the expression won't be escaped #}
434
+
435
+ * Escaping is applied before printing, after any other filter is applied:
436
+
437
+ .. code-block:: jinja
438
+
439
+ {{ var|upper }} {# is equivalent to {{ var|upper|escape }} #}
440
+
441
+ * The `raw` filter should only be used at the end of the filter chain:
442
+
443
+ .. code-block:: jinja
444
+
445
+ {{ var|raw|upper }} {# will be escaped #}
446
+
447
+ {{ var|upper|raw }} {# won't be escaped #}
448
+
449
+ * Automatic escaping is not applied if the last filter in the chain is marked
450
+ safe for the current context (e.g. ``html`` or ``js``). ``escape`` and
451
+ ``escape('html')`` are marked safe for HTML, ``escape('js')`` is marked
452
+ safe for JavaScript, ``raw`` is marked safe for everything.
453
+
454
+ .. code-block:: jinja
455
+
456
+ {% autoescape 'js' %}
457
+ {{ var|escape('html') }} {# will be escaped for HTML and JavaScript #}
458
+ {{ var }} {# will be escaped for JavaScript #}
459
+ {{ var|escape('js') }} {# won't be double-escaped #}
460
+ {% endautoescape %}
461
+
462
+ .. note::
463
+
464
+ Note that autoescaping has some limitations as escaping is applied on
465
+ expressions after evaluation. For instance, when working with
466
+ concatenation, ``{{ foo|raw ~ bar }}`` won't give the expected result as
467
+ escaping is applied on the result of the concatenation, not on the
468
+ individual variables (so, the ``raw`` filter won't have any effect here).
469
+
470
+ Sandbox Extension
471
+ ~~~~~~~~~~~~~~~~~
472
+
473
+ The ``sandbox`` extension can be used to evaluate untrusted code. Access to
474
+ unsafe attributes and methods is prohibited. The sandbox security is managed
475
+ by a policy instance. By default, Twig comes with one policy class:
476
+ ``Twig_Sandbox_SecurityPolicy``. This class allows you to white-list some
477
+ tags, filters, properties, and methods::
478
+
479
+ $tags = array('if');
480
+ $filters = array('upper');
481
+ $methods = array(
482
+ 'Article' => array('getTitle', 'getBody'),
483
+ );
484
+ $properties = array(
485
+ 'Article' => array('title', 'body'),
486
+ );
487
+ $functions = array('range');
488
+ $policy = new Twig_Sandbox_SecurityPolicy($tags, $filters, $methods, $properties, $functions);
489
+
490
+ With the previous configuration, the security policy will only allow usage of
491
+ the ``if`` tag, and the ``upper`` filter. Moreover, the templates will only be
492
+ able to call the ``getTitle()`` and ``getBody()`` methods on ``Article``
493
+ objects, and the ``title`` and ``body`` public properties. Everything else
494
+ won't be allowed and will generate a ``Twig_Sandbox_SecurityError`` exception.
495
+
496
+ The policy object is the first argument of the sandbox constructor::
497
+
498
+ $sandbox = new Twig_Extension_Sandbox($policy);
499
+ $twig->addExtension($sandbox);
500
+
501
+ By default, the sandbox mode is disabled and should be enabled when including
502
+ untrusted template code by using the ``sandbox`` tag:
503
+
504
+ .. code-block:: jinja
505
+
506
+ {% sandbox %}
507
+ {% include 'user.html' %}
508
+ {% endsandbox %}
509
+
510
+ You can sandbox all templates by passing ``true`` as the second argument of
511
+ the extension constructor::
512
+
513
+ $sandbox = new Twig_Extension_Sandbox($policy, true);
514
+
515
+ Profiler Extension
516
+ ~~~~~~~~~~~~~~~~~~
517
+
518
+ .. versionadded:: 1.18
519
+ The Profile extension was added in Twig 1.18.
520
+
521
+ The ``profiler`` extension enables a profiler for Twig templates; it should
522
+ only be used on your development machines as it adds some overhead::
523
+
524
+ $profile = new Twig_Profiler_Profile();
525
+ $twig->addExtension(new Twig_Extension_Profiler($profile));
526
+
527
+ $dumper = new Twig_Profiler_Dumper_Text();
528
+ echo $dumper->dump($profile);
529
+
530
+ A profile contains information about time and memory consumption for template,
531
+ block, and macro executions.
532
+
533
+ You can also dump the data in a `Blackfire.io <https://blackfire.io/>`_
534
+ compatible format::
535
+
536
+ $dumper = new Twig_Profiler_Dumper_Blackfire();
537
+ file_put_contents('/path/to/profile.prof', $dumper->dump($profile));
538
+
539
+ Upload the profile to visualize it (create a `free account
540
+ <https://blackfire.io/signup>`_ first):
541
+
542
+ .. code-block:: sh
543
+
544
+ blackfire --slot=7 upload /path/to/profile.prof
545
+
546
+ Optimizer Extension
547
+ ~~~~~~~~~~~~~~~~~~~
548
+
549
+ The ``optimizer`` extension optimizes the node tree before compilation::
550
+
551
+ $twig->addExtension(new Twig_Extension_Optimizer());
552
+
553
+ By default, all optimizations are turned on. You can select the ones you want
554
+ to enable by passing them to the constructor::
555
+
556
+ $optimizer = new Twig_Extension_Optimizer(Twig_NodeVisitor_Optimizer::OPTIMIZE_FOR);
557
+
558
+ $twig->addExtension($optimizer);
559
+
560
+ Twig supports the following optimizations:
561
+
562
+ * ``Twig_NodeVisitor_Optimizer::OPTIMIZE_ALL``, enables all optimizations
563
+ (this is the default value).
564
+ * ``Twig_NodeVisitor_Optimizer::OPTIMIZE_NONE``, disables all optimizations.
565
+ This reduces the compilation time, but it can increase the execution time
566
+ and the consumed memory.
567
+ * ``Twig_NodeVisitor_Optimizer::OPTIMIZE_FOR``, optimizes the ``for`` tag by
568
+ removing the ``loop`` variable creation whenever possible.
569
+ * ``Twig_NodeVisitor_Optimizer::OPTIMIZE_RAW_FILTER``, removes the ``raw``
570
+ filter whenever possible.
571
+ * ``Twig_NodeVisitor_Optimizer::OPTIMIZE_VAR_ACCESS``, simplifies the creation
572
+ and access of variables in the compiled templates whenever possible.
573
+
574
+ Exceptions
575
+ ----------
576
+
577
+ Twig can throw exceptions:
578
+
579
+ * ``Twig_Error``: The base exception for all errors.
580
+
581
+ * ``Twig_Error_Syntax``: Thrown to tell the user that there is a problem with
582
+ the template syntax.
583
+
584
+ * ``Twig_Error_Runtime``: Thrown when an error occurs at runtime (when a filter
585
+ does not exist for instance).
586
+
587
+ * ``Twig_Error_Loader``: Thrown when an error occurs during template loading.
588
+
589
+ * ``Twig_Sandbox_SecurityError``: Thrown when an unallowed tag, filter, or
590
+ method is called in a sandboxed template.
vendor/twig/twig/doc/coding_standards.rst ADDED
@@ -0,0 +1,101 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ Coding Standards
2
+ ================
3
+
4
+ When writing Twig templates, we recommend you to follow these official coding
5
+ standards:
6
+
7
+ * Put one (and only one) space after the start of a delimiter (``{{``, ``{%``,
8
+ and ``{#``) and before the end of a delimiter (``}}``, ``%}``, and ``#}``):
9
+
10
+ .. code-block:: jinja
11
+
12
+ {{ foo }}
13
+ {# comment #}
14
+ {% if foo %}{% endif %}
15
+
16
+ When using the whitespace control character, do not put any spaces between
17
+ it and the delimiter:
18
+
19
+ .. code-block:: jinja
20
+
21
+ {{- foo -}}
22
+ {#- comment -#}
23
+ {%- if foo -%}{%- endif -%}
24
+
25
+ * Put one (and only one) space before and after the following operators:
26
+ comparison operators (``==``, ``!=``, ``<``, ``>``, ``>=``, ``<=``), math
27
+ operators (``+``, ``-``, ``/``, ``*``, ``%``, ``//``, ``**``), logic
28
+ operators (``not``, ``and``, ``or``), ``~``, ``is``, ``in``, and the ternary
29
+ operator (``?:``):
30
+
31
+ .. code-block:: jinja
32
+
33
+ {{ 1 + 2 }}
34
+ {{ foo ~ bar }}
35
+ {{ true ? true : false }}
36
+
37
+ * Put one (and only one) space after the ``:`` sign in hashes and ``,`` in
38
+ arrays and hashes:
39
+
40
+ .. code-block:: jinja
41
+
42
+ {{ [1, 2, 3] }}
43
+ {{ {'foo': 'bar'} }}
44
+
45
+ * Do not put any spaces after an opening parenthesis and before a closing
46
+ parenthesis in expressions:
47
+
48
+ .. code-block:: jinja
49
+
50
+ {{ 1 + (2 * 3) }}
51
+
52
+ * Do not put any spaces before and after string delimiters:
53
+
54
+ .. code-block:: jinja
55
+
56
+ {{ 'foo' }}
57
+ {{ "foo" }}
58
+
59
+ * Do not put any spaces before and after the following operators: ``|``,
60
+ ``.``, ``..``, ``[]``:
61
+
62
+ .. code-block:: jinja
63
+
64
+ {{ foo|upper|lower }}
65
+ {{ user.name }}
66
+ {{ user[name] }}
67
+ {% for i in 1..12 %}{% endfor %}
68
+
69
+ * Do not put any spaces before and after the parenthesis used for filter and
70
+ function calls:
71
+
72
+ .. code-block:: jinja
73
+
74
+ {{ foo|default('foo') }}
75
+ {{ range(1..10) }}
76
+
77
+ * Do not put any spaces before and after the opening and the closing of arrays
78
+ and hashes:
79
+
80
+ .. code-block:: jinja
81
+
82
+ {{ [1, 2, 3] }}
83
+ {{ {'foo': 'bar'} }}
84
+
85
+ * Use lower cased and underscored variable names:
86
+
87
+ .. code-block:: jinja
88
+
89
+ {% set foo = 'foo' %}
90
+ {% set foo_bar = 'foo' %}
91
+
92
+ * Indent your code inside tags (use the same indentation as the one used for
93
+ the target language of the rendered template):
94
+
95
+ .. code-block:: jinja
96
+
97
+ {% block foo %}
98
+ {% if true %}
99
+ true
100
+ {% endif %}
101
+ {% endblock %}
vendor/twig/twig/doc/deprecated.rst ADDED
@@ -0,0 +1,224 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ Deprecated Features
2
+ ===================
3
+
4
+ This document lists all deprecated features in Twig. Deprecated features are
5
+ kept for backward compatibility and removed in the next major release (a
6
+ feature that was deprecated in Twig 1.x is removed in Twig 2.0).
7
+
8
+ Deprecation Notices
9
+ -------------------
10
+
11
+ As of Twig 1.21, Twig generates deprecation notices when a template uses
12
+ deprecated features. See :ref:`deprecation-notices` for more information.
13
+
14
+ Macros
15
+ ------
16
+
17
+ As of Twig 2.0, macros imported in a file are not available in child templates
18
+ anymore (via an ``include`` call for instance). You need to import macros
19
+ explicitly in each file where you are using them.
20
+
21
+ Token Parsers
22
+ -------------
23
+
24
+ * As of Twig 1.x, the token parser broker sub-system is deprecated. The
25
+ following class and interface will be removed in 2.0:
26
+
27
+ * ``Twig_TokenParserBrokerInterface``
28
+ * ``Twig_TokenParserBroker``
29
+
30
+ * As of Twig 1.27, ``Twig_Parser::getFilename()`` is deprecated. From a token
31
+ parser, use ``$this->parser->getStream()->getSourceContext()->getPath()`` instead.
32
+
33
+ * As of Twig 1.27, ``Twig_Parser::getEnvironment()`` is deprecated.
34
+
35
+ Extensions
36
+ ----------
37
+
38
+ * As of Twig 1.x, the ability to remove an extension is deprecated and the
39
+ ``Twig_Environment::removeExtension()`` method will be removed in 2.0.
40
+
41
+ * As of Twig 1.23, the ``Twig_ExtensionInterface::initRuntime()`` method is
42
+ deprecated. You have two options to avoid the deprecation notice: if you
43
+ implement this method to store the environment for your custom filters,
44
+ functions, or tests, use the ``needs_environment`` option instead; if you
45
+ have more complex needs, explicitly implement
46
+ ``Twig_Extension_InitRuntimeInterface`` (not recommended).
47
+
48
+ * As of Twig 1.23, the ``Twig_ExtensionInterface::getGlobals()`` method is
49
+ deprecated. Implement ``Twig_Extension_GlobalsInterface`` to avoid
50
+ deprecation notices.
51
+
52
+ * As of Twig 1.26, the ``Twig_ExtensionInterface::getName()`` method is
53
+ deprecated and it is not used internally anymore.
54
+
55
+ PEAR
56
+ ----
57
+
58
+ PEAR support has been discontinued in Twig 1.15.1, and no PEAR packages are
59
+ provided anymore. Use Composer instead.
60
+
61
+ Filters
62
+ -------
63
+
64
+ * As of Twig 1.x, use ``Twig_SimpleFilter`` to add a filter. The following
65
+ classes and interfaces will be removed in 2.0:
66
+
67
+ * ``Twig_FilterInterface``
68
+ * ``Twig_FilterCallableInterface``
69
+ * ``Twig_Filter``
70
+ * ``Twig_Filter_Function``
71
+ * ``Twig_Filter_Method``
72
+ * ``Twig_Filter_Node``
73
+
74
+ * As of Twig 2.x, the ``Twig_SimpleFilter`` class is deprecated and will be
75
+ removed in Twig 3.x (use ``Twig_Filter`` instead). In Twig 2.x,
76
+ ``Twig_SimpleFilter`` is just an alias for ``Twig_Filter``.
77
+
78
+ Functions
79
+ ---------
80
+
81
+ * As of Twig 1.x, use ``Twig_SimpleFunction`` to add a function. The following
82
+ classes and interfaces will be removed in 2.0:
83
+
84
+ * ``Twig_FunctionInterface``
85
+ * ``Twig_FunctionCallableInterface``
86
+ * ``Twig_Function``
87
+ * ``Twig_Function_Function``
88
+ * ``Twig_Function_Method``
89
+ * ``Twig_Function_Node``
90
+
91
+ * As of Twig 2.x, the ``Twig_SimpleFunction`` class is deprecated and will be
92
+ removed in Twig 3.x (use ``Twig_Function`` instead). In Twig 2.x,
93
+ ``Twig_SimpleFunction`` is just an alias for ``Twig_Function``.
94
+
95
+ Tests
96
+ -----
97
+
98
+ * As of Twig 1.x, use ``Twig_SimpleTest`` to add a test. The following classes
99
+ and interfaces will be removed in 2.0:
100
+
101
+ * ``Twig_TestInterface``
102
+ * ``Twig_TestCallableInterface``
103
+ * ``Twig_Test``
104
+ * ``Twig_Test_Function``
105
+ * ``Twig_Test_Method``
106
+ * ``Twig_Test_Node``
107
+
108
+ * As of Twig 2.x, the ``Twig_SimpleTest`` class is deprecated and will be
109
+ removed in Twig 3.x (use ``Twig_Test`` instead). In Twig 2.x,
110
+ ``Twig_SimpleTest`` is just an alias for ``Twig_Test``.
111
+
112
+ * The ``sameas`` and ``divisibleby`` tests are deprecated in favor of ``same
113
+ as`` and ``divisible by`` respectively.
114
+
115
+ Tags
116
+ ----
117
+
118
+ * As of Twig 1.x, the ``raw`` tag is deprecated. You should use ``verbatim``
119
+ instead.
120
+
121
+ Nodes
122
+ -----
123
+
124
+ * As of Twig 1.x, ``Node::toXml()`` is deprecated and will be removed in Twig
125
+ 2.0.
126
+
127
+ * As of Twig 1.26, ``Node::$nodes`` should only contains ``Twig_Node``
128
+ instances, storing a ``null`` value is deprecated and won't be possible in
129
+ Twig 2.x.
130
+
131
+ * As of Twig 1.27, the ``filename`` attribute on ``Twig_Node_Module`` is
132
+ deprecated. Use ``getName()`` instead.
133
+
134
+ * As of Twig 1.27, the ``Twig_Node::getFilename()/Twig_Node::getLine()``
135
+ methods are deprecated, use
136
+ ``Twig_Node::getTemplateName()/Twig_Node::getTemplateLine()`` instead.
137
+
138
+ Interfaces
139
+ ----------
140
+
141
+ * As of Twig 2.x, the following interfaces are deprecated and empty (they will
142
+ be removed in Twig 3.0):
143
+
144
+ * ``Twig_CompilerInterface`` (use ``Twig_Compiler`` instead)
145
+ * ``Twig_LexerInterface`` (use ``Twig_Lexer`` instead)
146
+ * ``Twig_NodeInterface`` (use ``Twig_Node`` instead)
147
+ * ``Twig_ParserInterface`` (use ``Twig_Parser`` instead)
148
+ * ``Twig_ExistsLoaderInterface`` (merged with ``Twig_LoaderInterface``)
149
+ * ``Twig_SourceContextLoaderInterface`` (merged with ``Twig_LoaderInterface``)
150
+ * ``Twig_TemplateInterface`` (use ``Twig_Template`` instead, and use
151
+ those constants Twig_Template::ANY_CALL, Twig_Template::ARRAY_CALL,
152
+ Twig_Template::METHOD_CALL)
153
+
154
+ Compiler
155
+ --------
156
+
157
+ * As of Twig 1.26, the ``Twig_Compiler::getFilename()`` has been deprecated.
158
+ You should not use it anyway as its values is not reliable.
159
+
160
+ * As of Twig 1.27, the ``Twig_Compiler::addIndentation()`` has been deprecated.
161
+ Use ``Twig_Compiler::write('')`` instead.
162
+
163
+ Loaders
164
+ -------
165
+
166
+ * As of Twig 1.x, ``Twig_Loader_String`` is deprecated and will be removed in
167
+ 2.0. You can render a string via ``Twig_Environment::createTemplate()``.
168
+
169
+ * As of Twig 1.27, ``Twig_LoaderInterface::getSource()`` is deprecated.
170
+ Implement ``Twig_SourceContextLoaderInterface`` instead and use
171
+ ``getSourceContext()``.
172
+
173
+ Node Visitors
174
+ -------------
175
+
176
+ * Because of the removal of ``Twig_NodeInterface`` in 2.0, you need to extend
177
+ ``Twig_BaseNodeVisitor`` instead of implementing ``Twig_NodeVisitorInterface``
178
+ directly to make your node visitors compatible with both Twig 1.x and 2.x.
179
+
180
+ Globals
181
+ -------
182
+
183
+ * As of Twig 2.x, the ability to register a global variable after the runtime
184
+ or the extensions have been initialized is not possible anymore (but
185
+ changing the value of an already registered global is possible).
186
+
187
+ * As of Twig 1.x, using the ``_self`` global variable to get access to the
188
+ current ``Twig_Template`` instance is deprecated; most usages only need the
189
+ current template name, which will continue to work in Twig 2.0. In Twig 2.0,
190
+ ``_self`` returns the current template name instead of the current
191
+ ``Twig_Template`` instance. If you are using ``{{ _self.templateName }}``,
192
+ just replace it with ``{{ _self }}``.
193
+
194
+ Miscellaneous
195
+ -------------
196
+
197
+ * As of Twig 1.x, ``Twig_Environment::clearTemplateCache()``,
198
+ ``Twig_Environment::writeCacheFile()``,
199
+ ``Twig_Environment::clearCacheFiles()``,
200
+ ``Twig_Environment::getCacheFilename()``,
201
+ ``Twig_Environment::getTemplateClassPrefix()``,
202
+ ``Twig_Environment::getLexer()``, ``Twig_Environment::getParser()``, and
203
+ ``Twig_Environment::getCompiler()`` are deprecated and will be removed in 2.0.
204
+
205
+ * As of Twig 1.x, ``Twig_Template::getEnvironment()`` and
206
+ ``Twig_TemplateInterface::getEnvironment()`` are deprecated and will be
207
+ removed in 2.0.
208
+
209
+ * As of Twig 1.21, setting the environment option ``autoescape`` to ``true`` is
210
+ deprecated and will be removed in 2.0. Use ``"html"`` instead.
211
+
212
+ * As of Twig 1.27, ``Twig_Error::getTemplateFile()`` and
213
+ ``Twig_Error::setTemplateFile()`` are deprecated. Use
214
+ ``Twig_Error::getTemplateName()`` and ``Twig_Error::setTemplateName()``
215
+ instead.
216
+
217
+ * As of Twig 1.27, ``Twig_Template::getSource()`` is deprecated. Use
218
+ ``Twig_Template::getSourceContext()`` instead.
219
+
220
+ * As of Twig 1.27, ``Twig_Parser::addHandler()`` and
221
+ ``Twig_Parser::addNodeVisitor()`` are deprecated and will be removed in 2.0.
222
+
223
+ * As of Twig 1.29, some classes are marked as being final via the `@final`
224
+ annotation. Those classes will be marked as final in 2.0.
vendor/twig/twig/doc/filters/abs.rst ADDED
@@ -0,0 +1,18 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ``abs``
2
+ =======
3
+
4
+ The ``abs`` filter returns the absolute value.
5
+
6
+ .. code-block:: jinja
7
+
8
+ {# number = -5 #}
9
+
10
+ {{ number|abs }}
11
+
12
+ {# outputs 5 #}
13
+
14
+ .. note::
15
+
16
+ Internally, Twig uses the PHP `abs`_ function.
17
+
18
+ .. _`abs`: http://php.net/abs
vendor/twig/twig/doc/filters/batch.rst ADDED
@@ -0,0 +1,51 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ``batch``
2
+ =========
3
+
4
+ .. versionadded:: 1.12.3
5
+ The ``batch`` filter was added in Twig 1.12.3.
6
+
7
+ The ``batch`` filter "batches" items by returning a list of lists with the
8
+ given number of items. A second parameter can be provided and used to fill in
9
+ missing items:
10
+
11
+ .. code-block:: jinja
12
+
13
+ {% set items = ['a', 'b', 'c', 'd', 'e', 'f', 'g'] %}
14
+
15
+ <table>
16
+ {% for row in items|batch(3, 'No item') %}
17
+ <tr>
18
+ {% for column in row %}
19
+ <td>{{ column }}</td>
20
+ {% endfor %}
21
+ </tr>
22
+ {% endfor %}
23
+ </table>
24
+
25
+ The above example will be rendered as:
26
+
27
+ .. code-block:: jinja
28
+
29
+ <table>
30
+ <tr>
31
+ <td>a</td>
32
+ <td>b</td>
33
+ <td>c</td>
34
+ </tr>
35
+ <tr>
36
+ <td>d</td>
37
+ <td>e</td>
38
+ <td>f</td>
39
+ </tr>
40
+ <tr>
41
+ <td>g</td>
42
+ <td>No item</td>
43
+ <td>No item</td>
44
+ </tr>
45
+ </table>
46
+
47
+ Arguments
48
+ ---------
49
+
50
+ * ``size``: The size of the batch; fractional numbers will be rounded up
51
+ * ``fill``: Used to fill in missing items
vendor/twig/twig/doc/filters/capitalize.rst ADDED
@@ -0,0 +1,11 @@
 
 
 
 
 
 
 
 
 
 
 
1
+ ``capitalize``
2
+ ==============
3
+
4
+ The ``capitalize`` filter capitalizes a value. The first character will be
5
+ uppercase, all others lowercase:
6
+
7
+ .. code-block:: jinja
8
+
9
+ {{ 'my first car'|capitalize }}
10
+
11
+ {# outputs 'My first car' #}
vendor/twig/twig/doc/filters/convert_encoding.rst ADDED
@@ -0,0 +1,28 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ``convert_encoding``
2
+ ====================
3
+
4
+ .. versionadded:: 1.4
5
+ The ``convert_encoding`` filter was added in Twig 1.4.
6
+
7
+ The ``convert_encoding`` filter converts a string from one encoding to
8
+ another. The first argument is the expected output charset and the second one
9
+ is the input charset:
10
+
11
+ .. code-block:: jinja
12
+
13
+ {{ data|convert_encoding('UTF-8', 'iso-2022-jp') }}
14
+
15
+ .. note::
16
+
17
+ This filter relies on the `iconv`_ or `mbstring`_ extension, so one of
18
+ them must be installed. In case both are installed, `mbstring`_ is used by
19
+ default (Twig before 1.8.1 uses `iconv`_ by default).
20
+
21
+ Arguments
22
+ ---------
23
+
24
+ * ``to``: The output charset
25
+ * ``from``: The input charset
26
+
27
+ .. _`iconv`: http://php.net/iconv
28
+ .. _`mbstring`: http://php.net/mbstring
vendor/twig/twig/doc/filters/date.rst ADDED
@@ -0,0 +1,100 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ``date``
2
+ ========
3
+
4
+ .. versionadded:: 1.1
5
+ The timezone support has been added in Twig 1.1.
6
+
7
+ .. versionadded:: 1.5
8
+ The default date format support has been added in Twig 1.5.
9
+
10
+ .. versionadded:: 1.6.1
11
+ The default timezone support has been added in Twig 1.6.1.
12
+
13
+ .. versionadded:: 1.11.0
14
+ The introduction of the false value for the timezone was introduced in Twig 1.11.0
15
+
16
+ The ``date`` filter formats a date to a given format:
17
+
18
+ .. code-block:: jinja
19
+
20
+ {{ post.published_at|date("m/d/Y") }}
21
+
22
+ The format specifier is the same as supported by `date`_,
23
+ except when the filtered data is of type `DateInterval`_, when the format must conform to
24
+ `DateInterval::format`_ instead.
25
+
26
+ The ``date`` filter accepts strings (it must be in a format supported by the
27
+ `strtotime`_ function), `DateTime`_ instances, or `DateInterval`_ instances. For
28
+ instance, to display the current date, filter the word "now":
29
+
30
+ .. code-block:: jinja
31
+
32
+ {{ "now"|date("m/d/Y") }}
33
+
34
+ To escape words and characters in the date format use ``\\`` in front of each
35
+ character:
36
+
37
+ .. code-block:: jinja
38
+
39
+ {{ post.published_at|date("F jS \\a\\t g:ia") }}
40
+
41
+ If the value passed to the ``date`` filter is ``null``, it will return the
42
+ current date by default. If an empty string is desired instead of the current
43
+ date, use a ternary operator:
44
+
45
+ .. code-block:: jinja
46
+
47
+ {{ post.published_at is empty ? "" : post.published_at|date("m/d/Y") }}
48
+
49
+ If no format is provided, Twig will use the default one: ``F j, Y H:i``. This
50
+ default can be easily changed by calling the ``setDateFormat()`` method on the
51
+ ``core`` extension instance. The first argument is the default format for
52
+ dates and the second one is the default format for date intervals:
53
+
54
+ .. code-block:: php
55
+
56
+ $twig = new Twig_Environment($loader);
57
+ $twig->getExtension('Twig_Extension_Core')->setDateFormat('d/m/Y', '%d days');
58
+
59
+ // before Twig 1.26
60
+ $twig->getExtension('core')->setDateFormat('d/m/Y', '%d days');
61
+
62
+ Timezone
63
+ --------
64
+
65
+ By default, the date is displayed by applying the default timezone (the one
66
+ specified in php.ini or declared in Twig -- see below), but you can override
67
+ it by explicitly specifying a timezone:
68
+
69
+ .. code-block:: jinja
70
+
71
+ {{ post.published_at|date("m/d/Y", "Europe/Paris") }}
72
+
73
+ If the date is already a DateTime object, and if you want to keep its current
74
+ timezone, pass ``false`` as the timezone value:
75
+
76
+ .. code-block:: jinja
77
+
78
+ {{ post.published_at|date("m/d/Y", false) }}
79
+
80
+ The default timezone can also be set globally by calling ``setTimezone()``:
81
+
82
+ .. code-block:: php
83
+
84
+ $twig = new Twig_Environment($loader);
85
+ $twig->getExtension('Twig_Extension_Core')->setTimezone('Europe/Paris');
86
+
87
+ // before Twig 1.26
88
+ $twig->getExtension('core')->setTimezone('Europe/Paris');
89
+
90
+ Arguments
91
+ ---------
92
+
93
+ * ``format``: The date format
94
+ * ``timezone``: The date timezone
95
+
96
+ .. _`strtotime`: http://www.php.net/strtotime
97
+ .. _`DateTime`: http://www.php.net/DateTime
98
+ .. _`DateInterval`: http://www.php.net/DateInterval
99
+ .. _`date`: http://www.php.net/date
100
+ .. _`DateInterval::format`: http://www.php.net/DateInterval.format
vendor/twig/twig/doc/filters/date_modify.rst ADDED
@@ -0,0 +1,23 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ``date_modify``
2
+ ===============
3
+
4
+ .. versionadded:: 1.9.0
5
+ The date_modify filter has been added in Twig 1.9.0.
6
+
7
+ The ``date_modify`` filter modifies a date with a given modifier string:
8
+
9
+ .. code-block:: jinja
10
+
11
+ {{ post.published_at|date_modify("+1 day")|date("m/d/Y") }}
12
+
13
+ The ``date_modify`` filter accepts strings (it must be in a format supported
14
+ by the `strtotime`_ function) or `DateTime`_ instances. You can easily combine
15
+ it with the :doc:`date<date>` filter for formatting.
16
+
17
+ Arguments
18
+ ---------
19
+
20
+ * ``modifier``: The modifier
21
+
22
+ .. _`strtotime`: http://www.php.net/strtotime
23
+ .. _`DateTime`: http://www.php.net/DateTime
vendor/twig/twig/doc/filters/default.rst ADDED
@@ -0,0 +1,33 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ``default``
2
+ ===========
3
+
4
+ The ``default`` filter returns the passed default value if the value is
5
+ undefined or empty, otherwise the value of the variable:
6
+
7
+ .. code-block:: jinja
8
+
9
+ {{ var|default('var is not defined') }}
10
+
11
+ {{ var.foo|default('foo item on var is not defined') }}
12
+
13
+ {{ var['foo']|default('foo item on var is not defined') }}
14
+
15
+ {{ ''|default('passed var is empty') }}
16
+
17
+ When using the ``default`` filter on an expression that uses variables in some
18
+ method calls, be sure to use the ``default`` filter whenever a variable can be
19
+ undefined:
20
+
21
+ .. code-block:: jinja
22
+
23
+ {{ var.method(foo|default('foo'))|default('foo') }}
24
+
25
+ .. note::
26
+
27
+ Read the documentation for the :doc:`defined<../tests/defined>` and
28
+ :doc:`empty<../tests/empty>` tests to learn more about their semantics.
29
+
30
+ Arguments
31
+ ---------
32
+
33
+ * ``default``: The default value
vendor/twig/twig/doc/filters/escape.rst ADDED
@@ -0,0 +1,119 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ``escape``
2
+ ==========
3
+
4
+ .. versionadded:: 1.9.0
5
+ The ``css``, ``url``, and ``html_attr`` strategies were added in Twig
6
+ 1.9.0.
7
+
8
+ .. versionadded:: 1.14.0
9
+ The ability to define custom escapers was added in Twig 1.14.0.
10
+
11
+ The ``escape`` filter escapes a string for safe insertion into the final
12
+ output. It supports different escaping strategies depending on the template
13
+ context.
14
+
15
+ By default, it uses the HTML escaping strategy:
16
+
17
+ .. code-block:: jinja
18
+
19
+ {{ user.username|escape }}
20
+
21
+ For convenience, the ``e`` filter is defined as an alias:
22
+
23
+ .. code-block:: jinja
24
+
25
+ {{ user.username|e }}
26
+
27
+ The ``escape`` filter can also be used in other contexts than HTML thanks to
28
+ an optional argument which defines the escaping strategy to use:
29
+
30
+ .. code-block:: jinja
31
+
32
+ {{ user.username|e }}
33
+ {# is equivalent to #}
34
+ {{ user.username|e('html') }}
35
+
36
+ And here is how to escape variables included in JavaScript code:
37
+
38
+ .. code-block:: jinja
39
+
40
+ {{ user.username|escape('js') }}
41
+ {{ user.username|e('js') }}
42
+
43
+ The ``escape`` filter supports the following escaping strategies:
44
+
45
+ * ``html``: escapes a string for the **HTML body** context.
46
+
47
+ * ``js``: escapes a string for the **JavaScript context**.
48
+
49
+ * ``css``: escapes a string for the **CSS context**. CSS escaping can be
50
+ applied to any string being inserted into CSS and escapes everything except
51
+ alphanumerics.
52
+
53
+ * ``url``: escapes a string for the **URI or parameter contexts**. This should
54
+ not be used to escape an entire URI; only a subcomponent being inserted.
55
+
56
+ * ``html_attr``: escapes a string for the **HTML attribute** context.
57
+
58
+ .. note::
59
+
60
+ Internally, ``escape`` uses the PHP native `htmlspecialchars`_ function
61
+ for the HTML escaping strategy.
62
+
63
+ .. caution::
64
+
65
+ When using automatic escaping, Twig tries to not double-escape a variable
66
+ when the automatic escaping strategy is the same as the one applied by the
67
+ escape filter; but that does not work when using a variable as the
68
+ escaping strategy:
69
+
70
+ .. code-block:: jinja
71
+
72
+ {% set strategy = 'html' %}
73
+
74
+ {% autoescape 'html' %}
75
+ {{ var|escape('html') }} {# won't be double-escaped #}
76
+ {{ var|escape(strategy) }} {# will be double-escaped #}
77
+ {% endautoescape %}
78
+
79
+ When using a variable as the escaping strategy, you should disable
80
+ automatic escaping:
81
+
82
+ .. code-block:: jinja
83
+
84
+ {% set strategy = 'html' %}
85
+
86
+ {% autoescape 'html' %}
87
+ {{ var|escape(strategy)|raw }} {# won't be double-escaped #}
88
+ {% endautoescape %}
89
+
90
+ Custom Escapers
91
+ ---------------
92
+
93
+ You can define custom escapers by calling the ``setEscaper()`` method on the
94
+ ``core`` extension instance. The first argument is the escaper name (to be
95
+ used in the ``escape`` call) and the second one must be a valid PHP callable:
96
+
97
+ .. code-block:: php
98
+
99
+ $twig = new Twig_Environment($loader);
100
+ $twig->getExtension('Twig_Extension_Core')->setEscaper('csv', 'csv_escaper');
101
+
102
+ // before Twig 1.26
103
+ $twig->getExtension('core')->setEscaper('csv', 'csv_escaper');
104
+
105
+ When called by Twig, the callable receives the Twig environment instance, the
106
+ string to escape, and the charset.
107
+
108
+ .. note::
109
+
110
+ Built-in escapers cannot be overridden mainly they should be considered as
111
+ the final implementation and also for better performance.
112
+
113
+ Arguments
114
+ ---------
115
+
116
+ * ``strategy``: The escaping strategy
117
+ * ``charset``: The string charset
118
+
119
+ .. _`htmlspecialchars`: http://php.net/htmlspecialchars
vendor/twig/twig/doc/filters/first.rst ADDED
@@ -0,0 +1,25 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ``first``
2
+ =========
3
+
4
+ .. versionadded:: 1.12.2
5
+ The ``first`` filter was added in Twig 1.12.2.
6
+
7
+ The ``first`` filter returns the first "element" of a sequence, a mapping, or
8
+ a string:
9
+
10
+ .. code-block:: jinja
11
+
12
+ {{ [1, 2, 3, 4]|first }}
13
+ {# outputs 1 #}
14
+
15
+ {{ { a: 1, b: 2, c: 3, d: 4 }|first }}
16
+ {# outputs 1 #}
17
+
18
+ {{ '1234'|first }}
19
+ {# outputs 1 #}
20
+
21
+ .. note::
22
+
23
+ It also works with objects implementing the `Traversable`_ interface.
24
+
25
+ .. _`Traversable`: http://php.net/manual/en/class.traversable.php
vendor/twig/twig/doc/filters/format.rst ADDED
@@ -0,0 +1,16 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ``format``
2
+ ==========
3
+
4
+ The ``format`` filter formats a given string by replacing the placeholders
5
+ (placeholders follows the `sprintf`_ notation):
6
+
7
+ .. code-block:: jinja
8
+
9
+ {{ "I like %s and %s."|format(foo, "bar") }}
10
+
11
+ {# outputs I like foo and bar
12
+ if the foo parameter equals to the foo string. #}
13
+
14
+ .. _`sprintf`: http://www.php.net/sprintf
15
+
16
+ .. seealso:: :doc:`replace<replace>`
vendor/twig/twig/doc/filters/index.rst ADDED
@@ -0,0 +1,37 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ Filters
2
+ =======
3
+
4
+ .. toctree::
5
+ :maxdepth: 1
6
+
7
+ abs
8
+ batch
9
+ capitalize
10
+ convert_encoding
11
+ date
12
+ date_modify
13
+ default
14
+ escape
15
+ first
16
+ format
17
+ join
18
+ json_encode
19
+ keys
20
+ last
21
+ length
22
+ lower
23
+ merge
24
+ nl2br
25
+ number_format
26
+ raw
27
+ replace
28
+ reverse
29
+ round
30
+ slice
31
+ sort
32
+ split
33
+ striptags
34
+ title
35
+ trim
36
+ upper
37
+ url_encode
vendor/twig/twig/doc/filters/join.rst ADDED
@@ -0,0 +1,23 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ``join``
2
+ ========
3
+
4
+ The ``join`` filter returns a string which is the concatenation of the items
5
+ of a sequence:
6
+
7
+ .. code-block:: jinja
8
+
9
+ {{ [1, 2, 3]|join }}
10
+ {# returns 123 #}
11
+
12
+ The separator between elements is an empty string per default, but you can
13
+ define it with the optional first parameter:
14
+
15
+ .. code-block:: jinja
16
+
17
+ {{ [1, 2, 3]|join('|') }}
18
+ {# outputs 1|2|3 #}
19
+
20
+ Arguments
21
+ ---------
22
+
23
+ * ``glue``: The separator
vendor/twig/twig/doc/filters/json_encode.rst ADDED
@@ -0,0 +1,21 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ``json_encode``
2
+ ===============
3
+
4
+ The ``json_encode`` filter returns the JSON representation of a value:
5
+
6
+ .. code-block:: jinja
7
+
8
+ {{ data|json_encode() }}
9
+
10
+ .. note::
11
+
12
+ Internally, Twig uses the PHP `json_encode`_ function.
13
+
14
+ Arguments
15
+ ---------
16
+
17
+ * ``options``: A bitmask of `json_encode options`_ (``{{
18
+ data|json_encode(constant('JSON_PRETTY_PRINT')) }}``)
19
+
20
+ .. _`json_encode`: http://php.net/json_encode
21
+ .. _`json_encode options`: http://www.php.net/manual/en/json.constants.php
vendor/twig/twig/doc/filters/keys.rst ADDED
@@ -0,0 +1,11 @@
 
 
 
 
 
 
 
 
 
 
 
1
+ ``keys``
2
+ ========
3
+
4
+ The ``keys`` filter returns the keys of an array. It is useful when you want to
5
+ iterate over the keys of an array:
6
+
7
+ .. code-block:: jinja
8
+
9
+ {% for key in array|keys %}
10
+ ...
11
+ {% endfor %}
vendor/twig/twig/doc/filters/last.rst ADDED
@@ -0,0 +1,25 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ``last``
2
+ ========
3
+
4
+ .. versionadded:: 1.12.2
5
+ The ``last`` filter was added in Twig 1.12.2.
6
+
7
+ The ``last`` filter returns the last "element" of a sequence, a mapping, or
8
+ a string:
9
+
10
+ .. code-block:: jinja
11
+
12
+ {{ [1, 2, 3, 4]|last }}
13
+ {# outputs 4 #}
14
+
15
+ {{ { a: 1, b: 2, c: 3, d: 4 }|last }}
16
+ {# outputs 4 #}
17
+
18
+ {{ '1234'|last }}
19
+ {# outputs 4 #}
20
+
21
+ .. note::
22
+
23
+ It also works with objects implementing the `Traversable`_ interface.
24
+
25
+ .. _`Traversable`: http://php.net/manual/en/class.traversable.php
vendor/twig/twig/doc/filters/length.rst ADDED
@@ -0,0 +1,11 @@
 
 
 
 
 
 
 
 
 
 
 
1
+ ``length``
2
+ ==========
3
+
4
+ The ``length`` filter returns the number of items of a sequence or mapping, or
5
+ the length of a string:
6
+
7
+ .. code-block:: jinja
8
+
9
+ {% if users|length > 10 %}
10
+ ...
11
+ {% endif %}
vendor/twig/twig/doc/filters/lower.rst ADDED
@@ -0,0 +1,10 @@
 
 
 
 
 
 
 
 
 
 
1
+ ``lower``
2
+ =========
3
+
4
+ The ``lower`` filter converts a value to lowercase:
5
+
6
+ .. code-block:: jinja
7
+
8
+ {{ 'WELCOME'|lower }}
9
+
10
+ {# outputs 'welcome' #}
vendor/twig/twig/doc/filters/merge.rst ADDED
@@ -0,0 +1,48 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ``merge``
2
+ =========
3
+
4
+ The ``merge`` filter merges an array with another array:
5
+
6
+ .. code-block:: jinja
7
+
8
+ {% set values = [1, 2] %}
9
+
10
+ {% set values = values|merge(['apple', 'orange']) %}
11
+
12
+ {# values now contains [1, 2, 'apple', 'orange'] #}
13
+
14
+ New values are added at the end of the existing ones.
15
+
16
+ The ``merge`` filter also works on hashes:
17
+
18
+ .. code-block:: jinja
19
+
20
+ {% set items = { 'apple': 'fruit', 'orange': 'fruit', 'peugeot': 'unknown' } %}
21
+
22
+ {% set items = items|merge({ 'peugeot': 'car', 'renault': 'car' }) %}
23
+
24
+ {# items now contains { 'apple': 'fruit', 'orange': 'fruit', 'peugeot': 'car', 'renault': 'car' } #}
25
+
26
+ For hashes, the merging process occurs on the keys: if the key does not
27
+ already exist, it is added but if the key already exists, its value is
28
+ overridden.
29
+
30
+ .. tip::
31
+
32
+ If you want to ensure that some values are defined in an array (by given
33
+ default values), reverse the two elements in the call:
34
+
35
+ .. code-block:: jinja
36
+
37
+ {% set items = { 'apple': 'fruit', 'orange': 'fruit' } %}
38
+
39
+ {% set items = { 'apple': 'unknown' }|merge(items) %}
40
+
41
+ {# items now contains { 'apple': 'fruit', 'orange': 'fruit' } #}
42
+
43
+ .. note::
44
+
45
+ Internally, Twig uses the PHP `array_merge`_ function. It supports
46
+ Traversable objects by transforming those to arrays.
47
+
48
+ .. _`array_merge`: http://php.net/array_merge
vendor/twig/twig/doc/filters/nl2br.rst ADDED
@@ -0,0 +1,22 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ``nl2br``
2
+ =========
3
+
4
+ .. versionadded:: 1.5
5
+ The ``nl2br`` filter was added in Twig 1.5.
6
+
7
+ The ``nl2br`` filter inserts HTML line breaks before all newlines in a string:
8
+
9
+ .. code-block:: jinja
10
+
11
+ {{ "I like Twig.\nYou will like it too."|nl2br }}
12
+ {# outputs
13
+
14
+ I like Twig.<br />
15
+ You will like it too.
16
+
17
+ #}
18
+
19
+ .. note::
20
+
21
+ The ``nl2br`` filter pre-escapes the input before applying the
22
+ transformation.
vendor/twig/twig/doc/filters/number_format.rst ADDED
@@ -0,0 +1,48 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ``number_format``
2
+ =================
3
+
4
+ .. versionadded:: 1.5
5
+ The ``number_format`` filter was added in Twig 1.5
6
+
7
+ The ``number_format`` filter formats numbers. It is a wrapper around PHP's
8
+ `number_format`_ function:
9
+
10
+ .. code-block:: jinja
11
+
12
+ {{ 200.35|number_format }}
13
+
14
+ You can control the number of decimal places, decimal point, and thousands
15
+ separator using the additional arguments:
16
+
17
+ .. code-block:: jinja
18
+
19
+ {{ 9800.333|number_format(2, '.', ',') }}
20
+
21
+ If no formatting options are provided then Twig will use the default formatting
22
+ options of:
23
+
24
+ * 0 decimal places.
25
+ * ``.`` as the decimal point.
26
+ * ``,`` as the thousands separator.
27
+
28
+ These defaults can be easily changed through the core extension:
29
+
30
+ .. code-block:: php
31
+
32
+ $twig = new Twig_Environment($loader);
33
+ $twig->getExtension('Twig_Extension_Core')->setNumberFormat(3, '.', ',');
34
+
35
+ // before Twig 1.26
36
+ $twig->getExtension('core')->setNumberFormat(3, '.', ',');
37
+
38
+ The defaults set for ``number_format`` can be over-ridden upon each call using the
39
+ additional parameters.
40
+
41
+ Arguments
42
+ ---------
43
+
44
+ * ``decimal``: The number of decimal points to display
45
+ * ``decimal_point``: The character(s) to use for the decimal point
46
+ * ``thousand_sep``: The character(s) to use for the thousands separator
47
+
48
+ .. _`number_format`: http://php.net/number_format
vendor/twig/twig/doc/filters/raw.rst ADDED
@@ -0,0 +1,36 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ``raw``
2
+ =======
3
+
4
+ The ``raw`` filter marks the value as being "safe", which means that in an
5
+ environment with automatic escaping enabled this variable will not be escaped
6
+ if ``raw`` is the last filter applied to it:
7
+
8
+ .. code-block:: jinja
9
+
10
+ {% autoescape %}
11
+ {{ var|raw }} {# var won't be escaped #}
12
+ {% endautoescape %}
13
+
14
+ .. note::
15
+
16
+ Be careful when using the ``raw`` filter inside expressions:
17
+
18
+ .. code-block:: jinja
19
+
20
+ {% autoescape %}
21
+ {% set hello = '<strong>Hello</strong>' %}
22
+ {% set hola = '<strong>Hola</strong>' %}
23
+
24
+ {{ false ? '<strong>Hola</strong>' : hello|raw }}
25
+ does not render the same as
26
+ {{ false ? hola : hello|raw }}
27
+ but renders the same as
28
+ {{ (false ? hola : hello)|raw }}
29
+ {% endautoescape %}
30
+
31
+ The first ternary statement is not escaped: ``hello`` is marked as being
32
+ safe and Twig does not escape static values (see
33
+ :doc:`escape<../tags/autoescape>`). In the second ternary statement, even
34
+ if ``hello`` is marked as safe, ``hola`` remains unsafe and so is the whole
35
+ expression. The third ternary statement is marked as safe and the result is
36
+ not escaped.
vendor/twig/twig/doc/filters/replace.rst ADDED
@@ -0,0 +1,19 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ``replace``
2
+ ===========
3
+
4
+ The ``replace`` filter formats a given string by replacing the placeholders
5
+ (placeholders are free-form):
6
+
7
+ .. code-block:: jinja
8
+
9
+ {{ "I like %this% and %that%."|replace({'%this%': foo, '%that%': "bar"}) }}
10
+
11
+ {# outputs I like foo and bar
12
+ if the foo parameter equals to the foo string. #}
13
+
14
+ Arguments
15
+ ---------
16
+
17
+ * ``from``: The placeholder values
18
+
19
+ .. seealso:: :doc:`format<format>`
vendor/twig/twig/doc/filters/reverse.rst ADDED
@@ -0,0 +1,47 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ``reverse``
2
+ ===========
3
+
4
+ .. versionadded:: 1.6
5
+ Support for strings has been added in Twig 1.6.
6
+
7
+ The ``reverse`` filter reverses a sequence, a mapping, or a string:
8
+
9
+ .. code-block:: jinja
10
+
11
+ {% for user in users|reverse %}
12
+ ...
13
+ {% endfor %}
14
+
15
+ {{ '1234'|reverse }}
16
+
17
+ {# outputs 4321 #}
18
+
19
+ .. tip::
20
+
21
+ For sequences and mappings, numeric keys are not preserved. To reverse
22
+ them as well, pass ``true`` as an argument to the ``reverse`` filter:
23
+
24
+ .. code-block:: jinja
25
+
26
+ {% for key, value in {1: "a", 2: "b", 3: "c"}|reverse %}
27
+ {{ key }}: {{ value }}
28
+ {%- endfor %}
29
+
30
+ {# output: 0: c 1: b 2: a #}
31
+
32
+ {% for key, value in {1: "a", 2: "b", 3: "c"}|reverse(true) %}
33
+ {{ key }}: {{ value }}
34
+ {%- endfor %}
35
+
36
+ {# output: 3: c 2: b 1: a #}
37
+
38
+ .. note::
39
+
40
+ It also works with objects implementing the `Traversable`_ interface.
41
+
42
+ Arguments
43
+ ---------
44
+
45
+ * ``preserve_keys``: Preserve keys when reversing a mapping or a sequence.
46
+
47
+ .. _`Traversable`: http://php.net/Traversable
vendor/twig/twig/doc/filters/round.rst ADDED
@@ -0,0 +1,37 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ``round``
2
+ =========
3
+
4
+ .. versionadded:: 1.15.0
5
+ The ``round`` filter was added in Twig 1.15.0.
6
+
7
+ The ``round`` filter rounds a number to a given precision:
8
+
9
+ .. code-block:: jinja
10
+
11
+ {{ 42.55|round }}
12
+ {# outputs 43 #}
13
+
14
+ {{ 42.55|round(1, 'floor') }}
15
+ {# outputs 42.5 #}
16
+
17
+ The ``round`` filter takes two optional arguments; the first one specifies the
18
+ precision (default is ``0``) and the second the rounding method (default is
19
+ ``common``):
20
+
21
+ * ``common`` rounds either up or down (rounds the value up to precision decimal
22
+ places away from zero, when it is half way there -- making 1.5 into 2 and
23
+ -1.5 into -2);
24
+
25
+ * ``ceil`` always rounds up;
26
+
27
+ * ``floor`` always rounds down.
28
+
29
+ .. note::
30
+
31
+ The ``//`` operator is equivalent to ``|round(0, 'floor')``.
32
+
33
+ Arguments
34
+ ---------
35
+
36
+ * ``precision``: The rounding precision
37
+ * ``method``: The rounding method
vendor/twig/twig/doc/filters/slice.rst ADDED
@@ -0,0 +1,71 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ``slice``
2
+ ===========
3
+
4
+ .. versionadded:: 1.6
5
+ The ``slice`` filter was added in Twig 1.6.
6
+
7
+ The ``slice`` filter extracts a slice of a sequence, a mapping, or a string:
8
+
9
+ .. code-block:: jinja
10
+
11
+ {% for i in [1, 2, 3, 4, 5]|slice(1, 2) %}
12
+ {# will iterate over 2 and 3 #}
13
+ {% endfor %}
14
+
15
+ {{ '12345'|slice(1, 2) }}
16
+
17
+ {# outputs 23 #}
18
+
19
+ You can use any valid expression for both the start and the length:
20
+
21
+ .. code-block:: jinja
22
+
23
+ {% for i in [1, 2, 3, 4, 5]|slice(start, length) %}
24
+ {# ... #}
25
+ {% endfor %}
26
+
27
+ As syntactic sugar, you can also use the ``[]`` notation:
28
+
29
+ .. code-block:: jinja
30
+
31
+ {% for i in [1, 2, 3, 4, 5][start:length] %}
32
+ {# ... #}
33
+ {% endfor %}
34
+
35
+ {{ '12345'[1:2] }} {# will display "23" #}
36
+
37
+ {# you can omit the first argument -- which is the same as 0 #}
38
+ {{ '12345'[:2] }} {# will display "12" #}
39
+
40
+ {# you can omit the last argument -- which will select everything till the end #}
41
+ {{ '12345'[2:] }} {# will display "345" #}
42
+
43
+ The ``slice`` filter works as the `array_slice`_ PHP function for arrays and
44
+ `mb_substr`_ for strings with a fallback to `substr`_.
45
+
46
+ If the start is non-negative, the sequence will start at that start in the
47
+ variable. If start is negative, the sequence will start that far from the end
48
+ of the variable.
49
+
50
+ If length is given and is positive, then the sequence will have up to that
51
+ many elements in it. If the variable is shorter than the length, then only the
52
+ available variable elements will be present. If length is given and is
53
+ negative then the sequence will stop that many elements from the end of the
54
+ variable. If it is omitted, then the sequence will have everything from offset
55
+ up until the end of the variable.
56
+
57
+ .. note::
58
+
59
+ It also works with objects implementing the `Traversable`_ interface.
60
+
61
+ Arguments
62
+ ---------
63
+
64
+ * ``start``: The start of the slice
65
+ * ``length``: The size of the slice
66
+ * ``preserve_keys``: Whether to preserve key or not (when the input is an array)
67
+
68
+ .. _`Traversable`: http://php.net/manual/en/class.traversable.php
69
+ .. _`array_slice`: http://php.net/array_slice
70
+ .. _`mb_substr` : http://php.net/mb-substr
71
+ .. _`substr`: http://php.net/substr
vendor/twig/twig/doc/filters/sort.rst ADDED
@@ -0,0 +1,18 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ``sort``
2
+ ========
3
+
4
+ The ``sort`` filter sorts an array:
5
+
6
+ .. code-block:: jinja
7
+
8
+ {% for user in users|sort %}
9
+ ...
10
+ {% endfor %}
11
+
12
+ .. note::
13
+
14
+ Internally, Twig uses the PHP `asort`_ function to maintain index
15
+ association. It supports Traversable objects by transforming
16
+ those to arrays.
17
+
18
+ .. _`asort`: http://php.net/asort
vendor/twig/twig/doc/filters/split.rst ADDED
@@ -0,0 +1,53 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ``split``
2
+ =========
3
+
4
+ .. versionadded:: 1.10.3
5
+ The ``split`` filter was added in Twig 1.10.3.
6
+
7
+ The ``split`` filter splits a string by the given delimiter and returns a list
8
+ of strings:
9
+
10
+ .. code-block:: jinja
11
+
12
+ {% set foo = "one,two,three"|split(',') %}
13
+ {# foo contains ['one', 'two', 'three'] #}
14
+
15
+ You can also pass a ``limit`` argument:
16
+
17
+ * If ``limit`` is positive, the returned array will contain a maximum of
18
+ limit elements with the last element containing the rest of string;
19
+
20
+ * If ``limit`` is negative, all components except the last -limit are
21
+ returned;
22
+
23
+ * If ``limit`` is zero, then this is treated as 1.
24
+
25
+ .. code-block:: jinja
26
+
27
+ {% set foo = "one,two,three,four,five"|split(',', 3) %}
28
+ {# foo contains ['one', 'two', 'three,four,five'] #}
29
+
30
+ If the ``delimiter`` is an empty string, then value will be split by equal
31
+ chunks. Length is set by the ``limit`` argument (one character by default).
32
+
33
+ .. code-block:: jinja
34
+
35
+ {% set foo = "123"|split('') %}
36
+ {# foo contains ['1', '2', '3'] #}
37
+
38
+ {% set bar = "aabbcc"|split('', 2) %}
39
+ {# bar contains ['aa', 'bb', 'cc'] #}
40
+
41
+ .. note::
42
+
43
+ Internally, Twig uses the PHP `explode`_ or `str_split`_ (if delimiter is
44
+ empty) functions for string splitting.
45
+
46
+ Arguments
47
+ ---------
48
+
49
+ * ``delimiter``: The delimiter
50
+ * ``limit``: The limit argument
51
+
52
+ .. _`explode`: http://php.net/explode
53
+ .. _`str_split`: http://php.net/str_split
vendor/twig/twig/doc/filters/striptags.rst ADDED
@@ -0,0 +1,29 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ``striptags``
2
+ =============
3
+
4
+ The ``striptags`` filter strips SGML/XML tags and replace adjacent whitespace
5
+ by one space:
6
+
7
+ .. code-block:: jinja
8
+
9
+ {{ some_html|striptags }}
10
+
11
+ You can also provide tags which should not be stripped:
12
+
13
+ .. code-block:: jinja
14
+
15
+ {{ some_html|striptags('<br><p>') }}
16
+
17
+ In this example, the ``<br/>``, ``<br>``, ``<p>``, and ``</p>`` tags won't be
18
+ removed from the string.
19
+
20
+ .. note::
21
+
22
+ Internally, Twig uses the PHP `strip_tags`_ function.
23
+
24
+ Arguments
25
+ ---------
26
+
27
+ * ``allowable_tags``: Tags which should not be stripped
28
+
29
+ .. _`strip_tags`: http://php.net/strip_tags
vendor/twig/twig/doc/filters/title.rst ADDED
@@ -0,0 +1,11 @@
 
 
 
 
 
 
 
 
 
 
 
1
+ ``title``
2
+ =========
3
+
4
+ The ``title`` filter returns a titlecased version of the value. Words will
5
+ start with uppercase letters, all remaining characters are lowercase:
6
+
7
+ .. code-block:: jinja
8
+
9
+ {{ 'my first car'|title }}
10
+
11
+ {# outputs 'My First Car' #}
vendor/twig/twig/doc/filters/trim.rst ADDED
@@ -0,0 +1,45 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ``trim``
2
+ ========
3
+
4
+ .. versionadded:: 1.32
5
+ The ``side`` argument was added in Twig 1.32.
6
+
7
+ .. versionadded:: 1.6.2
8
+ The ``trim`` filter was added in Twig 1.6.2.
9
+
10
+ The ``trim`` filter strips whitespace (or other characters) from the beginning
11
+ and end of a string:
12
+
13
+ .. code-block:: jinja
14
+
15
+ {{ ' I like Twig. '|trim }}
16
+
17
+ {# outputs 'I like Twig.' #}
18
+
19
+ {{ ' I like Twig.'|trim('.') }}
20
+
21
+ {# outputs ' I like Twig' #}
22
+
23
+ {{ ' I like Twig. '|trim(side='left') }}
24
+
25
+ {# outputs 'I like Twig. ' #}
26
+
27
+ {{ ' I like Twig. '|trim(' ', 'right') }}
28
+
29
+ {# outputs ' I like Twig.' #}
30
+
31
+ .. note::
32
+
33
+ Internally, Twig uses the PHP `trim`_, `ltrim`_, and `rtrim`_ functions.
34
+
35
+ Arguments
36
+ ---------
37
+
38
+ * ``character_mask``: The characters to strip
39
+
40
+ * ``side``: The default is to strip from the left and the right (`both`) sides, but `left`
41
+ and `right` will strip from either the left side or right side only
42
+
43
+ .. _`trim`: http://php.net/trim
44
+ .. _`ltrim`: http://php.net/ltrim
45
+ .. _`rtrim`: http://php.net/rtrim
vendor/twig/twig/doc/filters/upper.rst ADDED
@@ -0,0 +1,10 @@
 
 
 
 
 
 
 
 
 
 
1
+ ``upper``
2
+ =========
3
+
4
+ The ``upper`` filter converts a value to uppercase:
5
+
6
+ .. code-block:: jinja
7
+
8
+ {{ 'welcome'|upper }}
9
+
10
+ {# outputs 'WELCOME' #}
vendor/twig/twig/doc/filters/url_encode.rst ADDED
@@ -0,0 +1,34 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ``url_encode``
2
+ ==============
3
+
4
+ .. versionadded:: 1.12.3
5
+ Support for encoding an array as query string was added in Twig 1.12.3.
6
+
7
+ .. versionadded:: 1.16.0
8
+ The ``raw`` argument was removed in Twig 1.16.0. Twig now always encodes
9
+ according to RFC 3986.
10
+
11
+ The ``url_encode`` filter percent encodes a given string as URL segment
12
+ or an array as query string:
13
+
14
+ .. code-block:: jinja
15
+
16
+ {{ "path-seg*ment"|url_encode }}
17
+ {# outputs "path-seg%2Ament" #}
18
+
19
+ {{ "string with spaces"|url_encode }}
20
+ {# outputs "string%20with%20spaces" #}
21
+
22
+ {{ {'param': 'value', 'foo': 'bar'}|url_encode }}
23
+ {# outputs "param=value&foo=bar" #}
24
+
25
+ .. note::
26
+
27
+ Internally, Twig uses the PHP `urlencode`_ (or `rawurlencode`_ if you pass
28
+ ``true`` as the first parameter) or the `http_build_query`_ function. Note
29
+ that as of Twig 1.16.0, ``urlencode`` **always** uses ``rawurlencode`` (the
30
+ ``raw`` argument was removed.)
31
+
32
+ .. _`urlencode`: http://php.net/urlencode
33
+ .. _`rawurlencode`: http://php.net/rawurlencode
34
+ .. _`http_build_query`: http://php.net/http_build_query
vendor/twig/twig/doc/functions/attribute.rst ADDED
@@ -0,0 +1,26 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ``attribute``
2
+ =============
3
+
4
+ .. versionadded:: 1.2
5
+ The ``attribute`` function was added in Twig 1.2.
6
+
7
+ The ``attribute`` function can be used to access a "dynamic" attribute of a
8
+ variable:
9
+
10
+ .. code-block:: jinja
11
+
12
+ {{ attribute(object, method) }}
13
+ {{ attribute(object, method, arguments) }}
14
+ {{ attribute(array, item) }}
15
+
16
+ In addition, the ``defined`` test can check for the existence of a dynamic
17
+ attribute:
18
+
19
+ .. code-block:: jinja
20
+
21
+ {{ attribute(object, method) is defined ? 'Method exists' : 'Method does not exist' }}
22
+
23
+ .. note::
24
+
25
+ The resolution algorithm is the same as the one used for the ``.``
26
+ notation, except that the item can be any valid expression.
vendor/twig/twig/doc/functions/block.rst ADDED
@@ -0,0 +1,41 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ``block``
2
+ =========
3
+
4
+ .. versionadded: 1.28
5
+ Using ``block`` with the ``defined`` test was added in Twig 1.28.
6
+
7
+ .. versionadded: 1.28
8
+ Support for the template argument was added in Twig 1.28.
9
+
10
+ When a template uses inheritance and if you want to print a block multiple
11
+ times, use the ``block`` function:
12
+
13
+ .. code-block:: jinja
14
+
15
+ <title>{% block title %}{% endblock %}</title>
16
+
17
+ <h1>{{ block('title') }}</h1>
18
+
19
+ {% block body %}{% endblock %}
20
+
21
+ The ``block`` function can also be used to display one block of another
22
+ template:
23
+
24
+ .. code-block:: jinja
25
+
26
+ {{ block("title", "common_blocks.twig") }}
27
+
28
+ Use the ``defined`` test to check if a block exists in the context of the
29
+ current template:
30
+
31
+ .. code-block:: jinja
32
+
33
+ {% if block("footer") is defined %}
34
+ ...
35
+ {% endif %}
36
+
37
+ {% if block("footer", "common_blocks.twig") is defined %}
38
+ ...
39
+ {% endif %}
40
+
41
+ .. seealso:: :doc:`extends<../tags/extends>`, :doc:`parent<../functions/parent>`
vendor/twig/twig/doc/functions/constant.rst ADDED
@@ -0,0 +1,29 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ``constant``
2
+ ============
3
+
4
+ .. versionadded: 1.12.1
5
+ constant now accepts object instances as the second argument.
6
+
7
+ .. versionadded: 1.28
8
+ Using ``constant`` with the ``defined`` test was added in Twig 1.28.
9
+
10
+ ``constant`` returns the constant value for a given string:
11
+
12
+ .. code-block:: jinja
13
+
14
+ {{ some_date|date(constant('DATE_W3C')) }}
15
+ {{ constant('Namespace\\Classname::CONSTANT_NAME') }}
16
+
17
+ As of 1.12.1 you can read constants from object instances as well:
18
+
19
+ .. code-block:: jinja
20
+
21
+ {{ constant('RSS', date) }}
22
+
23
+ Use the ``defined`` test to check if a constant is defined:
24
+
25
+ .. code-block:: jinja
26
+
27
+ {% if constant('SOME_CONST') is defined %}
28
+ ...
29
+ {% endif %}
vendor/twig/twig/doc/functions/cycle.rst ADDED
@@ -0,0 +1,28 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ``cycle``
2
+ =========
3
+
4
+ The ``cycle`` function cycles on an array of values:
5
+
6
+ .. code-block:: jinja
7
+
8
+ {% set start_year = date() | date('Y') %}
9
+ {% set end_year = start_year + 5 %}
10
+
11
+ {% for year in start_year..end_year %}
12
+ {{ cycle(['odd', 'even'], loop.index0) }}
13
+ {% endfor %}
14
+
15
+ The array can contain any number of values:
16
+
17
+ .. code-block:: jinja
18
+
19
+ {% set fruits = ['apple', 'orange', 'citrus'] %}
20
+
21
+ {% for i in 0..10 %}
22
+ {{ cycle(fruits, i) }}
23
+ {% endfor %}
24
+
25
+ Arguments
26
+ ---------
27
+
28
+ * ``position``: The cycle position
vendor/twig/twig/doc/functions/date.rst ADDED
@@ -0,0 +1,55 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ``date``
2
+ ========
3
+
4
+ .. versionadded:: 1.6
5
+ The date function has been added in Twig 1.6.
6
+
7
+ .. versionadded:: 1.6.1
8
+ The default timezone support has been added in Twig 1.6.1.
9
+
10
+ Converts an argument to a date to allow date comparison:
11
+
12
+ .. code-block:: jinja
13
+
14
+ {% if date(user.created_at) < date('-2days') %}
15
+ {# do something #}
16
+ {% endif %}
17
+
18
+ The argument must be in one of PHP’s supported `date and time formats`_.
19
+
20
+ You can pass a timezone as the second argument:
21
+
22
+ .. code-block:: jinja
23
+
24
+ {% if date(user.created_at) < date('-2days', 'Europe/Paris') %}
25
+ {# do something #}
26
+ {% endif %}
27
+
28
+ If no argument is passed, the function returns the current date:
29
+
30
+ .. code-block:: jinja
31
+
32
+ {% if date(user.created_at) < date() %}
33
+ {# always! #}
34
+ {% endif %}
35
+
36
+ .. note::
37
+
38
+ You can set the default timezone globally by calling ``setTimezone()`` on
39
+ the ``core`` extension instance:
40
+
41
+ .. code-block:: php
42
+
43
+ $twig = new Twig_Environment($loader);
44
+ $twig->getExtension('Twig_Extension_Core')->setTimezone('Europe/Paris');
45
+
46
+ // before Twig 1.26
47
+ $twig->getExtension('core')->setTimezone('Europe/Paris');
48
+
49
+ Arguments
50
+ ---------
51
+
52
+ * ``date``: The date
53
+ * ``timezone``: The timezone
54
+
55
+ .. _`date and time formats`: http://php.net/manual/en/datetime.formats.php
vendor/twig/twig/doc/functions/dump.rst ADDED
@@ -0,0 +1,69 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ``dump``
2
+ ========
3
+
4
+ .. versionadded:: 1.5
5
+ The ``dump`` function was added in Twig 1.5.
6
+
7
+ The ``dump`` function dumps information about a template variable. This is
8
+ mostly useful to debug a template that does not behave as expected by
9
+ introspecting its variables:
10
+
11
+ .. code-block:: jinja
12
+
13
+ {{ dump(user) }}
14
+
15
+ .. note::
16
+
17
+ The ``dump`` function is not available by default. You must add the
18
+ ``Twig_Extension_Debug`` extension explicitly when creating your Twig
19
+ environment::
20
+
21
+ $twig = new Twig_Environment($loader, array(
22
+ 'debug' => true,
23
+ // ...
24
+ ));
25
+ $twig->addExtension(new Twig_Extension_Debug());
26
+
27
+ Even when enabled, the ``dump`` function won't display anything if the
28
+ ``debug`` option on the environment is not enabled (to avoid leaking debug
29
+ information on a production server).
30
+
31
+ In an HTML context, wrap the output with a ``pre`` tag to make it easier to
32
+ read:
33
+
34
+ .. code-block:: jinja
35
+
36
+ <pre>
37
+ {{ dump(user) }}
38
+ </pre>
39
+
40
+ .. tip::
41
+
42
+ Using a ``pre`` tag is not needed when `XDebug`_ is enabled and
43
+ ``html_errors`` is ``on``; as a bonus, the output is also nicer with
44
+ XDebug enabled.
45
+
46
+ You can debug several variables by passing them as additional arguments:
47
+
48
+ .. code-block:: jinja
49
+
50
+ {{ dump(user, categories) }}
51
+
52
+ If you don't pass any value, all variables from the current context are
53
+ dumped:
54
+
55
+ .. code-block:: jinja
56
+
57
+ {{ dump() }}
58
+
59
+ .. note::
60
+
61
+ Internally, Twig uses the PHP `var_dump`_ function.
62
+
63
+ Arguments
64
+ ---------
65
+
66
+ * ``context``: The context to dump
67
+
68
+ .. _`XDebug`: http://xdebug.org/docs/display
69
+ .. _`var_dump`: http://php.net/var_dump
vendor/twig/twig/doc/functions/include.rst ADDED
@@ -0,0 +1,84 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ``include``
2
+ ===========
3
+
4
+ .. versionadded:: 1.12
5
+ The ``include`` function was added in Twig 1.12.
6
+
7
+ The ``include`` function returns the rendered content of a template:
8
+
9
+ .. code-block:: jinja
10
+
11
+ {{ include('template.html') }}
12
+ {{ include(some_var) }}
13
+
14
+ Included templates have access to the variables of the active context.
15
+
16
+ If you are using the filesystem loader, the templates are looked for in the
17
+ paths defined by it.
18
+
19
+ The context is passed by default to the template but you can also pass
20
+ additional variables:
21
+
22
+ .. code-block:: jinja
23
+
24
+ {# template.html will have access to the variables from the current context and the additional ones provided #}
25
+ {{ include('template.html', {foo: 'bar'}) }}
26
+
27
+ You can disable access to the context by setting ``with_context`` to
28
+ ``false``:
29
+
30
+ .. code-block:: jinja
31
+
32
+ {# only the foo variable will be accessible #}
33
+ {{ include('template.html', {foo: 'bar'}, with_context = false) }}
34
+
35
+ .. code-block:: jinja
36
+
37
+ {# no variables will be accessible #}
38
+ {{ include('template.html', with_context = false) }}
39
+
40
+ And if the expression evaluates to a ``Twig_Template`` or a
41
+ ``Twig_TemplateWrapper`` instance, Twig will use it directly::
42
+
43
+ // {{ include(template) }}
44
+
45
+ // deprecated as of Twig 1.28
46
+ $template = $twig->loadTemplate('some_template.twig');
47
+
48
+ // as of Twig 1.28
49
+ $template = $twig->load('some_template.twig');
50
+
51
+ $twig->display('template.twig', array('template' => $template));
52
+
53
+ When you set the ``ignore_missing`` flag, Twig will return an empty string if
54
+ the template does not exist:
55
+
56
+ .. code-block:: jinja
57
+
58
+ {{ include('sidebar.html', ignore_missing = true) }}
59
+
60
+ You can also provide a list of templates that are checked for existence before
61
+ inclusion. The first template that exists will be rendered:
62
+
63
+ .. code-block:: jinja
64
+
65
+ {{ include(['page_detailed.html', 'page.html']) }}
66
+
67
+ If ``ignore_missing`` is set, it will fall back to rendering nothing if none
68
+ of the templates exist, otherwise it will throw an exception.
69
+
70
+ When including a template created by an end user, you should consider
71
+ sandboxing it:
72
+
73
+ .. code-block:: jinja
74
+
75
+ {{ include('page.html', sandboxed = true) }}
76
+
77
+ Arguments
78
+ ---------
79
+
80
+ * ``template``: The template to render
81
+ * ``variables``: The variables to pass to the template
82
+ * ``with_context``: Whether to pass the current context variables or not
83
+ * ``ignore_missing``: Whether to ignore missing templates or not
84
+ * ``sandboxed``: Whether to sandbox the template or not
vendor/twig/twig/doc/functions/index.rst ADDED
@@ -0,0 +1,20 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ Functions
2
+ =========
3
+
4
+ .. toctree::
5
+ :maxdepth: 1
6
+
7
+ attribute
8
+ block
9
+ constant
10
+ cycle
11
+ date
12
+ dump
13
+ include
14
+ max
15
+ min
16
+ parent
17
+ random
18
+ range
19
+ source
20
+ template_from_string
vendor/twig/twig/doc/functions/max.rst ADDED
@@ -0,0 +1,20 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ``max``
2
+ =======
3
+
4
+ .. versionadded:: 1.15
5
+ The ``max`` function was added in Twig 1.15.
6
+
7
+ ``max`` returns the biggest value of a sequence or a set of values:
8
+
9
+ .. code-block:: jinja
10
+
11
+ {{ max(1, 3, 2) }}
12
+ {{ max([1, 3, 2]) }}
13
+
14
+ When called with a mapping, max ignores keys and only compares values:
15
+
16
+ .. code-block:: jinja
17
+
18
+ {{ max({2: "e", 1: "a", 3: "b", 5: "d", 4: "c"}) }}
19
+ {# returns "e" #}
20
+
vendor/twig/twig/doc/functions/min.rst ADDED
@@ -0,0 +1,20 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ``min``
2
+ =======
3
+
4
+ .. versionadded:: 1.15
5
+ The ``min`` function was added in Twig 1.15.
6
+
7
+ ``min`` returns the lowest value of a sequence or a set of values:
8
+
9
+ .. code-block:: jinja
10
+
11
+ {{ min(1, 3, 2) }}
12
+ {{ min([1, 3, 2]) }}
13
+
14
+ When called with a mapping, min ignores keys and only compares values:
15
+
16
+ .. code-block:: jinja
17
+
18
+ {{ min({2: "e", 3: "a", 1: "b", 5: "d", 4: "c"}) }}
19
+ {# returns "a" #}
20
+
vendor/twig/twig/doc/functions/parent.rst ADDED
@@ -0,0 +1,20 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ``parent``
2
+ ==========
3
+
4
+ When a template uses inheritance, it's possible to render the contents of the
5
+ parent block when overriding a block by using the ``parent`` function:
6
+
7
+ .. code-block:: jinja
8
+
9
+ {% extends "base.html" %}
10
+
11
+ {% block sidebar %}
12
+ <h3>Table Of Contents</h3>
13
+ ...
14
+ {{ parent() }}
15
+ {% endblock %}
16
+
17
+ The ``parent()`` call will return the content of the ``sidebar`` block as
18
+ defined in the ``base.html`` template.
19
+
20
+ .. seealso:: :doc:`extends<../tags/extends>`, :doc:`block<../functions/block>`, :doc:`block<../tags/block>`
vendor/twig/twig/doc/functions/random.rst ADDED
@@ -0,0 +1,29 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ``random``
2
+ ==========
3
+
4
+ .. versionadded:: 1.5
5
+ The ``random`` function was added in Twig 1.5.
6
+
7
+ .. versionadded:: 1.6
8
+ String and integer handling was added in Twig 1.6.
9
+
10
+ The ``random`` function returns a random value depending on the supplied
11
+ parameter type:
12
+
13
+ * a random item from a sequence;
14
+ * a random character from a string;
15
+ * a random integer between 0 and the integer parameter (inclusive).
16
+
17
+ .. code-block:: jinja
18
+
19
+ {{ random(['apple', 'orange', 'citrus']) }} {# example output: orange #}
20
+ {{ random('ABC') }} {# example output: C #}
21
+ {{ random() }} {# example output: 15386094 (works as the native PHP mt_rand function) #}
22
+ {{ random(5) }} {# example output: 3 #}
23
+
24
+ Arguments
25
+ ---------
26
+
27
+ * ``values``: The values
28
+
29
+ .. _`mt_rand`: http://php.net/mt_rand
vendor/twig/twig/doc/functions/range.rst ADDED
@@ -0,0 +1,58 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ``range``
2
+ =========
3
+
4
+ Returns a list containing an arithmetic progression of integers:
5
+
6
+ .. code-block:: jinja
7
+
8
+ {% for i in range(0, 3) %}
9
+ {{ i }},
10
+ {% endfor %}
11
+
12
+ {# outputs 0, 1, 2, 3, #}
13
+
14
+ When step is given (as the third parameter), it specifies the increment (or
15
+ decrement for negative values):
16
+
17
+ .. code-block:: jinja
18
+
19
+ {% for i in range(0, 6, 2) %}
20
+ {{ i }},
21
+ {% endfor %}
22
+
23
+ {# outputs 0, 2, 4, 6, #}
24
+
25
+ .. note::
26
+
27
+ Note that if the start is greater than the end, ``range`` assumes a step of
28
+ ``-1``:
29
+
30
+ .. code-block:: jinja
31
+
32
+ {% for i in range(3, 0) %}
33
+ {{ i }},
34
+ {% endfor %}
35
+
36
+ {# outputs 3, 2, 1, 0, #}
37
+
38
+ The Twig built-in ``..`` operator is just syntactic sugar for the ``range``
39
+ function (with a step of ``1``, or ``-1`` if the start is greater than the end):
40
+
41
+ .. code-block:: jinja
42
+
43
+ {% for i in 0..3 %}
44
+ {{ i }},
45
+ {% endfor %}
46
+
47
+ .. tip::
48
+
49
+ The ``range`` function works as the native PHP `range`_ function.
50
+
51
+ Arguments
52
+ ---------
53
+
54
+ * ``low``: The first value of the sequence.
55
+ * ``high``: The highest possible value of the sequence.
56
+ * ``step``: The increment between elements of the sequence.
57
+
58
+ .. _`range`: http://php.net/range
vendor/twig/twig/doc/functions/source.rst ADDED
@@ -0,0 +1,32 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ``source``
2
+ ==========
3
+
4
+ .. versionadded:: 1.15
5
+ The ``source`` function was added in Twig 1.15.
6
+
7
+ .. versionadded:: 1.18.3
8
+ The ``ignore_missing`` flag was added in Twig 1.18.3.
9
+
10
+ The ``source`` function returns the content of a template without rendering it:
11
+
12
+ .. code-block:: jinja
13
+
14
+ {{ source('template.html') }}
15
+ {{ source(some_var) }}
16
+
17
+ When you set the ``ignore_missing`` flag, Twig will return an empty string if
18
+ the template does not exist:
19
+
20
+ .. code-block:: jinja
21
+
22
+ {{ source('template.html', ignore_missing = true) }}
23
+
24
+ The function uses the same template loaders as the ones used to include
25
+ templates. So, if you are using the filesystem loader, the templates are looked
26
+ for in the paths defined by it.
27
+
28
+ Arguments
29
+ ---------
30
+
31
+ * ``name``: The name of the template to read
32
+ * ``ignore_missing``: Whether to ignore missing templates or not
vendor/twig/twig/doc/functions/template_from_string.rst ADDED
@@ -0,0 +1,32 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ``template_from_string``
2
+ ========================
3
+
4
+ .. versionadded:: 1.11
5
+ The ``template_from_string`` function was added in Twig 1.11.
6
+
7
+ The ``template_from_string`` function loads a template from a string:
8
+
9
+ .. code-block:: jinja
10
+
11
+ {{ include(template_from_string("Hello {{ name }}")) }}
12
+ {{ include(template_from_string(page.template)) }}
13
+
14
+ .. note::
15
+
16
+ The ``template_from_string`` function is not available by default. You
17
+ must add the ``Twig_Extension_StringLoader`` extension explicitly when
18
+ creating your Twig environment::
19
+
20
+ $twig = new Twig_Environment(...);
21
+ $twig->addExtension(new Twig_Extension_StringLoader());
22
+
23
+ .. note::
24
+
25
+ Even if you will probably always use the ``template_from_string`` function
26
+ with the ``include`` function, you can use it with any tag or function that
27
+ takes a template as an argument (like the ``embed`` or ``extends`` tags).
28
+
29
+ Arguments
30
+ ---------
31
+
32
+ * ``template``: The template
vendor/twig/twig/doc/index.rst ADDED
@@ -0,0 +1,19 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ Twig
2
+ ====
3
+
4
+ .. toctree::
5
+ :maxdepth: 2
6
+
7
+ intro
8
+ installation
9
+ templates
10
+ api
11
+ advanced
12
+ internals
13
+ deprecated
14
+ recipes
15
+ coding_standards
16
+ tags/index
17
+ filters/index
18
+ functions/index
19
+ tests/index
vendor/twig/twig/doc/installation.rst ADDED
@@ -0,0 +1,116 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ Installation
2
+ ============
3
+
4
+ You have multiple ways to install Twig.
5
+
6
+ Installing the Twig PHP package
7
+ -------------------------------
8
+
9
+ Installing via Composer (recommended)
10
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
11
+
12
+ Install `Composer`_ and run the following command to get the latest version:
13
+
14
+ .. code-block:: bash
15
+
16
+ composer require twig/twig:~1.0
17
+
18
+ Installing from the tarball release
19
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
20
+
21
+ 1. Download the most recent tarball from the `download page`_
22
+ 2. Verify the integrity of the tarball http://fabien.potencier.org/article/73/signing-project-releases
23
+ 3. Unpack the tarball
24
+ 4. Move the files somewhere in your project
25
+
26
+ Installing the development version
27
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
28
+
29
+ .. code-block:: bash
30
+
31
+ git clone git://github.com/twigphp/Twig.git
32
+
33
+ Installing the PEAR package
34
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~
35
+
36
+ .. note::
37
+
38
+ Using PEAR for installing Twig is deprecated and Twig 1.15.1 was the last
39
+ version published on the PEAR channel; use Composer instead.
40
+
41
+ .. code-block:: bash
42
+
43
+ pear channel-discover pear.twig-project.org
44
+ pear install twig/Twig
45
+
46
+ Installing the C extension
47
+ --------------------------
48
+
49
+ .. versionadded:: 1.4
50
+ The C extension was added in Twig 1.4.
51
+
52
+ .. note::
53
+
54
+ The C extension is **optional** but it brings some nice performance
55
+ improvements. Note that the extension is not a replacement for the PHP
56
+ code; it only implements a small part of the PHP code to improve the
57
+ performance at runtime; you must still install the regular PHP code.
58
+
59
+ Twig comes with a C extension that enhances the performance of the Twig
60
+ runtime engine; install it like any other PHP extensions:
61
+
62
+ .. code-block:: bash
63
+
64
+ cd ext/twig
65
+ phpize
66
+ ./configure
67
+ make
68
+ make install
69
+
70
+ .. note::
71
+
72
+ You can also install the C extension via PEAR (note that this method is
73
+ deprecated and newer versions of Twig are not available on the PEAR
74
+ channel):
75
+
76
+ .. code-block:: bash
77
+
78
+ pear channel-discover pear.twig-project.org
79
+ pear install twig/CTwig
80
+
81
+ For Windows:
82
+
83
+ 1. Setup the build environment following the `PHP documentation`_
84
+ 2. Put Twig's C extension source code into ``C:\php-sdk\phpdev\vcXX\x86\php-source-directory\ext\twig``
85
+ 3. Use the ``configure --disable-all --enable-cli --enable-twig=shared`` command instead of step 14
86
+ 4. ``nmake``
87
+ 5. Copy the ``C:\php-sdk\phpdev\vcXX\x86\php-source-directory\Release_TS\php_twig.dll`` file to your PHP setup.
88
+
89
+ .. tip::
90
+
91
+ For Windows ZendServer, ZTS is not enabled as mentioned in `Zend Server
92
+ FAQ`_.
93
+
94
+ You have to use ``configure --disable-all --disable-zts --enable-cli
95
+ --enable-twig=shared`` to be able to build the twig C extension for
96
+ ZendServer.
97
+
98
+ The built DLL will be available in
99
+ ``C:\\php-sdk\\phpdev\\vcXX\\x86\\php-source-directory\\Release``
100
+
101
+ Finally, enable the extension in your ``php.ini`` configuration file:
102
+
103
+ .. code-block:: ini
104
+
105
+ extension=twig.so #For Unix systems
106
+ extension=php_twig.dll #For Windows systems
107
+
108
+ And from now on, Twig will automatically compile your templates to take
109
+ advantage of the C extension. Note that this extension does not replace the
110
+ PHP code but only provides an optimized version of the
111
+ ``Twig_Template::getAttribute()`` method.
112
+
113
+ .. _`download page`: https://github.com/twigphp/Twig/tags
114
+ .. _`Composer`: https://getcomposer.org/download/
115
+ .. _`PHP documentation`: https://wiki.php.net/internals/windows/stepbystepbuild
116
+ .. _`Zend Server FAQ`: http://www.zend.com/en/products/server/faq#faqD6
vendor/twig/twig/doc/internals.rst ADDED
@@ -0,0 +1,142 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ Twig Internals
2
+ ==============
3
+
4
+ Twig is very extensible and you can easily hack it. Keep in mind that you
5
+ should probably try to create an extension before hacking the core, as most
6
+ features and enhancements can be handled with extensions. This chapter is also
7
+ useful for people who want to understand how Twig works under the hood.
8
+
9
+ How does Twig work?
10
+ -------------------
11
+
12
+ The rendering of a Twig template can be summarized into four key steps:
13
+
14
+ * **Load** the template: If the template is already compiled, load it and go
15
+ to the *evaluation* step, otherwise:
16
+
17
+ * First, the **lexer** tokenizes the template source code into small pieces
18
+ for easier processing;
19
+ * Then, the **parser** converts the token stream into a meaningful tree
20
+ of nodes (the Abstract Syntax Tree);
21
+ * Eventually, the *compiler* transforms the AST into PHP code.
22
+
23
+ * **Evaluate** the template: It basically means calling the ``display()``
24
+ method of the compiled template and passing it the context.
25
+
26
+ The Lexer
27
+ ---------
28
+
29
+ The lexer tokenizes a template source code into a token stream (each token is
30
+ an instance of ``Twig_Token``, and the stream is an instance of
31
+ ``Twig_TokenStream``). The default lexer recognizes 13 different token types:
32
+
33
+ * ``Twig_Token::BLOCK_START_TYPE``, ``Twig_Token::BLOCK_END_TYPE``: Delimiters for blocks (``{% %}``)
34
+ * ``Twig_Token::VAR_START_TYPE``, ``Twig_Token::VAR_END_TYPE``: Delimiters for variables (``{{ }}``)
35
+ * ``Twig_Token::TEXT_TYPE``: A text outside an expression;
36
+ * ``Twig_Token::NAME_TYPE``: A name in an expression;
37
+ * ``Twig_Token::NUMBER_TYPE``: A number in an expression;
38
+ * ``Twig_Token::STRING_TYPE``: A string in an expression;
39
+ * ``Twig_Token::OPERATOR_TYPE``: An operator;
40
+ * ``Twig_Token::PUNCTUATION_TYPE``: A punctuation sign;
41
+ * ``Twig_Token::INTERPOLATION_START_TYPE``, ``Twig_Token::INTERPOLATION_END_TYPE`` (as of Twig 1.5): Delimiters for string interpolation;
42
+ * ``Twig_Token::EOF_TYPE``: Ends of template.
43
+
44
+ You can manually convert a source code into a token stream by calling the
45
+ ``tokenize()`` method of an environment::
46
+
47
+ $stream = $twig->tokenize(new Twig_Source($source, $identifier));
48
+
49
+ .. versionadded:: 1.27
50
+ ``Twig_Source`` was introduced in version 1.27, pass the source and the
51
+ identifier directly on previous versions.
52
+
53
+ As the stream has a ``__toString()`` method, you can have a textual
54
+ representation of it by echoing the object::
55
+
56
+ echo $stream."\n";
57
+
58
+ Here is the output for the ``Hello {{ name }}`` template:
59
+
60
+ .. code-block:: text
61
+
62
+ TEXT_TYPE(Hello )
63
+ VAR_START_TYPE()
64
+ NAME_TYPE(name)
65
+ VAR_END_TYPE()
66
+ EOF_TYPE()
67
+
68
+ .. note::
69
+
70
+ The default lexer (``Twig_Lexer``) can be changed by calling
71
+ the ``setLexer()`` method::
72
+
73
+ $twig->setLexer($lexer);
74
+
75
+ The Parser
76
+ ----------
77
+
78
+ The parser converts the token stream into an AST (Abstract Syntax Tree), or a
79
+ node tree (an instance of ``Twig_Node_Module``). The core extension defines
80
+ the basic nodes like: ``for``, ``if``, ... and the expression nodes.
81
+
82
+ You can manually convert a token stream into a node tree by calling the
83
+ ``parse()`` method of an environment::
84
+
85
+ $nodes = $twig->parse($stream);
86
+
87
+ Echoing the node object gives you a nice representation of the tree::
88
+
89
+ echo $nodes."\n";
90
+
91
+ Here is the output for the ``Hello {{ name }}`` template:
92
+
93
+ .. code-block:: text
94
+
95
+ Twig_Node_Module(
96
+ Twig_Node_Text(Hello )
97
+ Twig_Node_Print(
98
+ Twig_Node_Expression_Name(name)
99
+ )
100
+ )
101
+
102
+ .. note::
103
+
104
+ The default parser (``Twig_TokenParser``) can be changed by calling the
105
+ ``setParser()`` method::
106
+
107
+ $twig->setParser($parser);
108
+
109
+ The Compiler
110
+ ------------
111
+
112
+ The last step is done by the compiler. It takes a node tree as an input and
113
+ generates PHP code usable for runtime execution of the template.
114
+
115
+ You can manually compile a node tree to PHP code with the ``compile()`` method
116
+ of an environment::
117
+
118
+ $php = $twig->compile($nodes);
119
+
120
+ The generated template for a ``Hello {{ name }}`` template reads as follows
121
+ (the actual output can differ depending on the version of Twig you are
122
+ using)::
123
+
124
+ /* Hello {{ name }} */
125
+ class __TwigTemplate_1121b6f109fe93ebe8c6e22e3712bceb extends Twig_Template
126
+ {
127
+ protected function doDisplay(array $context, array $blocks = array())
128
+ {
129
+ // line 1
130
+ echo "Hello ";
131
+ echo twig_escape_filter($this->env, isset($context["name"]) ? $context["name"] : null), "html", null, true);
132
+ }
133
+
134
+ // some more code
135
+ }
136
+
137
+ .. note::
138
+
139
+ The default compiler (``Twig_Compiler``) can be changed by calling the
140
+ ``setCompiler()`` method::
141
+
142
+ $twig->setCompiler($compiler);
vendor/twig/twig/doc/intro.rst ADDED
@@ -0,0 +1,85 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ Introduction
2
+ ============
3
+
4
+ This is the documentation for Twig, the flexible, fast, and secure template
5
+ engine for PHP.
6
+
7
+ If you have any exposure to other text-based template languages, such as
8
+ Smarty, Django, or Jinja, you should feel right at home with Twig. It's both
9
+ designer and developer friendly by sticking to PHP's principles and adding
10
+ functionality useful for templating environments.
11
+
12
+ The key-features are...
13
+
14
+ * *Fast*: Twig compiles templates down to plain optimized PHP code. The
15
+ overhead compared to regular PHP code was reduced to the very minimum.
16
+
17
+ * *Secure*: Twig has a sandbox mode to evaluate untrusted template code. This
18
+ allows Twig to be used as a template language for applications where users
19
+ may modify the template design.
20
+
21
+ * *Flexible*: Twig is powered by a flexible lexer and parser. This allows the
22
+ developer to define their own custom tags and filters, and to create their own DSL.
23
+
24
+ Twig is used by many Open-Source projects like Symfony, Drupal8, eZPublish,
25
+ phpBB, Piwik, OroCRM; and many frameworks have support for it as well like
26
+ Slim, Yii, Laravel, Codeigniter and Kohana — just to name a few.
27
+
28
+ Prerequisites
29
+ -------------
30
+
31
+ Twig needs at least **PHP 5.2.7** to run.
32
+
33
+ Installation
34
+ ------------
35
+
36
+ The recommended way to install Twig is via Composer:
37
+
38
+ .. code-block:: bash
39
+
40
+ composer require "twig/twig:~1.0"
41
+
42
+ .. note::
43
+
44
+ To learn more about the other installation methods, read the
45
+ :doc:`installation<installation>` chapter; it also explains how to install
46
+ the Twig C extension.
47
+
48
+ Basic API Usage
49
+ ---------------
50
+
51
+ This section gives you a brief introduction to the PHP API for Twig.
52
+
53
+ .. code-block:: php
54
+
55
+ require_once '/path/to/vendor/autoload.php';
56
+
57
+ $loader = new Twig_Loader_Array(array(
58
+ 'index' => 'Hello {{ name }}!',
59
+ ));
60
+ $twig = new Twig_Environment($loader);
61
+
62
+ echo $twig->render('index', array('name' => 'Fabien'));
63
+
64
+ Twig uses a loader (``Twig_Loader_Array``) to locate templates, and an
65
+ environment (``Twig_Environment``) to store the configuration.
66
+
67
+ The ``render()`` method loads the template passed as a first argument and
68
+ renders it with the variables passed as a second argument.
69
+
70
+ As templates are generally stored on the filesystem, Twig also comes with a
71
+ filesystem loader::
72
+
73
+ $loader = new Twig_Loader_Filesystem('/path/to/templates');
74
+ $twig = new Twig_Environment($loader, array(
75
+ 'cache' => '/path/to/compilation_cache',
76
+ ));
77
+
78
+ echo $twig->render('index.html', array('name' => 'Fabien'));
79
+
80
+ .. tip::
81
+
82
+ If you are not using Composer, use the Twig built-in autoloader::
83
+
84
+ require_once '/path/to/lib/Twig/Autoloader.php';
85
+ Twig_Autoloader::register();
vendor/twig/twig/doc/recipes.rst ADDED
@@ -0,0 +1,568 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ Recipes
2
+ =======
3
+
4
+ .. _deprecation-notices:
5
+
6
+ Displaying Deprecation Notices
7
+ ------------------------------
8
+
9
+ .. versionadded:: 1.21
10
+ This works as of Twig 1.21.
11
+
12
+ Deprecated features generate deprecation notices (via a call to the
13
+ ``trigger_error()`` PHP function). By default, they are silenced and never
14
+ displayed nor logged.
15
+
16
+ To easily remove all deprecated feature usages from your templates, write and
17
+ run a script along the lines of the following::
18
+
19
+ require_once __DIR__.'/vendor/autoload.php';
20
+
21
+ $twig = create_your_twig_env();
22
+
23
+ $deprecations = new Twig_Util_DeprecationCollector($twig);
24
+
25
+ print_r($deprecations->collectDir(__DIR__.'/templates'));
26
+
27
+ The ``collectDir()`` method compiles all templates found in a directory,
28
+ catches deprecation notices, and return them.
29
+
30
+ .. tip::
31
+
32
+ If your templates are not stored on the filesystem, use the ``collect()``
33
+ method instead. ``collect()`` takes a ``Traversable`` which must return
34
+ template names as keys and template contents as values (as done by
35
+ ``Twig_Util_TemplateDirIterator``).
36
+
37
+ However, this code won't find all deprecations (like using deprecated some Twig
38
+ classes). To catch all notices, register a custom error handler like the one
39
+ below::
40
+
41
+ $deprecations = array();
42
+ set_error_handler(function ($type, $msg) use (&$deprecations) {
43
+ if (E_USER_DEPRECATED === $type) {
44
+ $deprecations[] = $msg;
45
+ }
46
+ });
47
+
48
+ // run your application
49
+
50
+ print_r($deprecations);
51
+
52
+ Note that most deprecation notices are triggered during **compilation**, so
53
+ they won't be generated when templates are already cached.
54
+
55
+ .. tip::
56
+
57
+ If you want to manage the deprecation notices from your PHPUnit tests, have
58
+ a look at the `symfony/phpunit-bridge
59
+ <https://github.com/symfony/phpunit-bridge>`_ package, which eases the
60
+ process a lot.
61
+
62
+ Making a Layout conditional
63
+ ---------------------------
64
+
65
+ Working with Ajax means that the same content is sometimes displayed as is,
66
+ and sometimes decorated with a layout. As Twig layout template names can be
67
+ any valid expression, you can pass a variable that evaluates to ``true`` when
68
+ the request is made via Ajax and choose the layout accordingly:
69
+
70
+ .. code-block:: jinja
71
+
72
+ {% extends request.ajax ? "base_ajax.html" : "base.html" %}
73
+
74
+ {% block content %}
75
+ This is the content to be displayed.
76
+ {% endblock %}
77
+
78
+ Making an Include dynamic
79
+ -------------------------
80
+
81
+ When including a template, its name does not need to be a string. For
82
+ instance, the name can depend on the value of a variable:
83
+
84
+ .. code-block:: jinja
85
+
86
+ {% include var ~ '_foo.html' %}
87
+
88
+ If ``var`` evaluates to ``index``, the ``index_foo.html`` template will be
89
+ rendered.
90
+
91
+ As a matter of fact, the template name can be any valid expression, such as
92
+ the following:
93
+
94
+ .. code-block:: jinja
95
+
96
+ {% include var|default('index') ~ '_foo.html' %}
97
+
98
+ Overriding a Template that also extends itself
99
+ ----------------------------------------------
100
+
101
+ A template can be customized in two different ways:
102
+
103
+ * *Inheritance*: A template *extends* a parent template and overrides some
104
+ blocks;
105
+
106
+ * *Replacement*: If you use the filesystem loader, Twig loads the first
107
+ template it finds in a list of configured directories; a template found in a
108
+ directory *replaces* another one from a directory further in the list.
109
+
110
+ But how do you combine both: *replace* a template that also extends itself
111
+ (aka a template in a directory further in the list)?
112
+
113
+ Let's say that your templates are loaded from both ``.../templates/mysite``
114
+ and ``.../templates/default`` in this order. The ``page.twig`` template,
115
+ stored in ``.../templates/default`` reads as follows:
116
+
117
+ .. code-block:: jinja
118
+
119
+ {# page.twig #}
120
+ {% extends "layout.twig" %}
121
+
122
+ {% block content %}
123
+ {% endblock %}
124
+
125
+ You can replace this template by putting a file with the same name in
126
+ ``.../templates/mysite``. And if you want to extend the original template, you
127
+ might be tempted to write the following:
128
+
129
+ .. code-block:: jinja
130
+
131
+ {# page.twig in .../templates/mysite #}
132
+ {% extends "page.twig" %} {# from .../templates/default #}
133
+
134
+ Of course, this will not work as Twig will always load the template from
135
+ ``.../templates/mysite``.
136
+
137
+ It turns out it is possible to get this to work, by adding a directory right
138
+ at the end of your template directories, which is the parent of all of the
139
+ other directories: ``.../templates`` in our case. This has the effect of
140
+ making every template file within our system uniquely addressable. Most of the
141
+ time you will use the "normal" paths, but in the special case of wanting to
142
+ extend a template with an overriding version of itself we can reference its
143
+ parent's full, unambiguous template path in the extends tag:
144
+
145
+ .. code-block:: jinja
146
+
147
+ {# page.twig in .../templates/mysite #}
148
+ {% extends "default/page.twig" %} {# from .../templates #}
149
+
150
+ .. note::
151
+
152
+ This recipe was inspired by the following Django wiki page:
153
+ http://code.djangoproject.com/wiki/ExtendingTemplates
154
+
155
+ Customizing the Syntax
156
+ ----------------------
157
+
158
+ Twig allows some syntax customization for the block delimiters. It's not
159
+ recommended to use this feature as templates will be tied with your custom
160
+ syntax. But for specific projects, it can make sense to change the defaults.
161
+
162
+ To change the block delimiters, you need to create your own lexer object::
163
+
164
+ $twig = new Twig_Environment();
165
+
166
+ $lexer = new Twig_Lexer($twig, array(
167
+ 'tag_comment' => array('{#', '#}'),
168
+ 'tag_block' => array('{%', '%}'),
169
+ 'tag_variable' => array('{{', '}}'),
170
+ 'interpolation' => array('#{', '}'),
171
+ ));
172
+ $twig->setLexer($lexer);
173
+
174
+ Here are some configuration example that simulates some other template engines
175
+ syntax::
176
+
177
+ // Ruby erb syntax
178
+ $lexer = new Twig_Lexer($twig, array(
179
+ 'tag_comment' => array('<%#', '%>'),
180
+ 'tag_block' => array('<%', '%>'),
181
+ 'tag_variable' => array('<%=', '%>'),
182
+ ));
183
+
184
+ // SGML Comment Syntax
185
+ $lexer = new Twig_Lexer($twig, array(
186
+ 'tag_comment' => array('<!--#', '-->'),
187
+ 'tag_block' => array('<!--', '-->'),
188
+ 'tag_variable' => array('${', '}'),
189
+ ));
190
+
191
+ // Smarty like
192
+ $lexer = new Twig_Lexer($twig, array(
193
+ 'tag_comment' => array('{*', '*}'),
194
+ 'tag_block' => array('{', '}'),
195
+ 'tag_variable' => array('{$', '}'),
196
+ ));
197
+
198
+ Using dynamic Object Properties
199
+ -------------------------------
200
+
201
+ When Twig encounters a variable like ``article.title``, it tries to find a
202
+ ``title`` public property in the ``article`` object.
203
+
204
+ It also works if the property does not exist but is rather defined dynamically
205
+ thanks to the magic ``__get()`` method; you just need to also implement the
206
+ ``__isset()`` magic method like shown in the following snippet of code::
207
+
208
+ class Article
209
+ {
210
+ public function __get($name)
211
+ {
212
+ if ('title' == $name) {
213
+ return 'The title';
214
+ }
215
+
216
+ // throw some kind of error
217
+ }
218
+
219
+ public function __isset($name)
220
+ {
221
+ if ('title' == $name) {
222
+ return true;
223
+ }
224
+
225
+ return false;
226
+ }
227
+ }
228
+
229
+ Accessing the parent Context in Nested Loops
230
+ --------------------------------------------
231
+
232
+ Sometimes, when using nested loops, you need to access the parent context. The
233
+ parent context is always accessible via the ``loop.parent`` variable. For
234
+ instance, if you have the following template data::
235
+
236
+ $data = array(
237
+ 'topics' => array(
238
+ 'topic1' => array('Message 1 of topic 1', 'Message 2 of topic 1'),
239
+ 'topic2' => array('Message 1 of topic 2', 'Message 2 of topic 2'),
240
+ ),
241
+ );
242
+
243
+ And the following template to display all messages in all topics:
244
+
245
+ .. code-block:: jinja
246
+
247
+ {% for topic, messages in topics %}
248
+ * {{ loop.index }}: {{ topic }}
249
+ {% for message in messages %}
250
+ - {{ loop.parent.loop.index }}.{{ loop.index }}: {{ message }}
251
+ {% endfor %}
252
+ {% endfor %}
253
+
254
+ The output will be similar to:
255
+
256
+ .. code-block:: text
257
+
258
+ * 1: topic1
259
+ - 1.1: The message 1 of topic 1
260
+ - 1.2: The message 2 of topic 1
261
+ * 2: topic2
262
+ - 2.1: The message 1 of topic 2
263
+ - 2.2: The message 2 of topic 2
264
+
265
+ In the inner loop, the ``loop.parent`` variable is used to access the outer
266
+ context. So, the index of the current ``topic`` defined in the outer for loop
267
+ is accessible via the ``loop.parent.loop.index`` variable.
268
+
269
+ Defining undefined Functions and Filters on the Fly
270
+ ---------------------------------------------------
271
+
272
+ When a function (or a filter) is not defined, Twig defaults to throw a
273
+ ``Twig_Error_Syntax`` exception. However, it can also call a `callback`_ (any
274
+ valid PHP callable) which should return a function (or a filter).
275
+
276
+ For filters, register callbacks with ``registerUndefinedFilterCallback()``.
277
+ For functions, use ``registerUndefinedFunctionCallback()``::
278
+
279
+ // auto-register all native PHP functions as Twig functions
280
+ // don't try this at home as it's not secure at all!
281
+ $twig->registerUndefinedFunctionCallback(function ($name) {
282
+ if (function_exists($name)) {
283
+ return new Twig_SimpleFunction($name, $name);
284
+ }
285
+
286
+ return false;
287
+ });
288
+
289
+ If the callable is not able to return a valid function (or filter), it must
290
+ return ``false``.
291
+
292
+ If you register more than one callback, Twig will call them in turn until one
293
+ does not return ``false``.
294
+
295
+ .. tip::
296
+
297
+ As the resolution of functions and filters is done during compilation,
298
+ there is no overhead when registering these callbacks.
299
+
300
+ Validating the Template Syntax
301
+ ------------------------------
302
+
303
+ When template code is provided by a third-party (through a web interface for
304
+ instance), it might be interesting to validate the template syntax before
305
+ saving it. If the template code is stored in a `$template` variable, here is
306
+ how you can do it::
307
+
308
+ try {
309
+ $twig->parse($twig->tokenize(new Twig_Source($template)));
310
+
311
+ // the $template is valid
312
+ } catch (Twig_Error_Syntax $e) {
313
+ // $template contains one or more syntax errors
314
+ }
315
+
316
+ If you iterate over a set of files, you can pass the filename to the
317
+ ``tokenize()`` method to get the filename in the exception message::
318
+
319
+ foreach ($files as $file) {
320
+ try {
321
+ $twig->parse($twig->tokenize(new Twig_Source($template, $file->getFilename(), $file)));
322
+
323
+ // the $template is valid
324
+ } catch (Twig_Error_Syntax $e) {
325
+ // $template contains one or more syntax errors
326
+ }
327
+ }
328
+
329
+ .. versionadded:: 1.27
330
+ ``Twig_Source`` was introduced in version 1.27, pass the source and the
331
+ identifier directly on previous versions.
332
+
333
+ .. note::
334
+
335
+ This method won't catch any sandbox policy violations because the policy
336
+ is enforced during template rendering (as Twig needs the context for some
337
+ checks like allowed methods on objects).
338
+
339
+ Refreshing modified Templates when OPcache or APC is enabled
340
+ ------------------------------------------------------------
341
+
342
+ When using OPcache with ``opcache.validate_timestamps`` set to ``0`` or APC
343
+ with ``apc.stat`` set to ``0`` and Twig cache enabled, clearing the template
344
+ cache won't update the cache.
345
+
346
+ To get around this, force Twig to invalidate the bytecode cache::
347
+
348
+ $twig = new Twig_Environment($loader, array(
349
+ 'cache' => new Twig_Cache_Filesystem('/some/cache/path', Twig_Cache_Filesystem::FORCE_BYTECODE_INVALIDATION),
350
+ // ...
351
+ ));
352
+
353
+ .. note::
354
+
355
+ Before Twig 1.22, you should extend ``Twig_Environment`` instead::
356
+
357
+ class OpCacheAwareTwigEnvironment extends Twig_Environment
358
+ {
359
+ protected function writeCacheFile($file, $content)
360
+ {
361
+ parent::writeCacheFile($file, $content);
362
+
363
+ // Compile cached file into bytecode cache
364
+ if (function_exists('opcache_invalidate')) {
365
+ opcache_invalidate($file, true);
366
+ } elseif (function_exists('apc_compile_file')) {
367
+ apc_compile_file($file);
368
+ }
369
+ }
370
+ }
371
+
372
+ Reusing a stateful Node Visitor
373
+ -------------------------------
374
+
375
+ When attaching a visitor to a ``Twig_Environment`` instance, Twig uses it to
376
+ visit *all* templates it compiles. If you need to keep some state information
377
+ around, you probably want to reset it when visiting a new template.
378
+
379
+ This can be easily achieved with the following code::
380
+
381
+ protected $someTemplateState = array();
382
+
383
+ public function enterNode(Twig_NodeInterface $node, Twig_Environment $env)
384
+ {
385
+ if ($node instanceof Twig_Node_Module) {
386
+ // reset the state as we are entering a new template
387
+ $this->someTemplateState = array();
388
+ }
389
+
390
+ // ...
391
+
392
+ return $node;
393
+ }
394
+
395
+ Using a Database to store Templates
396
+ -----------------------------------
397
+
398
+ If you are developing a CMS, templates are usually stored in a database. This
399
+ recipe gives you a simple PDO template loader you can use as a starting point
400
+ for your own.
401
+
402
+ First, let's create a temporary in-memory SQLite3 database to work with::
403
+
404
+ $dbh = new PDO('sqlite::memory:');
405
+ $dbh->exec('CREATE TABLE templates (name STRING, source STRING, last_modified INTEGER)');
406
+ $base = '{% block content %}{% endblock %}';
407
+ $index = '
408
+ {% extends "base.twig" %}
409
+ {% block content %}Hello {{ name }}{% endblock %}
410
+ ';
411
+ $now = time();
412
+ $dbh->exec("INSERT INTO templates (name, source, last_modified) VALUES ('base.twig', '$base', $now)");
413
+ $dbh->exec("INSERT INTO templates (name, source, last_modified) VALUES ('index.twig', '$index', $now)");
414
+
415
+ We have created a simple ``templates`` table that hosts two templates:
416
+ ``base.twig`` and ``index.twig``.
417
+
418
+ Now, let's define a loader able to use this database::
419
+
420
+ class DatabaseTwigLoader implements Twig_LoaderInterface, Twig_ExistsLoaderInterface, Twig_SourceContextLoaderInterface
421
+ {
422
+ protected $dbh;
423
+
424
+ public function __construct(PDO $dbh)
425
+ {
426
+ $this->dbh = $dbh;
427
+ }
428
+
429
+ public function getSource($name)
430
+ {
431
+ if (false === $source = $this->getValue('source', $name)) {
432
+ throw new Twig_Error_Loader(sprintf('Template "%s" does not exist.', $name));
433
+ }
434
+
435
+ return $source;
436
+ }
437
+
438
+ // Twig_SourceContextLoaderInterface as of Twig 1.27
439
+ public function getSourceContext($name)
440
+ {
441
+ if (false === $source = $this->getValue('source', $name)) {
442
+ throw new Twig_Error_Loader(sprintf('Template "%s" does not exist.', $name));
443
+ }
444
+
445
+ return new Twig_Source($source, $name);
446
+ }
447
+
448
+ // Twig_ExistsLoaderInterface as of Twig 1.11
449
+ public function exists($name)
450
+ {
451
+ return $name === $this->getValue('name', $name);
452
+ }
453
+
454
+ public function getCacheKey($name)
455
+ {
456
+ return $name;
457
+ }
458
+
459
+ public function isFresh($name, $time)
460
+ {
461
+ if (false === $lastModified = $this->getValue('last_modified', $name)) {
462
+ return false;
463
+ }
464
+
465
+ return $lastModified <= $time;
466
+ }
467
+
468
+ protected function getValue($column, $name)
469
+ {
470
+ $sth = $this->dbh->prepare('SELECT '.$column.' FROM templates WHERE name = :name');
471
+ $sth->execute(array(':name' => (string) $name));
472
+
473
+ return $sth->fetchColumn();
474
+ }
475
+ }
476
+
477
+ Finally, here is an example on how you can use it::
478
+
479
+ $loader = new DatabaseTwigLoader($dbh);
480
+ $twig = new Twig_Environment($loader);
481
+
482
+ echo $twig->render('index.twig', array('name' => 'Fabien'));
483
+
484
+ Using different Template Sources
485
+ --------------------------------
486
+
487
+ This recipe is the continuation of the previous one. Even if you store the
488
+ contributed templates in a database, you might want to keep the original/base
489
+ templates on the filesystem. When templates can be loaded from different
490
+ sources, you need to use the ``Twig_Loader_Chain`` loader.
491
+
492
+ As you can see in the previous recipe, we reference the template in the exact
493
+ same way as we would have done it with a regular filesystem loader. This is
494
+ the key to be able to mix and match templates coming from the database, the
495
+ filesystem, or any other loader for that matter: the template name should be a
496
+ logical name, and not the path from the filesystem::
497
+
498
+ $loader1 = new DatabaseTwigLoader($dbh);
499
+ $loader2 = new Twig_Loader_Array(array(
500
+ 'base.twig' => '{% block content %}{% endblock %}',
501
+ ));
502
+ $loader = new Twig_Loader_Chain(array($loader1, $loader2));
503
+
504
+ $twig = new Twig_Environment($loader);
505
+
506
+ echo $twig->render('index.twig', array('name' => 'Fabien'));
507
+
508
+ Now that the ``base.twig`` templates is defined in an array loader, you can
509
+ remove it from the database, and everything else will still work as before.
510
+
511
+ Loading a Template from a String
512
+ --------------------------------
513
+
514
+ From a template, you can easily load a template stored in a string via the
515
+ ``template_from_string`` function (available as of Twig 1.11 via the
516
+ ``Twig_Extension_StringLoader`` extension):
517
+
518
+ .. code-block:: jinja
519
+
520
+ {{ include(template_from_string("Hello {{ name }}")) }}
521
+
522
+ From PHP, it's also possible to load a template stored in a string via
523
+ ``Twig_Environment::createTemplate()`` (available as of Twig 1.18)::
524
+
525
+ $template = $twig->createTemplate('hello {{ name }}');
526
+ echo $template->render(array('name' => 'Fabien'));
527
+
528
+ .. note::
529
+
530
+ Never use the ``Twig_Loader_String`` loader, which has severe limitations.
531
+
532
+ Using Twig and AngularJS in the same Templates
533
+ ----------------------------------------------
534
+
535
+ Mixing different template syntaxes in the same file is not a recommended
536
+ practice as both AngularJS and Twig use the same delimiters in their syntax:
537
+ ``{{`` and ``}}``.
538
+
539
+ Still, if you want to use AngularJS and Twig in the same template, there are
540
+ two ways to make it work depending on the amount of AngularJS you need to
541
+ include in your templates:
542
+
543
+ * Escaping the AngularJS delimiters by wrapping AngularJS sections with the
544
+ ``{% verbatim %}`` tag or by escaping each delimiter via ``{{ '{{' }}`` and
545
+ ``{{ '}}' }}``;
546
+
547
+ * Changing the delimiters of one of the template engines (depending on which
548
+ engine you introduced last):
549
+
550
+ * For AngularJS, change the interpolation tags using the
551
+ ``interpolateProvider`` service, for instance at the module initialization
552
+ time:
553
+
554
+ ..code-block:: javascript
555
+
556
+ angular.module('myApp', []).config(function($interpolateProvider) {
557
+ $interpolateProvider.startSymbol('{[').endSymbol(']}');
558
+ });
559
+
560
+ * For Twig, change the delimiters via the ``tag_variable`` Lexer option:
561
+
562
+ ..code-block:: php
563
+
564
+ $env->setLexer(new Twig_Lexer($env, array(
565
+ 'tag_variable' => array('{[', ']}'),
566
+ )));
567
+
568
+ .. _callback: http://www.php.net/manual/en/function.is-callable.php
vendor/twig/twig/doc/tags/autoescape.rst ADDED
@@ -0,0 +1,81 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ``autoescape``
2
+ ==============
3
+
4
+ Whether automatic escaping is enabled or not, you can mark a section of a
5
+ template to be escaped or not by using the ``autoescape`` tag:
6
+
7
+ .. code-block:: jinja
8
+
9
+ {% autoescape %}
10
+ Everything will be automatically escaped in this block
11
+ using the HTML strategy
12
+ {% endautoescape %}
13
+
14
+ {% autoescape 'html' %}
15
+ Everything will be automatically escaped in this block
16
+ using the HTML strategy
17
+ {% endautoescape %}
18
+
19
+ {% autoescape 'js' %}
20
+ Everything will be automatically escaped in this block
21
+ using the js escaping strategy
22
+ {% endautoescape %}
23
+
24
+ {% autoescape false %}
25
+ Everything will be outputted as is in this block
26
+ {% endautoescape %}
27
+
28
+ .. note::
29
+
30
+ Before Twig 1.8, the syntax was different:
31
+
32
+ .. code-block:: jinja
33
+
34
+ {% autoescape true %}
35
+ Everything will be automatically escaped in this block
36
+ using the HTML strategy
37
+ {% endautoescape %}
38
+
39
+ {% autoescape false %}
40
+ Everything will be outputted as is in this block
41
+ {% endautoescape %}
42
+
43
+ {% autoescape true js %}
44
+ Everything will be automatically escaped in this block
45
+ using the js escaping strategy
46
+ {% endautoescape %}
47
+
48
+ When automatic escaping is enabled everything is escaped by default except for
49
+ values explicitly marked as safe. Those can be marked in the template by using
50
+ the :doc:`raw<../filters/raw>` filter:
51
+
52
+ .. code-block:: jinja
53
+
54
+ {% autoescape %}
55
+ {{ safe_value|raw }}
56
+ {% endautoescape %}
57
+
58
+ Functions returning template data (like :doc:`macros<macro>` and
59
+ :doc:`parent<../functions/parent>`) always return safe markup.
60
+
61
+ .. note::
62
+
63
+ Twig is smart enough to not escape an already escaped value by the
64
+ :doc:`escape<../filters/escape>` filter.
65
+
66
+ .. note::
67
+
68
+ Twig does not escape static expressions:
69
+
70
+ .. code-block:: jinja
71
+
72
+ {% set hello = "<strong>Hello</strong>" %}
73
+ {{ hello }}
74
+ {{ "<strong>world</strong>" }}
75
+
76
+ Will be rendered "<strong>Hello</strong> **world**".
77
+
78
+ .. note::
79
+
80
+ The chapter :doc:`Twig for Developers<../api>` gives more information
81
+ about when and how automatic escaping is applied.
vendor/twig/twig/doc/tags/block.rst ADDED
@@ -0,0 +1,11 @@
 
 
 
 
 
 
 
 
 
 
 
1
+ ``block``
2
+ =========
3
+
4
+ Blocks are used for inheritance and act as placeholders and replacements at
5
+ the same time. They are documented in detail in the documentation for the
6
+ :doc:`extends<../tags/extends>` tag.
7
+
8
+ Block names should consist of alphanumeric characters, and underscores. Dashes
9
+ are not permitted.
10
+
11
+ .. seealso:: :doc:`block<../functions/block>`, :doc:`parent<../functions/parent>`, :doc:`use<../tags/use>`, :doc:`extends<../tags/extends>`
vendor/twig/twig/doc/tags/do.rst ADDED
@@ -0,0 +1,12 @@
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ``do``
2
+ ======
3
+
4
+ .. versionadded:: 1.5
5
+ The ``do`` tag was added in Twig 1.5.
6
+
7
+ The ``do`` tag works exactly like the regular variable expression (``{{ ...
8
+ }}``) just that it doesn't print anything:
9
+
10
+ .. code-block:: jinja
11
+
12
+ {% do 1 + 2 %}
vendor/twig/twig/doc/tags/embed.rst ADDED
@@ -0,0 +1,178 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ``embed``
2
+ =========
3
+
4
+ .. versionadded:: 1.8
5
+ The ``embed`` tag was added in Twig 1.8.
6
+
7
+ The ``embed`` tag combines the behaviour of :doc:`include<include>` and
8
+ :doc:`extends<extends>`.
9
+ It allows you to include another template's contents, just like ``include``
10
+ does. But it also allows you to override any block defined inside the
11
+ included template, like when extending a template.
12
+
13
+ Think of an embedded template as a "micro layout skeleton".
14
+
15
+ .. code-block:: jinja
16
+
17
+ {% embed "teasers_skeleton.twig" %}
18
+ {# These blocks are defined in "teasers_skeleton.twig" #}
19
+ {# and we override them right here: #}
20
+ {% block left_teaser %}
21
+ Some content for the left teaser box
22
+ {% endblock %}
23
+ {% block right_teaser %}
24
+ Some content for the right teaser box
25
+ {% endblock %}
26
+ {% endembed %}
27
+
28
+ The ``embed`` tag takes the idea of template inheritance to the level of
29
+ content fragments. While template inheritance allows for "document skeletons",
30
+ which are filled with life by child templates, the ``embed`` tag allows you to
31
+ create "skeletons" for smaller units of content and re-use and fill them
32
+ anywhere you like.
33
+
34
+ Since the use case may not be obvious, let's look at a simplified example.
35
+ Imagine a base template shared by multiple HTML pages, defining a single block
36
+ named "content":
37
+
38
+ .. code-block:: text
39
+
40
+ ┌─── page layout ─────────────────────┐
41
+ │ │
42
+ │ ┌── block "content" ──┐ │
43
+ │ │ │ │
44
+ │ │ │ │
45
+ │ │ (child template to │ │
46
+ │ │ put content here) │ │
47
+ │ │ │ │
48
+ │ │ │ │
49
+ │ └─────────────────────┘ │
50
+ │ │
51
+ └─────────────────────────────────────┘
52
+
53
+ Some pages ("foo" and "bar") share the same content structure -
54
+ two vertically stacked boxes:
55
+
56
+ .. code-block:: text
57
+
58
+ ┌─── page layout ─────────────────────┐
59
+ │ │
60
+ │ ┌── block "content" ──┐ │
61
+ │ │ ┌─ block "top" ───┐ │ │
62
+ │ │ │ │ │ │
63
+ │ │ └─────────────────┘ │ │
64
+ │ │ ┌─ block "bottom" ┐ │ │
65
+ │ │ │ │ │ │
66
+ │ │ └─────────────────┘ │ │
67
+ │ └─────────────────────┘ │
68
+ │ │
69
+ └─────────────────────────────────────┘
70
+
71
+ While other pages ("boom" and "baz") share a different content structure -
72
+ two boxes side by side:
73
+
74
+ .. code-block:: text
75
+
76
+ ┌─── page layout ─────────────────────┐
77
+ │ │
78
+ │ ┌── block "content" ──┐ │
79
+ │ │ │ │
80
+ │ │ ┌ block ┐ ┌ block ┐ │ │
81
+ │ │ │"left" │ │"right"│ │ │
82
+ │ │ │ │ │ │ │ │
83
+ │ │ │ │ │ │ │ │
84
+ │ │ └───────┘ └───────┘ │ │
85
+ │ └─────────────────────┘ │
86
+ │ │
87
+ └─────────────────────────────────────┘
88
+
89
+ Without the ``embed`` tag, you have two ways to design your templates:
90
+
91
+ * Create two "intermediate" base templates that extend the master layout
92
+ template: one with vertically stacked boxes to be used by the "foo" and
93
+ "bar" pages and another one with side-by-side boxes for the "boom" and
94
+ "baz" pages.
95
+
96
+ * Embed the markup for the top/bottom and left/right boxes into each page
97
+ template directly.
98
+
99
+ These two solutions do not scale well because they each have a major drawback:
100
+
101
+ * The first solution may indeed work for this simplified example. But imagine
102
+ we add a sidebar, which may again contain different, recurring structures
103
+ of content. Now we would need to create intermediate base templates for
104
+ all occurring combinations of content structure and sidebar structure...
105
+ and so on.
106
+
107
+ * The second solution involves duplication of common code with all its negative
108
+ consequences: any change involves finding and editing all affected copies
109
+ of the structure, correctness has to be verified for each copy, copies may
110
+ go out of sync by careless modifications etc.
111
+
112
+ In such a situation, the ``embed`` tag comes in handy. The common layout
113
+ code can live in a single base template, and the two different content structures,
114
+ let's call them "micro layouts" go into separate templates which are embedded
115
+ as necessary:
116
+
117
+ Page template ``foo.twig``:
118
+
119
+ .. code-block:: jinja
120
+
121
+ {% extends "layout_skeleton.twig" %}
122
+
123
+ {% block content %}
124
+ {% embed "vertical_boxes_skeleton.twig" %}
125
+ {% block top %}
126
+ Some content for the top box
127
+ {% endblock %}
128
+
129
+ {% block bottom %}
130
+ Some content for the bottom box
131
+ {% endblock %}
132
+ {% endembed %}
133
+ {% endblock %}
134
+
135
+ And here is the code for ``vertical_boxes_skeleton.twig``:
136
+
137
+ .. code-block:: html+jinja
138
+
139
+ <div class="top_box">
140
+ {% block top %}
141
+ Top box default content
142
+ {% endblock %}
143
+ </div>
144
+
145
+ <div class="bottom_box">
146
+ {% block bottom %}
147
+ Bottom box default content
148
+ {% endblock %}
149
+ </div>
150
+
151
+ The goal of the ``vertical_boxes_skeleton.twig`` template being to factor
152
+ out the HTML markup for the boxes.
153
+
154
+ The ``embed`` tag takes the exact same arguments as the ``include`` tag:
155
+
156
+ .. code-block:: jinja
157
+
158
+ {% embed "base" with {'foo': 'bar'} %}
159
+ ...
160
+ {% endembed %}
161
+
162
+ {% embed "base" with {'foo': 'bar'} only %}
163
+ ...
164
+ {% endembed %}
165
+
166
+ {% embed "base" ignore missing %}
167
+ ...
168
+ {% endembed %}
169
+
170
+ .. warning::
171
+
172
+ As embedded templates do not have "names", auto-escaping strategies based
173
+ on the template name won't work as expected if you change the context (for
174
+ instance, if you embed a CSS/JavaScript template into an HTML one). In that
175
+ case, explicitly set the default auto-escaping strategy with the
176
+ ``autoescape`` tag.
177
+
178
+ .. seealso:: :doc:`include<../tags/include>`
vendor/twig/twig/doc/tags/extends.rst ADDED
@@ -0,0 +1,272 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ``extends``
2
+ ===========
3
+
4
+ The ``extends`` tag can be used to extend a template from another one.
5
+
6
+ .. note::
7
+
8
+ Like PHP, Twig does not support multiple inheritance. So you can only have
9
+ one extends tag called per rendering. However, Twig supports horizontal
10
+ :doc:`reuse<use>`.
11
+
12
+ Let's define a base template, ``base.html``, which defines a simple HTML
13
+ skeleton document:
14
+
15
+ .. code-block:: html+jinja
16
+
17
+ <!DOCTYPE html>
18
+ <html>
19
+ <head>
20
+ {% block head %}
21
+ <link rel="stylesheet" href="style.css" />
22
+ <title>{% block title %}{% endblock %} - My Webpage</title>
23
+ {% endblock %}
24
+ </head>
25
+ <body>
26
+ <div id="content">{% block content %}{% endblock %}</div>
27
+ <div id="footer">
28
+ {% block footer %}
29
+ &copy; Copyright 2011 by <a href="http://domain.invalid/">you</a>.
30
+ {% endblock %}
31
+ </div>
32
+ </body>
33
+ </html>
34
+
35
+ In this example, the :doc:`block<block>` tags define four blocks that child
36
+ templates can fill in.
37
+
38
+ All the ``block`` tag does is to tell the template engine that a child
39
+ template may override those portions of the template.
40
+
41
+ Child Template
42
+ --------------
43
+
44
+ A child template might look like this:
45
+
46
+ .. code-block:: jinja
47
+
48
+ {% extends "base.html" %}
49
+
50
+ {% block title %}Index{% endblock %}
51
+ {% block head %}
52
+ {{ parent() }}
53
+ <style type="text/css">
54
+ .important { color: #336699; }
55
+ </style>
56
+ {% endblock %}
57
+ {% block content %}
58
+ <h1>Index</h1>
59
+ <p class="important">
60
+ Welcome on my awesome homepage.
61
+ </p>
62
+ {% endblock %}
63
+
64
+ The ``extends`` tag is the key here. It tells the template engine that this
65
+ template "extends" another template. When the template system evaluates this
66
+ template, first it locates the parent. The extends tag should be the first tag
67
+ in the template.
68
+
69
+ Note that since the child template doesn't define the ``footer`` block, the
70
+ value from the parent template is used instead.
71
+
72
+ You can't define multiple ``block`` tags with the same name in the same
73
+ template. This limitation exists because a block tag works in "both"
74
+ directions. That is, a block tag doesn't just provide a hole to fill - it also
75
+ defines the content that fills the hole in the *parent*. If there were two
76
+ similarly-named ``block`` tags in a template, that template's parent wouldn't
77
+ know which one of the blocks' content to use.
78
+
79
+ If you want to print a block multiple times you can however use the
80
+ ``block`` function:
81
+
82
+ .. code-block:: jinja
83
+
84
+ <title>{% block title %}{% endblock %}</title>
85
+ <h1>{{ block('title') }}</h1>
86
+ {% block body %}{% endblock %}
87
+
88
+ Parent Blocks
89
+ -------------
90
+
91
+ It's possible to render the contents of the parent block by using the
92
+ :doc:`parent<../functions/parent>` function. This gives back the results of
93
+ the parent block:
94
+
95
+ .. code-block:: jinja
96
+
97
+ {% block sidebar %}
98
+ <h3>Table Of Contents</h3>
99
+ ...
100
+ {{ parent() }}
101
+ {% endblock %}
102
+
103
+ Named Block End-Tags
104
+ --------------------
105
+
106
+ Twig allows you to put the name of the block after the end tag for better
107
+ readability:
108
+
109
+ .. code-block:: jinja
110
+
111
+ {% block sidebar %}
112
+ {% block inner_sidebar %}
113
+ ...
114
+ {% endblock inner_sidebar %}
115
+ {% endblock sidebar %}
116
+
117
+ Of course, the name after the ``endblock`` word must match the block name.
118
+
119
+ Block Nesting and Scope
120
+ -----------------------
121
+
122
+ Blocks can be nested for more complex layouts. Per default, blocks have access
123
+ to variables from outer scopes:
124
+
125
+ .. code-block:: jinja
126
+
127
+ {% for item in seq %}
128
+ <li>{% block loop_item %}{{ item }}{% endblock %}</li>
129
+ {% endfor %}
130
+
131
+ Block Shortcuts
132
+ ---------------
133
+
134
+ For blocks with little content, it's possible to use a shortcut syntax. The
135
+ following constructs do the same thing:
136
+
137
+ .. code-block:: jinja
138
+
139
+ {% block title %}
140
+ {{ page_title|title }}
141
+ {% endblock %}
142
+
143
+ .. code-block:: jinja
144
+
145
+ {% block title page_title|title %}
146
+
147
+ Dynamic Inheritance
148
+ -------------------
149
+
150
+ Twig supports dynamic inheritance by using a variable as the base template:
151
+
152
+ .. code-block:: jinja
153
+
154
+ {% extends some_var %}
155
+
156
+ If the variable evaluates to a ``Twig_Template`` or a ``Twig_TemplateWrapper``
157
+ instance, Twig will use it as the parent template::
158
+
159
+ // {% extends layout %}
160
+
161
+ // deprecated as of Twig 1.28
162
+ $layout = $twig->loadTemplate('some_layout_template.twig');
163
+
164
+ // as of Twig 1.28
165
+ $layout = $twig->load('some_layout_template.twig');
166
+
167
+ $twig->display('template.twig', array('layout' => $layout));
168
+
169
+ .. versionadded:: 1.2
170
+ The possibility to pass an array of templates has been added in Twig 1.2.
171
+
172
+ You can also provide a list of templates that are checked for existence. The
173
+ first template that exists will be used as a parent:
174
+
175
+ .. code-block:: jinja
176
+
177
+ {% extends ['layout.html', 'base_layout.html'] %}
178
+
179
+ Conditional Inheritance
180
+ -----------------------
181
+
182
+ As the template name for the parent can be any valid Twig expression, it's
183
+ possible to make the inheritance mechanism conditional:
184
+
185
+ .. code-block:: jinja
186
+
187
+ {% extends standalone ? "minimum.html" : "base.html" %}
188
+
189
+ In this example, the template will extend the "minimum.html" layout template
190
+ if the ``standalone`` variable evaluates to ``true``, and "base.html"
191
+ otherwise.
192
+
193
+ How do blocks work?
194
+ -------------------
195
+
196
+ A block provides a way to change how a certain part of a template is rendered
197
+ but it does not interfere in any way with the logic around it.
198
+
199
+ Let's take the following example to illustrate how a block works and more
200
+ importantly, how it does not work:
201
+
202
+ .. code-block:: jinja
203
+
204
+ {# base.twig #}
205
+
206
+ {% for post in posts %}
207
+ {% block post %}
208
+ <h1>{{ post.title }}</h1>
209
+ <p>{{ post.body }}</p>
210
+ {% endblock %}
211
+ {% endfor %}
212
+
213
+ If you render this template, the result would be exactly the same with or
214
+ without the ``block`` tag. The ``block`` inside the ``for`` loop is just a way
215
+ to make it overridable by a child template:
216
+
217
+ .. code-block:: jinja
218
+
219
+ {# child.twig #}
220
+
221
+ {% extends "base.twig" %}
222
+
223
+ {% block post %}
224
+ <article>
225
+ <header>{{ post.title }}</header>
226
+ <section>{{ post.text }}</section>
227
+ </article>
228
+ {% endblock %}
229
+
230
+ Now, when rendering the child template, the loop is going to use the block
231
+ defined in the child template instead of the one defined in the base one; the
232
+ executed template is then equivalent to the following one:
233
+
234
+ .. code-block:: jinja
235
+
236
+ {% for post in posts %}
237
+ <article>
238
+ <header>{{ post.title }}</header>
239
+ <section>{{ post.text }}</section>
240
+ </article>
241
+ {% endfor %}
242
+
243
+ Let's take another example: a block included within an ``if`` statement:
244
+
245
+ .. code-block:: jinja
246
+
247
+ {% if posts is empty %}
248
+ {% block head %}
249
+ {{ parent() }}
250
+
251
+ <meta name="robots" content="noindex, follow">
252
+ {% endblock head %}
253
+ {% endif %}
254
+
255
+ Contrary to what you might think, this template does not define a block
256
+ conditionally; it just makes overridable by a child template the output of
257
+ what will be rendered when the condition is ``true``.
258
+
259
+ If you want the output to be displayed conditionally, use the following
260
+ instead:
261
+
262
+ .. code-block:: jinja
263
+
264
+ {% block head %}
265
+ {{ parent() }}
266
+
267
+ {% if posts is empty %}
268
+ <meta name="robots" content="noindex, follow">
269
+ {% endif %}
270
+ {% endblock head %}
271
+
272
+ .. seealso:: :doc:`block<../functions/block>`, :doc:`block<../tags/block>`, :doc:`parent<../functions/parent>`, :doc:`use<../tags/use>`
vendor/twig/twig/doc/tags/filter.rst ADDED
@@ -0,0 +1,21 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ``filter``
2
+ ==========
3
+
4
+ Filter sections allow you to apply regular Twig filters on a block of template
5
+ data. Just wrap the code in the special ``filter`` section:
6
+
7
+ .. code-block:: jinja
8
+
9
+ {% filter upper %}
10
+ This text becomes uppercase
11
+ {% endfilter %}
12
+
13
+ You can also chain filters:
14
+
15
+ .. code-block:: jinja
16
+
17
+ {% filter lower|escape %}
18
+ <strong>SOME TEXT</strong>
19
+ {% endfilter %}
20
+
21
+ {# outputs "&lt;strong&gt;some text&lt;/strong&gt;" #}
vendor/twig/twig/doc/tags/flush.rst ADDED
@@ -0,0 +1,17 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ``flush``
2
+ =========
3
+
4
+ .. versionadded:: 1.5
5
+ The flush tag was added in Twig 1.5.
6
+
7
+ The ``flush`` tag tells Twig to flush the output buffer:
8
+
9
+ .. code-block:: jinja
10
+
11
+ {% flush %}
12
+
13
+ .. note::
14
+
15
+ Internally, Twig uses the PHP `flush`_ function.
16
+
17
+ .. _`flush`: http://php.net/flush
vendor/twig/twig/doc/tags/for.rst ADDED
@@ -0,0 +1,172 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ``for``
2
+ =======
3
+
4
+ Loop over each item in a sequence. For example, to display a list of users
5
+ provided in a variable called ``users``:
6
+
7
+ .. code-block:: jinja
8
+
9
+ <h1>Members</h1>
10
+ <ul>
11
+ {% for user in users %}
12
+ <li>{{ user.username|e }}</li>
13
+ {% endfor %}
14
+ </ul>
15
+
16
+ .. note::
17
+
18
+ A sequence can be either an array or an object implementing the
19
+ ``Traversable`` interface.
20
+
21
+ If you do need to iterate over a sequence of numbers, you can use the ``..``
22
+ operator:
23
+
24
+ .. code-block:: jinja
25
+
26
+ {% for i in 0..10 %}
27
+ * {{ i }}
28
+ {% endfor %}
29
+
30
+ The above snippet of code would print all numbers from 0 to 10.
31
+
32
+ It can be also useful with letters:
33
+
34
+ .. code-block:: jinja
35
+
36
+ {% for letter in 'a'..'z' %}
37
+ * {{ letter }}
38
+ {% endfor %}
39
+
40
+ The ``..`` operator can take any expression at both sides:
41
+
42
+ .. code-block:: jinja
43
+
44
+ {% for letter in 'a'|upper..'z'|upper %}
45
+ * {{ letter }}
46
+ {% endfor %}
47
+
48
+ .. tip:
49
+
50
+ If you need a step different from 1, you can use the ``range`` function
51
+ instead.
52
+
53
+ The `loop` variable
54
+ -------------------
55
+
56
+ Inside of a ``for`` loop block you can access some special variables:
57
+
58
+ ===================== =============================================================
59
+ Variable Description
60
+ ===================== =============================================================
61
+ ``loop.index`` The current iteration of the loop. (1 indexed)
62
+ ``loop.index0`` The current iteration of the loop. (0 indexed)
63
+ ``loop.revindex`` The number of iterations from the end of the loop (1 indexed)
64
+ ``loop.revindex0`` The number of iterations from the end of the loop (0 indexed)
65
+ ``loop.first`` True if first iteration
66
+ ``loop.last`` True if last iteration
67
+ ``loop.length`` The number of items in the sequence
68
+ ``loop.parent`` The parent context
69
+ ===================== =============================================================
70
+
71
+ .. code-block:: jinja
72
+
73
+ {% for user in users %}
74
+ {{ loop.index }} - {{ user.username }}
75
+ {% endfor %}
76
+
77
+ .. note::
78
+
79
+ The ``loop.length``, ``loop.revindex``, ``loop.revindex0``, and
80
+ ``loop.last`` variables are only available for PHP arrays, or objects that
81
+ implement the ``Countable`` interface. They are also not available when
82
+ looping with a condition.
83
+
84
+ .. versionadded:: 1.2
85
+ The ``if`` modifier support has been added in Twig 1.2.
86
+
87
+ Adding a condition
88
+ ------------------
89
+
90
+ Unlike in PHP, it's not possible to ``break`` or ``continue`` in a loop. You
91
+ can however filter the sequence during iteration which allows you to skip
92
+ items. The following example skips all the users which are not active:
93
+
94
+ .. code-block:: jinja
95
+
96
+ <ul>
97
+ {% for user in users if user.active %}
98
+ <li>{{ user.username|e }}</li>
99
+ {% endfor %}
100
+ </ul>
101
+
102
+ The advantage is that the special loop variable will count correctly thus not
103
+ counting the users not iterated over. Keep in mind that properties like
104
+ ``loop.last`` will not be defined when using loop conditions.
105
+
106
+ .. note::
107
+
108
+ Using the ``loop`` variable within the condition is not recommended as it
109
+ will probably not be doing what you expect it to. For instance, adding a
110
+ condition like ``loop.index > 4`` won't work as the index is only
111
+ incremented when the condition is true (so the condition will never
112
+ match).
113
+
114
+ The `else` Clause
115
+ -----------------
116
+
117
+ If no iteration took place because the sequence was empty, you can render a
118
+ replacement block by using ``else``:
119
+
120
+ .. code-block:: jinja
121
+
122
+ <ul>
123
+ {% for user in users %}
124
+ <li>{{ user.username|e }}</li>
125
+ {% else %}
126
+ <li><em>no user found</em></li>
127
+ {% endfor %}
128
+ </ul>
129
+
130
+ Iterating over Keys
131
+ -------------------
132
+
133
+ By default, a loop iterates over the values of the sequence. You can iterate
134
+ on keys by using the ``keys`` filter:
135
+
136
+ .. code-block:: jinja
137
+
138
+ <h1>Members</h1>
139
+ <ul>
140
+ {% for key in users|keys %}
141
+ <li>{{ key }}</li>
142
+ {% endfor %}
143
+ </ul>
144
+
145
+ Iterating over Keys and Values
146
+ ------------------------------
147
+
148
+ You can also access both keys and values:
149
+
150
+ .. code-block:: jinja
151
+
152
+ <h1>Members</h1>
153
+ <ul>
154
+ {% for key, user in users %}
155
+ <li>{{ key }}: {{ user.username|e }}</li>
156
+ {% endfor %}
157
+ </ul>
158
+
159
+ Iterating over a Subset
160
+ -----------------------
161
+
162
+ You might want to iterate over a subset of values. This can be achieved using
163
+ the :doc:`slice <../filters/slice>` filter:
164
+
165
+ .. code-block:: jinja
166
+
167
+ <h1>Top Ten Members</h1>
168
+ <ul>
169
+ {% for user in users|slice(0, 10) %}
170
+ <li>{{ user.username|e }}</li>
171
+ {% endfor %}
172
+ </ul>
vendor/twig/twig/doc/tags/from.rst ADDED
@@ -0,0 +1,8 @@
 
 
 
 
 
 
 
 
1
+ ``from``
2
+ ========
3
+
4
+ The ``from`` tag imports :doc:`macro<../tags/macro>` names into the current
5
+ namespace. The tag is documented in detail in the documentation for the
6
+ :doc:`import<../tags/import>` tag.
7
+
8
+ .. seealso:: :doc:`macro<../tags/macro>`, :doc:`import<../tags/import>`
vendor/twig/twig/doc/tags/if.rst ADDED
@@ -0,0 +1,76 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ``if``
2
+ ======
3
+
4
+ The ``if`` statement in Twig is comparable with the if statements of PHP.
5
+
6
+ In the simplest form you can use it to test if an expression evaluates to
7
+ ``true``:
8
+
9
+ .. code-block:: jinja
10
+
11
+ {% if online == false %}
12
+ <p>Our website is in maintenance mode. Please, come back later.</p>
13
+ {% endif %}
14
+
15
+ You can also test if an array is not empty:
16
+
17
+ .. code-block:: jinja
18
+
19
+ {% if users %}
20
+ <ul>
21
+ {% for user in users %}
22
+ <li>{{ user.username|e }}</li>
23
+ {% endfor %}
24
+ </ul>
25
+ {% endif %}
26
+
27
+ .. note::
28
+
29
+ If you want to test if the variable is defined, use ``if users is
30
+ defined`` instead.
31
+
32
+ You can also use ``not`` to check for values that evaluate to ``false``:
33
+
34
+ .. code-block:: jinja
35
+
36
+ {% if not user.subscribed %}
37
+ <p>You are not subscribed to our mailing list.</p>
38
+ {% endif %}
39
+
40
+ For multiple conditions, ``and`` and ``or`` can be used:
41
+
42
+ .. code-block:: jinja
43
+
44
+ {% if temperature > 18 and temperature < 27 %}
45
+ <p>It's a nice day for a walk in the park.</p>
46
+ {% endif %}
47
+
48
+ For multiple branches ``elseif`` and ``else`` can be used like in PHP. You can
49
+ use more complex ``expressions`` there too:
50
+
51
+ .. code-block:: jinja
52
+
53
+ {% if kenny.sick %}
54
+ Kenny is sick.
55
+ {% elseif kenny.dead %}
56
+ You killed Kenny! You bastard!!!
57
+ {% else %}
58
+ Kenny looks okay --- so far
59
+ {% endif %}
60
+
61
+ .. note::
62
+
63
+ The rules to determine if an expression is ``true`` or ``false`` are the
64
+ same as in PHP; here are the edge cases rules:
65
+
66
+ ====================== ====================
67
+ Value Boolean evaluation
68
+ ====================== ====================
69
+ empty string false
70
+ numeric zero false
71
+ whitespace-only string true
72
+ empty array false
73
+ null false
74
+ non-empty array true
75
+ object true
76
+ ====================== ====================
vendor/twig/twig/doc/tags/import.rst ADDED
@@ -0,0 +1,57 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ``import``
2
+ ==========
3
+
4
+ Twig supports putting often used code into :doc:`macros<../tags/macro>`. These
5
+ macros can go into different templates and get imported from there.
6
+
7
+ There are two ways to import templates. You can import the complete template
8
+ into a variable or request specific macros from it.
9
+
10
+ Imagine we have a helper module that renders forms (called ``forms.html``):
11
+
12
+ .. code-block:: jinja
13
+
14
+ {% macro input(name, value, type, size) %}
15
+ <input type="{{ type|default('text') }}" name="{{ name }}" value="{{ value|e }}" size="{{ size|default(20) }}" />
16
+ {% endmacro %}
17
+
18
+ {% macro textarea(name, value, rows, cols) %}
19
+ <textarea name="{{ name }}" rows="{{ rows|default(10) }}" cols="{{ cols|default(40) }}">{{ value|e }}</textarea>
20
+ {% endmacro %}
21
+
22
+ The easiest and most flexible is importing the whole module into a variable.
23
+ That way you can access the attributes:
24
+
25
+ .. code-block:: jinja
26
+
27
+ {% import 'forms.html' as forms %}
28
+
29
+ <dl>
30
+ <dt>Username</dt>
31
+ <dd>{{ forms.input('username') }}</dd>
32
+ <dt>Password</dt>
33
+ <dd>{{ forms.input('password', null, 'password') }}</dd>
34
+ </dl>
35
+ <p>{{ forms.textarea('comment') }}</p>
36
+
37
+ Alternatively you can import names from the template into the current
38
+ namespace:
39
+
40
+ .. code-block:: jinja
41
+
42
+ {% from 'forms.html' import input as input_field, textarea %}
43
+
44
+ <dl>
45
+ <dt>Username</dt>
46
+ <dd>{{ input_field('username') }}</dd>
47
+ <dt>Password</dt>
48
+ <dd>{{ input_field('password', '', 'password') }}</dd>
49
+ </dl>
50
+ <p>{{ textarea('comment') }}</p>
51
+
52
+ .. tip::
53
+
54
+ To import macros from the current file, use the special ``_self`` variable
55
+ for the source.
56
+
57
+ .. seealso:: :doc:`macro<../tags/macro>`, :doc:`from<../tags/from>`
vendor/twig/twig/doc/tags/include.rst ADDED
@@ -0,0 +1,90 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ``include``
2
+ ===========
3
+
4
+ The ``include`` statement includes a template and returns the rendered content
5
+ of that file into the current namespace:
6
+
7
+ .. code-block:: jinja
8
+
9
+ {% include 'header.html' %}
10
+ Body
11
+ {% include 'footer.html' %}
12
+
13
+ Included templates have access to the variables of the active context.
14
+
15
+ If you are using the filesystem loader, the templates are looked for in the
16
+ paths defined by it.
17
+
18
+ You can add additional variables by passing them after the ``with`` keyword:
19
+
20
+ .. code-block:: jinja
21
+
22
+ {# template.html will have access to the variables from the current context and the additional ones provided #}
23
+ {% include 'template.html' with {'foo': 'bar'} %}
24
+
25
+ {% set vars = {'foo': 'bar'} %}
26
+ {% include 'template.html' with vars %}
27
+
28
+ You can disable access to the context by appending the ``only`` keyword:
29
+
30
+ .. code-block:: jinja
31
+
32
+ {# only the foo variable will be accessible #}
33
+ {% include 'template.html' with {'foo': 'bar'} only %}
34
+
35
+ .. code-block:: jinja
36
+
37
+ {# no variables will be accessible #}
38
+ {% include 'template.html' only %}
39
+
40
+ .. tip::
41
+
42
+ When including a template created by an end user, you should consider
43
+ sandboxing it. More information in the :doc:`Twig for Developers<../api>`
44
+ chapter and in the :doc:`sandbox<../tags/sandbox>` tag documentation.
45
+
46
+ The template name can be any valid Twig expression:
47
+
48
+ .. code-block:: jinja
49
+
50
+ {% include some_var %}
51
+ {% include ajax ? 'ajax.html' : 'not_ajax.html' %}
52
+
53
+ And if the expression evaluates to a ``Twig_Template`` or a
54
+ ``Twig_TemplateWrapper`` instance, Twig will use it directly::
55
+
56
+ // {% include template %}
57
+
58
+ // deprecated as of Twig 1.28
59
+ $template = $twig->loadTemplate('some_template.twig');
60
+
61
+ // as of Twig 1.28
62
+ $template = $twig->load('some_template.twig');
63
+
64
+ $twig->display('template.twig', array('template' => $template));
65
+
66
+ .. versionadded:: 1.2
67
+ The ``ignore missing`` feature has been added in Twig 1.2.
68
+
69
+ You can mark an include with ``ignore missing`` in which case Twig will ignore
70
+ the statement if the template to be included does not exist. It has to be
71
+ placed just after the template name. Here some valid examples:
72
+
73
+ .. code-block:: jinja
74
+
75
+ {% include 'sidebar.html' ignore missing %}
76
+ {% include 'sidebar.html' ignore missing with {'foo': 'bar'} %}
77
+ {% include 'sidebar.html' ignore missing only %}
78
+
79
+ .. versionadded:: 1.2
80
+ The possibility to pass an array of templates has been added in Twig 1.2.
81
+
82
+ You can also provide a list of templates that are checked for existence before
83
+ inclusion. The first template that exists will be included:
84
+
85
+ .. code-block:: jinja
86
+
87
+ {% include ['page_detailed.html', 'page.html'] %}
88
+
89
+ If ``ignore missing`` is given, it will fall back to rendering nothing if none
90
+ of the templates exist, otherwise it will throw an exception.
vendor/twig/twig/doc/tags/index.rst ADDED
@@ -0,0 +1,25 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ Tags
2
+ ====
3
+
4
+ .. toctree::
5
+ :maxdepth: 1
6
+
7
+ autoescape
8
+ block
9
+ do
10
+ embed
11
+ extends
12
+ filter
13
+ flush
14
+ for
15
+ from
16
+ if
17
+ import
18
+ include
19
+ macro
20
+ sandbox
21
+ set
22
+ spaceless
23
+ use
24
+ verbatim
25
+ with
vendor/twig/twig/doc/tags/macro.rst ADDED
@@ -0,0 +1,103 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ``macro``
2
+ =========
3
+
4
+ Macros are comparable with functions in regular programming languages. They
5
+ are useful to put often used HTML idioms into reusable elements to not repeat
6
+ yourself.
7
+
8
+ Here is a small example of a macro that renders a form element:
9
+
10
+ .. code-block:: jinja
11
+
12
+ {% macro input(name, value, type, size) %}
13
+ <input type="{{ type|default('text') }}" name="{{ name }}" value="{{ value|e }}" size="{{ size|default(20) }}" />
14
+ {% endmacro %}
15
+
16
+ Macros differ from native PHP functions in a few ways:
17
+
18
+ * Default argument values are defined by using the ``default`` filter in the
19
+ macro body;
20
+
21
+ * Arguments of a macro are always optional.
22
+
23
+ * If extra positional arguments are passed to a macro, they end up in the
24
+ special ``varargs`` variable as a list of values.
25
+
26
+ But as with PHP functions, macros don't have access to the current template
27
+ variables.
28
+
29
+ .. tip::
30
+
31
+ You can pass the whole context as an argument by using the special
32
+ ``_context`` variable.
33
+
34
+ Import
35
+ ------
36
+
37
+ Macros can be defined in any template, and need to be "imported" before being
38
+ used (see the documentation for the :doc:`import<../tags/import>` tag for more
39
+ information):
40
+
41
+ .. code-block:: jinja
42
+
43
+ {% import "forms.html" as forms %}
44
+
45
+ The above ``import`` call imports the "forms.html" file (which can contain only
46
+ macros, or a template and some macros), and import the functions as items of
47
+ the ``forms`` variable.
48
+
49
+ The macro can then be called at will:
50
+
51
+ .. code-block:: jinja
52
+
53
+ <p>{{ forms.input('username') }}</p>
54
+ <p>{{ forms.input('password', null, 'password') }}</p>
55
+
56
+ If macros are defined and used in the same template, you can use the
57
+ special ``_self`` variable to import them:
58
+
59
+ .. code-block:: jinja
60
+
61
+ {% import _self as forms %}
62
+
63
+ <p>{{ forms.input('username') }}</p>
64
+
65
+ .. warning::
66
+
67
+ When you define a macro in the template where you are going to use it, you
68
+ might be tempted to call the macro directly via ``_self.input()`` instead
69
+ of importing it; even if seems to work, this is just a side-effect of the
70
+ current implementation and it won't work anymore in Twig 2.x.
71
+
72
+ When you want to use a macro in another macro from the same file, you need to
73
+ import it locally:
74
+
75
+ .. code-block:: jinja
76
+
77
+ {% macro input(name, value, type, size) %}
78
+ <input type="{{ type|default('text') }}" name="{{ name }}" value="{{ value|e }}" size="{{ size|default(20) }}" />
79
+ {% endmacro %}
80
+
81
+ {% macro wrapped_input(name, value, type, size) %}
82
+ {% import _self as forms %}
83
+
84
+ <div class="field">
85
+ {{ forms.input(name, value, type, size) }}
86
+ </div>
87
+ {% endmacro %}
88
+
89
+ Named Macro End-Tags
90
+ --------------------
91
+
92
+ Twig allows you to put the name of the macro after the end tag for better
93
+ readability:
94
+
95
+ .. code-block:: jinja
96
+
97
+ {% macro input() %}
98
+ ...
99
+ {% endmacro input %}
100
+
101
+ Of course, the name after the ``endmacro`` word must match the macro name.
102
+
103
+ .. seealso:: :doc:`from<../tags/from>`, :doc:`import<../tags/import>`
vendor/twig/twig/doc/tags/sandbox.rst ADDED
@@ -0,0 +1,30 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ``sandbox``
2
+ ===========
3
+
4
+ The ``sandbox`` tag can be used to enable the sandboxing mode for an included
5
+ template, when sandboxing is not enabled globally for the Twig environment:
6
+
7
+ .. code-block:: jinja
8
+
9
+ {% sandbox %}
10
+ {% include 'user.html' %}
11
+ {% endsandbox %}
12
+
13
+ .. warning::
14
+
15
+ The ``sandbox`` tag is only available when the sandbox extension is
16
+ enabled (see the :doc:`Twig for Developers<../api>` chapter).
17
+
18
+ .. note::
19
+
20
+ The ``sandbox`` tag can only be used to sandbox an include tag and it
21
+ cannot be used to sandbox a section of a template. The following example
22
+ won't work:
23
+
24
+ .. code-block:: jinja
25
+
26
+ {% sandbox %}
27
+ {% for i in 1..2 %}
28
+ {{ i }}
29
+ {% endfor %}
30
+ {% endsandbox %}
vendor/twig/twig/doc/tags/set.rst ADDED
@@ -0,0 +1,78 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ``set``
2
+ =======
3
+
4
+ Inside code blocks you can also assign values to variables. Assignments use
5
+ the ``set`` tag and can have multiple targets.
6
+
7
+ Here is how you can assign the ``bar`` value to the ``foo`` variable:
8
+
9
+ .. code-block:: jinja
10
+
11
+ {% set foo = 'bar' %}
12
+
13
+ After the ``set`` call, the ``foo`` variable is available in the template like
14
+ any other ones:
15
+
16
+ .. code-block:: jinja
17
+
18
+ {# displays bar #}
19
+ {{ foo }}
20
+
21
+ The assigned value can be any valid :ref:`Twig expressions
22
+ <twig-expressions>`:
23
+
24
+ .. code-block:: jinja
25
+
26
+ {% set foo = [1, 2] %}
27
+ {% set foo = {'foo': 'bar'} %}
28
+ {% set foo = 'foo' ~ 'bar' %}
29
+
30
+ Several variables can be assigned in one block:
31
+
32
+ .. code-block:: jinja
33
+
34
+ {% set foo, bar = 'foo', 'bar' %}
35
+
36
+ {# is equivalent to #}
37
+
38
+ {% set foo = 'foo' %}
39
+ {% set bar = 'bar' %}
40
+
41
+ The ``set`` tag can also be used to 'capture' chunks of text:
42
+
43
+ .. code-block:: jinja
44
+
45
+ {% set foo %}
46
+ <div id="pagination">
47
+ ...
48
+ </div>
49
+ {% endset %}
50
+
51
+ .. caution::
52
+
53
+ If you enable automatic output escaping, Twig will only consider the
54
+ content to be safe when capturing chunks of text.
55
+
56
+ .. note::
57
+
58
+ Note that loops are scoped in Twig; therefore a variable declared inside a
59
+ ``for`` loop is not accessible outside the loop itself:
60
+
61
+ .. code-block:: jinja
62
+
63
+ {% for item in list %}
64
+ {% set foo = item %}
65
+ {% endfor %}
66
+
67
+ {# foo is NOT available #}
68
+
69
+ If you want to access the variable, just declare it before the loop:
70
+
71
+ .. code-block:: jinja
72
+
73
+ {% set foo = "" %}
74
+ {% for item in list %}
75
+ {% set foo = item %}
76
+ {% endfor %}
77
+
78
+ {# foo is available #}
vendor/twig/twig/doc/tags/spaceless.rst ADDED
@@ -0,0 +1,37 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ``spaceless``
2
+ =============
3
+
4
+ Use the ``spaceless`` tag to remove whitespace *between HTML tags*, not
5
+ whitespace within HTML tags or whitespace in plain text:
6
+
7
+ .. code-block:: jinja
8
+
9
+ {% spaceless %}
10
+ <div>
11
+ <strong>foo</strong>
12
+ </div>
13
+ {% endspaceless %}
14
+
15
+ {# output will be <div><strong>foo</strong></div> #}
16
+
17
+ This tag is not meant to "optimize" the size of the generated HTML content but
18
+ merely to avoid extra whitespace between HTML tags to avoid browser rendering
19
+ quirks under some circumstances.
20
+
21
+ .. tip::
22
+
23
+ If you want to optimize the size of the generated HTML content, gzip
24
+ compress the output instead.
25
+
26
+ .. tip::
27
+
28
+ If you want to create a tag that actually removes all extra whitespace in
29
+ an HTML string, be warned that this is not as easy as it seems to be
30
+ (think of ``textarea`` or ``pre`` tags for instance). Using a third-party
31
+ library like Tidy is probably a better idea.
32
+
33
+ .. tip::
34
+
35
+ For more information on whitespace control, read the
36
+ :ref:`dedicated section <templates-whitespace-control>` of the documentation and learn how
37
+ you can also use the whitespace control modifier on your tags.
vendor/twig/twig/doc/tags/use.rst ADDED
@@ -0,0 +1,124 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ``use``
2
+ =======
3
+
4
+ .. versionadded:: 1.1
5
+ Horizontal reuse was added in Twig 1.1.
6
+
7
+ .. note::
8
+
9
+ Horizontal reuse is an advanced Twig feature that is hardly ever needed in
10
+ regular templates. It is mainly used by projects that need to make
11
+ template blocks reusable without using inheritance.
12
+
13
+ Template inheritance is one of the most powerful features of Twig but it is
14
+ limited to single inheritance; a template can only extend one other template.
15
+ This limitation makes template inheritance simple to understand and easy to
16
+ debug:
17
+
18
+ .. code-block:: jinja
19
+
20
+ {% extends "base.html" %}
21
+
22
+ {% block title %}{% endblock %}
23
+ {% block content %}{% endblock %}
24
+
25
+ Horizontal reuse is a way to achieve the same goal as multiple inheritance,
26
+ but without the associated complexity:
27
+
28
+ .. code-block:: jinja
29
+
30
+ {% extends "base.html" %}
31
+
32
+ {% use "blocks.html" %}
33
+
34
+ {% block title %}{% endblock %}
35
+ {% block content %}{% endblock %}
36
+
37
+ The ``use`` statement tells Twig to import the blocks defined in
38
+ ``blocks.html`` into the current template (it's like macros, but for blocks):
39
+
40
+ .. code-block:: jinja
41
+
42
+ {# blocks.html #}
43
+
44
+ {% block sidebar %}{% endblock %}
45
+
46
+ In this example, the ``use`` statement imports the ``sidebar`` block into the
47
+ main template. The code is mostly equivalent to the following one (the
48
+ imported blocks are not outputted automatically):
49
+
50
+ .. code-block:: jinja
51
+
52
+ {% extends "base.html" %}
53
+
54
+ {% block sidebar %}{% endblock %}
55
+ {% block title %}{% endblock %}
56
+ {% block content %}{% endblock %}
57
+
58
+ .. note::
59
+
60
+ The ``use`` tag only imports a template if it does not extend another
61
+ template, if it does not define macros, and if the body is empty. But it
62
+ can *use* other templates.
63
+
64
+ .. note::
65
+
66
+ Because ``use`` statements are resolved independently of the context
67
+ passed to the template, the template reference cannot be an expression.
68
+
69
+ The main template can also override any imported block. If the template
70
+ already defines the ``sidebar`` block, then the one defined in ``blocks.html``
71
+ is ignored. To avoid name conflicts, you can rename imported blocks:
72
+
73
+ .. code-block:: jinja
74
+
75
+ {% extends "base.html" %}
76
+
77
+ {% use "blocks.html" with sidebar as base_sidebar, title as base_title %}
78
+
79
+ {% block sidebar %}{% endblock %}
80
+ {% block title %}{% endblock %}
81
+ {% block content %}{% endblock %}
82
+
83
+ .. versionadded:: 1.3
84
+ The ``parent()`` support was added in Twig 1.3.
85
+
86
+ The ``parent()`` function automatically determines the correct inheritance
87
+ tree, so it can be used when overriding a block defined in an imported
88
+ template:
89
+
90
+ .. code-block:: jinja
91
+
92
+ {% extends "base.html" %}
93
+
94
+ {% use "blocks.html" %}
95
+
96
+ {% block sidebar %}
97
+ {{ parent() }}
98
+ {% endblock %}
99
+
100
+ {% block title %}{% endblock %}
101
+ {% block content %}{% endblock %}
102
+
103
+ In this example, ``parent()`` will correctly call the ``sidebar`` block from
104
+ the ``blocks.html`` template.
105
+
106
+ .. tip::
107
+
108
+ In Twig 1.2, renaming allows you to simulate inheritance by calling the
109
+ "parent" block:
110
+
111
+ .. code-block:: jinja
112
+
113
+ {% extends "base.html" %}
114
+
115
+ {% use "blocks.html" with sidebar as parent_sidebar %}
116
+
117
+ {% block sidebar %}
118
+ {{ block('parent_sidebar') }}
119
+ {% endblock %}
120
+
121
+ .. note::
122
+
123
+ You can use as many ``use`` statements as you want in any given template.
124
+ If two imported templates define the same block, the latest one wins.
vendor/twig/twig/doc/tags/verbatim.rst ADDED
@@ -0,0 +1,24 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ``verbatim``
2
+ ============
3
+
4
+ .. versionadded:: 1.12
5
+ The ``verbatim`` tag was added in Twig 1.12 (it was named ``raw`` before).
6
+
7
+ The ``verbatim`` tag marks sections as being raw text that should not be
8
+ parsed. For example to put Twig syntax as example into a template you can use
9
+ this snippet:
10
+
11
+ .. code-block:: jinja
12
+
13
+ {% verbatim %}
14
+ <ul>
15
+ {% for item in seq %}
16
+ <li>{{ item }}</li>
17
+ {% endfor %}
18
+ </ul>
19
+ {% endverbatim %}
20
+
21
+ .. note::
22
+
23
+ The ``verbatim`` tag works in the exact same way as the old ``raw`` tag,
24
+ but was renamed to avoid confusion with the ``raw`` filter.
vendor/twig/twig/doc/tags/with.rst ADDED
@@ -0,0 +1,44 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ``with``
2
+ ========
3
+
4
+ .. versionadded:: 1.28
5
+ The ``with`` tag was added in Twig 1.28.
6
+
7
+ Use the ``with`` tag to create a new inner scope. Variables set within this
8
+ scope are not visible outside of the scope:
9
+
10
+ .. code-block:: jinja
11
+
12
+ {% with %}
13
+ {% set foo = 42 %}
14
+ {{ foo }} foo is 42 here
15
+ {% endwith %}
16
+ foo is not visible here any longer
17
+
18
+ Instead of defining variables at the beginning of the scope, you can pass a
19
+ hash of variables you want to define in the ``with`` tag; the previous example
20
+ is equivalent to the following one:
21
+
22
+ .. code-block:: jinja
23
+
24
+ {% with { foo: 42 } %}
25
+ {{ foo }} foo is 42 here
26
+ {% endwith %}
27
+ foo is not visible here any longer
28
+
29
+ {# it works with any expression that resolves to a hash #}
30
+ {% set vars = { foo: 42 } %}
31
+ {% with vars %}
32
+ ...
33
+ {% endwith %}
34
+
35
+ By default, the inner scope has access to the outer scope context; you can
36
+ disable this behavior by appending the ``only`` keyword:
37
+
38
+ .. code-block:: jinja
39
+
40
+ {% set bar = 'bar' %}
41
+ {% with { foo: 42 } only %}
42
+ {# only foo is defined #}
43
+ {# bar is not defined #}
44
+ {% endwith %}
vendor/twig/twig/doc/templates.rst ADDED
@@ -0,0 +1,908 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ Twig for Template Designers
2
+ ===========================
3
+
4
+ This document describes the syntax and semantics of the template engine and
5
+ will be most useful as reference to those creating Twig templates.
6
+
7
+ Synopsis
8
+ --------
9
+
10
+ A template is simply a text file. It can generate any text-based format (HTML,
11
+ XML, CSV, LaTeX, etc.). It doesn't have a specific extension, ``.html`` or
12
+ ``.xml`` are just fine.
13
+
14
+ A template contains **variables** or **expressions**, which get replaced with
15
+ values when the template is evaluated, and **tags**, which control the logic
16
+ of the template.
17
+
18
+ Below is a minimal template that illustrates a few basics. We will cover further
19
+ details later on:
20
+
21
+ .. code-block:: html+jinja
22
+
23
+ <!DOCTYPE html>
24
+ <html>
25
+ <head>
26
+ <title>My Webpage</title>
27
+ </head>
28
+ <body>
29
+ <ul id="navigation">
30
+ {% for item in navigation %}
31
+ <li><a href="{{ item.href }}">{{ item.caption }}</a></li>
32
+ {% endfor %}
33
+ </ul>
34
+
35
+ <h1>My Webpage</h1>
36
+ {{ a_variable }}
37
+ </body>
38
+ </html>
39
+
40
+ There are two kinds of delimiters: ``{% ... %}`` and ``{{ ... }}``. The first
41
+ one is used to execute statements such as for-loops, the latter prints the
42
+ result of an expression to the template.
43
+
44
+ IDEs Integration
45
+ ----------------
46
+
47
+ Many IDEs support syntax highlighting and auto-completion for Twig:
48
+
49
+ * *Textmate* via the `Twig bundle`_
50
+ * *Vim* via the `Jinja syntax plugin`_ or the `vim-twig plugin`_
51
+ * *Netbeans* via the `Twig syntax plugin`_ (until 7.1, native as of 7.2)
52
+ * *PhpStorm* (native as of 2.1)
53
+ * *Eclipse* via the `Twig plugin`_
54
+ * *Sublime Text* via the `Twig bundle`_
55
+ * *GtkSourceView* via the `Twig language definition`_ (used by gedit and other projects)
56
+ * *Coda* and *SubEthaEdit* via the `Twig syntax mode`_
57
+ * *Coda 2* via the `other Twig syntax mode`_
58
+ * *Komodo* and *Komodo Edit* via the Twig highlight/syntax check mode
59
+ * *Notepad++* via the `Notepad++ Twig Highlighter`_
60
+ * *Emacs* via `web-mode.el`_
61
+ * *Atom* via the `PHP-twig for atom`_
62
+ * *Visual Studio Code* via the `Twig pack`_
63
+
64
+ Also, `TwigFiddle`_ is an online service that allows you to execute Twig templates
65
+ from a browser; it supports all versions of Twig.
66
+
67
+ Variables
68
+ ---------
69
+
70
+ The application passes variables to the templates for manipulation in the
71
+ template. Variables may have attributes or elements you can access,
72
+ too. The visual representation of a variable depends heavily on the application providing
73
+ it.
74
+
75
+ You can use a dot (``.``) to access attributes of a variable (methods or
76
+ properties of a PHP object, or items of a PHP array), or the so-called
77
+ "subscript" syntax (``[]``):
78
+
79
+ .. code-block:: jinja
80
+
81
+ {{ foo.bar }}
82
+ {{ foo['bar'] }}
83
+
84
+ When the attribute contains special characters (like ``-`` that would be
85
+ interpreted as the minus operator), use the ``attribute`` function instead to
86
+ access the variable attribute:
87
+
88
+ .. code-block:: jinja
89
+
90
+ {# equivalent to the non-working foo.data-foo #}
91
+ {{ attribute(foo, 'data-foo') }}
92
+
93
+ .. note::
94
+
95
+ It's important to know that the curly braces are *not* part of the
96
+ variable but the print statement. When accessing variables inside tags,
97
+ don't put the braces around them.
98
+
99
+ If a variable or attribute does not exist, you will receive a ``null`` value
100
+ when the ``strict_variables`` option is set to ``false``; alternatively, if ``strict_variables``
101
+ is set, Twig will throw an error (see :ref:`environment options<environment_options>`).
102
+
103
+ .. sidebar:: Implementation
104
+
105
+ For convenience's sake ``foo.bar`` does the following things on the PHP
106
+ layer:
107
+
108
+ * check if ``foo`` is an array and ``bar`` a valid element;
109
+ * if not, and if ``foo`` is an object, check that ``bar`` is a valid property;
110
+ * if not, and if ``foo`` is an object, check that ``bar`` is a valid method
111
+ (even if ``bar`` is the constructor - use ``__construct()`` instead);
112
+ * if not, and if ``foo`` is an object, check that ``getBar`` is a valid method;
113
+ * if not, and if ``foo`` is an object, check that ``isBar`` is a valid method;
114
+ * if not, return a ``null`` value.
115
+
116
+ ``foo['bar']`` on the other hand only works with PHP arrays:
117
+
118
+ * check if ``foo`` is an array and ``bar`` a valid element;
119
+ * if not, return a ``null`` value.
120
+
121
+ .. note::
122
+
123
+ If you want to access a dynamic attribute of a variable, use the
124
+ :doc:`attribute<functions/attribute>` function instead.
125
+
126
+ Global Variables
127
+ ~~~~~~~~~~~~~~~~
128
+
129
+ The following variables are always available in templates:
130
+
131
+ * ``_self``: references the current template;
132
+ * ``_context``: references the current context;
133
+ * ``_charset``: references the current charset.
134
+
135
+ Setting Variables
136
+ ~~~~~~~~~~~~~~~~~
137
+
138
+ You can assign values to variables inside code blocks. Assignments use the
139
+ :doc:`set<tags/set>` tag:
140
+
141
+ .. code-block:: jinja
142
+
143
+ {% set foo = 'foo' %}
144
+ {% set foo = [1, 2] %}
145
+ {% set foo = {'foo': 'bar'} %}
146
+
147
+ Filters
148
+ -------
149
+
150
+ Variables can be modified by **filters**. Filters are separated from the
151
+ variable by a pipe symbol (``|``) and may have optional arguments in
152
+ parentheses. Multiple filters can be chained. The output of one filter is
153
+ applied to the next.
154
+
155
+ The following example removes all HTML tags from the ``name`` and title-cases
156
+ it:
157
+
158
+ .. code-block:: jinja
159
+
160
+ {{ name|striptags|title }}
161
+
162
+ Filters that accept arguments have parentheses around the arguments. This
163
+ example will join a list by commas:
164
+
165
+ .. code-block:: jinja
166
+
167
+ {{ list|join(', ') }}
168
+
169
+ To apply a filter on a section of code, wrap it in the
170
+ :doc:`filter<tags/filter>` tag:
171
+
172
+ .. code-block:: jinja
173
+
174
+ {% filter upper %}
175
+ This text becomes uppercase
176
+ {% endfilter %}
177
+
178
+ Go to the :doc:`filters<filters/index>` page to learn more about built-in
179
+ filters.
180
+
181
+ Functions
182
+ ---------
183
+
184
+ Functions can be called to generate content. Functions are called by their
185
+ name followed by parentheses (``()``) and may have arguments.
186
+
187
+ For instance, the ``range`` function returns a list containing an arithmetic
188
+ progression of integers:
189
+
190
+ .. code-block:: jinja
191
+
192
+ {% for i in range(0, 3) %}
193
+ {{ i }},
194
+ {% endfor %}
195
+
196
+ Go to the :doc:`functions<functions/index>` page to learn more about the
197
+ built-in functions.
198
+
199
+ Named Arguments
200
+ ---------------
201
+
202
+ .. versionadded:: 1.12
203
+ Support for named arguments was added in Twig 1.12.
204
+
205
+ .. code-block:: jinja
206
+
207
+ {% for i in range(low=1, high=10, step=2) %}
208
+ {{ i }},
209
+ {% endfor %}
210
+
211
+ Using named arguments makes your templates more explicit about the meaning of
212
+ the values you pass as arguments:
213
+
214
+ .. code-block:: jinja
215
+
216
+ {{ data|convert_encoding('UTF-8', 'iso-2022-jp') }}
217
+
218
+ {# versus #}
219
+
220
+ {{ data|convert_encoding(from='iso-2022-jp', to='UTF-8') }}
221
+
222
+ Named arguments also allow you to skip some arguments for which you don't want
223
+ to change the default value:
224
+
225
+ .. code-block:: jinja
226
+
227
+ {# the first argument is the date format, which defaults to the global date format if null is passed #}
228
+ {{ "now"|date(null, "Europe/Paris") }}
229
+
230
+ {# or skip the format value by using a named argument for the time zone #}
231
+ {{ "now"|date(timezone="Europe/Paris") }}
232
+
233
+ You can also use both positional and named arguments in one call, in which
234
+ case positional arguments must always come before named arguments:
235
+
236
+ .. code-block:: jinja
237
+
238
+ {{ "now"|date('d/m/Y H:i', timezone="Europe/Paris") }}
239
+
240
+ .. tip::
241
+
242
+ Each function and filter documentation page has a section where the names
243
+ of all arguments are listed when supported.
244
+
245
+ Control Structure
246
+ -----------------
247
+
248
+ A control structure refers to all those things that control the flow of a
249
+ program - conditionals (i.e. ``if``/``elseif``/``else``), ``for``-loops, as
250
+ well as things like blocks. Control structures appear inside ``{% ... %}``
251
+ blocks.
252
+
253
+ For example, to display a list of users provided in a variable called
254
+ ``users``, use the :doc:`for<tags/for>` tag:
255
+
256
+ .. code-block:: jinja
257
+
258
+ <h1>Members</h1>
259
+ <ul>
260
+ {% for user in users %}
261
+ <li>{{ user.username|e }}</li>
262
+ {% endfor %}
263
+ </ul>
264
+
265
+ The :doc:`if<tags/if>` tag can be used to test an expression:
266
+
267
+ .. code-block:: jinja
268
+
269
+ {% if users|length > 0 %}
270
+ <ul>
271
+ {% for user in users %}
272
+ <li>{{ user.username|e }}</li>
273
+ {% endfor %}
274
+ </ul>
275
+ {% endif %}
276
+
277
+ Go to the :doc:`tags<tags/index>` page to learn more about the built-in tags.
278
+
279
+ Comments
280
+ --------
281
+
282
+ To comment-out part of a line in a template, use the comment syntax ``{# ...
283
+ #}``. This is useful for debugging or to add information for other template
284
+ designers or yourself:
285
+
286
+ .. code-block:: jinja
287
+
288
+ {# note: disabled template because we no longer use this
289
+ {% for user in users %}
290
+ ...
291
+ {% endfor %}
292
+ #}
293
+
294
+ Including other Templates
295
+ -------------------------
296
+
297
+ The :doc:`include<functions/include>` function is useful to include a template
298
+ and return the rendered content of that template into the current one:
299
+
300
+ .. code-block:: jinja
301
+
302
+ {{ include('sidebar.html') }}
303
+
304
+ By default, included templates have access to the same context as the template
305
+ which includes them. This means that any variable defined in the main template
306
+ will be available in the included template too:
307
+
308
+ .. code-block:: jinja
309
+
310
+ {% for box in boxes %}
311
+ {{ include('render_box.html') }}
312
+ {% endfor %}
313
+
314
+ The included template ``render_box.html`` is able to access the ``box`` variable.
315
+
316
+ The filename of the template depends on the template loader. For instance, the
317
+ ``Twig_Loader_Filesystem`` allows you to access other templates by giving the
318
+ filename. You can access templates in subdirectories with a slash:
319
+
320
+ .. code-block:: jinja
321
+
322
+ {{ include('sections/articles/sidebar.html') }}
323
+
324
+ This behavior depends on the application embedding Twig.
325
+
326
+ Template Inheritance
327
+ --------------------
328
+
329
+ The most powerful part of Twig is template inheritance. Template inheritance
330
+ allows you to build a base "skeleton" template that contains all the common
331
+ elements of your site and defines **blocks** that child templates can
332
+ override.
333
+
334
+ Sounds complicated but it is very basic. It's easier to understand it by
335
+ starting with an example.
336
+
337
+ Let's define a base template, ``base.html``, which defines a simple HTML
338
+ skeleton document that you might use for a simple two-column page:
339
+
340
+ .. code-block:: html+jinja
341
+
342
+ <!DOCTYPE html>
343
+ <html>
344
+ <head>
345
+ {% block head %}
346
+ <link rel="stylesheet" href="style.css" />
347
+ <title>{% block title %}{% endblock %} - My Webpage</title>
348
+ {% endblock %}
349
+ </head>
350
+ <body>
351
+ <div id="content">{% block content %}{% endblock %}</div>
352
+ <div id="footer">
353
+ {% block footer %}
354
+ &copy; Copyright 2011 by <a href="http://domain.invalid/">you</a>.
355
+ {% endblock %}
356
+ </div>
357
+ </body>
358
+ </html>
359
+
360
+ In this example, the :doc:`block<tags/block>` tags define four blocks that
361
+ child templates can fill in. All the ``block`` tag does is to tell the
362
+ template engine that a child template may override those portions of the
363
+ template.
364
+
365
+ A child template might look like this:
366
+
367
+ .. code-block:: jinja
368
+
369
+ {% extends "base.html" %}
370
+
371
+ {% block title %}Index{% endblock %}
372
+ {% block head %}
373
+ {{ parent() }}
374
+ <style type="text/css">
375
+ .important { color: #336699; }
376
+ </style>
377
+ {% endblock %}
378
+ {% block content %}
379
+ <h1>Index</h1>
380
+ <p class="important">
381
+ Welcome to my awesome homepage.
382
+ </p>
383
+ {% endblock %}
384
+
385
+ The :doc:`extends<tags/extends>` tag is the key here. It tells the template
386
+ engine that this template "extends" another template. When the template system
387
+ evaluates this template, first it locates the parent. The extends tag should
388
+ be the first tag in the template.
389
+
390
+ Note that since the child template doesn't define the ``footer`` block, the
391
+ value from the parent template is used instead.
392
+
393
+ It's possible to render the contents of the parent block by using the
394
+ :doc:`parent<functions/parent>` function. This gives back the results of the
395
+ parent block:
396
+
397
+ .. code-block:: jinja
398
+
399
+ {% block sidebar %}
400
+ <h3>Table Of Contents</h3>
401
+ ...
402
+ {{ parent() }}
403
+ {% endblock %}
404
+
405
+ .. tip::
406
+
407
+ The documentation page for the :doc:`extends<tags/extends>` tag describes
408
+ more advanced features like block nesting, scope, dynamic inheritance, and
409
+ conditional inheritance.
410
+
411
+ .. note::
412
+
413
+ Twig also supports multiple inheritance with the so called horizontal reuse
414
+ with the help of the :doc:`use<tags/use>` tag. This is an advanced feature
415
+ hardly ever needed in regular templates.
416
+
417
+ HTML Escaping
418
+ -------------
419
+
420
+ When generating HTML from templates, there's always a risk that a variable
421
+ will include characters that affect the resulting HTML. There are two
422
+ approaches: manually escaping each variable or automatically escaping
423
+ everything by default.
424
+
425
+ Twig supports both, automatic escaping is enabled by default.
426
+
427
+ The automatic escaping strategy can be configured via the
428
+ :ref:`autoescape<environment_options>` option and defaults to ``html``.
429
+
430
+ Working with Manual Escaping
431
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~
432
+
433
+ If manual escaping is enabled, it is **your** responsibility to escape
434
+ variables if needed. What to escape? Any variable you don't trust.
435
+
436
+ Escaping works by piping the variable through the
437
+ :doc:`escape<filters/escape>` or ``e`` filter:
438
+
439
+ .. code-block:: jinja
440
+
441
+ {{ user.username|e }}
442
+
443
+ By default, the ``escape`` filter uses the ``html`` strategy, but depending on
444
+ the escaping context, you might want to explicitly use any other available
445
+ strategies:
446
+
447
+ .. code-block:: jinja
448
+
449
+ {{ user.username|e('js') }}
450
+ {{ user.username|e('css') }}
451
+ {{ user.username|e('url') }}
452
+ {{ user.username|e('html_attr') }}
453
+
454
+ Working with Automatic Escaping
455
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
456
+
457
+ Whether automatic escaping is enabled or not, you can mark a section of a
458
+ template to be escaped or not by using the :doc:`autoescape<tags/autoescape>`
459
+ tag:
460
+
461
+ .. code-block:: jinja
462
+
463
+ {% autoescape %}
464
+ Everything will be automatically escaped in this block (using the HTML strategy)
465
+ {% endautoescape %}
466
+
467
+ By default, auto-escaping uses the ``html`` escaping strategy. If you output
468
+ variables in other contexts, you need to explicitly escape them with the
469
+ appropriate escaping strategy:
470
+
471
+ .. code-block:: jinja
472
+
473
+ {% autoescape 'js' %}
474
+ Everything will be automatically escaped in this block (using the JS strategy)
475
+ {% endautoescape %}
476
+
477
+ Escaping
478
+ --------
479
+
480
+ It is sometimes desirable or even necessary to have Twig ignore parts it would
481
+ otherwise handle as variables or blocks. For example if the default syntax is
482
+ used and you want to use ``{{`` as raw string in the template and not start a
483
+ variable you have to use a trick.
484
+
485
+ The easiest way is to output the variable delimiter (``{{``) by using a variable
486
+ expression:
487
+
488
+ .. code-block:: jinja
489
+
490
+ {{ '{{' }}
491
+
492
+ For bigger sections it makes sense to mark a block
493
+ :doc:`verbatim<tags/verbatim>`.
494
+
495
+ Macros
496
+ ------
497
+
498
+ .. versionadded:: 1.12
499
+ Support for default argument values was added in Twig 1.12.
500
+
501
+ Macros are comparable with functions in regular programming languages. They
502
+ are useful to reuse often used HTML fragments to not repeat yourself.
503
+
504
+ A macro is defined via the :doc:`macro<tags/macro>` tag. Here is a small example
505
+ (subsequently called ``forms.html``) of a macro that renders a form element:
506
+
507
+ .. code-block:: jinja
508
+
509
+ {% macro input(name, value, type, size) %}
510
+ <input type="{{ type|default('text') }}" name="{{ name }}" value="{{ value|e }}" size="{{ size|default(20) }}" />
511
+ {% endmacro %}
512
+
513
+ Macros can be defined in any template, and need to be "imported" via the
514
+ :doc:`import<tags/import>` tag before being used:
515
+
516
+ .. code-block:: jinja
517
+
518
+ {% import "forms.html" as forms %}
519
+
520
+ <p>{{ forms.input('username') }}</p>
521
+
522
+ Alternatively, you can import individual macro names from a template into the
523
+ current namespace via the :doc:`from<tags/from>` tag and optionally alias them:
524
+
525
+ .. code-block:: jinja
526
+
527
+ {% from 'forms.html' import input as input_field %}
528
+
529
+ <dl>
530
+ <dt>Username</dt>
531
+ <dd>{{ input_field('username') }}</dd>
532
+ <dt>Password</dt>
533
+ <dd>{{ input_field('password', '', 'password') }}</dd>
534
+ </dl>
535
+
536
+ A default value can also be defined for macro arguments when not provided in a
537
+ macro call:
538
+
539
+ .. code-block:: jinja
540
+
541
+ {% macro input(name, value = "", type = "text", size = 20) %}
542
+ <input type="{{ type }}" name="{{ name }}" value="{{ value|e }}" size="{{ size }}" />
543
+ {% endmacro %}
544
+
545
+ If extra positional arguments are passed to a macro call, they end up in the
546
+ special ``varargs`` variable as a list of values.
547
+
548
+ .. _twig-expressions:
549
+
550
+ Expressions
551
+ -----------
552
+
553
+ Twig allows expressions everywhere. These work very similar to regular PHP and
554
+ even if you're not working with PHP you should feel comfortable with it.
555
+
556
+ .. note::
557
+
558
+ The operator precedence is as follows, with the lowest-precedence
559
+ operators listed first: ``b-and``, ``b-xor``, ``b-or``, ``or``, ``and``,
560
+ ``==``, ``!=``, ``<``, ``>``, ``>=``, ``<=``, ``in``, ``matches``,
561
+ ``starts with``, ``ends with``, ``..``, ``+``, ``-``, ``~``, ``*``, ``/``,
562
+ ``//``, ``%``, ``is``, ``**``, ``|``, ``[]``, and ``.``:
563
+
564
+ .. code-block:: jinja
565
+
566
+ {% set greeting = 'Hello ' %}
567
+ {% set name = 'Fabien' %}
568
+
569
+ {{ greeting ~ name|lower }} {# Hello fabien #}
570
+
571
+ {# use parenthesis to change precedence #}
572
+ {{ (greeting ~ name)|lower }} {# hello fabien #}
573
+
574
+ Literals
575
+ ~~~~~~~~
576
+
577
+ .. versionadded:: 1.5
578
+ Support for hash keys as names and expressions was added in Twig 1.5.
579
+
580
+ The simplest form of expressions are literals. Literals are representations
581
+ for PHP types such as strings, numbers, and arrays. The following literals
582
+ exist:
583
+
584
+ * ``"Hello World"``: Everything between two double or single quotes is a
585
+ string. They are useful whenever you need a string in the template (for
586
+ example as arguments to function calls, filters or just to extend or include
587
+ a template). A string can contain a delimiter if it is preceded by a
588
+ backslash (``\``) -- like in ``'It\'s good'``. If the string contains a
589
+ backslash (e.g. ``'c:\Program Files'``) escape it by doubling it
590
+ (e.g. ``'c:\\Program Files'``).
591
+
592
+ * ``42`` / ``42.23``: Integers and floating point numbers are created by just
593
+ writing the number down. If a dot is present the number is a float,
594
+ otherwise an integer.
595
+
596
+ * ``["foo", "bar"]``: Arrays are defined by a sequence of expressions
597
+ separated by a comma (``,``) and wrapped with squared brackets (``[]``).
598
+
599
+ * ``{"foo": "bar"}``: Hashes are defined by a list of keys and values
600
+ separated by a comma (``,``) and wrapped with curly braces (``{}``):
601
+
602
+ .. code-block:: jinja
603
+
604
+ {# keys as string #}
605
+ { 'foo': 'foo', 'bar': 'bar' }
606
+
607
+ {# keys as names (equivalent to the previous hash) -- as of Twig 1.5 #}
608
+ { foo: 'foo', bar: 'bar' }
609
+
610
+ {# keys as integer #}
611
+ { 2: 'foo', 4: 'bar' }
612
+
613
+ {# keys as expressions (the expression must be enclosed into parentheses) -- as of Twig 1.5 #}
614
+ { (1 + 1): 'foo', (a ~ 'b'): 'bar' }
615
+
616
+ * ``true`` / ``false``: ``true`` represents the true value, ``false``
617
+ represents the false value.
618
+
619
+ * ``null``: ``null`` represents no specific value. This is the value returned
620
+ when a variable does not exist. ``none`` is an alias for ``null``.
621
+
622
+ Arrays and hashes can be nested:
623
+
624
+ .. code-block:: jinja
625
+
626
+ {% set foo = [1, {"foo": "bar"}] %}
627
+
628
+ .. tip::
629
+
630
+ Using double-quoted or single-quoted strings has no impact on performance
631
+ but string interpolation is only supported in double-quoted strings.
632
+
633
+ Math
634
+ ~~~~
635
+
636
+ Twig allows you to calculate with values. This is rarely useful in templates
637
+ but exists for completeness' sake. The following operators are supported:
638
+
639
+ * ``+``: Adds two objects together (the operands are casted to numbers). ``{{
640
+ 1 + 1 }}`` is ``2``.
641
+
642
+ * ``-``: Subtracts the second number from the first one. ``{{ 3 - 2 }}`` is
643
+ ``1``.
644
+
645
+ * ``/``: Divides two numbers. The returned value will be a floating point
646
+ number. ``{{ 1 / 2 }}`` is ``{{ 0.5 }}``.
647
+
648
+ * ``%``: Calculates the remainder of an integer division. ``{{ 11 % 7 }}`` is
649
+ ``4``.
650
+
651
+ * ``//``: Divides two numbers and returns the floored integer result. ``{{ 20
652
+ // 7 }}`` is ``2``, ``{{ -20 // 7 }}`` is ``-3`` (this is just syntactic
653
+ sugar for the :doc:`round<filters/round>` filter).
654
+
655
+ * ``*``: Multiplies the left operand with the right one. ``{{ 2 * 2 }}`` would
656
+ return ``4``.
657
+
658
+ * ``**``: Raises the left operand to the power of the right operand. ``{{ 2 **
659
+ 3 }}`` would return ``8``.
660
+
661
+ Logic
662
+ ~~~~~
663
+
664
+ You can combine multiple expressions with the following operators:
665
+
666
+ * ``and``: Returns true if the left and the right operands are both true.
667
+
668
+ * ``or``: Returns true if the left or the right operand is true.
669
+
670
+ * ``not``: Negates a statement.
671
+
672
+ * ``(expr)``: Groups an expression.
673
+
674
+ .. note::
675
+
676
+ Twig also support bitwise operators (``b-and``, ``b-xor``, and ``b-or``).
677
+
678
+ .. note::
679
+
680
+ Operators are case sensitive.
681
+
682
+ Comparisons
683
+ ~~~~~~~~~~~
684
+
685
+ The following comparison operators are supported in any expression: ``==``,
686
+ ``!=``, ``<``, ``>``, ``>=``, and ``<=``.
687
+
688
+ You can also check if a string ``starts with`` or ``ends with`` another
689
+ string:
690
+
691
+ .. code-block:: jinja
692
+
693
+ {% if 'Fabien' starts with 'F' %}
694
+ {% endif %}
695
+
696
+ {% if 'Fabien' ends with 'n' %}
697
+ {% endif %}
698
+
699
+ .. note::
700
+
701
+ For complex string comparisons, the ``matches`` operator allows you to use
702
+ `regular expressions`_:
703
+
704
+ .. code-block:: jinja
705
+
706
+ {% if phone matches '/^[\\d\\.]+$/' %}
707
+ {% endif %}
708
+
709
+ Containment Operator
710
+ ~~~~~~~~~~~~~~~~~~~~
711
+
712
+ The ``in`` operator performs containment test.
713
+
714
+ It returns ``true`` if the left operand is contained in the right:
715
+
716
+ .. code-block:: jinja
717
+
718
+ {# returns true #}
719
+
720
+ {{ 1 in [1, 2, 3] }}
721
+
722
+ {{ 'cd' in 'abcde' }}
723
+
724
+ .. tip::
725
+
726
+ You can use this filter to perform a containment test on strings, arrays,
727
+ or objects implementing the ``Traversable`` interface.
728
+
729
+ To perform a negative test, use the ``not in`` operator:
730
+
731
+ .. code-block:: jinja
732
+
733
+ {% if 1 not in [1, 2, 3] %}
734
+
735
+ {# is equivalent to #}
736
+ {% if not (1 in [1, 2, 3]) %}
737
+
738
+ Test Operator
739
+ ~~~~~~~~~~~~~
740
+
741
+ The ``is`` operator performs tests. Tests can be used to test a variable against
742
+ a common expression. The right operand is name of the test:
743
+
744
+ .. code-block:: jinja
745
+
746
+ {# find out if a variable is odd #}
747
+
748
+ {{ name is odd }}
749
+
750
+ Tests can accept arguments too:
751
+
752
+ .. code-block:: jinja
753
+
754
+ {% if post.status is constant('Post::PUBLISHED') %}
755
+
756
+ Tests can be negated by using the ``is not`` operator:
757
+
758
+ .. code-block:: jinja
759
+
760
+ {% if post.status is not constant('Post::PUBLISHED') %}
761
+
762
+ {# is equivalent to #}
763
+ {% if not (post.status is constant('Post::PUBLISHED')) %}
764
+
765
+ Go to the :doc:`tests<tests/index>` page to learn more about the built-in
766
+ tests.
767
+
768
+ Other Operators
769
+ ~~~~~~~~~~~~~~~
770
+
771
+ .. versionadded:: 1.12.0
772
+ Support for the extended ternary operator was added in Twig 1.12.0.
773
+
774
+ The following operators don't fit into any of the other categories:
775
+
776
+ * ``|``: Applies a filter.
777
+
778
+ * ``..``: Creates a sequence based on the operand before and after the operator
779
+ (this is just syntactic sugar for the :doc:`range<functions/range>` function):
780
+
781
+ .. code-block:: jinja
782
+
783
+ {{ 1..5 }}
784
+
785
+ {# equivalent to #}
786
+ {{ range(1, 5) }}
787
+
788
+ Note that you must use parentheses when combining it with the filter operator
789
+ due to the :ref:`operator precedence rules <twig-expressions>`:
790
+
791
+ .. code-block:: jinja
792
+
793
+ (1..5)|join(', ')
794
+
795
+ * ``~``: Converts all operands into strings and concatenates them. ``{{ "Hello
796
+ " ~ name ~ "!" }}`` would return (assuming ``name`` is ``'John'``) ``Hello
797
+ John!``.
798
+
799
+ * ``.``, ``[]``: Gets an attribute of an object.
800
+
801
+ * ``?:``: The ternary operator:
802
+
803
+ .. code-block:: jinja
804
+
805
+ {{ foo ? 'yes' : 'no' }}
806
+
807
+ {# as of Twig 1.12.0 #}
808
+ {{ foo ?: 'no' }} is the same as {{ foo ? foo : 'no' }}
809
+ {{ foo ? 'yes' }} is the same as {{ foo ? 'yes' : '' }}
810
+
811
+ * ``??``: The null-coalescing operator:
812
+
813
+ .. code-block:: jinja
814
+
815
+ {# returns the value of foo if it is defined and not null, 'no' otherwise #}
816
+ {{ foo ?? 'no' }}
817
+
818
+ String Interpolation
819
+ ~~~~~~~~~~~~~~~~~~~~
820
+
821
+ .. versionadded:: 1.5
822
+ String interpolation was added in Twig 1.5.
823
+
824
+ String interpolation (``#{expression}``) allows any valid expression to appear
825
+ within a *double-quoted string*. The result of evaluating that expression is
826
+ inserted into the string:
827
+
828
+ .. code-block:: jinja
829
+
830
+ {{ "foo #{bar} baz" }}
831
+ {{ "foo #{1 + 2} baz" }}
832
+
833
+ .. _templates-whitespace-control:
834
+
835
+ Whitespace Control
836
+ ------------------
837
+
838
+ .. versionadded:: 1.1
839
+ Tag level whitespace control was added in Twig 1.1.
840
+
841
+ The first newline after a template tag is removed automatically (like in PHP.)
842
+ Whitespace is not further modified by the template engine, so each whitespace
843
+ (spaces, tabs, newlines etc.) is returned unchanged.
844
+
845
+ Use the ``spaceless`` tag to remove whitespace *between HTML tags*:
846
+
847
+ .. code-block:: jinja
848
+
849
+ {% spaceless %}
850
+ <div>
851
+ <strong>foo bar</strong>
852
+ </div>
853
+ {% endspaceless %}
854
+
855
+ {# output will be <div><strong>foo bar</strong></div> #}
856
+
857
+ In addition to the spaceless tag you can also control whitespace on a per tag
858
+ level. By using the whitespace control modifier on your tags, you can trim
859
+ leading and or trailing whitespace:
860
+
861
+ .. code-block:: jinja
862
+
863
+ {% set value = 'no spaces' %}
864
+ {#- No leading/trailing whitespace -#}
865
+ {%- if true -%}
866
+ {{- value -}}
867
+ {%- endif -%}
868
+
869
+ {# output 'no spaces' #}
870
+
871
+ The above sample shows the default whitespace control modifier, and how you can
872
+ use it to remove whitespace around tags. Trimming space will consume all whitespace
873
+ for that side of the tag. It is possible to use whitespace trimming on one side
874
+ of a tag:
875
+
876
+ .. code-block:: jinja
877
+
878
+ {% set value = 'no spaces' %}
879
+ <li> {{- value }} </li>
880
+
881
+ {# outputs '<li>no spaces </li>' #}
882
+
883
+ Extensions
884
+ ----------
885
+
886
+ Twig can be easily extended.
887
+
888
+ If you are looking for new tags, filters, or functions, have a look at the Twig official
889
+ `extension repository`_.
890
+
891
+ If you want to create your own, read the :ref:`Creating an
892
+ Extension<creating_extensions>` chapter.
893
+
894
+ .. _`Twig bundle`: https://github.com/Anomareh/PHP-Twig.tmbundle
895
+ .. _`Jinja syntax plugin`: http://jinja.pocoo.org/docs/integration/#vim
896
+ .. _`vim-twig plugin`: https://github.com/evidens/vim-twig
897
+ .. _`Twig syntax plugin`: http://plugins.netbeans.org/plugin/37069/php-twig
898
+ .. _`Twig plugin`: https://github.com/pulse00/Twig-Eclipse-Plugin
899
+ .. _`Twig language definition`: https://github.com/gabrielcorpse/gedit-twig-template-language
900
+ .. _`extension repository`: http://github.com/twigphp/Twig-extensions
901
+ .. _`Twig syntax mode`: https://github.com/bobthecow/Twig-HTML.mode
902
+ .. _`other Twig syntax mode`: https://github.com/muxx/Twig-HTML.mode
903
+ .. _`Notepad++ Twig Highlighter`: https://github.com/Banane9/notepadplusplus-twig
904
+ .. _`web-mode.el`: http://web-mode.org/
905
+ .. _`regular expressions`: http://php.net/manual/en/pcre.pattern.php
906
+ .. _`PHP-twig for atom`: https://github.com/reesef/php-twig
907
+ .. _`TwigFiddle`: http://twigfiddle.com/
908
+ .. _`Twig pack`: https://marketplace.visualstudio.com/items?itemName=bajdzis.vscode-twig-pack
vendor/twig/twig/doc/tests/constant.rst ADDED
@@ -0,0 +1,22 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ``constant``
2
+ ============
3
+
4
+ .. versionadded: 1.13.1
5
+ constant now accepts object instances as the second argument.
6
+
7
+ ``constant`` checks if a variable has the exact same value as a constant. You
8
+ can use either global constants or class constants:
9
+
10
+ .. code-block:: jinja
11
+
12
+ {% if post.status is constant('Post::PUBLISHED') %}
13
+ the status attribute is exactly the same as Post::PUBLISHED
14
+ {% endif %}
15
+
16
+ You can test constants from object instances as well:
17
+
18
+ .. code-block:: jinja
19
+
20
+ {% if post.status is constant('PUBLISHED', post) %}
21
+ the status attribute is exactly the same as Post::PUBLISHED
22
+ {% endif %}
vendor/twig/twig/doc/tests/defined.rst ADDED
@@ -0,0 +1,30 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ``defined``
2
+ ===========
3
+
4
+ ``defined`` checks if a variable is defined in the current context. This is very
5
+ useful if you use the ``strict_variables`` option:
6
+
7
+ .. code-block:: jinja
8
+
9
+ {# defined works with variable names #}
10
+ {% if foo is defined %}
11
+ ...
12
+ {% endif %}
13
+
14
+ {# and attributes on variables names #}
15
+ {% if foo.bar is defined %}
16
+ ...
17
+ {% endif %}
18
+
19
+ {% if foo['bar'] is defined %}
20
+ ...
21
+ {% endif %}
22
+
23
+ When using the ``defined`` test on an expression that uses variables in some
24
+ method calls, be sure that they are all defined first:
25
+
26
+ .. code-block:: jinja
27
+
28
+ {% if var is defined and foo.method(var) is defined %}
29
+ ...
30
+ {% endif %}
vendor/twig/twig/doc/tests/divisibleby.rst ADDED
@@ -0,0 +1,14 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ``divisible by``
2
+ ================
3
+
4
+ .. versionadded:: 1.14.2
5
+ The ``divisible by`` test was added in Twig 1.14.2 as an alias for
6
+ ``divisibleby``.
7
+
8
+ ``divisible by`` checks if a variable is divisible by a number:
9
+
10
+ .. code-block:: jinja
11
+
12
+ {% if loop.index is divisible by(3) %}
13
+ ...
14
+ {% endif %}
vendor/twig/twig/doc/tests/empty.rst ADDED
@@ -0,0 +1,11 @@
 
 
 
 
 
 
 
 
 
 
 
1
+ ``empty``
2
+ =========
3
+
4
+ ``empty`` checks if a variable is an empty string, an empty array, an empty
5
+ hash, exactly ``false``, or exactly ``null``:
6
+
7
+ .. code-block:: jinja
8
+
9
+ {% if foo is empty %}
10
+ ...
11
+ {% endif %}
vendor/twig/twig/doc/tests/even.rst ADDED
@@ -0,0 +1,10 @@
 
 
 
 
 
 
 
 
 
 
1
+ ``even``
2
+ ========
3
+
4
+ ``even`` returns ``true`` if the given number is even:
5
+
6
+ .. code-block:: jinja
7
+
8
+ {{ var is even }}
9
+
10
+ .. seealso:: :doc:`odd<../tests/odd>`
vendor/twig/twig/doc/tests/index.rst ADDED
@@ -0,0 +1,15 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ Tests
2
+ =====
3
+
4
+ .. toctree::
5
+ :maxdepth: 1
6
+
7
+ constant
8
+ defined
9
+ divisibleby
10
+ empty
11
+ even
12
+ iterable
13
+ null
14
+ odd
15
+ sameas
vendor/twig/twig/doc/tests/iterable.rst ADDED
@@ -0,0 +1,19 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ``iterable``
2
+ ============
3
+
4
+ .. versionadded:: 1.7
5
+ The iterable test was added in Twig 1.7.
6
+
7
+ ``iterable`` checks if a variable is an array or a traversable object:
8
+
9
+ .. code-block:: jinja
10
+
11
+ {# evaluates to true if the foo variable is iterable #}
12
+ {% if users is iterable %}
13
+ {% for user in users %}
14
+ Hello {{ user }}!
15
+ {% endfor %}
16
+ {% else %}
17
+ {# users is probably a string #}
18
+ Hello {{ users }}!
19
+ {% endif %}
vendor/twig/twig/doc/tests/null.rst ADDED
@@ -0,0 +1,12 @@
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ``null``
2
+ ========
3
+
4
+ ``null`` returns ``true`` if the variable is ``null``:
5
+
6
+ .. code-block:: jinja
7
+
8
+ {{ var is null }}
9
+
10
+ .. note::
11
+
12
+ ``none`` is an alias for ``null``.
vendor/twig/twig/doc/tests/odd.rst ADDED
@@ -0,0 +1,10 @@
 
 
 
 
 
 
 
 
 
 
1
+ ``odd``
2
+ =======
3
+
4
+ ``odd`` returns ``true`` if the given number is odd:
5
+
6
+ .. code-block:: jinja
7
+
8
+ {{ var is odd }}
9
+
10
+ .. seealso:: :doc:`even<../tests/even>`
vendor/twig/twig/doc/tests/sameas.rst ADDED
@@ -0,0 +1,14 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ``same as``
2
+ ===========
3
+
4
+ .. versionadded:: 1.14.2
5
+ The ``same as`` test was added in Twig 1.14.2 as an alias for ``sameas``.
6
+
7
+ ``same as`` checks if a variable is the same as another variable.
8
+ This is the equivalent to ``===`` in PHP:
9
+
10
+ .. code-block:: jinja
11
+
12
+ {% if foo.attribute is same as(false) %}
13
+ the foo attribute really is the 'false' PHP value
14
+ {% endif %}
vendor/twig/twig/ext/twig/.gitignore ADDED
@@ -0,0 +1,30 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ *.sw*
2
+ .deps
3
+ Makefile
4
+ Makefile.fragments
5
+ Makefile.global
6
+ Makefile.objects
7
+ acinclude.m4
8
+ aclocal.m4
9
+ build/
10
+ config.cache
11
+ config.guess
12
+ config.h
13
+ config.h.in
14
+ config.log
15
+ config.nice
16
+ config.status
17
+ config.sub
18
+ configure
19
+ configure.in
20
+ install-sh
21
+ libtool
22
+ ltmain.sh
23
+ missing
24
+ mkinstalldirs
25
+ run-tests.php
26
+ twig.loT
27
+ .libs/
28
+ modules/
29
+ twig.la
30
+ twig.lo
vendor/twig/twig/ext/twig/config.m4 ADDED
@@ -0,0 +1,8 @@
 
 
 
 
 
 
 
 
1
+ dnl config.m4 for extension twig
2
+
3
+ PHP_ARG_ENABLE(twig, whether to enable twig support,
4
+ [ --enable-twig Enable twig support])
5
+
6
+ if test "$PHP_TWIG" != "no"; then
7
+ PHP_NEW_EXTENSION(twig, twig.c, $ext_shared)
8
+ fi
vendor/twig/twig/ext/twig/config.w32 ADDED
@@ -0,0 +1,8 @@
 
 
 
 
 
 
 
 
1
+ // vim:ft=javascript
2
+
3
+ ARG_ENABLE("twig", "Twig support", "no");
4
+
5
+ if (PHP_TWIG != "no") {
6
+ AC_DEFINE('HAVE_TWIG', 1);
7
+ EXTENSION('twig', 'twig.c');
8
+ }
vendor/twig/twig/ext/twig/php_twig.h ADDED
@@ -0,0 +1,35 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /*
2
+ +----------------------------------------------------------------------+
3
+ | Twig Extension |
4
+ +----------------------------------------------------------------------+
5
+ | Copyright (c) 2011 Derick Rethans |
6
+ +----------------------------------------------------------------------+
7
+ | Redistribution and use in source and binary forms, with or without |
8
+ | modification, are permitted provided that the conditions mentioned |
9
+ | in the accompanying LICENSE file are met (BSD-3-Clause). |
10
+ +----------------------------------------------------------------------+
11
+ | Author: Derick Rethans <derick@derickrethans.nl> |
12
+ +----------------------------------------------------------------------+
13
+ */
14
+
15
+ #ifndef PHP_TWIG_H
16
+ #define PHP_TWIG_H
17
+
18
+ #define PHP_TWIG_VERSION "1.32.0"
19
+
20
+ #include "php.h"
21
+
22
+ extern zend_module_entry twig_module_entry;
23
+ #define phpext_twig_ptr &twig_module_entry
24
+ #ifndef PHP_WIN32
25
+ zend_module_entry *get_module(void);
26
+ #endif
27
+
28
+ #ifdef ZTS
29
+ #include "TSRM.h"
30
+ #endif
31
+
32
+ PHP_FUNCTION(twig_template_get_attributes);
33
+ PHP_RSHUTDOWN_FUNCTION(twig);
34
+
35
+ #endif
vendor/twig/twig/ext/twig/twig.c ADDED
@@ -0,0 +1,1215 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /*
2
+ +----------------------------------------------------------------------+
3
+ | Twig Extension |
4
+ +----------------------------------------------------------------------+
5
+ | Copyright (c) 2011 Derick Rethans |
6
+ +----------------------------------------------------------------------+
7
+ | Redistribution and use in source and binary forms, with or without |
8
+ | modification, are permitted provided that the conditions mentioned |
9
+ | in the accompanying LICENSE file are met (BSD-3-Clause). |
10
+ +----------------------------------------------------------------------+
11
+ | Author: Derick Rethans <derick@derickrethans.nl> |
12
+ +----------------------------------------------------------------------+
13
+ */
14
+
15
+ #ifdef HAVE_CONFIG_H
16
+ #include "config.h"
17
+ #endif
18
+
19
+ #include "php.h"
20
+ #include "php_twig.h"
21
+ #include "ext/standard/php_var.h"
22
+ #include "ext/standard/php_string.h"
23
+ #include "ext/standard/php_smart_str.h"
24
+ #include "ext/spl/spl_exceptions.h"
25
+
26
+ #include "Zend/zend_object_handlers.h"
27
+ #include "Zend/zend_interfaces.h"
28
+ #include "Zend/zend_exceptions.h"
29
+
30
+ #ifndef Z_ADDREF_P
31
+ #define Z_ADDREF_P(pz) (pz)->refcount++
32
+ #endif
33
+
34
+ #ifndef E_USER_DEPRECATED
35
+ #define E_USER_DEPRECATED (1<<14L)
36
+ #endif
37
+
38
+ #define FREE_DTOR(z) \
39
+ zval_dtor(z); \
40
+ efree(z);
41
+
42
+ #if PHP_VERSION_ID >= 50300
43
+ #define APPLY_TSRMLS_DC TSRMLS_DC
44
+ #define APPLY_TSRMLS_CC TSRMLS_CC
45
+ #define APPLY_TSRMLS_FETCH()
46
+ #else
47
+ #define APPLY_TSRMLS_DC
48
+ #define APPLY_TSRMLS_CC
49
+ #define APPLY_TSRMLS_FETCH() TSRMLS_FETCH()
50
+ #endif
51
+
52
+ ZEND_BEGIN_ARG_INFO_EX(twig_template_get_attribute_args, ZEND_SEND_BY_VAL, ZEND_RETURN_VALUE, 6)
53
+ ZEND_ARG_INFO(0, template)
54
+ ZEND_ARG_INFO(0, object)
55
+ ZEND_ARG_INFO(0, item)
56
+ ZEND_ARG_INFO(0, arguments)
57
+ ZEND_ARG_INFO(0, type)
58
+ ZEND_ARG_INFO(0, isDefinedTest)
59
+ ZEND_END_ARG_INFO()
60
+
61
+ #ifndef PHP_FE_END
62
+ #define PHP_FE_END { NULL, NULL, NULL}
63
+ #endif
64
+
65
+ static const zend_function_entry twig_functions[] = {
66
+ PHP_FE(twig_template_get_attributes, twig_template_get_attribute_args)
67
+ PHP_FE_END
68
+ };
69
+
70
+ PHP_RSHUTDOWN_FUNCTION(twig)
71
+ {
72
+ #if ZEND_DEBUG
73
+ CG(unclean_shutdown) = 0; /* get rid of PHPUnit's exit() and report memleaks */
74
+ #endif
75
+ return SUCCESS;
76
+ }
77
+
78
+ zend_module_entry twig_module_entry = {
79
+ STANDARD_MODULE_HEADER,
80
+ "twig",
81
+ twig_functions,
82
+ NULL,
83
+ NULL,
84
+ NULL,
85
+ PHP_RSHUTDOWN(twig),
86
+ NULL,
87
+ PHP_TWIG_VERSION,
88
+ STANDARD_MODULE_PROPERTIES
89
+ };
90
+
91
+
92
+ #ifdef COMPILE_DL_TWIG
93
+ ZEND_GET_MODULE(twig)
94
+ #endif
95
+
96
+ static int TWIG_ARRAY_KEY_EXISTS(zval *array, zval *key)
97
+ {
98
+ if (Z_TYPE_P(array) != IS_ARRAY) {
99
+ return 0;
100
+ }
101
+
102
+ switch (Z_TYPE_P(key)) {
103
+ case IS_NULL:
104
+ return zend_hash_exists(Z_ARRVAL_P(array), "", 1);
105
+
106
+ case IS_BOOL:
107
+ case IS_DOUBLE:
108
+ convert_to_long(key);
109
+ case IS_LONG:
110
+ return zend_hash_index_exists(Z_ARRVAL_P(array), Z_LVAL_P(key));
111
+
112
+ default:
113
+ convert_to_string(key);
114
+ return zend_symtable_exists(Z_ARRVAL_P(array), Z_STRVAL_P(key), Z_STRLEN_P(key) + 1);
115
+ }
116
+ }
117
+
118
+ static int TWIG_INSTANCE_OF(zval *object, zend_class_entry *interface TSRMLS_DC)
119
+ {
120
+ if (Z_TYPE_P(object) != IS_OBJECT) {
121
+ return 0;
122
+ }
123
+ return instanceof_function(Z_OBJCE_P(object), interface TSRMLS_CC);
124
+ }
125
+
126
+ static int TWIG_INSTANCE_OF_USERLAND(zval *object, char *interface TSRMLS_DC)
127
+ {
128
+ zend_class_entry **pce;
129
+ if (Z_TYPE_P(object) != IS_OBJECT) {
130
+ return 0;
131
+ }
132
+ if (zend_lookup_class(interface, strlen(interface), &pce TSRMLS_CC) == FAILURE) {
133
+ return 0;
134
+ }
135
+ return instanceof_function(Z_OBJCE_P(object), *pce TSRMLS_CC);
136
+ }
137
+
138
+ static zval *TWIG_GET_ARRAYOBJECT_ELEMENT(zval *object, zval *offset TSRMLS_DC)
139
+ {
140
+ zend_class_entry *ce = Z_OBJCE_P(object);
141
+ zval *retval;
142
+
143
+ if (Z_TYPE_P(object) == IS_OBJECT) {
144
+ SEPARATE_ARG_IF_REF(offset);
145
+ zend_call_method_with_1_params(&object, ce, NULL, "offsetget", &retval, offset);
146
+
147
+ zval_ptr_dtor(&offset);
148
+
149
+ if (!retval) {
150
+ if (!EG(exception)) {
151
+ zend_error(E_ERROR, "Undefined offset for object of type %s used as array.", ce->name);
152
+ }
153
+ return NULL;
154
+ }
155
+
156
+ return retval;
157
+ }
158
+ return NULL;
159
+ }
160
+
161
+ static int TWIG_ISSET_ARRAYOBJECT_ELEMENT(zval *object, zval *offset TSRMLS_DC)
162
+ {
163
+ zend_class_entry *ce = Z_OBJCE_P(object);
164
+ zval *retval;
165
+
166
+ if (Z_TYPE_P(object) == IS_OBJECT) {
167
+ SEPARATE_ARG_IF_REF(offset);
168
+ zend_call_method_with_1_params(&object, ce, NULL, "offsetexists", &retval, offset);
169
+
170
+ zval_ptr_dtor(&offset);
171
+
172
+ if (!retval) {
173
+ if (!EG(exception)) {
174
+ zend_error(E_ERROR, "Undefined offset for object of type %s used as array.", ce->name);
175
+ }
176
+ return 0;
177
+ }
178
+
179
+ return (retval && Z_TYPE_P(retval) == IS_BOOL && Z_LVAL_P(retval));
180
+ }
181
+ return 0;
182
+ }
183
+
184
+ static char *TWIG_STRTOLOWER(const char *str, int str_len)
185
+ {
186
+ char *item_dup;
187
+
188
+ item_dup = estrndup(str, str_len);
189
+ php_strtolower(item_dup, str_len);
190
+ return item_dup;
191
+ }
192
+
193
+ static zval *TWIG_CALL_USER_FUNC_ARRAY(zval *object, char *function, zval *arguments TSRMLS_DC)
194
+ {
195
+ zend_fcall_info fci;
196
+ zval ***args = NULL;
197
+ int arg_count = 0;
198
+ HashTable *table;
199
+ HashPosition pos;
200
+ int i = 0;
201
+ zval *retval_ptr;
202
+ zval *zfunction;
203
+
204
+ if (arguments) {
205
+ table = HASH_OF(arguments);
206
+ args = safe_emalloc(sizeof(zval **), table->nNumOfElements, 0);
207
+
208
+ zend_hash_internal_pointer_reset_ex(table, &pos);
209
+
210
+ while (zend_hash_get_current_data_ex(table, (void **)&args[i], &pos) == SUCCESS) {
211
+ i++;
212
+ zend_hash_move_forward_ex(table, &pos);
213
+ }
214
+ arg_count = table->nNumOfElements;
215
+ }
216
+
217
+ MAKE_STD_ZVAL(zfunction);
218
+ ZVAL_STRING(zfunction, function, 1);
219
+ fci.size = sizeof(fci);
220
+ fci.function_table = EG(function_table);
221
+ fci.function_name = zfunction;
222
+ fci.symbol_table = NULL;
223
+ #if PHP_VERSION_ID >= 50300
224
+ fci.object_ptr = object;
225
+ #else
226
+ fci.object_pp = &object;
227
+ #endif
228
+ fci.retval_ptr_ptr = &retval_ptr;
229
+ fci.param_count = arg_count;
230
+ fci.params = args;
231
+ fci.no_separation = 0;
232
+
233
+ if (zend_call_function(&fci, NULL TSRMLS_CC) == FAILURE) {
234
+ ALLOC_INIT_ZVAL(retval_ptr);
235
+ ZVAL_BOOL(retval_ptr, 0);
236
+ }
237
+
238
+ if (args) {
239
+ efree(fci.params);
240
+ }
241
+ FREE_DTOR(zfunction);
242
+ return retval_ptr;
243
+ }
244
+
245
+ static int TWIG_CALL_BOOLEAN(zval *object, char *functionName TSRMLS_DC)
246
+ {
247
+ zval *ret;
248
+ int res;
249
+
250
+ ret = TWIG_CALL_USER_FUNC_ARRAY(object, functionName, NULL TSRMLS_CC);
251
+ res = Z_LVAL_P(ret);
252
+ zval_ptr_dtor(&ret);
253
+ return res;
254
+ }
255
+
256
+ static zval *TWIG_GET_STATIC_PROPERTY(zval *class, char *prop_name TSRMLS_DC)
257
+ {
258
+ zval **tmp_zval;
259
+ zend_class_entry *ce;
260
+
261
+ if (class == NULL || Z_TYPE_P(class) != IS_OBJECT) {
262
+ return NULL;
263
+ }
264
+
265
+ ce = zend_get_class_entry(class TSRMLS_CC);
266
+ #if PHP_VERSION_ID >= 50400
267
+ tmp_zval = zend_std_get_static_property(ce, prop_name, strlen(prop_name), 0, NULL TSRMLS_CC);
268
+ #else
269
+ tmp_zval = zend_std_get_static_property(ce, prop_name, strlen(prop_name), 0 TSRMLS_CC);
270
+ #endif
271
+ return *tmp_zval;
272
+ }
273
+
274
+ static zval *TWIG_GET_ARRAY_ELEMENT_ZVAL(zval *class, zval *prop_name TSRMLS_DC)
275
+ {
276
+ zval **tmp_zval;
277
+
278
+ if (class == NULL || Z_TYPE_P(class) != IS_ARRAY) {
279
+ if (class != NULL && Z_TYPE_P(class) == IS_OBJECT && TWIG_INSTANCE_OF(class, zend_ce_arrayaccess TSRMLS_CC)) {
280
+ // array access object
281
+ return TWIG_GET_ARRAYOBJECT_ELEMENT(class, prop_name TSRMLS_CC);
282
+ }
283
+ return NULL;
284
+ }
285
+
286
+ switch(Z_TYPE_P(prop_name)) {
287
+ case IS_NULL:
288
+ zend_hash_find(HASH_OF(class), "", 1, (void**) &tmp_zval);
289
+ return *tmp_zval;
290
+
291
+ case IS_BOOL:
292
+ case IS_DOUBLE:
293
+ convert_to_long(prop_name);
294
+ case IS_LONG:
295
+ zend_hash_index_find(HASH_OF(class), Z_LVAL_P(prop_name), (void **) &tmp_zval);
296
+ return *tmp_zval;
297
+
298
+ case IS_STRING:
299
+ zend_symtable_find(HASH_OF(class), Z_STRVAL_P(prop_name), Z_STRLEN_P(prop_name) + 1, (void**) &tmp_zval);
300
+ return *tmp_zval;
301
+ }
302
+
303
+ return NULL;
304
+ }
305
+
306
+ static zval *TWIG_GET_ARRAY_ELEMENT(zval *class, char *prop_name, int prop_name_length TSRMLS_DC)
307
+ {
308
+ zval **tmp_zval;
309
+
310
+ if (class == NULL/* || Z_TYPE_P(class) != IS_ARRAY*/) {
311
+ return NULL;
312
+ }
313
+
314
+ if (class != NULL && Z_TYPE_P(class) == IS_OBJECT && TWIG_INSTANCE_OF(class, zend_ce_arrayaccess TSRMLS_CC)) {
315
+ // array access object
316
+ zval *tmp_name_zval;
317
+ zval *tmp_ret_zval;
318
+
319
+ ALLOC_INIT_ZVAL(tmp_name_zval);
320
+ ZVAL_STRING(tmp_name_zval, prop_name, 1);
321
+ tmp_ret_zval = TWIG_GET_ARRAYOBJECT_ELEMENT(class, tmp_name_zval TSRMLS_CC);
322
+ FREE_DTOR(tmp_name_zval);
323
+ return tmp_ret_zval;
324
+ }
325
+
326
+ if (zend_symtable_find(HASH_OF(class), prop_name, prop_name_length+1, (void**)&tmp_zval) == SUCCESS) {
327
+ return *tmp_zval;
328
+ }
329
+ return NULL;
330
+ }
331
+
332
+ static zval *TWIG_PROPERTY(zval *object, zval *propname TSRMLS_DC)
333
+ {
334
+ zval *tmp = NULL;
335
+
336
+ if (Z_OBJ_HT_P(object)->read_property) {
337
+ #if PHP_VERSION_ID >= 50400
338
+ tmp = Z_OBJ_HT_P(object)->read_property(object, propname, BP_VAR_IS, NULL TSRMLS_CC);
339
+ #else
340
+ tmp = Z_OBJ_HT_P(object)->read_property(object, propname, BP_VAR_IS TSRMLS_CC);
341
+ #endif
342
+ if (tmp == EG(uninitialized_zval_ptr)) {
343
+ ZVAL_NULL(tmp);
344
+ }
345
+ }
346
+ return tmp;
347
+ }
348
+
349
+ static int TWIG_HAS_PROPERTY(zval *object, zval *propname TSRMLS_DC)
350
+ {
351
+ if (Z_OBJ_HT_P(object)->has_property) {
352
+ #if PHP_VERSION_ID >= 50400
353
+ return Z_OBJ_HT_P(object)->has_property(object, propname, 0, NULL TSRMLS_CC);
354
+ #else
355
+ return Z_OBJ_HT_P(object)->has_property(object, propname, 0 TSRMLS_CC);
356
+ #endif
357
+ }
358
+ return 0;
359
+ }
360
+
361
+ static int TWIG_HAS_DYNAMIC_PROPERTY(zval *object, char *prop, int prop_len TSRMLS_DC)
362
+ {
363
+ if (Z_OBJ_HT_P(object)->get_properties) {
364
+ return zend_hash_quick_exists(
365
+ Z_OBJ_HT_P(object)->get_properties(object TSRMLS_CC), // the properties hash
366
+ prop, // property name
367
+ prop_len + 1, // property length
368
+ zend_get_hash_value(prop, prop_len + 1) // hash value
369
+ );
370
+ }
371
+ return 0;
372
+ }
373
+
374
+ static zval *TWIG_PROPERTY_CHAR(zval *object, char *propname TSRMLS_DC)
375
+ {
376
+ zval *tmp_name_zval, *tmp;
377
+
378
+ ALLOC_INIT_ZVAL(tmp_name_zval);
379
+ ZVAL_STRING(tmp_name_zval, propname, 1);
380
+ tmp = TWIG_PROPERTY(object, tmp_name_zval TSRMLS_CC);
381
+ FREE_DTOR(tmp_name_zval);
382
+ return tmp;
383
+ }
384
+
385
+ static zval *TWIG_CALL_S(zval *object, char *method, char *arg0 TSRMLS_DC)
386
+ {
387
+ zend_fcall_info fci;
388
+ zval **args[1];
389
+ zval *argument;
390
+ zval *zfunction;
391
+ zval *retval_ptr;
392
+
393
+ MAKE_STD_ZVAL(argument);
394
+ ZVAL_STRING(argument, arg0, 1);
395
+ args[0] = &argument;
396
+
397
+ MAKE_STD_ZVAL(zfunction);
398
+ ZVAL_STRING(zfunction, method, 1);
399
+ fci.size = sizeof(fci);
400
+ fci.function_table = EG(function_table);
401
+ fci.function_name = zfunction;
402
+ fci.symbol_table = NULL;
403
+ #if PHP_VERSION_ID >= 50300
404
+ fci.object_ptr = object;
405
+ #else
406
+ fci.object_pp = &object;
407
+ #endif
408
+ fci.retval_ptr_ptr = &retval_ptr;
409
+ fci.param_count = 1;
410
+ fci.params = args;
411
+ fci.no_separation = 0;
412
+
413
+ if (zend_call_function(&fci, NULL TSRMLS_CC) == FAILURE) {
414
+ FREE_DTOR(zfunction);
415
+ zval_ptr_dtor(&argument);
416
+ return 0;
417
+ }
418
+ FREE_DTOR(zfunction);
419
+ zval_ptr_dtor(&argument);
420
+ return retval_ptr;
421
+ }
422
+
423
+ static int TWIG_CALL_SB(zval *object, char *method, char *arg0 TSRMLS_DC)
424
+ {
425
+ zval *retval_ptr;
426
+ int success;
427
+
428
+ retval_ptr = TWIG_CALL_S(object, method, arg0 TSRMLS_CC);
429
+ success = (retval_ptr && (Z_TYPE_P(retval_ptr) == IS_BOOL) && Z_LVAL_P(retval_ptr));
430
+
431
+ if (retval_ptr) {
432
+ zval_ptr_dtor(&retval_ptr);
433
+ }
434
+
435
+ return success;
436
+ }
437
+
438
+ static int TWIG_CALL_ZZ(zval *object, char *method, zval *arg1, zval *arg2 TSRMLS_DC)
439
+ {
440
+ zend_fcall_info fci;
441
+ zval **args[2];
442
+ zval *zfunction;
443
+ zval *retval_ptr;
444
+ int success;
445
+
446
+ args[0] = &arg1;
447
+ args[1] = &arg2;
448
+
449
+ MAKE_STD_ZVAL(zfunction);
450
+ ZVAL_STRING(zfunction, method, 1);
451
+ fci.size = sizeof(fci);
452
+ fci.function_table = EG(function_table);
453
+ fci.function_name = zfunction;
454
+ fci.symbol_table = NULL;
455
+ #if PHP_VERSION_ID >= 50300
456
+ fci.object_ptr = object;
457
+ #else
458
+ fci.object_pp = &object;
459
+ #endif
460
+ fci.retval_ptr_ptr = &retval_ptr;
461
+ fci.param_count = 2;
462
+ fci.params = args;
463
+ fci.no_separation = 0;
464
+
465
+ if (zend_call_function(&fci, NULL TSRMLS_CC) == FAILURE) {
466
+ FREE_DTOR(zfunction);
467
+ return 0;
468
+ }
469
+
470
+ FREE_DTOR(zfunction);
471
+
472
+ success = (retval_ptr && (Z_TYPE_P(retval_ptr) == IS_BOOL) && Z_LVAL_P(retval_ptr));
473
+ if (retval_ptr) {
474
+ zval_ptr_dtor(&retval_ptr);
475
+ }
476
+
477
+ return success;
478
+ }
479
+
480
+ #ifndef Z_SET_REFCOUNT_P
481
+ # define Z_SET_REFCOUNT_P(pz, rc) pz->refcount = rc
482
+ # define Z_UNSET_ISREF_P(pz) pz->is_ref = 0
483
+ #endif
484
+
485
+ static void TWIG_NEW(zval *object, char *class, zval *arg0, zval *arg1 TSRMLS_DC)
486
+ {
487
+ zend_class_entry **pce;
488
+
489
+ if (zend_lookup_class(class, strlen(class), &pce TSRMLS_CC) == FAILURE) {
490
+ return;
491
+ }
492
+
493
+ Z_TYPE_P(object) = IS_OBJECT;
494
+ object_init_ex(object, *pce);
495
+ Z_SET_REFCOUNT_P(object, 1);
496
+ Z_UNSET_ISREF_P(object);
497
+
498
+ TWIG_CALL_ZZ(object, "__construct", arg0, arg1 TSRMLS_CC);
499
+ }
500
+
501
+ static int twig_add_array_key_to_string(void *pDest APPLY_TSRMLS_DC, int num_args, va_list args, zend_hash_key *hash_key)
502
+ {
503
+ smart_str *buf;
504
+ char *joiner;
505
+ APPLY_TSRMLS_FETCH();
506
+
507
+ buf = va_arg(args, smart_str*);
508
+ joiner = va_arg(args, char*);
509
+
510
+ if (buf->len != 0) {
511
+ smart_str_appends(buf, joiner);
512
+ }
513
+
514
+ if (hash_key->nKeyLength == 0) {
515
+ smart_str_append_long(buf, (long) hash_key->h);
516
+ } else {
517
+ char *key, *tmp_str;
518
+ int key_len, tmp_len;
519
+ key = php_addcslashes(hash_key->arKey, hash_key->nKeyLength - 1, &key_len, 0, "'\\", 2 TSRMLS_CC);
520
+ tmp_str = php_str_to_str_ex(key, key_len, "\0", 1, "' . \"\\0\" . '", 12, &tmp_len, 0, NULL);
521
+
522
+ smart_str_appendl(buf, tmp_str, tmp_len);
523
+ efree(key);
524
+ efree(tmp_str);
525
+ }
526
+
527
+ return 0;
528
+ }
529
+
530
+ static char *TWIG_IMPLODE_ARRAY_KEYS(char *joiner, zval *array TSRMLS_DC)
531
+ {
532
+ smart_str collector = { 0, 0, 0 };
533
+
534
+ smart_str_appendl(&collector, "", 0);
535
+ zend_hash_apply_with_arguments(HASH_OF(array) APPLY_TSRMLS_CC, twig_add_array_key_to_string, 2, &collector, joiner);
536
+ smart_str_0(&collector);
537
+
538
+ return collector.c;
539
+ }
540
+
541
+ static void TWIG_RUNTIME_ERROR(zval *template TSRMLS_DC, char *message, ...)
542
+ {
543
+ char *buffer;
544
+ va_list args;
545
+ zend_class_entry **pce;
546
+ zval *ex;
547
+ zval *constructor;
548
+ zval *zmessage;
549
+ zval *lineno;
550
+ zval *filename_func;
551
+ zval *filename;
552
+ zval *constructor_args[3];
553
+ zval *constructor_retval;
554
+
555
+ if (zend_lookup_class("Twig_Error_Runtime", strlen("Twig_Error_Runtime"), &pce TSRMLS_CC) == FAILURE) {
556
+ return;
557
+ }
558
+
559
+ va_start(args, message);
560
+ vspprintf(&buffer, 0, message, args);
561
+ va_end(args);
562
+
563
+ MAKE_STD_ZVAL(ex);
564
+ object_init_ex(ex, *pce);
565
+
566
+ // Call Twig_Error constructor
567
+ MAKE_STD_ZVAL(constructor);
568
+ MAKE_STD_ZVAL(zmessage);
569
+ MAKE_STD_ZVAL(lineno);
570
+ MAKE_STD_ZVAL(filename);
571
+ MAKE_STD_ZVAL(filename_func);
572
+ MAKE_STD_ZVAL(constructor_retval);
573
+
574
+ ZVAL_STRINGL(constructor, "__construct", sizeof("__construct")-1, 1);
575
+ ZVAL_STRING(zmessage, buffer, 1);
576
+ ZVAL_LONG(lineno, -1);
577
+
578
+ // Get template filename
579
+ ZVAL_STRINGL(filename_func, "getTemplateName", sizeof("getTemplateName")-1, 1);
580
+ call_user_function(EG(function_table), &template, filename_func, filename, 0, 0 TSRMLS_CC);
581
+
582
+ constructor_args[0] = zmessage;
583
+ constructor_args[1] = lineno;
584
+ constructor_args[2] = filename;
585
+ call_user_function(EG(function_table), &ex, constructor, constructor_retval, 3, constructor_args TSRMLS_CC);
586
+
587
+ zval_ptr_dtor(&constructor_retval);
588
+ zval_ptr_dtor(&zmessage);
589
+ zval_ptr_dtor(&lineno);
590
+ zval_ptr_dtor(&filename);
591
+ FREE_DTOR(constructor);
592
+ FREE_DTOR(filename_func);
593
+ efree(buffer);
594
+
595
+ zend_throw_exception_object(ex TSRMLS_CC);
596
+ }
597
+
598
+ static char *TWIG_GET_CLASS_NAME(zval *object TSRMLS_DC)
599
+ {
600
+ char *class_name;
601
+ zend_uint class_name_len;
602
+
603
+ if (Z_TYPE_P(object) != IS_OBJECT) {
604
+ return "";
605
+ }
606
+ #if PHP_API_VERSION >= 20100412
607
+ zend_get_object_classname(object, (const char **) &class_name, &class_name_len TSRMLS_CC);
608
+ #else
609
+ zend_get_object_classname(object, &class_name, &class_name_len TSRMLS_CC);
610
+ #endif
611
+ return class_name;
612
+ }
613
+
614
+ static int twig_add_method_to_class(void *pDest APPLY_TSRMLS_DC, int num_args, va_list args, zend_hash_key *hash_key)
615
+ {
616
+ zend_class_entry *ce;
617
+ zval *retval;
618
+ char *item;
619
+ size_t item_len;
620
+ zend_function *mptr = (zend_function *) pDest;
621
+ APPLY_TSRMLS_FETCH();
622
+
623
+ if (!(mptr->common.fn_flags & ZEND_ACC_PUBLIC)) {
624
+ return 0;
625
+ }
626
+
627
+ ce = *va_arg(args, zend_class_entry**);
628
+ retval = va_arg(args, zval*);
629
+
630
+ item_len = strlen(mptr->common.function_name);
631
+ item = estrndup(mptr->common.function_name, item_len);
632
+ php_strtolower(item, item_len);
633
+
634
+ if (strcmp("getenvironment", item) == 0) {
635
+ zend_class_entry **twig_template_ce;
636
+ if (zend_lookup_class("Twig_Template", strlen("Twig_Template"), &twig_template_ce TSRMLS_CC) == FAILURE) {
637
+ return 0;
638
+ }
639
+ if (instanceof_function(ce, *twig_template_ce TSRMLS_CC)) {
640
+ return 0;
641
+ }
642
+ }
643
+
644
+ add_assoc_stringl_ex(retval, item, item_len+1, item, item_len, 0);
645
+
646
+ return 0;
647
+ }
648
+
649
+ static int twig_add_property_to_class(void *pDest APPLY_TSRMLS_DC, int num_args, va_list args, zend_hash_key *hash_key)
650
+ {
651
+ zend_class_entry *ce;
652
+ zval *retval;
653
+ char *class_name, *prop_name;
654
+ zend_property_info *pptr = (zend_property_info *) pDest;
655
+ APPLY_TSRMLS_FETCH();
656
+
657
+ if (!(pptr->flags & ZEND_ACC_PUBLIC) || (pptr->flags & ZEND_ACC_STATIC)) {
658
+ return 0;
659
+ }
660
+
661
+ ce = *va_arg(args, zend_class_entry**);
662
+ retval = va_arg(args, zval*);
663
+
664
+ #if PHP_API_VERSION >= 20100412
665
+ zend_unmangle_property_name(pptr->name, pptr->name_length, (const char **) &class_name, (const char **) &prop_name);
666
+ #else
667
+ zend_unmangle_property_name(pptr->name, pptr->name_length, &class_name, &prop_name);
668
+ #endif
669
+
670
+ add_assoc_string(retval, prop_name, prop_name, 1);
671
+
672
+ return 0;
673
+ }
674
+
675
+ static void twig_add_class_to_cache(zval *cache, zval *object, char *class_name TSRMLS_DC)
676
+ {
677
+ zval *class_info, *class_methods, *class_properties;
678
+ zend_class_entry *class_ce;
679
+
680
+ class_ce = zend_get_class_entry(object TSRMLS_CC);
681
+
682
+ ALLOC_INIT_ZVAL(class_info);
683
+ ALLOC_INIT_ZVAL(class_methods);
684
+ ALLOC_INIT_ZVAL(class_properties);
685
+ array_init(class_info);
686
+ array_init(class_methods);
687
+ array_init(class_properties);
688
+ // add all methods to self::cache[$class]['methods']
689
+ zend_hash_apply_with_arguments(&class_ce->function_table APPLY_TSRMLS_CC, twig_add_method_to_class, 2, &class_ce, class_methods);
690
+ zend_hash_apply_with_arguments(&class_ce->properties_info APPLY_TSRMLS_CC, twig_add_property_to_class, 2, &class_ce, class_properties);
691
+
692
+ add_assoc_zval(class_info, "methods", class_methods);
693
+ add_assoc_zval(class_info, "properties", class_properties);
694
+ add_assoc_zval(cache, class_name, class_info);
695
+ }
696
+
697
+ /* {{{ proto mixed twig_template_get_attributes(TwigTemplate template, mixed object, mixed item, array arguments, string type, boolean isDefinedTest, boolean ignoreStrictCheck)
698
+ A C implementation of TwigTemplate::getAttribute() */
699
+ PHP_FUNCTION(twig_template_get_attributes)
700
+ {
701
+ zval *template;
702
+ zval *object;
703
+ char *item;
704
+ int item_len;
705
+ zval *zitem, ztmpitem;
706
+ zval *arguments = NULL;
707
+ zval *ret = NULL;
708
+ char *type = NULL;
709
+ int type_len = 0;
710
+ zend_bool isDefinedTest = 0;
711
+ zend_bool ignoreStrictCheck = 0;
712
+ int free_ret = 0;
713
+ zval *tmp_self_cache;
714
+ char *class_name = NULL;
715
+ zval *tmp_class;
716
+ char *type_name;
717
+
718
+ if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ozz|asbb", &template, &object, &zitem, &arguments, &type, &type_len, &isDefinedTest, &ignoreStrictCheck) == FAILURE) {
719
+ return;
720
+ }
721
+
722
+ // convert the item to a string
723
+ ztmpitem = *zitem;
724
+ zval_copy_ctor(&ztmpitem);
725
+ convert_to_string(&ztmpitem);
726
+ item_len = Z_STRLEN(ztmpitem);
727
+ item = estrndup(Z_STRVAL(ztmpitem), item_len);
728
+ zval_dtor(&ztmpitem);
729
+
730
+ if (!type) {
731
+ type = "any";
732
+ }
733
+
734
+ /*
735
+ // array
736
+ if (Twig_Template::METHOD_CALL !== $type) {
737
+ $arrayItem = is_bool($item) || is_float($item) ? (int) $item : $item;
738
+
739
+ if ((is_array($object) && array_key_exists($arrayItem, $object))
740
+ || ($object instanceof ArrayAccess && isset($object[$arrayItem]))
741
+ ) {
742
+ if ($isDefinedTest) {
743
+ return true;
744
+ }
745
+
746
+ return $object[$arrayItem];
747
+ }
748
+ */
749
+
750
+
751
+ if (strcmp("method", type) != 0) {
752
+ if ((TWIG_ARRAY_KEY_EXISTS(object, zitem))
753
+ || (TWIG_INSTANCE_OF(object, zend_ce_arrayaccess TSRMLS_CC) && TWIG_ISSET_ARRAYOBJECT_ELEMENT(object, zitem TSRMLS_CC))
754
+ ) {
755
+
756
+ if (isDefinedTest) {
757
+ efree(item);
758
+ RETURN_TRUE;
759
+ }
760
+
761
+ ret = TWIG_GET_ARRAY_ELEMENT_ZVAL(object, zitem TSRMLS_CC);
762
+
763
+ if (!ret) {
764
+ ret = &EG(uninitialized_zval);
765
+ }
766
+ RETVAL_ZVAL(ret, 1, 0);
767
+ if (free_ret) {
768
+ zval_ptr_dtor(&ret);
769
+ }
770
+ efree(item);
771
+ return;
772
+ }
773
+ /*
774
+ if (Twig_Template::ARRAY_CALL === $type) {
775
+ if ($isDefinedTest) {
776
+ return false;
777
+ }
778
+ if ($ignoreStrictCheck || !$this->env->isStrictVariables()) {
779
+ return null;
780
+ }
781
+ */
782
+ if (strcmp("array", type) == 0 || Z_TYPE_P(object) != IS_OBJECT) {
783
+ if (isDefinedTest) {
784
+ efree(item);
785
+ RETURN_FALSE;
786
+ }
787
+ if (ignoreStrictCheck || !TWIG_CALL_BOOLEAN(TWIG_PROPERTY_CHAR(template, "env" TSRMLS_CC), "isStrictVariables" TSRMLS_CC)) {
788
+ efree(item);
789
+ return;
790
+ }
791
+ /*
792
+ if ($object instanceof ArrayAccess) {
793
+ $message = sprintf('Key "%s" in object with ArrayAccess of class "%s" does not exist', $arrayItem, get_class($object));
794
+ } elseif (is_object($object)) {
795
+ $message = sprintf('Impossible to access a key "%s" on an object of class "%s" that does not implement ArrayAccess interface', $item, get_class($object));
796
+ } elseif (is_array($object)) {
797
+ if (empty($object)) {
798
+ $message = sprintf('Key "%s" does not exist as the array is empty', $arrayItem);
799
+ } else {
800
+ $message = sprintf('Key "%s" for array with keys "%s" does not exist', $arrayItem, implode(', ', array_keys($object)));
801
+ }
802
+ } elseif (Twig_Template::ARRAY_CALL === $type) {
803
+ if (null === $object) {
804
+ $message = sprintf('Impossible to access a key ("%s") on a null variable', $item);
805
+ } else {
806
+ $message = sprintf('Impossible to access a key ("%s") on a %s variable ("%s")', $item, gettype($object), $object);
807
+ }
808
+ } elseif (null === $object) {
809
+ $message = sprintf('Impossible to access an attribute ("%s") on a null variable', $item);
810
+ } else {
811
+ $message = sprintf('Impossible to access an attribute ("%s") on a %s variable ("%s")', $item, gettype($object), $object);
812
+ }
813
+ throw new Twig_Error_Runtime($message, -1, $this->getTemplateName());
814
+ }
815
+ }
816
+ */
817
+ if (TWIG_INSTANCE_OF(object, zend_ce_arrayaccess TSRMLS_CC)) {
818
+ TWIG_RUNTIME_ERROR(template TSRMLS_CC, "Key \"%s\" in object with ArrayAccess of class \"%s\" does not exist.", item, TWIG_GET_CLASS_NAME(object TSRMLS_CC));
819
+ } else if (Z_TYPE_P(object) == IS_OBJECT) {
820
+ TWIG_RUNTIME_ERROR(template TSRMLS_CC, "Impossible to access a key \"%s\" on an object of class \"%s\" that does not implement ArrayAccess interface.", item, TWIG_GET_CLASS_NAME(object TSRMLS_CC));
821
+ } else if (Z_TYPE_P(object) == IS_ARRAY) {
822
+ if (0 == zend_hash_num_elements(Z_ARRVAL_P(object))) {
823
+ TWIG_RUNTIME_ERROR(template TSRMLS_CC, "Key \"%s\" does not exist as the array is empty.", item);
824
+ } else {
825
+ char *array_keys = TWIG_IMPLODE_ARRAY_KEYS(", ", object TSRMLS_CC);
826
+ TWIG_RUNTIME_ERROR(template TSRMLS_CC, "Key \"%s\" for array with keys \"%s\" does not exist.", item, array_keys);
827
+ efree(array_keys);
828
+ }
829
+ } else {
830
+ char *type_name = zend_zval_type_name(object);
831
+ Z_ADDREF_P(object);
832
+ if (Z_TYPE_P(object) == IS_NULL) {
833
+ convert_to_string(object);
834
+ TWIG_RUNTIME_ERROR(template TSRMLS_CC,
835
+ (strcmp("array", type) == 0)
836
+ ? "Impossible to access a key (\"%s\") on a %s variable."
837
+ : "Impossible to access an attribute (\"%s\") on a %s variable.",
838
+ item, type_name);
839
+ } else {
840
+ convert_to_string(object);
841
+ TWIG_RUNTIME_ERROR(template TSRMLS_CC,
842
+ (strcmp("array", type) == 0)
843
+ ? "Impossible to access a key (\"%s\") on a %s variable (\"%s\")."
844
+ : "Impossible to access an attribute (\"%s\") on a %s variable (\"%s\").",
845
+ item, type_name, Z_STRVAL_P(object));
846
+ }
847
+ zval_ptr_dtor(&object);
848
+ }
849
+ efree(item);
850
+ return;
851
+ }
852
+ }
853
+
854
+ /*
855
+ if (!is_object($object)) {
856
+ if ($isDefinedTest) {
857
+ return false;
858
+ }
859
+ */
860
+
861
+ if (Z_TYPE_P(object) != IS_OBJECT) {
862
+ if (isDefinedTest) {
863
+ efree(item);
864
+ RETURN_FALSE;
865
+ }
866
+ /*
867
+ if ($ignoreStrictCheck || !$this->env->isStrictVariables()) {
868
+ return null;
869
+ }
870
+
871
+ if (null === $object) {
872
+ $message = sprintf('Impossible to invoke a method ("%s") on a null variable', $item);
873
+ } else {
874
+ $message = sprintf('Impossible to invoke a method ("%s") on a %s variable ("%s")', $item, gettype($object), $object);
875
+ }
876
+
877
+ throw new Twig_Error_Runtime($message, -1, $this->getTemplateName());
878
+ }
879
+ */
880
+ if (ignoreStrictCheck || !TWIG_CALL_BOOLEAN(TWIG_PROPERTY_CHAR(template, "env" TSRMLS_CC), "isStrictVariables" TSRMLS_CC)) {
881
+ efree(item);
882
+ return;
883
+ }
884
+
885
+ type_name = zend_zval_type_name(object);
886
+ Z_ADDREF_P(object);
887
+ if (Z_TYPE_P(object) == IS_NULL) {
888
+ convert_to_string_ex(&object);
889
+
890
+ TWIG_RUNTIME_ERROR(template TSRMLS_CC, "Impossible to invoke a method (\"%s\") on a %s variable.", item, type_name);
891
+ } else {
892
+ convert_to_string_ex(&object);
893
+
894
+ TWIG_RUNTIME_ERROR(template TSRMLS_CC, "Impossible to invoke a method (\"%s\") on a %s variable (\"%s\").", item, type_name, Z_STRVAL_P(object));
895
+ }
896
+
897
+ zval_ptr_dtor(&object);
898
+ efree(item);
899
+ return;
900
+ }
901
+ /*
902
+ $class = get_class($object);
903
+ */
904
+
905
+ class_name = TWIG_GET_CLASS_NAME(object TSRMLS_CC);
906
+ tmp_self_cache = TWIG_GET_STATIC_PROPERTY(template, "cache" TSRMLS_CC);
907
+ tmp_class = TWIG_GET_ARRAY_ELEMENT(tmp_self_cache, class_name, strlen(class_name) TSRMLS_CC);
908
+
909
+ if (!tmp_class) {
910
+ twig_add_class_to_cache(tmp_self_cache, object, class_name TSRMLS_CC);
911
+ tmp_class = TWIG_GET_ARRAY_ELEMENT(tmp_self_cache, class_name, strlen(class_name) TSRMLS_CC);
912
+ }
913
+ efree(class_name);
914
+
915
+ /*
916
+ // object property
917
+ if (Twig_Template::METHOD_CALL !== $type && !$object instanceof Twig_Template) {
918
+ if (isset($object->$item) || array_key_exists((string) $item, $object)) {
919
+ if ($isDefinedTest) {
920
+ return true;
921
+ }
922
+
923
+ if ($this->env->hasExtension('Twig_Extension_Sandbox')) {
924
+ $this->env->getExtension('Twig_Extension_Sandbox')->checkPropertyAllowed($object, $item);
925
+ }
926
+
927
+ return $object->$item;
928
+ }
929
+ }
930
+ */
931
+ if (strcmp("method", type) != 0 && !TWIG_INSTANCE_OF_USERLAND(object, "Twig_Template" TSRMLS_CC)) {
932
+ zval *tmp_properties, *tmp_item;
933
+
934
+ tmp_properties = TWIG_GET_ARRAY_ELEMENT(tmp_class, "properties", strlen("properties") TSRMLS_CC);
935
+ tmp_item = TWIG_GET_ARRAY_ELEMENT(tmp_properties, item, item_len TSRMLS_CC);
936
+
937
+ if (tmp_item || TWIG_HAS_PROPERTY(object, zitem TSRMLS_CC) || TWIG_HAS_DYNAMIC_PROPERTY(object, item, item_len TSRMLS_CC)) {
938
+ if (isDefinedTest) {
939
+ efree(item);
940
+ RETURN_TRUE;
941
+ }
942
+ if (TWIG_CALL_SB(TWIG_PROPERTY_CHAR(template, "env" TSRMLS_CC), "hasExtension", "Twig_Extension_Sandbox" TSRMLS_CC)) {
943
+ TWIG_CALL_ZZ(TWIG_CALL_S(TWIG_PROPERTY_CHAR(template, "env" TSRMLS_CC), "getExtension", "Twig_Extension_Sandbox" TSRMLS_CC), "checkPropertyAllowed", object, zitem TSRMLS_CC);
944
+ }
945
+ if (EG(exception)) {
946
+ efree(item);
947
+ return;
948
+ }
949
+
950
+ ret = TWIG_PROPERTY(object, zitem TSRMLS_CC);
951
+ efree(item);
952
+ RETURN_ZVAL(ret, 1, 0);
953
+ }
954
+ }
955
+ /*
956
+ // object method
957
+ if (!isset(self::$cache[$class]['methods'])) {
958
+ if ($object instanceof self) {
959
+ $ref = new ReflectionClass($class);
960
+ $methods = array();
961
+
962
+ foreach ($ref->getMethods(ReflectionMethod::IS_PUBLIC) as $refMethod) {
963
+ $methodName = strtolower($refMethod->name);
964
+
965
+ // Accessing the environment from templates is forbidden to prevent untrusted changes to the environment
966
+ if ('getenvironment' !== $methodName) {
967
+ $methods[$methodName] = true;
968
+ }
969
+ }
970
+
971
+ self::$cache[$class]['methods'] = $methods;
972
+ } else {
973
+ self::$cache[$class]['methods'] = array_change_key_case(array_flip(get_class_methods($object)));
974
+ }
975
+ }
976
+
977
+ $call = false;
978
+ $lcItem = strtolower($item);
979
+ if (isset(self::$cache[$class]['methods'][$lcItem])) {
980
+ $method = (string) $item;
981
+ } elseif (isset(self::$cache[$class]['methods']['get'.$lcItem])) {
982
+ $method = 'get'.$item;
983
+ } elseif (isset(self::$cache[$class]['methods']['is'.$lcItem])) {
984
+ $method = 'is'.$item;
985
+ } elseif (isset(self::$cache[$class]['methods']['__call'])) {
986
+ $method = (string) $item;
987
+ $call = true;
988
+ */
989
+ {
990
+ int call = 0;
991
+ char *lcItem = TWIG_STRTOLOWER(item, item_len);
992
+ int lcItem_length;
993
+ char *method = NULL;
994
+ char *methodForDeprecation = NULL;
995
+ char *tmp_method_name_get;
996
+ char *tmp_method_name_is;
997
+ zval *zmethod;
998
+ zval *tmp_methods;
999
+
1000
+ lcItem_length = strlen(lcItem);
1001
+ tmp_method_name_get = emalloc(4 + lcItem_length);
1002
+ tmp_method_name_is = emalloc(3 + lcItem_length);
1003
+
1004
+ sprintf(tmp_method_name_get, "get%s", lcItem);
1005
+ sprintf(tmp_method_name_is, "is%s", lcItem);
1006
+
1007
+ tmp_methods = TWIG_GET_ARRAY_ELEMENT(tmp_class, "methods", strlen("methods") TSRMLS_CC);
1008
+ methodForDeprecation = emalloc(item_len + 1);
1009
+ sprintf(methodForDeprecation, "%s", item);
1010
+
1011
+ if (TWIG_GET_ARRAY_ELEMENT(tmp_methods, lcItem, lcItem_length TSRMLS_CC)) {
1012
+ method = item;
1013
+ } else if (TWIG_GET_ARRAY_ELEMENT(tmp_methods, tmp_method_name_get, lcItem_length + 3 TSRMLS_CC)) {
1014
+ method = tmp_method_name_get;
1015
+ } else if (TWIG_GET_ARRAY_ELEMENT(tmp_methods, tmp_method_name_is, lcItem_length + 2 TSRMLS_CC)) {
1016
+ method = tmp_method_name_is;
1017
+ } else if (TWIG_GET_ARRAY_ELEMENT(tmp_methods, "__call", 6 TSRMLS_CC)) {
1018
+ method = item;
1019
+ call = 1;
1020
+ /*
1021
+ } else {
1022
+ if ($isDefinedTest) {
1023
+ return false;
1024
+ }
1025
+
1026
+ if ($ignoreStrictCheck || !$this->env->isStrictVariables()) {
1027
+ return null;
1028
+ }
1029
+
1030
+ throw new Twig_Error_Runtime(sprintf('Method "%s" for object "%s" does not exist.', $item, get_class($object)), -1, $this->getTemplateName());
1031
+ }
1032
+
1033
+ if ($isDefinedTest) {
1034
+ return true;
1035
+ }
1036
+ */
1037
+ } else {
1038
+ efree(tmp_method_name_get);
1039
+ efree(tmp_method_name_is);
1040
+ efree(lcItem);
1041
+
1042
+ if (isDefinedTest) {
1043
+ efree(item);
1044
+ RETURN_FALSE;
1045
+ }
1046
+ if (ignoreStrictCheck || !TWIG_CALL_BOOLEAN(TWIG_PROPERTY_CHAR(template, "env" TSRMLS_CC), "isStrictVariables" TSRMLS_CC)) {
1047
+ efree(item);
1048
+ return;
1049
+ }
1050
+ TWIG_RUNTIME_ERROR(template TSRMLS_CC, "Neither the property \"%s\" nor one of the methods \"%s()\", \"get%s()\"/\"is%s()\" or \"__call()\" exist and have public access in class \"%s\".", item, item, item, item, TWIG_GET_CLASS_NAME(object TSRMLS_CC));
1051
+ efree(item);
1052
+ return;
1053
+ }
1054
+
1055
+ if (isDefinedTest) {
1056
+ efree(tmp_method_name_get);
1057
+ efree(tmp_method_name_is);
1058
+ efree(lcItem);efree(item);
1059
+ RETURN_TRUE;
1060
+ }
1061
+ /*
1062
+ if ($this->env->hasExtension('Twig_Extension_Sandbox')) {
1063
+ $this->env->getExtension('Twig_Extension_Sandbox')->checkMethodAllowed($object, $method);
1064
+ }
1065
+ */
1066
+ MAKE_STD_ZVAL(zmethod);
1067
+ ZVAL_STRING(zmethod, method, 1);
1068
+ if (TWIG_CALL_SB(TWIG_PROPERTY_CHAR(template, "env" TSRMLS_CC), "hasExtension", "Twig_Extension_Sandbox" TSRMLS_CC)) {
1069
+ TWIG_CALL_ZZ(TWIG_CALL_S(TWIG_PROPERTY_CHAR(template, "env" TSRMLS_CC), "getExtension", "Twig_Extension_Sandbox" TSRMLS_CC), "checkMethodAllowed", object, zmethod TSRMLS_CC);
1070
+ }
1071
+ zval_ptr_dtor(&zmethod);
1072
+ if (EG(exception)) {
1073
+ efree(tmp_method_name_get);
1074
+ efree(tmp_method_name_is);
1075
+ efree(lcItem);efree(item);
1076
+ return;
1077
+ }
1078
+ /*
1079
+ // Some objects throw exceptions when they have __call, and the method we try
1080
+ // to call is not supported. If ignoreStrictCheck is true, we should return null.
1081
+ try {
1082
+ $ret = call_user_func_array(array($object, $method), $arguments);
1083
+ } catch (BadMethodCallException $e) {
1084
+ if ($call && ($ignoreStrictCheck || !$this->env->isStrictVariables())) {
1085
+ return null;
1086
+ }
1087
+ throw $e;
1088
+ }
1089
+ */
1090
+ ret = TWIG_CALL_USER_FUNC_ARRAY(object, method, arguments TSRMLS_CC);
1091
+ if (EG(exception) && TWIG_INSTANCE_OF(EG(exception), spl_ce_BadMethodCallException TSRMLS_CC)) {
1092
+ if (ignoreStrictCheck || !TWIG_CALL_BOOLEAN(TWIG_PROPERTY_CHAR(template, "env" TSRMLS_CC), "isStrictVariables" TSRMLS_CC)) {
1093
+ efree(tmp_method_name_get);
1094
+ efree(tmp_method_name_is);
1095
+ efree(lcItem);efree(item);
1096
+ zend_clear_exception(TSRMLS_C);
1097
+ return;
1098
+ }
1099
+ }
1100
+ free_ret = 1;
1101
+ efree(tmp_method_name_get);
1102
+ efree(tmp_method_name_is);
1103
+ efree(lcItem);
1104
+ /*
1105
+ // @deprecated in 1.28
1106
+ if ($object instanceof Twig_TemplateInterface) {
1107
+ $self = $object->getTemplateName() === $this->getTemplateName();
1108
+ $message = sprintf('Calling "%s" on template "%s" from template "%s" is deprecated since version 1.28 and won\'t be supported anymore in 2.0.', $item, $object->getTemplateName(), $this->getTemplateName());
1109
+ if ('renderBlock' === $method || 'displayBlock' === $method) {
1110
+ $message .= sprintf(' Use block("%s"%s) instead).', $arguments[0], $self ? '' : ', template');
1111
+ } elseif ('hasBlock' === $method) {
1112
+ $message .= sprintf(' Use "block("%s"%s) is defined" instead).', $arguments[0], $self ? '' : ', template');
1113
+ } elseif ('render' === $method || 'display' === $method) {
1114
+ $message .= sprintf(' Use include("%s") instead).', $object->getTemplateName());
1115
+ }
1116
+ @trigger_error($message, E_USER_DEPRECATED);
1117
+
1118
+ return $ret === '' ? '' : new Twig_Markup($ret, $this->env->getCharset());
1119
+ }
1120
+
1121
+ return $ret;
1122
+ */
1123
+ efree(item);
1124
+ // ret can be null, if e.g. the called method throws an exception
1125
+ if (ret) {
1126
+ if (TWIG_INSTANCE_OF_USERLAND(object, "Twig_TemplateInterface" TSRMLS_CC)) {
1127
+ int self;
1128
+ int old_error_reporting;
1129
+ zval *object_filename;
1130
+ zval *this_filename;
1131
+ zval *filename_func;
1132
+ char *deprecation_message_complement = NULL;
1133
+ char *deprecation_message = NULL;
1134
+
1135
+ MAKE_STD_ZVAL(object_filename);
1136
+ MAKE_STD_ZVAL(this_filename);
1137
+ MAKE_STD_ZVAL(filename_func);
1138
+
1139
+ // Get templates names
1140
+ ZVAL_STRINGL(filename_func, "getTemplateName", sizeof("getTemplateName")-1, 1);
1141
+ call_user_function(EG(function_table), &object, filename_func, object_filename, 0, 0 TSRMLS_CC);
1142
+ ZVAL_STRINGL(filename_func, "getTemplateName", sizeof("getTemplateName")-1, 1);
1143
+ call_user_function(EG(function_table), &template, filename_func, this_filename, 0, 0 TSRMLS_CC);
1144
+
1145
+ self = (strcmp(Z_STRVAL_P(object_filename), Z_STRVAL_P(this_filename)) == 0);
1146
+
1147
+ if (strcmp(methodForDeprecation, "renderBlock") == 0 || strcmp(methodForDeprecation, "displayBlock") == 0) {
1148
+ zval **arg0;
1149
+ zend_hash_index_find(HASH_OF(arguments), 0, (void **) &arg0);
1150
+ asprintf(
1151
+ &deprecation_message_complement,
1152
+ " Use block(\"%s\"%s) instead).",
1153
+ Z_STRVAL_PP(arg0),
1154
+ self ? "" : ", template"
1155
+ );
1156
+ } else if (strcmp(methodForDeprecation, "hasBlock") == 0) {
1157
+ zval **arg0;
1158
+ zend_hash_index_find(HASH_OF(arguments), 0, (void **) &arg0);
1159
+ asprintf(
1160
+ &deprecation_message_complement,
1161
+ " Use \"block(\"%s\"%s) is defined\" instead).",
1162
+ Z_STRVAL_PP(arg0),
1163
+ self ? "" : ", template"
1164
+ );
1165
+ } else if (strcmp(methodForDeprecation, "render") == 0 || strcmp(methodForDeprecation, "display") == 0) {
1166
+ asprintf(
1167
+ &deprecation_message_complement,
1168
+ " Use include(\"%s\") instead).",
1169
+ Z_STRVAL_P(object_filename)
1170
+ );
1171
+ } else {
1172
+ deprecation_message_complement = (char*)calloc(0, sizeof(char));
1173
+ }
1174
+
1175
+ asprintf(
1176
+ &deprecation_message,
1177
+ "Calling \"%s\" on template \"%s\" from template \"%s\" is deprecated since version 1.28 and won't be supported anymore in 2.0.%s",
1178
+ methodForDeprecation,
1179
+ Z_STRVAL_P(object_filename),
1180
+ Z_STRVAL_P(this_filename),
1181
+ deprecation_message_complement
1182
+ );
1183
+
1184
+ old_error_reporting = EG(error_reporting);
1185
+ EG(error_reporting) = 0;
1186
+ zend_error(E_USER_DEPRECATED, "%s", deprecation_message);
1187
+ EG(error_reporting) = old_error_reporting;
1188
+
1189
+ FREE_DTOR(filename_func)
1190
+ FREE_DTOR(object_filename)
1191
+ FREE_DTOR(this_filename)
1192
+ free(deprecation_message);
1193
+ free(deprecation_message_complement);
1194
+
1195
+ if (Z_STRLEN_P(ret) != 0) {
1196
+ zval *charset = TWIG_CALL_USER_FUNC_ARRAY(TWIG_PROPERTY_CHAR(template, "env" TSRMLS_CC), "getCharset", NULL TSRMLS_CC);
1197
+ TWIG_NEW(return_value, "Twig_Markup", ret, charset TSRMLS_CC);
1198
+ zval_ptr_dtor(&charset);
1199
+ if (ret) {
1200
+ zval_ptr_dtor(&ret);
1201
+ }
1202
+ efree(methodForDeprecation);
1203
+ return;
1204
+ }
1205
+ }
1206
+
1207
+ RETVAL_ZVAL(ret, 1, 0);
1208
+ if (free_ret) {
1209
+ zval_ptr_dtor(&ret);
1210
+ }
1211
+ }
1212
+
1213
+ efree(methodForDeprecation);
1214
+ }
1215
+ }
vendor/twig/twig/lib/Twig/Autoloader.php ADDED
@@ -0,0 +1,54 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * This file is part of Twig.
5
+ *
6
+ * (c) Fabien Potencier
7
+ *
8
+ * For the full copyright and license information, please view the LICENSE
9
+ * file that was distributed with this source code.
10
+ */
11
+
12
+ @trigger_error('The Twig_Autoloader class is deprecated since version 1.21 and will be removed in 2.0. Use Composer instead.', E_USER_DEPRECATED);
13
+
14
+ /**
15
+ * Autoloads Twig classes.
16
+ *
17
+ * @author Fabien Potencier <fabien@symfony.com>
18
+ *
19
+ * @deprecated since 1.21 and will be removed in 2.0. Use Composer instead. 2.0.
20
+ */
21
+ class Twig_Autoloader
22
+ {
23
+ /**
24
+ * Registers Twig_Autoloader as an SPL autoloader.
25
+ *
26
+ * @param bool $prepend whether to prepend the autoloader or not
27
+ */
28
+ public static function register($prepend = false)
29
+ {
30
+ @trigger_error('Using Twig_Autoloader is deprecated since version 1.21. Use Composer instead.', E_USER_DEPRECATED);
31
+
32
+ if (PHP_VERSION_ID < 50300) {
33
+ spl_autoload_register(array(__CLASS__, 'autoload'));
34
+ } else {
35
+ spl_autoload_register(array(__CLASS__, 'autoload'), true, $prepend);
36
+ }
37
+ }
38
+
39
+ /**
40
+ * Handles autoloading of classes.
41
+ *
42
+ * @param string $class a class name
43
+ */
44
+ public static function autoload($class)
45
+ {
46
+ if (0 !== strpos($class, 'Twig')) {
47
+ return;
48
+ }
49
+
50
+ if (is_file($file = dirname(__FILE__).'/../'.str_replace(array('_', "\0"), array('/', ''), $class).'.php')) {
51
+ require $file;
52
+ }
53
+ }
54
+ }
vendor/twig/twig/lib/Twig/BaseNodeVisitor.php ADDED
@@ -0,0 +1,50 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * This file is part of Twig.
5
+ *
6
+ * (c) Fabien Potencier
7
+ *
8
+ * For the full copyright and license information, please view the LICENSE
9
+ * file that was distributed with this source code.
10
+ */
11
+
12
+ /**
13
+ * Twig_BaseNodeVisitor can be used to make node visitors compatible with Twig 1.x and 2.x.
14
+ *
15
+ * @author Fabien Potencier <fabien@symfony.com>
16
+ */
17
+ abstract class Twig_BaseNodeVisitor implements Twig_NodeVisitorInterface
18
+ {
19
+ final public function enterNode(Twig_NodeInterface $node, Twig_Environment $env)
20
+ {
21
+ if (!$node instanceof Twig_Node) {
22
+ throw new LogicException('Twig_BaseNodeVisitor only supports Twig_Node instances.');
23
+ }
24
+
25
+ return $this->doEnterNode($node, $env);
26
+ }
27
+
28
+ final public function leaveNode(Twig_NodeInterface $node, Twig_Environment $env)
29
+ {
30
+ if (!$node instanceof Twig_Node) {
31
+ throw new LogicException('Twig_BaseNodeVisitor only supports Twig_Node instances.');
32
+ }
33
+
34
+ return $this->doLeaveNode($node, $env);
35
+ }
36
+
37
+ /**
38
+ * Called before child nodes are visited.
39
+ *
40
+ * @return Twig_Node The modified node
41
+ */
42
+ abstract protected function doEnterNode(Twig_Node $node, Twig_Environment $env);
43
+
44
+ /**
45
+ * Called after child nodes are visited.
46
+ *
47
+ * @return Twig_Node|false The modified node or false if the node must be removed
48
+ */
49
+ abstract protected function doLeaveNode(Twig_Node $node, Twig_Environment $env);
50
+ }
vendor/twig/twig/lib/Twig/Cache/Filesystem.php ADDED
@@ -0,0 +1,86 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * This file is part of Twig.
5
+ *
6
+ * (c) Fabien Potencier
7
+ *
8
+ * For the full copyright and license information, please view the LICENSE
9
+ * file that was distributed with this source code.
10
+ */
11
+
12
+ /**
13
+ * Implements a cache on the filesystem.
14
+ *
15
+ * @author Andrew Tch <andrew@noop.lv>
16
+ */
17
+ class Twig_Cache_Filesystem implements Twig_CacheInterface
18
+ {
19
+ const FORCE_BYTECODE_INVALIDATION = 1;
20
+
21
+ private $directory;
22
+ private $options;
23
+
24
+ /**
25
+ * @param $directory string The root cache directory
26
+ * @param $options int A set of options
27
+ */
28
+ public function __construct($directory, $options = 0)
29
+ {
30
+ $this->directory = rtrim($directory, '\/').'/';
31
+ $this->options = $options;
32
+ }
33
+
34
+ public function generateKey($name, $className)
35
+ {
36
+ $hash = hash('sha256', $className);
37
+
38
+ return $this->directory.$hash[0].$hash[1].'/'.$hash.'.php';
39
+ }
40
+
41
+ public function load($key)
42
+ {
43
+ if (file_exists($key)) {
44
+ @include_once $key;
45
+ }
46
+ }
47
+
48
+ public function write($key, $content)
49
+ {
50
+ $dir = dirname($key);
51
+ if (!is_dir($dir)) {
52
+ if (false === @mkdir($dir, 0777, true) && !is_dir($dir)) {
53
+ throw new RuntimeException(sprintf('Unable to create the cache directory (%s).', $dir));
54
+ }
55
+ } elseif (!is_writable($dir)) {
56
+ throw new RuntimeException(sprintf('Unable to write in the cache directory (%s).', $dir));
57
+ }
58
+
59
+ $tmpFile = tempnam($dir, basename($key));
60
+ if (false !== @file_put_contents($tmpFile, $content) && @rename($tmpFile, $key)) {
61
+ @chmod($key, 0666 & ~umask());
62
+
63
+ if (self::FORCE_BYTECODE_INVALIDATION == ($this->options & self::FORCE_BYTECODE_INVALIDATION)) {
64
+ // Compile cached file into bytecode cache
65
+ if (function_exists('opcache_invalidate')) {
66
+ opcache_invalidate($key, true);
67
+ } elseif (function_exists('apc_compile_file')) {
68
+ apc_compile_file($key);
69
+ }
70
+ }
71
+
72
+ return;
73
+ }
74
+
75
+ throw new RuntimeException(sprintf('Failed to write cache file "%s".', $key));
76
+ }
77
+
78
+ public function getTimestamp($key)
79
+ {
80
+ if (!file_exists($key)) {
81
+ return 0;
82
+ }
83
+
84
+ return (int) @filemtime($key);
85
+ }
86
+ }
vendor/twig/twig/lib/Twig/Cache/Null.php ADDED
@@ -0,0 +1,38 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * This file is part of Twig.
5
+ *
6
+ * (c) Fabien Potencier
7
+ *
8
+ * For the full copyright and license information, please view the LICENSE
9
+ * file that was distributed with this source code.
10
+ */
11
+
12
+ /**
13
+ * Implements a no-cache strategy.
14
+ *
15
+ * @final
16
+ *
17
+ * @author Fabien Potencier <fabien@symfony.com>
18
+ */
19
+ class Twig_Cache_Null implements Twig_CacheInterface
20
+ {
21
+ public function generateKey($name, $className)
22
+ {
23
+ return '';
24
+ }
25
+
26
+ public function write($key, $content)
27
+ {
28
+ }
29
+
30
+ public function load($key)
31
+ {
32
+ }
33
+
34
+ public function getTimestamp($key)
35
+ {
36
+ return 0;
37
+ }
38
+ }
vendor/twig/twig/lib/Twig/CacheInterface.php ADDED
@@ -0,0 +1,56 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * This file is part of Twig.
5
+ *
6
+ * (c) Fabien Potencier
7
+ *
8
+ * For the full copyright and license information, please view the LICENSE
9
+ * file that was distributed with this source code.
10
+ */
11
+
12
+ /**
13
+ * Interface implemented by cache classes.
14
+ *
15
+ * It is highly recommended to always store templates on the filesystem to
16
+ * benefit from the PHP opcode cache. This interface is mostly useful if you
17
+ * need to implement a custom strategy for storing templates on the filesystem.
18
+ *
19
+ * @author Andrew Tch <andrew@noop.lv>
20
+ */
21
+ interface Twig_CacheInterface
22
+ {
23
+ /**
24
+ * Generates a cache key for the given template class name.
25
+ *
26
+ * @param string $name The template name
27
+ * @param string $className The template class name
28
+ *
29
+ * @return string
30
+ */
31
+ public function generateKey($name, $className);
32
+
33
+ /**
34
+ * Writes the compiled template to cache.
35
+ *
36
+ * @param string $key The cache key
37
+ * @param string $content The template representation as a PHP class
38
+ */
39
+ public function write($key, $content);
40
+
41
+ /**
42
+ * Loads a template from the cache.
43
+ *
44
+ * @param string $key The cache key
45
+ */
46
+ public function load($key);
47
+
48
+ /**
49
+ * Returns the modification timestamp of a key.
50
+ *
51
+ * @param string $key The cache key
52
+ *
53
+ * @return int
54
+ */
55
+ public function getTimestamp($key);
56
+ }
vendor/twig/twig/lib/Twig/Compiler.php ADDED
@@ -0,0 +1,281 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * This file is part of Twig.
5
+ *
6
+ * (c) Fabien Potencier
7
+ * (c) Armin Ronacher
8
+ *
9
+ * For the full copyright and license information, please view the LICENSE
10
+ * file that was distributed with this source code.
11
+ */
12
+
13
+ /**
14
+ * Compiles a node to PHP code.
15
+ *
16
+ * @author Fabien Potencier <fabien@symfony.com>
17
+ */
18
+ class Twig_Compiler implements Twig_CompilerInterface
19
+ {
20
+ protected $lastLine;
21
+ protected $source;
22
+ protected $indentation;
23
+ protected $env;
24
+ protected $debugInfo = array();
25
+ protected $sourceOffset;
26
+ protected $sourceLine;
27
+ protected $filename;
28
+
29
+ public function __construct(Twig_Environment $env)
30
+ {
31
+ $this->env = $env;
32
+ }
33
+
34
+ /**
35
+ * @deprecated since 1.25 (to be removed in 2.0)
36
+ */
37
+ public function getFilename()
38
+ {
39
+ @trigger_error(sprintf('The %s() method is deprecated since version 1.25 and will be removed in 2.0.', __FUNCTION__), E_USER_DEPRECATED);
40
+
41
+ return $this->filename;
42
+ }
43
+
44
+ /**
45
+ * Returns the environment instance related to this compiler.
46
+ *
47
+ * @return Twig_Environment
48
+ */
49
+ public function getEnvironment()
50
+ {
51
+ return $this->env;
52
+ }
53
+
54
+ /**
55
+ * Gets the current PHP code after compilation.
56
+ *
57
+ * @return string The PHP code
58
+ */
59
+ public function getSource()
60
+ {
61
+ return $this->source;
62
+ }
63
+
64
+ /**
65
+ * Compiles a node.
66
+ *
67
+ * @param Twig_NodeInterface $node The node to compile
68
+ * @param int $indentation The current indentation
69
+ *
70
+ * @return $this
71
+ */
72
+ public function compile(Twig_NodeInterface $node, $indentation = 0)
73
+ {
74
+ $this->lastLine = null;
75
+ $this->source = '';
76
+ $this->debugInfo = array();
77
+ $this->sourceOffset = 0;
78
+ // source code starts at 1 (as we then increment it when we encounter new lines)
79
+ $this->sourceLine = 1;
80
+ $this->indentation = $indentation;
81
+
82
+ if ($node instanceof Twig_Node_Module) {
83
+ // to be removed in 2.0
84
+ $this->filename = $node->getTemplateName();
85
+ }
86
+
87
+ $node->compile($this);
88
+
89
+ return $this;
90
+ }
91
+
92
+ public function subcompile(Twig_NodeInterface $node, $raw = true)
93
+ {
94
+ if (false === $raw) {
95
+ $this->source .= str_repeat(' ', $this->indentation * 4);
96
+ }
97
+
98
+ $node->compile($this);
99
+
100
+ return $this;
101
+ }
102
+
103
+ /**
104
+ * Adds a raw string to the compiled code.
105
+ *
106
+ * @param string $string The string
107
+ *
108
+ * @return $this
109
+ */
110
+ public function raw($string)
111
+ {
112
+ $this->source .= $string;
113
+
114
+ return $this;
115
+ }
116
+
117
+ /**
118
+ * Writes a string to the compiled code by adding indentation.
119
+ *
120
+ * @return $this
121
+ */
122
+ public function write()
123
+ {
124
+ $strings = func_get_args();
125
+ foreach ($strings as $string) {
126
+ $this->source .= str_repeat(' ', $this->indentation * 4).$string;
127
+ }
128
+
129
+ return $this;
130
+ }
131
+
132
+ /**
133
+ * Appends an indentation to the current PHP code after compilation.
134
+ *
135
+ * @return $this
136
+ *
137
+ * @deprecated since 1.27 (to be removed in 2.0).
138
+ */
139
+ public function addIndentation()
140
+ {
141
+ @trigger_error('The '.__METHOD__.' method is deprecated since version 1.27 and will be removed in 2.0. Use write(\'\') instead.', E_USER_DEPRECATED);
142
+
143
+ $this->source .= str_repeat(' ', $this->indentation * 4);
144
+
145
+ return $this;
146
+ }
147
+
148
+ /**
149
+ * Adds a quoted string to the compiled code.
150
+ *
151
+ * @param string $value The string
152
+ *
153
+ * @return $this
154
+ */
155
+ public function string($value)
156
+ {
157
+ $this->source .= sprintf('"%s"', addcslashes($value, "\0\t\"\$\\"));
158
+
159
+ return $this;
160
+ }
161
+
162
+ /**
163
+ * Returns a PHP representation of a given value.
164
+ *
165
+ * @param mixed $value The value to convert
166
+ *
167
+ * @return $this
168
+ */
169
+ public function repr($value)
170
+ {
171
+ if (is_int($value) || is_float($value)) {
172
+ if (false !== $locale = setlocale(LC_NUMERIC, 0)) {
173
+ setlocale(LC_NUMERIC, 'C');
174
+ }
175
+
176
+ $this->raw($value);
177
+
178
+ if (false !== $locale) {
179
+ setlocale(LC_NUMERIC, $locale);
180
+ }
181
+ } elseif (null === $value) {
182
+ $this->raw('null');
183
+ } elseif (is_bool($value)) {
184
+ $this->raw($value ? 'true' : 'false');
185
+ } elseif (is_array($value)) {
186
+ $this->raw('array(');
187
+ $first = true;
188
+ foreach ($value as $key => $v) {
189
+ if (!$first) {
190
+ $this->raw(', ');
191
+ }
192
+ $first = false;
193
+ $this->repr($key);
194
+ $this->raw(' => ');
195
+ $this->repr($v);
196
+ }
197
+ $this->raw(')');
198
+ } else {
199
+ $this->string($value);
200
+ }
201
+
202
+ return $this;
203
+ }
204
+
205
+ /**
206
+ * Adds debugging information.
207
+ *
208
+ * @return $this
209
+ */
210
+ public function addDebugInfo(Twig_NodeInterface $node)
211
+ {
212
+ if ($node->getTemplateLine() != $this->lastLine) {
213
+ $this->write(sprintf("// line %d\n", $node->getTemplateLine()));
214
+
215
+ // when mbstring.func_overload is set to 2
216
+ // mb_substr_count() replaces substr_count()
217
+ // but they have different signatures!
218
+ if (((int) ini_get('mbstring.func_overload')) & 2) {
219
+ @trigger_error('Support for having "mbstring.func_overload" different from 0 is deprecated version 1.29 and will be removed in 2.0.', E_USER_DEPRECATED);
220
+
221
+ // this is much slower than the "right" version
222
+ $this->sourceLine += mb_substr_count(mb_substr($this->source, $this->sourceOffset), "\n");
223
+ } else {
224
+ $this->sourceLine += substr_count($this->source, "\n", $this->sourceOffset);
225
+ }
226
+ $this->sourceOffset = strlen($this->source);
227
+ $this->debugInfo[$this->sourceLine] = $node->getTemplateLine();
228
+
229
+ $this->lastLine = $node->getTemplateLine();
230
+ }
231
+
232
+ return $this;
233
+ }
234
+
235
+ public function getDebugInfo()
236
+ {
237
+ ksort($this->debugInfo);
238
+
239
+ return $this->debugInfo;
240
+ }
241
+
242
+ /**
243
+ * Indents the generated code.
244
+ *
245
+ * @param int $step The number of indentation to add
246
+ *
247
+ * @return $this
248
+ */
249
+ public function indent($step = 1)
250
+ {
251
+ $this->indentation += $step;
252
+
253
+ return $this;
254
+ }
255
+
256
+ /**
257
+ * Outdents the generated code.
258
+ *
259
+ * @param int $step The number of indentation to remove
260
+ *
261
+ * @return $this
262
+ *
263
+ * @throws LogicException When trying to outdent too much so the indentation would become negative
264
+ */
265
+ public function outdent($step = 1)
266
+ {
267
+ // can't outdent by more steps than the current indentation level
268
+ if ($this->indentation < $step) {
269
+ throw new LogicException('Unable to call outdent() as the indentation would become negative.');
270
+ }
271
+
272
+ $this->indentation -= $step;
273
+
274
+ return $this;
275
+ }
276
+
277
+ public function getVarName()
278
+ {
279
+ return sprintf('__internal_%s', hash('sha256', uniqid(mt_rand(), true), false));
280
+ }
281
+ }
vendor/twig/twig/lib/Twig/CompilerInterface.php ADDED
@@ -0,0 +1,34 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * This file is part of Twig.
5
+ *
6
+ * (c) Fabien Potencier
7
+ *
8
+ * For the full copyright and license information, please view the LICENSE
9
+ * file that was distributed with this source code.
10
+ */
11
+
12
+ /**
13
+ * Interface implemented by compiler classes.
14
+ *
15
+ * @author Fabien Potencier <fabien@symfony.com>
16
+ *
17
+ * @deprecated since 1.12 (to be removed in 3.0)
18
+ */
19
+ interface Twig_CompilerInterface
20
+ {
21
+ /**
22
+ * Compiles a node.
23
+ *
24
+ * @return $this
25
+ */
26
+ public function compile(Twig_NodeInterface $node);
27
+
28
+ /**
29
+ * Gets the current PHP code after compilation.
30
+ *
31
+ * @return string The PHP code
32
+ */
33
+ public function getSource();
34
+ }
vendor/twig/twig/lib/Twig/ContainerRuntimeLoader.php ADDED
@@ -0,0 +1,37 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * This file is part of Twig.
5
+ *
6
+ * (c) Fabien Potencier
7
+ *
8
+ * For the full copyright and license information, please view the LICENSE
9
+ * file that was distributed with this source code.
10
+ */
11
+
12
+ use Psr\Container\ContainerInterface;
13
+
14
+ /**
15
+ * Lazily loads Twig runtime implementations from a PSR-11 container.
16
+ *
17
+ * Note that the runtime services MUST use their class names as identifiers.
18
+ *
19
+ * @author Fabien Potencier <fabien@symfony.com>
20
+ * @author Robin Chalas <robin.chalas@gmail.com>
21
+ */
22
+ class Twig_ContainerRuntimeLoader implements Twig_RuntimeLoaderInterface
23
+ {
24
+ private $container;
25
+
26
+ public function __construct(ContainerInterface $container)
27
+ {
28
+ $this->container = $container;
29
+ }
30
+
31
+ public function load($class)
32
+ {
33
+ if ($this->container->has($class)) {
34
+ return $this->container->get($class);
35
+ }
36
+ }
37
+ }
vendor/twig/twig/lib/Twig/Environment.php ADDED
@@ -0,0 +1,1561 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * This file is part of Twig.
5
+ *
6
+ * (c) Fabien Potencier
7
+ *
8
+ * For the full copyright and license information, please view the LICENSE
9
+ * file that was distributed with this source code.
10
+ */
11
+
12
+ /**
13
+ * Stores the Twig configuration.
14
+ *
15
+ * @author Fabien Potencier <fabien@symfony.com>
16
+ */
17
+ class Twig_Environment
18
+ {
19
+ const VERSION = '1.32.0';
20
+ const VERSION_ID = 13200;
21
+ const MAJOR_VERSION = 1;
22
+ const MINOR_VERSION = 32;
23
+ const RELEASE_VERSION = 0;
24
+ const EXTRA_VERSION = '';
25
+
26
+ protected $charset;
27
+ protected $loader;
28
+ protected $debug;
29
+ protected $autoReload;
30
+ protected $cache;
31
+ protected $lexer;
32
+ protected $parser;
33
+ protected $compiler;
34
+ protected $baseTemplateClass;
35
+ protected $extensions;
36
+ protected $parsers;
37
+ protected $visitors;
38
+ protected $filters;
39
+ protected $tests;
40
+ protected $functions;
41
+ protected $globals;
42
+ protected $runtimeInitialized = false;
43
+ protected $extensionInitialized = false;
44
+ protected $loadedTemplates;
45
+ protected $strictVariables;
46
+ protected $unaryOperators;
47
+ protected $binaryOperators;
48
+ protected $templateClassPrefix = '__TwigTemplate_';
49
+ protected $functionCallbacks = array();
50
+ protected $filterCallbacks = array();
51
+ protected $staging;
52
+
53
+ private $originalCache;
54
+ private $bcWriteCacheFile = false;
55
+ private $bcGetCacheFilename = false;
56
+ private $lastModifiedExtension = 0;
57
+ private $extensionsByClass = array();
58
+ private $runtimeLoaders = array();
59
+ private $runtimes = array();
60
+ private $optionsHash;
61
+
62
+ /**
63
+ * Constructor.
64
+ *
65
+ * Available options:
66
+ *
67
+ * * debug: When set to true, it automatically set "auto_reload" to true as
68
+ * well (default to false).
69
+ *
70
+ * * charset: The charset used by the templates (default to UTF-8).
71
+ *
72
+ * * base_template_class: The base template class to use for generated
73
+ * templates (default to Twig_Template).
74
+ *
75
+ * * cache: An absolute path where to store the compiled templates,
76
+ * a Twig_Cache_Interface implementation,
77
+ * or false to disable compilation cache (default).
78
+ *
79
+ * * auto_reload: Whether to reload the template if the original source changed.
80
+ * If you don't provide the auto_reload option, it will be
81
+ * determined automatically based on the debug value.
82
+ *
83
+ * * strict_variables: Whether to ignore invalid variables in templates
84
+ * (default to false).
85
+ *
86
+ * * autoescape: Whether to enable auto-escaping (default to html):
87
+ * * false: disable auto-escaping
88
+ * * true: equivalent to html
89
+ * * html, js: set the autoescaping to one of the supported strategies
90
+ * * name: set the autoescaping strategy based on the template name extension
91
+ * * PHP callback: a PHP callback that returns an escaping strategy based on the template "name"
92
+ *
93
+ * * optimizations: A flag that indicates which optimizations to apply
94
+ * (default to -1 which means that all optimizations are enabled;
95
+ * set it to 0 to disable).
96
+ *
97
+ * @param Twig_LoaderInterface $loader
98
+ * @param array $options An array of options
99
+ */
100
+ public function __construct(Twig_LoaderInterface $loader = null, $options = array())
101
+ {
102
+ if (null !== $loader) {
103
+ $this->setLoader($loader);
104
+ } else {
105
+ @trigger_error('Not passing a Twig_LoaderInterface as the first constructor argument of Twig_Environment is deprecated since version 1.21.', E_USER_DEPRECATED);
106
+ }
107
+
108
+ $options = array_merge(array(
109
+ 'debug' => false,
110
+ 'charset' => 'UTF-8',
111
+ 'base_template_class' => 'Twig_Template',
112
+ 'strict_variables' => false,
113
+ 'autoescape' => 'html',
114
+ 'cache' => false,
115
+ 'auto_reload' => null,
116
+ 'optimizations' => -1,
117
+ ), $options);
118
+
119
+ $this->debug = (bool) $options['debug'];
120
+ $this->charset = strtoupper($options['charset']);
121
+ $this->baseTemplateClass = $options['base_template_class'];
122
+ $this->autoReload = null === $options['auto_reload'] ? $this->debug : (bool) $options['auto_reload'];
123
+ $this->strictVariables = (bool) $options['strict_variables'];
124
+ $this->setCache($options['cache']);
125
+
126
+ $this->addExtension(new Twig_Extension_Core());
127
+ $this->addExtension(new Twig_Extension_Escaper($options['autoescape']));
128
+ $this->addExtension(new Twig_Extension_Optimizer($options['optimizations']));
129
+ $this->staging = new Twig_Extension_Staging();
130
+
131
+ // For BC
132
+ if (is_string($this->originalCache)) {
133
+ $r = new ReflectionMethod($this, 'writeCacheFile');
134
+ if ($r->getDeclaringClass()->getName() !== __CLASS__) {
135
+ @trigger_error('The Twig_Environment::writeCacheFile method is deprecated since version 1.22 and will be removed in Twig 2.0.', E_USER_DEPRECATED);
136
+
137
+ $this->bcWriteCacheFile = true;
138
+ }
139
+
140
+ $r = new ReflectionMethod($this, 'getCacheFilename');
141
+ if ($r->getDeclaringClass()->getName() !== __CLASS__) {
142
+ @trigger_error('The Twig_Environment::getCacheFilename method is deprecated since version 1.22 and will be removed in Twig 2.0.', E_USER_DEPRECATED);
143
+
144
+ $this->bcGetCacheFilename = true;
145
+ }
146
+ }
147
+ }
148
+
149
+ /**
150
+ * Gets the base template class for compiled templates.
151
+ *
152
+ * @return string The base template class name
153
+ */
154
+ public function getBaseTemplateClass()
155
+ {
156
+ return $this->baseTemplateClass;
157
+ }
158
+
159
+ /**
160
+ * Sets the base template class for compiled templates.
161
+ *
162
+ * @param string $class The base template class name
163
+ */
164
+ public function setBaseTemplateClass($class)
165
+ {
166
+ $this->baseTemplateClass = $class;
167
+ $this->updateOptionsHash();
168
+ }
169
+
170
+ /**
171
+ * Enables debugging mode.
172
+ */
173
+ public function enableDebug()
174
+ {
175
+ $this->debug = true;
176
+ $this->updateOptionsHash();
177
+ }
178
+
179
+ /**
180
+ * Disables debugging mode.
181
+ */
182
+ public function disableDebug()
183
+ {
184
+ $this->debug = false;
185
+ $this->updateOptionsHash();
186
+ }
187
+
188
+ /**
189
+ * Checks if debug mode is enabled.
190
+ *
191
+ * @return bool true if debug mode is enabled, false otherwise
192
+ */
193
+ public function isDebug()
194
+ {
195
+ return $this->debug;
196
+ }
197
+
198
+ /**
199
+ * Enables the auto_reload option.
200
+ */
201
+ public function enableAutoReload()
202
+ {
203
+ $this->autoReload = true;
204
+ }
205
+
206
+ /**
207
+ * Disables the auto_reload option.
208
+ */
209
+ public function disableAutoReload()
210
+ {
211
+ $this->autoReload = false;
212
+ }
213
+
214
+ /**
215
+ * Checks if the auto_reload option is enabled.
216
+ *
217
+ * @return bool true if auto_reload is enabled, false otherwise
218
+ */
219
+ public function isAutoReload()
220
+ {
221
+ return $this->autoReload;
222
+ }
223
+
224
+ /**
225
+ * Enables the strict_variables option.
226
+ */
227
+ public function enableStrictVariables()
228
+ {
229
+ $this->strictVariables = true;
230
+ $this->updateOptionsHash();
231
+ }
232
+
233
+ /**
234
+ * Disables the strict_variables option.
235
+ */
236
+ public function disableStrictVariables()
237
+ {
238
+ $this->strictVariables = false;
239
+ $this->updateOptionsHash();
240
+ }
241
+
242
+ /**
243
+ * Checks if the strict_variables option is enabled.
244
+ *
245
+ * @return bool true if strict_variables is enabled, false otherwise
246
+ */
247
+ public function isStrictVariables()
248
+ {
249
+ return $this->strictVariables;
250
+ }
251
+
252
+ /**
253
+ * Gets the current cache implementation.
254
+ *
255
+ * @param bool $original Whether to return the original cache option or the real cache instance
256
+ *
257
+ * @return Twig_CacheInterface|string|false A Twig_CacheInterface implementation,
258
+ * an absolute path to the compiled templates,
259
+ * or false to disable cache
260
+ */
261
+ public function getCache($original = true)
262
+ {
263
+ return $original ? $this->originalCache : $this->cache;
264
+ }
265
+
266
+ /**
267
+ * Sets the current cache implementation.
268
+ *
269
+ * @param Twig_CacheInterface|string|false $cache A Twig_CacheInterface implementation,
270
+ * an absolute path to the compiled templates,
271
+ * or false to disable cache
272
+ */
273
+ public function setCache($cache)
274
+ {
275
+ if (is_string($cache)) {
276
+ $this->originalCache = $cache;
277
+ $this->cache = new Twig_Cache_Filesystem($cache);
278
+ } elseif (false === $cache) {
279
+ $this->originalCache = $cache;
280
+ $this->cache = new Twig_Cache_Null();
281
+ } elseif (null === $cache) {
282
+ @trigger_error('Using "null" as the cache strategy is deprecated since version 1.23 and will be removed in Twig 2.0.', E_USER_DEPRECATED);
283
+ $this->originalCache = false;
284
+ $this->cache = new Twig_Cache_Null();
285
+ } elseif ($cache instanceof Twig_CacheInterface) {
286
+ $this->originalCache = $this->cache = $cache;
287
+ } else {
288
+ throw new LogicException(sprintf('Cache can only be a string, false, or a Twig_CacheInterface implementation.'));
289
+ }
290
+ }
291
+
292
+ /**
293
+ * Gets the cache filename for a given template.
294
+ *
295
+ * @param string $name The template name
296
+ *
297
+ * @return string|false The cache file name or false when caching is disabled
298
+ *
299
+ * @deprecated since 1.22 (to be removed in 2.0)
300
+ */
301
+ public function getCacheFilename($name)
302
+ {
303
+ @trigger_error(sprintf('The %s method is deprecated since version 1.22 and will be removed in Twig 2.0.', __METHOD__), E_USER_DEPRECATED);
304
+
305
+ $key = $this->cache->generateKey($name, $this->getTemplateClass($name));
306
+
307
+ return !$key ? false : $key;
308
+ }
309
+
310
+ /**
311
+ * Gets the template class associated with the given string.
312
+ *
313
+ * The generated template class is based on the following parameters:
314
+ *
315
+ * * The cache key for the given template;
316
+ * * The currently enabled extensions;
317
+ * * Whether the Twig C extension is available or not;
318
+ * * PHP version;
319
+ * * Twig version;
320
+ * * Options with what environment was created.
321
+ *
322
+ * @param string $name The name for which to calculate the template class name
323
+ * @param int|null $index The index if it is an embedded template
324
+ *
325
+ * @return string The template class name
326
+ */
327
+ public function getTemplateClass($name, $index = null)
328
+ {
329
+ $key = $this->getLoader()->getCacheKey($name).$this->optionsHash;
330
+
331
+ return $this->templateClassPrefix.hash('sha256', $key).(null === $index ? '' : '_'.$index);
332
+ }
333
+
334
+ /**
335
+ * Gets the template class prefix.
336
+ *
337
+ * @return string The template class prefix
338
+ *
339
+ * @deprecated since 1.22 (to be removed in 2.0)
340
+ */
341
+ public function getTemplateClassPrefix()
342
+ {
343
+ @trigger_error(sprintf('The %s method is deprecated since version 1.22 and will be removed in Twig 2.0.', __METHOD__), E_USER_DEPRECATED);
344
+
345
+ return $this->templateClassPrefix;
346
+ }
347
+
348
+ /**
349
+ * Renders a template.
350
+ *
351
+ * @param string $name The template name
352
+ * @param array $context An array of parameters to pass to the template
353
+ *
354
+ * @return string The rendered template
355
+ *
356
+ * @throws Twig_Error_Loader When the template cannot be found
357
+ * @throws Twig_Error_Syntax When an error occurred during compilation
358
+ * @throws Twig_Error_Runtime When an error occurred during rendering
359
+ */
360
+ public function render($name, array $context = array())
361
+ {
362
+ return $this->loadTemplate($name)->render($context);
363
+ }
364
+
365
+ /**
366
+ * Displays a template.
367
+ *
368
+ * @param string $name The template name
369
+ * @param array $context An array of parameters to pass to the template
370
+ *
371
+ * @throws Twig_Error_Loader When the template cannot be found
372
+ * @throws Twig_Error_Syntax When an error occurred during compilation
373
+ * @throws Twig_Error_Runtime When an error occurred during rendering
374
+ */
375
+ public function display($name, array $context = array())
376
+ {
377
+ $this->loadTemplate($name)->display($context);
378
+ }
379
+
380
+ /**
381
+ * Loads a template.
382
+ *
383
+ * @param string|Twig_TemplateWrapper|Twig_Template $name The template name
384
+ *
385
+ * @return Twig_TemplateWrapper
386
+ */
387
+ public function load($name)
388
+ {
389
+ if ($name instanceof Twig_TemplateWrapper) {
390
+ return $name;
391
+ }
392
+
393
+ if ($name instanceof Twig_Template) {
394
+ return new Twig_TemplateWrapper($this, $name);
395
+ }
396
+
397
+ return new Twig_TemplateWrapper($this, $this->loadTemplate($name));
398
+ }
399
+
400
+ /**
401
+ * Loads a template internal representation.
402
+ *
403
+ * This method is for internal use only and should never be called
404
+ * directly.
405
+ *
406
+ * @param string $name The template name
407
+ * @param int $index The index if it is an embedded template
408
+ *
409
+ * @return Twig_TemplateInterface A template instance representing the given template name
410
+ *
411
+ * @throws Twig_Error_Loader When the template cannot be found
412
+ * @throws Twig_Error_Runtime When a previously generated cache is corrupted
413
+ * @throws Twig_Error_Syntax When an error occurred during compilation
414
+ *
415
+ * @internal
416
+ */
417
+ public function loadTemplate($name, $index = null)
418
+ {
419
+ $cls = $mainCls = $this->getTemplateClass($name);
420
+ if (null !== $index) {
421
+ $cls .= '_'.$index;
422
+ }
423
+
424
+ if (isset($this->loadedTemplates[$cls])) {
425
+ return $this->loadedTemplates[$cls];
426
+ }
427
+
428
+ if (!class_exists($cls, false)) {
429
+ if ($this->bcGetCacheFilename) {
430
+ $key = $this->getCacheFilename($name);
431
+ } else {
432
+ $key = $this->cache->generateKey($name, $mainCls);
433
+ }
434
+
435
+ if (!$this->isAutoReload() || $this->isTemplateFresh($name, $this->cache->getTimestamp($key))) {
436
+ $this->cache->load($key);
437
+ }
438
+
439
+ if (!class_exists($cls, false)) {
440
+ $loader = $this->getLoader();
441
+ if (!$loader instanceof Twig_SourceContextLoaderInterface) {
442
+ $source = new Twig_Source($loader->getSource($name), $name);
443
+ } else {
444
+ $source = $loader->getSourceContext($name);
445
+ }
446
+
447
+ $content = $this->compileSource($source);
448
+
449
+ if ($this->bcWriteCacheFile) {
450
+ $this->writeCacheFile($key, $content);
451
+ } else {
452
+ $this->cache->write($key, $content);
453
+ $this->cache->load($key);
454
+ }
455
+
456
+ if (!class_exists($mainCls, false)) {
457
+ /* Last line of defense if either $this->bcWriteCacheFile was used,
458
+ * $this->cache is implemented as a no-op or we have a race condition
459
+ * where the cache was cleared between the above calls to write to and load from
460
+ * the cache.
461
+ */
462
+ eval('?>'.$content);
463
+ }
464
+ }
465
+
466
+ if (!class_exists($cls, false)) {
467
+ throw new Twig_Error_Runtime(sprintf('Failed to load Twig template "%s", index "%s": cache is corrupted.', $name, $index), -1, $source);
468
+ }
469
+ }
470
+
471
+ if (!$this->runtimeInitialized) {
472
+ $this->initRuntime();
473
+ }
474
+
475
+ return $this->loadedTemplates[$cls] = new $cls($this);
476
+ }
477
+
478
+ /**
479
+ * Creates a template from source.
480
+ *
481
+ * This method should not be used as a generic way to load templates.
482
+ *
483
+ * @param string $template The template name
484
+ *
485
+ * @return Twig_Template A template instance representing the given template name
486
+ *
487
+ * @throws Twig_Error_Loader When the template cannot be found
488
+ * @throws Twig_Error_Syntax When an error occurred during compilation
489
+ */
490
+ public function createTemplate($template)
491
+ {
492
+ $name = sprintf('__string_template__%s', hash('sha256', uniqid(mt_rand(), true), false));
493
+
494
+ $loader = new Twig_Loader_Chain(array(
495
+ new Twig_Loader_Array(array($name => $template)),
496
+ $current = $this->getLoader(),
497
+ ));
498
+
499
+ $this->setLoader($loader);
500
+ try {
501
+ $template = $this->loadTemplate($name);
502
+ } catch (Exception $e) {
503
+ $this->setLoader($current);
504
+
505
+ throw $e;
506
+ } catch (Throwable $e) {
507
+ $this->setLoader($current);
508
+
509
+ throw $e;
510
+ }
511
+ $this->setLoader($current);
512
+
513
+ return $template;
514
+ }
515
+
516
+ /**
517
+ * Returns true if the template is still fresh.
518
+ *
519
+ * Besides checking the loader for freshness information,
520
+ * this method also checks if the enabled extensions have
521
+ * not changed.
522
+ *
523
+ * @param string $name The template name
524
+ * @param int $time The last modification time of the cached template
525
+ *
526
+ * @return bool true if the template is fresh, false otherwise
527
+ */
528
+ public function isTemplateFresh($name, $time)
529
+ {
530
+ if (0 === $this->lastModifiedExtension) {
531
+ foreach ($this->extensions as $extension) {
532
+ $r = new ReflectionObject($extension);
533
+ if (file_exists($r->getFileName()) && ($extensionTime = filemtime($r->getFileName())) > $this->lastModifiedExtension) {
534
+ $this->lastModifiedExtension = $extensionTime;
535
+ }
536
+ }
537
+ }
538
+
539
+ return $this->lastModifiedExtension <= $time && $this->getLoader()->isFresh($name, $time);
540
+ }
541
+
542
+ /**
543
+ * Tries to load a template consecutively from an array.
544
+ *
545
+ * Similar to loadTemplate() but it also accepts Twig_TemplateInterface instances and an array
546
+ * of templates where each is tried to be loaded.
547
+ *
548
+ * @param string|Twig_Template|array $names A template or an array of templates to try consecutively
549
+ *
550
+ * @return Twig_Template
551
+ *
552
+ * @throws Twig_Error_Loader When none of the templates can be found
553
+ * @throws Twig_Error_Syntax When an error occurred during compilation
554
+ */
555
+ public function resolveTemplate($names)
556
+ {
557
+ if (!is_array($names)) {
558
+ $names = array($names);
559
+ }
560
+
561
+ foreach ($names as $name) {
562
+ if ($name instanceof Twig_Template) {
563
+ return $name;
564
+ }
565
+
566
+ try {
567
+ return $this->loadTemplate($name);
568
+ } catch (Twig_Error_Loader $e) {
569
+ }
570
+ }
571
+
572
+ if (1 === count($names)) {
573
+ throw $e;
574
+ }
575
+
576
+ throw new Twig_Error_Loader(sprintf('Unable to find one of the following templates: "%s".', implode('", "', $names)));
577
+ }
578
+
579
+ /**
580
+ * Clears the internal template cache.
581
+ *
582
+ * @deprecated since 1.18.3 (to be removed in 2.0)
583
+ */
584
+ public function clearTemplateCache()
585
+ {
586
+ @trigger_error(sprintf('The %s method is deprecated since version 1.18.3 and will be removed in Twig 2.0.', __METHOD__), E_USER_DEPRECATED);
587
+
588
+ $this->loadedTemplates = array();
589
+ }
590
+
591
+ /**
592
+ * Clears the template cache files on the filesystem.
593
+ *
594
+ * @deprecated since 1.22 (to be removed in 2.0)
595
+ */
596
+ public function clearCacheFiles()
597
+ {
598
+ @trigger_error(sprintf('The %s method is deprecated since version 1.22 and will be removed in Twig 2.0.', __METHOD__), E_USER_DEPRECATED);
599
+
600
+ if (is_string($this->originalCache)) {
601
+ foreach (new RecursiveIteratorIterator(new RecursiveDirectoryIterator($this->originalCache), RecursiveIteratorIterator::LEAVES_ONLY) as $file) {
602
+ if ($file->isFile()) {
603
+ @unlink($file->getPathname());
604
+ }
605
+ }
606
+ }
607
+ }
608
+
609
+ /**
610
+ * Gets the Lexer instance.
611
+ *
612
+ * @return Twig_LexerInterface
613
+ *
614
+ * @deprecated since 1.25 (to be removed in 2.0)
615
+ */
616
+ public function getLexer()
617
+ {
618
+ @trigger_error(sprintf('The %s() method is deprecated since version 1.25 and will be removed in 2.0.', __FUNCTION__), E_USER_DEPRECATED);
619
+
620
+ if (null === $this->lexer) {
621
+ $this->lexer = new Twig_Lexer($this);
622
+ }
623
+
624
+ return $this->lexer;
625
+ }
626
+
627
+ public function setLexer(Twig_LexerInterface $lexer)
628
+ {
629
+ $this->lexer = $lexer;
630
+ }
631
+
632
+ /**
633
+ * Tokenizes a source code.
634
+ *
635
+ * @param string|Twig_Source $source The template source code
636
+ * @param string $name The template name (deprecated)
637
+ *
638
+ * @return Twig_TokenStream
639
+ *
640
+ * @throws Twig_Error_Syntax When the code is syntactically wrong
641
+ */
642
+ public function tokenize($source, $name = null)
643
+ {
644
+ if (!$source instanceof Twig_Source) {
645
+ @trigger_error(sprintf('Passing a string as the $source argument of %s() is deprecated since version 1.27. Pass a Twig_Source instance instead.', __METHOD__), E_USER_DEPRECATED);
646
+ $source = new Twig_Source($source, $name);
647
+ }
648
+
649
+ if (null === $this->lexer) {
650
+ $this->lexer = new Twig_Lexer($this);
651
+ }
652
+
653
+ return $this->lexer->tokenize($source);
654
+ }
655
+
656
+ /**
657
+ * Gets the Parser instance.
658
+ *
659
+ * @return Twig_ParserInterface
660
+ *
661
+ * @deprecated since 1.25 (to be removed in 2.0)
662
+ */
663
+ public function getParser()
664
+ {
665
+ @trigger_error(sprintf('The %s() method is deprecated since version 1.25 and will be removed in 2.0.', __FUNCTION__), E_USER_DEPRECATED);
666
+
667
+ if (null === $this->parser) {
668
+ $this->parser = new Twig_Parser($this);
669
+ }
670
+
671
+ return $this->parser;
672
+ }
673
+
674
+ public function setParser(Twig_ParserInterface $parser)
675
+ {
676
+ $this->parser = $parser;
677
+ }
678
+
679
+ /**
680
+ * Converts a token stream to a node tree.
681
+ *
682
+ * @return Twig_Node_Module
683
+ *
684
+ * @throws Twig_Error_Syntax When the token stream is syntactically or semantically wrong
685
+ */
686
+ public function parse(Twig_TokenStream $stream)
687
+ {
688
+ if (null === $this->parser) {
689
+ $this->parser = new Twig_Parser($this);
690
+ }
691
+
692
+ return $this->parser->parse($stream);
693
+ }
694
+
695
+ /**
696
+ * Gets the Compiler instance.
697
+ *
698
+ * @return Twig_CompilerInterface
699
+ *
700
+ * @deprecated since 1.25 (to be removed in 2.0)
701
+ */
702
+ public function getCompiler()
703
+ {
704
+ @trigger_error(sprintf('The %s() method is deprecated since version 1.25 and will be removed in 2.0.', __FUNCTION__), E_USER_DEPRECATED);
705
+
706
+ if (null === $this->compiler) {
707
+ $this->compiler = new Twig_Compiler($this);
708
+ }
709
+
710
+ return $this->compiler;
711
+ }
712
+
713
+ public function setCompiler(Twig_CompilerInterface $compiler)
714
+ {
715
+ $this->compiler = $compiler;
716
+ }
717
+
718
+ /**
719
+ * Compiles a node and returns the PHP code.
720
+ *
721
+ * @return string The compiled PHP source code
722
+ */
723
+ public function compile(Twig_NodeInterface $node)
724
+ {
725
+ if (null === $this->compiler) {
726
+ $this->compiler = new Twig_Compiler($this);
727
+ }
728
+
729
+ return $this->compiler->compile($node)->getSource();
730
+ }
731
+
732
+ /**
733
+ * Compiles a template source code.
734
+ *
735
+ * @param string|Twig_Source $source The template source code
736
+ * @param string $name The template name (deprecated)
737
+ *
738
+ * @return string The compiled PHP source code
739
+ *
740
+ * @throws Twig_Error_Syntax When there was an error during tokenizing, parsing or compiling
741
+ */
742
+ public function compileSource($source, $name = null)
743
+ {
744
+ if (!$source instanceof Twig_Source) {
745
+ @trigger_error(sprintf('Passing a string as the $source argument of %s() is deprecated since version 1.27. Pass a Twig_Source instance instead.', __METHOD__), E_USER_DEPRECATED);
746
+ $source = new Twig_Source($source, $name);
747
+ }
748
+
749
+ try {
750
+ return $this->compile($this->parse($this->tokenize($source)));
751
+ } catch (Twig_Error $e) {
752
+ $e->setSourceContext($source);
753
+ throw $e;
754
+ } catch (Exception $e) {
755
+ throw new Twig_Error_Syntax(sprintf('An exception has been thrown during the compilation of a template ("%s").', $e->getMessage()), -1, $source, $e);
756
+ }
757
+ }
758
+
759
+ public function setLoader(Twig_LoaderInterface $loader)
760
+ {
761
+ if (!$loader instanceof Twig_SourceContextLoaderInterface && 0 !== strpos(get_class($loader), 'Mock_Twig_LoaderInterface')) {
762
+ @trigger_error(sprintf('Twig loader "%s" should implement Twig_SourceContextLoaderInterface since version 1.27.', get_class($loader)), E_USER_DEPRECATED);
763
+ }
764
+
765
+ $this->loader = $loader;
766
+ }
767
+
768
+ /**
769
+ * Gets the Loader instance.
770
+ *
771
+ * @return Twig_LoaderInterface
772
+ */
773
+ public function getLoader()
774
+ {
775
+ if (null === $this->loader) {
776
+ throw new LogicException('You must set a loader first.');
777
+ }
778
+
779
+ return $this->loader;
780
+ }
781
+
782
+ /**
783
+ * Sets the default template charset.
784
+ *
785
+ * @param string $charset The default charset
786
+ */
787
+ public function setCharset($charset)
788
+ {
789
+ $this->charset = strtoupper($charset);
790
+ }
791
+
792
+ /**
793
+ * Gets the default template charset.
794
+ *
795
+ * @return string The default charset
796
+ */
797
+ public function getCharset()
798
+ {
799
+ return $this->charset;
800
+ }
801
+
802
+ /**
803
+ * Initializes the runtime environment.
804
+ *
805
+ * @deprecated since 1.23 (to be removed in 2.0)
806
+ */
807
+ public function initRuntime()
808
+ {
809
+ $this->runtimeInitialized = true;
810
+
811
+ foreach ($this->getExtensions() as $name => $extension) {
812
+ if (!$extension instanceof Twig_Extension_InitRuntimeInterface) {
813
+ $m = new ReflectionMethod($extension, 'initRuntime');
814
+
815
+ if ('Twig_Extension' !== $m->getDeclaringClass()->getName()) {
816
+ @trigger_error(sprintf('Defining the initRuntime() method in the "%s" extension is deprecated since version 1.23. Use the `needs_environment` option to get the Twig_Environment instance in filters, functions, or tests; or explicitly implement Twig_Extension_InitRuntimeInterface if needed (not recommended).', $name), E_USER_DEPRECATED);
817
+ }
818
+ }
819
+
820
+ $extension->initRuntime($this);
821
+ }
822
+ }
823
+
824
+ /**
825
+ * Returns true if the given extension is registered.
826
+ *
827
+ * @param string $class The extension class name
828
+ *
829
+ * @return bool Whether the extension is registered or not
830
+ */
831
+ public function hasExtension($class)
832
+ {
833
+ $class = ltrim($class, '\\');
834
+ if (isset($this->extensions[$class])) {
835
+ if ($class !== get_class($this->extensions[$class])) {
836
+ @trigger_error(sprintf('Referencing the "%s" extension by its name (defined by getName()) is deprecated since 1.26 and will be removed in Twig 2.0. Use the Fully Qualified Extension Class Name instead.', $class), E_USER_DEPRECATED);
837
+ }
838
+
839
+ return true;
840
+ }
841
+
842
+ return isset($this->extensionsByClass[$class]);
843
+ }
844
+
845
+ /**
846
+ * Adds a runtime loader.
847
+ */
848
+ public function addRuntimeLoader(Twig_RuntimeLoaderInterface $loader)
849
+ {
850
+ $this->runtimeLoaders[] = $loader;
851
+ }
852
+
853
+ /**
854
+ * Gets an extension by class name.
855
+ *
856
+ * @param string $class The extension class name
857
+ *
858
+ * @return Twig_ExtensionInterface
859
+ */
860
+ public function getExtension($class)
861
+ {
862
+ $class = ltrim($class, '\\');
863
+
864
+ if (isset($this->extensions[$class])) {
865
+ if ($class !== get_class($this->extensions[$class])) {
866
+ @trigger_error(sprintf('Referencing the "%s" extension by its name (defined by getName()) is deprecated since 1.26 and will be removed in Twig 2.0. Use the Fully Qualified Extension Class Name instead.', $class), E_USER_DEPRECATED);
867
+ }
868
+
869
+ return $this->extensions[$class];
870
+ }
871
+
872
+ if (!isset($this->extensionsByClass[$class])) {
873
+ throw new Twig_Error_Runtime(sprintf('The "%s" extension is not enabled.', $class));
874
+ }
875
+
876
+ return $this->extensionsByClass[$class];
877
+ }
878
+
879
+ /**
880
+ * Returns the runtime implementation of a Twig element (filter/function/test).
881
+ *
882
+ * @param string $class A runtime class name
883
+ *
884
+ * @return object The runtime implementation
885
+ *
886
+ * @throws Twig_Error_Runtime When the template cannot be found
887
+ */
888
+ public function getRuntime($class)
889
+ {
890
+ if (isset($this->runtimes[$class])) {
891
+ return $this->runtimes[$class];
892
+ }
893
+
894
+ foreach ($this->runtimeLoaders as $loader) {
895
+ if (null !== $runtime = $loader->load($class)) {
896
+ return $this->runtimes[$class] = $runtime;
897
+ }
898
+ }
899
+
900
+ throw new Twig_Error_Runtime(sprintf('Unable to load the "%s" runtime.', $class));
901
+ }
902
+
903
+ public function addExtension(Twig_ExtensionInterface $extension)
904
+ {
905
+ if ($this->extensionInitialized) {
906
+ throw new LogicException(sprintf('Unable to register extension "%s" as extensions have already been initialized.', $extension->getName()));
907
+ }
908
+
909
+ $class = get_class($extension);
910
+ if ($class !== $extension->getName()) {
911
+ if (isset($this->extensions[$extension->getName()])) {
912
+ unset($this->extensions[$extension->getName()], $this->extensionsByClass[$class]);
913
+ @trigger_error(sprintf('The possibility to register the same extension twice ("%s") is deprecated since version 1.23 and will be removed in Twig 2.0. Use proper PHP inheritance instead.', $extension->getName()), E_USER_DEPRECATED);
914
+ }
915
+ }
916
+
917
+ $this->lastModifiedExtension = 0;
918
+ $this->extensionsByClass[$class] = $extension;
919
+ $this->extensions[$extension->getName()] = $extension;
920
+ $this->updateOptionsHash();
921
+ }
922
+
923
+ /**
924
+ * Removes an extension by name.
925
+ *
926
+ * This method is deprecated and you should not use it.
927
+ *
928
+ * @param string $name The extension name
929
+ *
930
+ * @deprecated since 1.12 (to be removed in 2.0)
931
+ */
932
+ public function removeExtension($name)
933
+ {
934
+ @trigger_error(sprintf('The %s method is deprecated since version 1.12 and will be removed in Twig 2.0.', __METHOD__), E_USER_DEPRECATED);
935
+
936
+ if ($this->extensionInitialized) {
937
+ throw new LogicException(sprintf('Unable to remove extension "%s" as extensions have already been initialized.', $name));
938
+ }
939
+
940
+ $class = ltrim($name, '\\');
941
+ if (isset($this->extensions[$class])) {
942
+ if ($class !== get_class($this->extensions[$class])) {
943
+ @trigger_error(sprintf('Referencing the "%s" extension by its name (defined by getName()) is deprecated since 1.26 and will be removed in Twig 2.0. Use the Fully Qualified Extension Class Name instead.', $class), E_USER_DEPRECATED);
944
+ }
945
+
946
+ unset($this->extensions[$class]);
947
+ }
948
+
949
+ unset($this->extensions[$class]);
950
+ $this->updateOptionsHash();
951
+ }
952
+
953
+ /**
954
+ * Registers an array of extensions.
955
+ *
956
+ * @param array $extensions An array of extensions
957
+ */
958
+ public function setExtensions(array $extensions)
959
+ {
960
+ foreach ($extensions as $extension) {
961
+ $this->addExtension($extension);
962
+ }
963
+ }
964
+
965
+ /**
966
+ * Returns all registered extensions.
967
+ *
968
+ * @return Twig_ExtensionInterface[] An array of extensions (keys are for internal usage only and should not be relied on)
969
+ */
970
+ public function getExtensions()
971
+ {
972
+ return $this->extensions;
973
+ }
974
+
975
+ public function addTokenParser(Twig_TokenParserInterface $parser)
976
+ {
977
+ if ($this->extensionInitialized) {
978
+ throw new LogicException('Unable to add a token parser as extensions have already been initialized.');
979
+ }
980
+
981
+ $this->staging->addTokenParser($parser);
982
+ }
983
+
984
+ /**
985
+ * Gets the registered Token Parsers.
986
+ *
987
+ * @return Twig_TokenParserBrokerInterface
988
+ *
989
+ * @internal
990
+ */
991
+ public function getTokenParsers()
992
+ {
993
+ if (!$this->extensionInitialized) {
994
+ $this->initExtensions();
995
+ }
996
+
997
+ return $this->parsers;
998
+ }
999
+
1000
+ /**
1001
+ * Gets registered tags.
1002
+ *
1003
+ * Be warned that this method cannot return tags defined by Twig_TokenParserBrokerInterface classes.
1004
+ *
1005
+ * @return Twig_TokenParserInterface[]
1006
+ *
1007
+ * @internal
1008
+ */
1009
+ public function getTags()
1010
+ {
1011
+ $tags = array();
1012
+ foreach ($this->getTokenParsers()->getParsers() as $parser) {
1013
+ if ($parser instanceof Twig_TokenParserInterface) {
1014
+ $tags[$parser->getTag()] = $parser;
1015
+ }
1016
+ }
1017
+
1018
+ return $tags;
1019
+ }
1020
+
1021
+ public function addNodeVisitor(Twig_NodeVisitorInterface $visitor)
1022
+ {
1023
+ if ($this->extensionInitialized) {
1024
+ throw new LogicException('Unable to add a node visitor as extensions have already been initialized.');
1025
+ }
1026
+
1027
+ $this->staging->addNodeVisitor($visitor);
1028
+ }
1029
+
1030
+ /**
1031
+ * Gets the registered Node Visitors.
1032
+ *
1033
+ * @return Twig_NodeVisitorInterface[]
1034
+ *
1035
+ * @internal
1036
+ */
1037
+ public function getNodeVisitors()
1038
+ {
1039
+ if (!$this->extensionInitialized) {
1040
+ $this->initExtensions();
1041
+ }
1042
+
1043
+ return $this->visitors;
1044
+ }
1045
+
1046
+ /**
1047
+ * Registers a Filter.
1048
+ *
1049
+ * @param string|Twig_SimpleFilter $name The filter name or a Twig_SimpleFilter instance
1050
+ * @param Twig_FilterInterface|Twig_SimpleFilter $filter
1051
+ */
1052
+ public function addFilter($name, $filter = null)
1053
+ {
1054
+ if (!$name instanceof Twig_SimpleFilter && !($filter instanceof Twig_SimpleFilter || $filter instanceof Twig_FilterInterface)) {
1055
+ throw new LogicException('A filter must be an instance of Twig_FilterInterface or Twig_SimpleFilter.');
1056
+ }
1057
+
1058
+ if ($name instanceof Twig_SimpleFilter) {
1059
+ $filter = $name;
1060
+ $name = $filter->getName();
1061
+ } else {
1062
+ @trigger_error(sprintf('Passing a name as a first argument to the %s method is deprecated since version 1.21. Pass an instance of "Twig_SimpleFilter" instead when defining filter "%s".', __METHOD__, $name), E_USER_DEPRECATED);
1063
+ }
1064
+
1065
+ if ($this->extensionInitialized) {
1066
+ throw new LogicException(sprintf('Unable to add filter "%s" as extensions have already been initialized.', $name));
1067
+ }
1068
+
1069
+ $this->staging->addFilter($name, $filter);
1070
+ }
1071
+
1072
+ /**
1073
+ * Get a filter by name.
1074
+ *
1075
+ * Subclasses may override this method and load filters differently;
1076
+ * so no list of filters is available.
1077
+ *
1078
+ * @param string $name The filter name
1079
+ *
1080
+ * @return Twig_Filter|false A Twig_Filter instance or false if the filter does not exist
1081
+ *
1082
+ * @internal
1083
+ */
1084
+ public function getFilter($name)
1085
+ {
1086
+ if (!$this->extensionInitialized) {
1087
+ $this->initExtensions();
1088
+ }
1089
+
1090
+ if (isset($this->filters[$name])) {
1091
+ return $this->filters[$name];
1092
+ }
1093
+
1094
+ foreach ($this->filters as $pattern => $filter) {
1095
+ $pattern = str_replace('\\*', '(.*?)', preg_quote($pattern, '#'), $count);
1096
+
1097
+ if ($count) {
1098
+ if (preg_match('#^'.$pattern.'$#', $name, $matches)) {
1099
+ array_shift($matches);
1100
+ $filter->setArguments($matches);
1101
+
1102
+ return $filter;
1103
+ }
1104
+ }
1105
+ }
1106
+
1107
+ foreach ($this->filterCallbacks as $callback) {
1108
+ if (false !== $filter = call_user_func($callback, $name)) {
1109
+ return $filter;
1110
+ }
1111
+ }
1112
+
1113
+ return false;
1114
+ }
1115
+
1116
+ public function registerUndefinedFilterCallback($callable)
1117
+ {
1118
+ $this->filterCallbacks[] = $callable;
1119
+ }
1120
+
1121
+ /**
1122
+ * Gets the registered Filters.
1123
+ *
1124
+ * Be warned that this method cannot return filters defined with registerUndefinedFilterCallback.
1125
+ *
1126
+ * @return Twig_FilterInterface[]
1127
+ *
1128
+ * @see registerUndefinedFilterCallback
1129
+ *
1130
+ * @internal
1131
+ */
1132
+ public function getFilters()
1133
+ {
1134
+ if (!$this->extensionInitialized) {
1135
+ $this->initExtensions();
1136
+ }
1137
+
1138
+ return $this->filters;
1139
+ }
1140
+
1141
+ /**
1142
+ * Registers a Test.
1143
+ *
1144
+ * @param string|Twig_SimpleTest $name The test name or a Twig_SimpleTest instance
1145
+ * @param Twig_TestInterface|Twig_SimpleTest $test A Twig_TestInterface instance or a Twig_SimpleTest instance
1146
+ */
1147
+ public function addTest($name, $test = null)
1148
+ {
1149
+ if (!$name instanceof Twig_SimpleTest && !($test instanceof Twig_SimpleTest || $test instanceof Twig_TestInterface)) {
1150
+ throw new LogicException('A test must be an instance of Twig_TestInterface or Twig_SimpleTest.');
1151
+ }
1152
+
1153
+ if ($name instanceof Twig_SimpleTest) {
1154
+ $test = $name;
1155
+ $name = $test->getName();
1156
+ } else {
1157
+ @trigger_error(sprintf('Passing a name as a first argument to the %s method is deprecated since version 1.21. Pass an instance of "Twig_SimpleTest" instead when defining test "%s".', __METHOD__, $name), E_USER_DEPRECATED);
1158
+ }
1159
+
1160
+ if ($this->extensionInitialized) {
1161
+ throw new LogicException(sprintf('Unable to add test "%s" as extensions have already been initialized.', $name));
1162
+ }
1163
+
1164
+ $this->staging->addTest($name, $test);
1165
+ }
1166
+
1167
+ /**
1168
+ * Gets the registered Tests.
1169
+ *
1170
+ * @return Twig_TestInterface[]
1171
+ *
1172
+ * @internal
1173
+ */
1174
+ public function getTests()
1175
+ {
1176
+ if (!$this->extensionInitialized) {
1177
+ $this->initExtensions();
1178
+ }
1179
+
1180
+ return $this->tests;
1181
+ }
1182
+
1183
+ /**
1184
+ * Gets a test by name.
1185
+ *
1186
+ * @param string $name The test name
1187
+ *
1188
+ * @return Twig_Test|false A Twig_Test instance or false if the test does not exist
1189
+ *
1190
+ * @internal
1191
+ */
1192
+ public function getTest($name)
1193
+ {
1194
+ if (!$this->extensionInitialized) {
1195
+ $this->initExtensions();
1196
+ }
1197
+
1198
+ if (isset($this->tests[$name])) {
1199
+ return $this->tests[$name];
1200
+ }
1201
+
1202
+ return false;
1203
+ }
1204
+
1205
+ /**
1206
+ * Registers a Function.
1207
+ *
1208
+ * @param string|Twig_SimpleFunction $name The function name or a Twig_SimpleFunction instance
1209
+ * @param Twig_FunctionInterface|Twig_SimpleFunction $function
1210
+ */
1211
+ public function addFunction($name, $function = null)
1212
+ {
1213
+ if (!$name instanceof Twig_SimpleFunction && !($function instanceof Twig_SimpleFunction || $function instanceof Twig_FunctionInterface)) {
1214
+ throw new LogicException('A function must be an instance of Twig_FunctionInterface or Twig_SimpleFunction.');
1215
+ }
1216
+
1217
+ if ($name instanceof Twig_SimpleFunction) {
1218
+ $function = $name;
1219
+ $name = $function->getName();
1220
+ } else {
1221
+ @trigger_error(sprintf('Passing a name as a first argument to the %s method is deprecated since version 1.21. Pass an instance of "Twig_SimpleFunction" instead when defining function "%s".', __METHOD__, $name), E_USER_DEPRECATED);
1222
+ }
1223
+
1224
+ if ($this->extensionInitialized) {
1225
+ throw new LogicException(sprintf('Unable to add function "%s" as extensions have already been initialized.', $name));
1226
+ }
1227
+
1228
+ $this->staging->addFunction($name, $function);
1229
+ }
1230
+
1231
+ /**
1232
+ * Get a function by name.
1233
+ *
1234
+ * Subclasses may override this method and load functions differently;
1235
+ * so no list of functions is available.
1236
+ *
1237
+ * @param string $name function name
1238
+ *
1239
+ * @return Twig_Function|false A Twig_Function instance or false if the function does not exist
1240
+ *
1241
+ * @internal
1242
+ */
1243
+ public function getFunction($name)
1244
+ {
1245
+ if (!$this->extensionInitialized) {
1246
+ $this->initExtensions();
1247
+ }
1248
+
1249
+ if (isset($this->functions[$name])) {
1250
+ return $this->functions[$name];
1251
+ }
1252
+
1253
+ foreach ($this->functions as $pattern => $function) {
1254
+ $pattern = str_replace('\\*', '(.*?)', preg_quote($pattern, '#'), $count);
1255
+
1256
+ if ($count) {
1257
+ if (preg_match('#^'.$pattern.'$#', $name, $matches)) {
1258
+ array_shift($matches);
1259
+ $function->setArguments($matches);
1260
+
1261
+ return $function;
1262
+ }
1263
+ }
1264
+ }
1265
+
1266
+ foreach ($this->functionCallbacks as $callback) {
1267
+ if (false !== $function = call_user_func($callback, $name)) {
1268
+ return $function;
1269
+ }
1270
+ }
1271
+
1272
+ return false;
1273
+ }
1274
+
1275
+ public function registerUndefinedFunctionCallback($callable)
1276
+ {
1277
+ $this->functionCallbacks[] = $callable;
1278
+ }
1279
+
1280
+ /**
1281
+ * Gets registered functions.
1282
+ *
1283
+ * Be warned that this method cannot return functions defined with registerUndefinedFunctionCallback.
1284
+ *
1285
+ * @return Twig_FunctionInterface[]
1286
+ *
1287
+ * @see registerUndefinedFunctionCallback
1288
+ *
1289
+ * @internal
1290
+ */
1291
+ public function getFunctions()
1292
+ {
1293
+ if (!$this->extensionInitialized) {
1294
+ $this->initExtensions();
1295
+ }
1296
+
1297
+ return $this->functions;
1298
+ }
1299
+
1300
+ /**
1301
+ * Registers a Global.
1302
+ *
1303
+ * New globals can be added before compiling or rendering a template;
1304
+ * but after, you can only update existing globals.
1305
+ *
1306
+ * @param string $name The global name
1307
+ * @param mixed $value The global value
1308
+ */
1309
+ public function addGlobal($name, $value)
1310
+ {
1311
+ if ($this->extensionInitialized || $this->runtimeInitialized) {
1312
+ if (null === $this->globals) {
1313
+ $this->globals = $this->initGlobals();
1314
+ }
1315
+
1316
+ if (!array_key_exists($name, $this->globals)) {
1317
+ // The deprecation notice must be turned into the following exception in Twig 2.0
1318
+ @trigger_error(sprintf('Registering global variable "%s" at runtime or when the extensions have already been initialized is deprecated since version 1.21.', $name), E_USER_DEPRECATED);
1319
+ //throw new LogicException(sprintf('Unable to add global "%s" as the runtime or the extensions have already been initialized.', $name));
1320
+ }
1321
+ }
1322
+
1323
+ if ($this->extensionInitialized || $this->runtimeInitialized) {
1324
+ // update the value
1325
+ $this->globals[$name] = $value;
1326
+ } else {
1327
+ $this->staging->addGlobal($name, $value);
1328
+ }
1329
+ }
1330
+
1331
+ /**
1332
+ * Gets the registered Globals.
1333
+ *
1334
+ * @return array An array of globals
1335
+ *
1336
+ * @internal
1337
+ */
1338
+ public function getGlobals()
1339
+ {
1340
+ if (!$this->runtimeInitialized && !$this->extensionInitialized) {
1341
+ return $this->initGlobals();
1342
+ }
1343
+
1344
+ if (null === $this->globals) {
1345
+ $this->globals = $this->initGlobals();
1346
+ }
1347
+
1348
+ return $this->globals;
1349
+ }
1350
+
1351
+ /**
1352
+ * Merges a context with the defined globals.
1353
+ *
1354
+ * @param array $context An array representing the context
1355
+ *
1356
+ * @return array The context merged with the globals
1357
+ */
1358
+ public function mergeGlobals(array $context)
1359
+ {
1360
+ // we don't use array_merge as the context being generally
1361
+ // bigger than globals, this code is faster.
1362
+ foreach ($this->getGlobals() as $key => $value) {
1363
+ if (!array_key_exists($key, $context)) {
1364
+ $context[$key] = $value;
1365
+ }
1366
+ }
1367
+
1368
+ return $context;
1369
+ }
1370
+
1371
+ /**
1372
+ * Gets the registered unary Operators.
1373
+ *
1374
+ * @return array An array of unary operators
1375
+ *
1376
+ * @internal
1377
+ */
1378
+ public function getUnaryOperators()
1379
+ {
1380
+ if (!$this->extensionInitialized) {
1381
+ $this->initExtensions();
1382
+ }
1383
+
1384
+ return $this->unaryOperators;
1385
+ }
1386
+
1387
+ /**
1388
+ * Gets the registered binary Operators.
1389
+ *
1390
+ * @return array An array of binary operators
1391
+ *
1392
+ * @internal
1393
+ */
1394
+ public function getBinaryOperators()
1395
+ {
1396
+ if (!$this->extensionInitialized) {
1397
+ $this->initExtensions();
1398
+ }
1399
+
1400
+ return $this->binaryOperators;
1401
+ }
1402
+
1403
+ /**
1404
+ * @deprecated since 1.23 (to be removed in 2.0)
1405
+ */
1406
+ public function computeAlternatives($name, $items)
1407
+ {
1408
+ @trigger_error(sprintf('The %s method is deprecated since version 1.23 and will be removed in Twig 2.0.', __METHOD__), E_USER_DEPRECATED);
1409
+
1410
+ return Twig_Error_Syntax::computeAlternatives($name, $items);
1411
+ }
1412
+
1413
+ /**
1414
+ * @internal
1415
+ */
1416
+ protected function initGlobals()
1417
+ {
1418
+ $globals = array();
1419
+ foreach ($this->extensions as $name => $extension) {
1420
+ if (!$extension instanceof Twig_Extension_GlobalsInterface) {
1421
+ $m = new ReflectionMethod($extension, 'getGlobals');
1422
+
1423
+ if ('Twig_Extension' !== $m->getDeclaringClass()->getName()) {
1424
+ @trigger_error(sprintf('Defining the getGlobals() method in the "%s" extension without explicitly implementing Twig_Extension_GlobalsInterface is deprecated since version 1.23.', $name), E_USER_DEPRECATED);
1425
+ }
1426
+ }
1427
+
1428
+ $extGlob = $extension->getGlobals();
1429
+ if (!is_array($extGlob)) {
1430
+ throw new UnexpectedValueException(sprintf('"%s::getGlobals()" must return an array of globals.', get_class($extension)));
1431
+ }
1432
+
1433
+ $globals[] = $extGlob;
1434
+ }
1435
+
1436
+ $globals[] = $this->staging->getGlobals();
1437
+
1438
+ return call_user_func_array('array_merge', $globals);
1439
+ }
1440
+
1441
+ /**
1442
+ * @internal
1443
+ */
1444
+ protected function initExtensions()
1445
+ {
1446
+ if ($this->extensionInitialized) {
1447
+ return;
1448
+ }
1449
+
1450
+ $this->parsers = new Twig_TokenParserBroker(array(), array(), false);
1451
+ $this->filters = array();
1452
+ $this->functions = array();
1453
+ $this->tests = array();
1454
+ $this->visitors = array();
1455
+ $this->unaryOperators = array();
1456
+ $this->binaryOperators = array();
1457
+
1458
+ foreach ($this->extensions as $extension) {
1459
+ $this->initExtension($extension);
1460
+ }
1461
+ $this->initExtension($this->staging);
1462
+ // Done at the end only, so that an exception during initialization does not mark the environment as initialized when catching the exception
1463
+ $this->extensionInitialized = true;
1464
+ }
1465
+
1466
+ /**
1467
+ * @internal
1468
+ */
1469
+ protected function initExtension(Twig_ExtensionInterface $extension)
1470
+ {
1471
+ // filters
1472
+ foreach ($extension->getFilters() as $name => $filter) {
1473
+ if ($filter instanceof Twig_SimpleFilter) {
1474
+ $name = $filter->getName();
1475
+ } else {
1476
+ @trigger_error(sprintf('Using an instance of "%s" for filter "%s" is deprecated since version 1.21. Use Twig_SimpleFilter instead.', get_class($filter), $name), E_USER_DEPRECATED);
1477
+ }
1478
+
1479
+ $this->filters[$name] = $filter;
1480
+ }
1481
+
1482
+ // functions
1483
+ foreach ($extension->getFunctions() as $name => $function) {
1484
+ if ($function instanceof Twig_SimpleFunction) {
1485
+ $name = $function->getName();
1486
+ } else {
1487
+ @trigger_error(sprintf('Using an instance of "%s" for function "%s" is deprecated since version 1.21. Use Twig_SimpleFunction instead.', get_class($function), $name), E_USER_DEPRECATED);
1488
+ }
1489
+
1490
+ $this->functions[$name] = $function;
1491
+ }
1492
+
1493
+ // tests
1494
+ foreach ($extension->getTests() as $name => $test) {
1495
+ if ($test instanceof Twig_SimpleTest) {
1496
+ $name = $test->getName();
1497
+ } else {
1498
+ @trigger_error(sprintf('Using an instance of "%s" for test "%s" is deprecated since version 1.21. Use Twig_SimpleTest instead.', get_class($test), $name), E_USER_DEPRECATED);
1499
+ }
1500
+
1501
+ $this->tests[$name] = $test;
1502
+ }
1503
+
1504
+ // token parsers
1505
+ foreach ($extension->getTokenParsers() as $parser) {
1506
+ if ($parser instanceof Twig_TokenParserInterface) {
1507
+ $this->parsers->addTokenParser($parser);
1508
+ } elseif ($parser instanceof Twig_TokenParserBrokerInterface) {
1509
+ @trigger_error('Registering a Twig_TokenParserBrokerInterface instance is deprecated since version 1.21.', E_USER_DEPRECATED);
1510
+
1511
+ $this->parsers->addTokenParserBroker($parser);
1512
+ } else {
1513
+ throw new LogicException('getTokenParsers() must return an array of Twig_TokenParserInterface or Twig_TokenParserBrokerInterface instances.');
1514
+ }
1515
+ }
1516
+
1517
+ // node visitors
1518
+ foreach ($extension->getNodeVisitors() as $visitor) {
1519
+ $this->visitors[] = $visitor;
1520
+ }
1521
+
1522
+ // operators
1523
+ if ($operators = $extension->getOperators()) {
1524
+ if (!is_array($operators)) {
1525
+ throw new InvalidArgumentException(sprintf('"%s::getOperators()" must return an array with operators, got "%s".', get_class($extension), is_object($operators) ? get_class($operators) : gettype($operators).(is_resource($operators) ? '' : '#'.$operators)));
1526
+ }
1527
+
1528
+ if (2 !== count($operators)) {
1529
+ throw new InvalidArgumentException(sprintf('"%s::getOperators()" must return an array of 2 elements, got %d.', get_class($extension), count($operators)));
1530
+ }
1531
+
1532
+ $this->unaryOperators = array_merge($this->unaryOperators, $operators[0]);
1533
+ $this->binaryOperators = array_merge($this->binaryOperators, $operators[1]);
1534
+ }
1535
+ }
1536
+
1537
+ /**
1538
+ * @deprecated since 1.22 (to be removed in 2.0)
1539
+ */
1540
+ protected function writeCacheFile($file, $content)
1541
+ {
1542
+ $this->cache->write($file, $content);
1543
+ }
1544
+
1545
+ private function updateOptionsHash()
1546
+ {
1547
+ $hashParts = array_merge(
1548
+ array_keys($this->extensions),
1549
+ array(
1550
+ (int) function_exists('twig_template_get_attributes'),
1551
+ PHP_MAJOR_VERSION,
1552
+ PHP_MINOR_VERSION,
1553
+ self::VERSION,
1554
+ (int) $this->debug,
1555
+ $this->baseTemplateClass,
1556
+ (int) $this->strictVariables,
1557
+ )
1558
+ );
1559
+ $this->optionsHash = implode(':', $hashParts);
1560
+ }
1561
+ }
vendor/twig/twig/lib/Twig/Error.php ADDED
@@ -0,0 +1,365 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * This file is part of Twig.
5
+ *
6
+ * (c) Fabien Potencier
7
+ *
8
+ * For the full copyright and license information, please view the LICENSE
9
+ * file that was distributed with this source code.
10
+ */
11
+
12
+ /**
13
+ * Twig base exception.
14
+ *
15
+ * This exception class and its children must only be used when
16
+ * an error occurs during the loading of a template, when a syntax error
17
+ * is detected in a template, or when rendering a template. Other
18
+ * errors must use regular PHP exception classes (like when the template
19
+ * cache directory is not writable for instance).
20
+ *
21
+ * To help debugging template issues, this class tracks the original template
22
+ * name and line where the error occurred.
23
+ *
24
+ * Whenever possible, you must set these information (original template name
25
+ * and line number) yourself by passing them to the constructor. If some or all
26
+ * these information are not available from where you throw the exception, then
27
+ * this class will guess them automatically (when the line number is set to -1
28
+ * and/or the name is set to null). As this is a costly operation, this
29
+ * can be disabled by passing false for both the name and the line number
30
+ * when creating a new instance of this class.
31
+ *
32
+ * @author Fabien Potencier <fabien@symfony.com>
33
+ */
34
+ class Twig_Error extends Exception
35
+ {
36
+ protected $lineno;
37
+ // to be renamed to name in 2.0
38
+ protected $filename;
39
+ protected $rawMessage;
40
+ protected $previous;
41
+
42
+ private $sourcePath;
43
+ private $sourceCode;
44
+
45
+ /**
46
+ * Constructor.
47
+ *
48
+ * Set both the line number and the name to false to
49
+ * disable automatic guessing of the original template name
50
+ * and line number.
51
+ *
52
+ * Set the line number to -1 to enable its automatic guessing.
53
+ * Set the name to null to enable its automatic guessing.
54
+ *
55
+ * By default, automatic guessing is enabled.
56
+ *
57
+ * @param string $message The error message
58
+ * @param int $lineno The template line where the error occurred
59
+ * @param Twig_Source|string|null $source The source context where the error occurred
60
+ * @param Exception $previous The previous exception
61
+ */
62
+ public function __construct($message, $lineno = -1, $source = null, Exception $previous = null)
63
+ {
64
+ if (null === $source) {
65
+ $name = null;
66
+ } elseif (!$source instanceof Twig_Source) {
67
+ // for compat with the Twig C ext., passing the template name as string is accepted
68
+ $name = $source;
69
+ } else {
70
+ $name = $source->getName();
71
+ $this->sourceCode = $source->getCode();
72
+ $this->sourcePath = $source->getPath();
73
+ }
74
+ if (PHP_VERSION_ID < 50300) {
75
+ $this->previous = $previous;
76
+ parent::__construct('');
77
+ } else {
78
+ parent::__construct('', 0, $previous);
79
+ }
80
+
81
+ $this->lineno = $lineno;
82
+ $this->filename = $name;
83
+
84
+ if (-1 === $lineno || null === $name || null === $this->sourcePath) {
85
+ $this->guessTemplateInfo();
86
+ }
87
+
88
+ $this->rawMessage = $message;
89
+
90
+ $this->updateRepr();
91
+ }
92
+
93
+ /**
94
+ * Gets the raw message.
95
+ *
96
+ * @return string The raw message
97
+ */
98
+ public function getRawMessage()
99
+ {
100
+ return $this->rawMessage;
101
+ }
102
+
103
+ /**
104
+ * Gets the logical name where the error occurred.
105
+ *
106
+ * @return string The name
107
+ *
108
+ * @deprecated since 1.27 (to be removed in 2.0). Use getSourceContext() instead.
109
+ */
110
+ public function getTemplateFile()
111
+ {
112
+ @trigger_error(sprintf('The "%s" method is deprecated since version 1.27 and will be removed in 2.0. Use getSourceContext() instead.', __METHOD__), E_USER_DEPRECATED);
113
+
114
+ return $this->filename;
115
+ }
116
+
117
+ /**
118
+ * Sets the logical name where the error occurred.
119
+ *
120
+ * @param string $name The name
121
+ *
122
+ * @deprecated since 1.27 (to be removed in 2.0). Use setSourceContext() instead.
123
+ */
124
+ public function setTemplateFile($name)
125
+ {
126
+ @trigger_error(sprintf('The "%s" method is deprecated since version 1.27 and will be removed in 2.0. Use setSourceContext() instead.', __METHOD__), E_USER_DEPRECATED);
127
+
128
+ $this->filename = $name;
129
+
130
+ $this->updateRepr();
131
+ }
132
+
133
+ /**
134
+ * Gets the logical name where the error occurred.
135
+ *
136
+ * @return string The name
137
+ *
138
+ * @deprecated since 1.29 (to be removed in 2.0). Use getSourceContext() instead.
139
+ */
140
+ public function getTemplateName()
141
+ {
142
+ @trigger_error(sprintf('The "%s" method is deprecated since version 1.29 and will be removed in 2.0. Use getSourceContext() instead.', __METHOD__), E_USER_DEPRECATED);
143
+
144
+ return $this->filename;
145
+ }
146
+
147
+ /**
148
+ * Sets the logical name where the error occurred.
149
+ *
150
+ * @param string $name The name
151
+ *
152
+ * @deprecated since 1.29 (to be removed in 2.0). Use setSourceContext() instead.
153
+ */
154
+ public function setTemplateName($name)
155
+ {
156
+ @trigger_error(sprintf('The "%s" method is deprecated since version 1.29 and will be removed in 2.0. Use setSourceContext() instead.', __METHOD__), E_USER_DEPRECATED);
157
+
158
+ $this->filename = $name;
159
+ $this->sourceCode = $this->sourcePath = null;
160
+
161
+ $this->updateRepr();
162
+ }
163
+
164
+ /**
165
+ * Gets the template line where the error occurred.
166
+ *
167
+ * @return int The template line
168
+ */
169
+ public function getTemplateLine()
170
+ {
171
+ return $this->lineno;
172
+ }
173
+
174
+ /**
175
+ * Sets the template line where the error occurred.
176
+ *
177
+ * @param int $lineno The template line
178
+ */
179
+ public function setTemplateLine($lineno)
180
+ {
181
+ $this->lineno = $lineno;
182
+
183
+ $this->updateRepr();
184
+ }
185
+
186
+ /**
187
+ * Gets the source context of the Twig template where the error occurred.
188
+ *
189
+ * @return Twig_Source|null
190
+ */
191
+ public function getSourceContext()
192
+ {
193
+ return $this->filename ? new Twig_Source($this->sourceCode, $this->filename, $this->sourcePath) : null;
194
+ }
195
+
196
+ /**
197
+ * Sets the source context of the Twig template where the error occurred.
198
+ */
199
+ public function setSourceContext(Twig_Source $source = null)
200
+ {
201
+ if (null === $source) {
202
+ $this->sourceCode = $this->filename = $this->sourcePath = null;
203
+ } else {
204
+ $this->sourceCode = $source->getCode();
205
+ $this->filename = $source->getName();
206
+ $this->sourcePath = $source->getPath();
207
+ }
208
+
209
+ $this->updateRepr();
210
+ }
211
+
212
+ public function guess()
213
+ {
214
+ $this->guessTemplateInfo();
215
+ $this->updateRepr();
216
+ }
217
+
218
+ /**
219
+ * For PHP < 5.3.0, provides access to the getPrevious() method.
220
+ *
221
+ * @param string $method The method name
222
+ * @param array $arguments The parameters to be passed to the method
223
+ *
224
+ * @return Exception The previous exception or null
225
+ *
226
+ * @throws BadMethodCallException
227
+ */
228
+ public function __call($method, $arguments)
229
+ {
230
+ if ('getprevious' == strtolower($method)) {
231
+ return $this->previous;
232
+ }
233
+
234
+ throw new BadMethodCallException(sprintf('Method "Twig_Error::%s()" does not exist.', $method));
235
+ }
236
+
237
+ public function appendMessage($rawMessage)
238
+ {
239
+ $this->rawMessage .= $rawMessage;
240
+ $this->updateRepr();
241
+ }
242
+
243
+ /**
244
+ * @internal
245
+ */
246
+ protected function updateRepr()
247
+ {
248
+ $this->message = $this->rawMessage;
249
+
250
+ if ($this->sourcePath && $this->lineno > 0) {
251
+ $this->file = $this->sourcePath;
252
+ $this->line = $this->lineno;
253
+
254
+ return;
255
+ }
256
+
257
+ $dot = false;
258
+ if ('.' === substr($this->message, -1)) {
259
+ $this->message = substr($this->message, 0, -1);
260
+ $dot = true;
261
+ }
262
+
263
+ $questionMark = false;
264
+ if ('?' === substr($this->message, -1)) {
265
+ $this->message = substr($this->message, 0, -1);
266
+ $questionMark = true;
267
+ }
268
+
269
+ if ($this->filename) {
270
+ if (is_string($this->filename) || (is_object($this->filename) && method_exists($this->filename, '__toString'))) {
271
+ $name = sprintf('"%s"', $this->filename);
272
+ } else {
273
+ $name = json_encode($this->filename);
274
+ }
275
+ $this->message .= sprintf(' in %s', $name);
276
+ }
277
+
278
+ if ($this->lineno && $this->lineno >= 0) {
279
+ $this->message .= sprintf(' at line %d', $this->lineno);
280
+ }
281
+
282
+ if ($dot) {
283
+ $this->message .= '.';
284
+ }
285
+
286
+ if ($questionMark) {
287
+ $this->message .= '?';
288
+ }
289
+ }
290
+
291
+ /**
292
+ * @internal
293
+ */
294
+ protected function guessTemplateInfo()
295
+ {
296
+ $template = null;
297
+ $templateClass = null;
298
+
299
+ if (PHP_VERSION_ID >= 50306) {
300
+ $backtrace = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS | DEBUG_BACKTRACE_PROVIDE_OBJECT);
301
+ } else {
302
+ $backtrace = debug_backtrace();
303
+ }
304
+
305
+ foreach ($backtrace as $trace) {
306
+ if (isset($trace['object']) && $trace['object'] instanceof Twig_Template && 'Twig_Template' !== get_class($trace['object'])) {
307
+ $currentClass = get_class($trace['object']);
308
+ $isEmbedContainer = 0 === strpos($templateClass, $currentClass);
309
+ if (null === $this->filename || ($this->filename == $trace['object']->getTemplateName() && !$isEmbedContainer)) {
310
+ $template = $trace['object'];
311
+ $templateClass = get_class($trace['object']);
312
+ }
313
+ }
314
+ }
315
+
316
+ // update template name
317
+ if (null !== $template && null === $this->filename) {
318
+ $this->filename = $template->getTemplateName();
319
+ }
320
+
321
+ // update template path if any
322
+ if (null !== $template && null === $this->sourcePath) {
323
+ $src = $template->getSourceContext();
324
+ $this->sourceCode = $src->getCode();
325
+ $this->sourcePath = $src->getPath();
326
+ }
327
+
328
+ if (null === $template || $this->lineno > -1) {
329
+ return;
330
+ }
331
+
332
+ $r = new ReflectionObject($template);
333
+ $file = $r->getFileName();
334
+
335
+ // hhvm has a bug where eval'ed files comes out as the current directory
336
+ if (is_dir($file)) {
337
+ $file = '';
338
+ }
339
+
340
+ $exceptions = array($e = $this);
341
+ while (($e instanceof self || method_exists($e, 'getPrevious')) && $e = $e->getPrevious()) {
342
+ $exceptions[] = $e;
343
+ }
344
+
345
+ while ($e = array_pop($exceptions)) {
346
+ $traces = $e->getTrace();
347
+ array_unshift($traces, array('file' => $e->getFile(), 'line' => $e->getLine()));
348
+
349
+ while ($trace = array_shift($traces)) {
350
+ if (!isset($trace['file']) || !isset($trace['line']) || $file != $trace['file']) {
351
+ continue;
352
+ }
353
+
354
+ foreach ($template->getDebugInfo() as $codeLine => $templateLine) {
355
+ if ($codeLine <= $trace['line']) {
356
+ // update template line
357
+ $this->lineno = $templateLine;
358
+
359
+ return;
360
+ }
361
+ }
362
+ }
363
+ }
364
+ }
365
+ }
vendor/twig/twig/lib/Twig/Error/Loader.php ADDED
@@ -0,0 +1,38 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * This file is part of Twig.
5
+ *
6
+ * (c) Fabien Potencier
7
+ *
8
+ * For the full copyright and license information, please view the LICENSE
9
+ * file that was distributed with this source code.
10
+ */
11
+
12
+ /**
13
+ * Exception thrown when an error occurs during template loading.
14
+ *
15
+ * Automatic template information guessing is always turned off as
16
+ * if a template cannot be loaded, there is nothing to guess.
17
+ * However, when a template is loaded from another one, then, we need
18
+ * to find the current context and this is automatically done by
19
+ * Twig_Template::displayWithErrorHandling().
20
+ *
21
+ * This strategy makes Twig_Environment::resolveTemplate() much faster.
22
+ *
23
+ * @author Fabien Potencier <fabien@symfony.com>
24
+ */
25
+ class Twig_Error_Loader extends Twig_Error
26
+ {
27
+ public function __construct($message, $lineno = -1, $source = null, Exception $previous = null)
28
+ {
29
+ if (PHP_VERSION_ID < 50300) {
30
+ $this->previous = $previous;
31
+ Exception::__construct('');
32
+ } else {
33
+ Exception::__construct('', 0, $previous);
34
+ }
35
+ $this->appendMessage($message);
36
+ $this->setTemplateLine(false);
37
+ }
38
+ }
vendor/twig/twig/lib/Twig/Error/Runtime.php ADDED
@@ -0,0 +1,20 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * This file is part of Twig.
5
+ *
6
+ * (c) Fabien Potencier
7
+ * (c) Armin Ronacher
8
+ *
9
+ * For the full copyright and license information, please view the LICENSE
10
+ * file that was distributed with this source code.
11
+ */
12
+
13
+ /**
14
+ * Exception thrown when an error occurs at runtime.
15
+ *
16
+ * @author Fabien Potencier <fabien@symfony.com>
17
+ */
18
+ class Twig_Error_Runtime extends Twig_Error
19
+ {
20
+ }
vendor/twig/twig/lib/Twig/Error/Syntax.php ADDED
@@ -0,0 +1,53 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * This file is part of Twig.
5
+ *
6
+ * (c) Fabien Potencier
7
+ * (c) Armin Ronacher
8
+ *
9
+ * For the full copyright and license information, please view the LICENSE
10
+ * file that was distributed with this source code.
11
+ */
12
+
13
+ /**
14
+ * Exception thrown when a syntax error occurs during lexing or parsing of a template.
15
+ *
16
+ * @author Fabien Potencier <fabien@symfony.com>
17
+ */
18
+ class Twig_Error_Syntax extends Twig_Error
19
+ {
20
+ /**
21
+ * Tweaks the error message to include suggestions.
22
+ *
23
+ * @param string $name The original name of the item that does not exist
24
+ * @param array $items An array of possible items
25
+ */
26
+ public function addSuggestions($name, array $items)
27
+ {
28
+ if (!$alternatives = self::computeAlternatives($name, $items)) {
29
+ return;
30
+ }
31
+
32
+ $this->appendMessage(sprintf(' Did you mean "%s"?', implode('", "', $alternatives)));
33
+ }
34
+
35
+ /**
36
+ * @internal
37
+ *
38
+ * To be merged with the addSuggestions() method in 2.0.
39
+ */
40
+ public static function computeAlternatives($name, $items)
41
+ {
42
+ $alternatives = array();
43
+ foreach ($items as $item) {
44
+ $lev = levenshtein($name, $item);
45
+ if ($lev <= strlen($name) / 3 || false !== strpos($item, $name)) {
46
+ $alternatives[$item] = $lev;
47
+ }
48
+ }
49
+ asort($alternatives);
50
+
51
+ return array_keys($alternatives);
52
+ }
53
+ }
vendor/twig/twig/lib/Twig/ExistsLoaderInterface.php ADDED
@@ -0,0 +1,29 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * This file is part of Twig.
5
+ *
6
+ * (c) Fabien Potencier
7
+ *
8
+ * For the full copyright and license information, please view the LICENSE
9
+ * file that was distributed with this source code.
10
+ */
11
+
12
+ /**
13
+ * Adds an exists() method for loaders.
14
+ *
15
+ * @author Florin Patan <florinpatan@gmail.com>
16
+ *
17
+ * @deprecated since 1.12 (to be removed in 3.0)
18
+ */
19
+ interface Twig_ExistsLoaderInterface
20
+ {
21
+ /**
22
+ * Check if we have the source code of a template, given its name.
23
+ *
24
+ * @param string $name The name of the template to check if we can load
25
+ *
26
+ * @return bool If the template source code is handled by this loader or not
27
+ */
28
+ public function exists($name);
29
+ }
vendor/twig/twig/lib/Twig/ExpressionParser.php ADDED
@@ -0,0 +1,739 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * This file is part of Twig.
5
+ *
6
+ * (c) Fabien Potencier
7
+ * (c) Armin Ronacher
8
+ *
9
+ * For the full copyright and license information, please view the LICENSE
10
+ * file that was distributed with this source code.
11
+ */
12
+
13
+ /**
14
+ * Parses expressions.
15
+ *
16
+ * This parser implements a "Precedence climbing" algorithm.
17
+ *
18
+ * @see http://www.engr.mun.ca/~theo/Misc/exp_parsing.htm
19
+ * @see http://en.wikipedia.org/wiki/Operator-precedence_parser
20
+ *
21
+ * @author Fabien Potencier <fabien@symfony.com>
22
+ *
23
+ * @internal
24
+ */
25
+ class Twig_ExpressionParser
26
+ {
27
+ const OPERATOR_LEFT = 1;
28
+ const OPERATOR_RIGHT = 2;
29
+
30
+ protected $parser;
31
+ protected $unaryOperators;
32
+ protected $binaryOperators;
33
+
34
+ private $env;
35
+
36
+ public function __construct(Twig_Parser $parser, $env = null)
37
+ {
38
+ $this->parser = $parser;
39
+
40
+ if ($env instanceof Twig_Environment) {
41
+ $this->env = $env;
42
+ $this->unaryOperators = $env->getUnaryOperators();
43
+ $this->binaryOperators = $env->getBinaryOperators();
44
+ } else {
45
+ @trigger_error('Passing the operators as constructor arguments to '.__METHOD__.' is deprecated since version 1.27. Pass the environment instead.', E_USER_DEPRECATED);
46
+
47
+ $this->env = $parser->getEnvironment();
48
+ $this->unaryOperators = func_get_arg(1);
49
+ $this->binaryOperators = func_get_arg(2);
50
+ }
51
+ }
52
+
53
+ public function parseExpression($precedence = 0)
54
+ {
55
+ $expr = $this->getPrimary();
56
+ $token = $this->parser->getCurrentToken();
57
+ while ($this->isBinary($token) && $this->binaryOperators[$token->getValue()]['precedence'] >= $precedence) {
58
+ $op = $this->binaryOperators[$token->getValue()];
59
+ $this->parser->getStream()->next();
60
+
61
+ if ('is not' === $token->getValue()) {
62
+ $expr = $this->parseNotTestExpression($expr);
63
+ } elseif ('is' === $token->getValue()) {
64
+ $expr = $this->parseTestExpression($expr);
65
+ } elseif (isset($op['callable'])) {
66
+ $expr = call_user_func($op['callable'], $this->parser, $expr);
67
+ } else {
68
+ $expr1 = $this->parseExpression(self::OPERATOR_LEFT === $op['associativity'] ? $op['precedence'] + 1 : $op['precedence']);
69
+ $class = $op['class'];
70
+ $expr = new $class($expr, $expr1, $token->getLine());
71
+ }
72
+
73
+ $token = $this->parser->getCurrentToken();
74
+ }
75
+
76
+ if (0 === $precedence) {
77
+ return $this->parseConditionalExpression($expr);
78
+ }
79
+
80
+ return $expr;
81
+ }
82
+
83
+ protected function getPrimary()
84
+ {
85
+ $token = $this->parser->getCurrentToken();
86
+
87
+ if ($this->isUnary($token)) {
88
+ $operator = $this->unaryOperators[$token->getValue()];
89
+ $this->parser->getStream()->next();
90
+ $expr = $this->parseExpression($operator['precedence']);
91
+ $class = $operator['class'];
92
+
93
+ return $this->parsePostfixExpression(new $class($expr, $token->getLine()));
94
+ } elseif ($token->test(Twig_Token::PUNCTUATION_TYPE, '(')) {
95
+ $this->parser->getStream()->next();
96
+ $expr = $this->parseExpression();
97
+ $this->parser->getStream()->expect(Twig_Token::PUNCTUATION_TYPE, ')', 'An opened parenthesis is not properly closed');
98
+
99
+ return $this->parsePostfixExpression($expr);
100
+ }
101
+
102
+ return $this->parsePrimaryExpression();
103
+ }
104
+
105
+ protected function parseConditionalExpression($expr)
106
+ {
107
+ while ($this->parser->getStream()->nextIf(Twig_Token::PUNCTUATION_TYPE, '?')) {
108
+ if (!$this->parser->getStream()->nextIf(Twig_Token::PUNCTUATION_TYPE, ':')) {
109
+ $expr2 = $this->parseExpression();
110
+ if ($this->parser->getStream()->nextIf(Twig_Token::PUNCTUATION_TYPE, ':')) {
111
+ $expr3 = $this->parseExpression();
112
+ } else {
113
+ $expr3 = new Twig_Node_Expression_Constant('', $this->parser->getCurrentToken()->getLine());
114
+ }
115
+ } else {
116
+ $expr2 = $expr;
117
+ $expr3 = $this->parseExpression();
118
+ }
119
+
120
+ $expr = new Twig_Node_Expression_Conditional($expr, $expr2, $expr3, $this->parser->getCurrentToken()->getLine());
121
+ }
122
+
123
+ return $expr;
124
+ }
125
+
126
+ protected function isUnary(Twig_Token $token)
127
+ {
128
+ return $token->test(Twig_Token::OPERATOR_TYPE) && isset($this->unaryOperators[$token->getValue()]);
129
+ }
130
+
131
+ protected function isBinary(Twig_Token $token)
132
+ {
133
+ return $token->test(Twig_Token::OPERATOR_TYPE) && isset($this->binaryOperators[$token->getValue()]);
134
+ }
135
+
136
+ public function parsePrimaryExpression()
137
+ {
138
+ $token = $this->parser->getCurrentToken();
139
+ switch ($token->getType()) {
140
+ case Twig_Token::NAME_TYPE:
141
+ $this->parser->getStream()->next();
142
+ switch ($token->getValue()) {
143
+ case 'true':
144
+ case 'TRUE':
145
+ $node = new Twig_Node_Expression_Constant(true, $token->getLine());
146
+ break;
147
+
148
+ case 'false':
149
+ case 'FALSE':
150
+ $node = new Twig_Node_Expression_Constant(false, $token->getLine());
151
+ break;
152
+
153
+ case 'none':
154
+ case 'NONE':
155
+ case 'null':
156
+ case 'NULL':
157
+ $node = new Twig_Node_Expression_Constant(null, $token->getLine());
158
+ break;
159
+
160
+ default:
161
+ if ('(' === $this->parser->getCurrentToken()->getValue()) {
162
+ $node = $this->getFunctionNode($token->getValue(), $token->getLine());
163
+ } else {
164
+ $node = new Twig_Node_Expression_Name($token->getValue(), $token->getLine());
165
+ }
166
+ }
167
+ break;
168
+
169
+ case Twig_Token::NUMBER_TYPE:
170
+ $this->parser->getStream()->next();
171
+ $node = new Twig_Node_Expression_Constant($token->getValue(), $token->getLine());
172
+ break;
173
+
174
+ case Twig_Token::STRING_TYPE:
175
+ case Twig_Token::INTERPOLATION_START_TYPE:
176
+ $node = $this->parseStringExpression();
177
+ break;
178
+
179
+ case Twig_Token::OPERATOR_TYPE:
180
+ if (preg_match(Twig_Lexer::REGEX_NAME, $token->getValue(), $matches) && $matches[0] == $token->getValue()) {
181
+ // in this context, string operators are variable names
182
+ $this->parser->getStream()->next();
183
+ $node = new Twig_Node_Expression_Name($token->getValue(), $token->getLine());
184
+ break;
185
+ } elseif (isset($this->unaryOperators[$token->getValue()])) {
186
+ $class = $this->unaryOperators[$token->getValue()]['class'];
187
+
188
+ $ref = new ReflectionClass($class);
189
+ $negClass = 'Twig_Node_Expression_Unary_Neg';
190
+ $posClass = 'Twig_Node_Expression_Unary_Pos';
191
+ if (!(in_array($ref->getName(), array($negClass, $posClass)) || $ref->isSubclassOf($negClass) || $ref->isSubclassOf($posClass))) {
192
+ throw new Twig_Error_Syntax(sprintf('Unexpected unary operator "%s".', $token->getValue()), $token->getLine(), $this->parser->getStream()->getSourceContext());
193
+ }
194
+
195
+ $this->parser->getStream()->next();
196
+ $expr = $this->parsePrimaryExpression();
197
+
198
+ $node = new $class($expr, $token->getLine());
199
+ break;
200
+ }
201
+
202
+ default:
203
+ if ($token->test(Twig_Token::PUNCTUATION_TYPE, '[')) {
204
+ $node = $this->parseArrayExpression();
205
+ } elseif ($token->test(Twig_Token::PUNCTUATION_TYPE, '{')) {
206
+ $node = $this->parseHashExpression();
207
+ } else {
208
+ throw new Twig_Error_Syntax(sprintf('Unexpected token "%s" of value "%s".', Twig_Token::typeToEnglish($token->getType()), $token->getValue()), $token->getLine(), $this->parser->getStream()->getSourceContext());
209
+ }
210
+ }
211
+
212
+ return $this->parsePostfixExpression($node);
213
+ }
214
+
215
+ public function parseStringExpression()
216
+ {
217
+ $stream = $this->parser->getStream();
218
+
219
+ $nodes = array();
220
+ // a string cannot be followed by another string in a single expression
221
+ $nextCanBeString = true;
222
+ while (true) {
223
+ if ($nextCanBeString && $token = $stream->nextIf(Twig_Token::STRING_TYPE)) {
224
+ $nodes[] = new Twig_Node_Expression_Constant($token->getValue(), $token->getLine());
225
+ $nextCanBeString = false;
226
+ } elseif ($stream->nextIf(Twig_Token::INTERPOLATION_START_TYPE)) {
227
+ $nodes[] = $this->parseExpression();
228
+ $stream->expect(Twig_Token::INTERPOLATION_END_TYPE);
229
+ $nextCanBeString = true;
230
+ } else {
231
+ break;
232
+ }
233
+ }
234
+
235
+ $expr = array_shift($nodes);
236
+ foreach ($nodes as $node) {
237
+ $expr = new Twig_Node_Expression_Binary_Concat($expr, $node, $node->getTemplateLine());
238
+ }
239
+
240
+ return $expr;
241
+ }
242
+
243
+ public function parseArrayExpression()
244
+ {
245
+ $stream = $this->parser->getStream();
246
+ $stream->expect(Twig_Token::PUNCTUATION_TYPE, '[', 'An array element was expected');
247
+
248
+ $node = new Twig_Node_Expression_Array(array(), $stream->getCurrent()->getLine());
249
+ $first = true;
250
+ while (!$stream->test(Twig_Token::PUNCTUATION_TYPE, ']')) {
251
+ if (!$first) {
252
+ $stream->expect(Twig_Token::PUNCTUATION_TYPE, ',', 'An array element must be followed by a comma');
253
+
254
+ // trailing ,?
255
+ if ($stream->test(Twig_Token::PUNCTUATION_TYPE, ']')) {
256
+ break;
257
+ }
258
+ }
259
+ $first = false;
260
+
261
+ $node->addElement($this->parseExpression());
262
+ }
263
+ $stream->expect(Twig_Token::PUNCTUATION_TYPE, ']', 'An opened array is not properly closed');
264
+
265
+ return $node;
266
+ }
267
+
268
+ public function parseHashExpression()
269
+ {
270
+ $stream = $this->parser->getStream();
271
+ $stream->expect(Twig_Token::PUNCTUATION_TYPE, '{', 'A hash element was expected');
272
+
273
+ $node = new Twig_Node_Expression_Array(array(), $stream->getCurrent()->getLine());
274
+ $first = true;
275
+ while (!$stream->test(Twig_Token::PUNCTUATION_TYPE, '}')) {
276
+ if (!$first) {
277
+ $stream->expect(Twig_Token::PUNCTUATION_TYPE, ',', 'A hash value must be followed by a comma');
278
+
279
+ // trailing ,?
280
+ if ($stream->test(Twig_Token::PUNCTUATION_TYPE, '}')) {
281
+ break;
282
+ }
283
+ }
284
+ $first = false;
285
+
286
+ // a hash key can be:
287
+ //
288
+ // * a number -- 12
289
+ // * a string -- 'a'
290
+ // * a name, which is equivalent to a string -- a
291
+ // * an expression, which must be enclosed in parentheses -- (1 + 2)
292
+ if (($token = $stream->nextIf(Twig_Token::STRING_TYPE)) || ($token = $stream->nextIf(Twig_Token::NAME_TYPE)) || $token = $stream->nextIf(Twig_Token::NUMBER_TYPE)) {
293
+ $key = new Twig_Node_Expression_Constant($token->getValue(), $token->getLine());
294
+ } elseif ($stream->test(Twig_Token::PUNCTUATION_TYPE, '(')) {
295
+ $key = $this->parseExpression();
296
+ } else {
297
+ $current = $stream->getCurrent();
298
+
299
+ throw new Twig_Error_Syntax(sprintf('A hash key must be a quoted string, a number, a name, or an expression enclosed in parentheses (unexpected token "%s" of value "%s".', Twig_Token::typeToEnglish($current->getType()), $current->getValue()), $current->getLine(), $stream->getSourceContext());
300
+ }
301
+
302
+ $stream->expect(Twig_Token::PUNCTUATION_TYPE, ':', 'A hash key must be followed by a colon (:)');
303
+ $value = $this->parseExpression();
304
+
305
+ $node->addElement($value, $key);
306
+ }
307
+ $stream->expect(Twig_Token::PUNCTUATION_TYPE, '}', 'An opened hash is not properly closed');
308
+
309
+ return $node;
310
+ }
311
+
312
+ public function parsePostfixExpression($node)
313
+ {
314
+ while (true) {
315
+ $token = $this->parser->getCurrentToken();
316
+ if ($token->getType() == Twig_Token::PUNCTUATION_TYPE) {
317
+ if ('.' == $token->getValue() || '[' == $token->getValue()) {
318
+ $node = $this->parseSubscriptExpression($node);
319
+ } elseif ('|' == $token->getValue()) {
320
+ $node = $this->parseFilterExpression($node);
321
+ } else {
322
+ break;
323
+ }
324
+ } else {
325
+ break;
326
+ }
327
+ }
328
+
329
+ return $node;
330
+ }
331
+
332
+ public function getFunctionNode($name, $line)
333
+ {
334
+ switch ($name) {
335
+ case 'parent':
336
+ $this->parseArguments();
337
+ if (!count($this->parser->getBlockStack())) {
338
+ throw new Twig_Error_Syntax('Calling "parent" outside a block is forbidden.', $line, $this->parser->getStream()->getSourceContext());
339
+ }
340
+
341
+ if (!$this->parser->getParent() && !$this->parser->hasTraits()) {
342
+ throw new Twig_Error_Syntax('Calling "parent" on a template that does not extend nor "use" another template is forbidden.', $line, $this->parser->getStream()->getSourceContext());
343
+ }
344
+
345
+ return new Twig_Node_Expression_Parent($this->parser->peekBlockStack(), $line);
346
+ case 'block':
347
+ $args = $this->parseArguments();
348
+ if (count($args) < 1) {
349
+ throw new Twig_Error_Syntax('The "block" function takes one argument (the block name).', $line, $this->parser->getStream()->getSourceContext());
350
+ }
351
+
352
+ return new Twig_Node_Expression_BlockReference($args->getNode(0), count($args) > 1 ? $args->getNode(1) : null, $line);
353
+ case 'attribute':
354
+ $args = $this->parseArguments();
355
+ if (count($args) < 2) {
356
+ throw new Twig_Error_Syntax('The "attribute" function takes at least two arguments (the variable and the attributes).', $line, $this->parser->getStream()->getSourceContext());
357
+ }
358
+
359
+ return new Twig_Node_Expression_GetAttr($args->getNode(0), $args->getNode(1), count($args) > 2 ? $args->getNode(2) : null, Twig_Template::ANY_CALL, $line);
360
+ default:
361
+ if (null !== $alias = $this->parser->getImportedSymbol('function', $name)) {
362
+ $arguments = new Twig_Node_Expression_Array(array(), $line);
363
+ foreach ($this->parseArguments() as $n) {
364
+ $arguments->addElement($n);
365
+ }
366
+
367
+ $node = new Twig_Node_Expression_MethodCall($alias['node'], $alias['name'], $arguments, $line);
368
+ $node->setAttribute('safe', true);
369
+
370
+ return $node;
371
+ }
372
+
373
+ $args = $this->parseArguments(true);
374
+ $class = $this->getFunctionNodeClass($name, $line);
375
+
376
+ return new $class($name, $args, $line);
377
+ }
378
+ }
379
+
380
+ public function parseSubscriptExpression($node)
381
+ {
382
+ $stream = $this->parser->getStream();
383
+ $token = $stream->next();
384
+ $lineno = $token->getLine();
385
+ $arguments = new Twig_Node_Expression_Array(array(), $lineno);
386
+ $type = Twig_Template::ANY_CALL;
387
+ if ($token->getValue() == '.') {
388
+ $token = $stream->next();
389
+ if (
390
+ $token->getType() == Twig_Token::NAME_TYPE
391
+ ||
392
+ $token->getType() == Twig_Token::NUMBER_TYPE
393
+ ||
394
+ ($token->getType() == Twig_Token::OPERATOR_TYPE && preg_match(Twig_Lexer::REGEX_NAME, $token->getValue()))
395
+ ) {
396
+ $arg = new Twig_Node_Expression_Constant($token->getValue(), $lineno);
397
+
398
+ if ($stream->test(Twig_Token::PUNCTUATION_TYPE, '(')) {
399
+ $type = Twig_Template::METHOD_CALL;
400
+ foreach ($this->parseArguments() as $n) {
401
+ $arguments->addElement($n);
402
+ }
403
+ }
404
+ } else {
405
+ throw new Twig_Error_Syntax('Expected name or number.', $lineno, $stream->getSourceContext());
406
+ }
407
+
408
+ if ($node instanceof Twig_Node_Expression_Name && null !== $this->parser->getImportedSymbol('template', $node->getAttribute('name'))) {
409
+ if (!$arg instanceof Twig_Node_Expression_Constant) {
410
+ throw new Twig_Error_Syntax(sprintf('Dynamic macro names are not supported (called on "%s").', $node->getAttribute('name')), $token->getLine(), $stream->getSourceContext());
411
+ }
412
+
413
+ $name = $arg->getAttribute('value');
414
+
415
+ if ($this->parser->isReservedMacroName($name)) {
416
+ throw new Twig_Error_Syntax(sprintf('"%s" cannot be called as macro as it is a reserved keyword.', $name), $token->getLine(), $stream->getSourceContext());
417
+ }
418
+
419
+ $node = new Twig_Node_Expression_MethodCall($node, 'get'.$name, $arguments, $lineno);
420
+ $node->setAttribute('safe', true);
421
+
422
+ return $node;
423
+ }
424
+ } else {
425
+ $type = Twig_Template::ARRAY_CALL;
426
+
427
+ // slice?
428
+ $slice = false;
429
+ if ($stream->test(Twig_Token::PUNCTUATION_TYPE, ':')) {
430
+ $slice = true;
431
+ $arg = new Twig_Node_Expression_Constant(0, $token->getLine());
432
+ } else {
433
+ $arg = $this->parseExpression();
434
+ }
435
+
436
+ if ($stream->nextIf(Twig_Token::PUNCTUATION_TYPE, ':')) {
437
+ $slice = true;
438
+ }
439
+
440
+ if ($slice) {
441
+ if ($stream->test(Twig_Token::PUNCTUATION_TYPE, ']')) {
442
+ $length = new Twig_Node_Expression_Constant(null, $token->getLine());
443
+ } else {
444
+ $length = $this->parseExpression();
445
+ }
446
+
447
+ $class = $this->getFilterNodeClass('slice', $token->getLine());
448
+ $arguments = new Twig_Node(array($arg, $length));
449
+ $filter = new $class($node, new Twig_Node_Expression_Constant('slice', $token->getLine()), $arguments, $token->getLine());
450
+
451
+ $stream->expect(Twig_Token::PUNCTUATION_TYPE, ']');
452
+
453
+ return $filter;
454
+ }
455
+
456
+ $stream->expect(Twig_Token::PUNCTUATION_TYPE, ']');
457
+ }
458
+
459
+ return new Twig_Node_Expression_GetAttr($node, $arg, $arguments, $type, $lineno);
460
+ }
461
+
462
+ public function parseFilterExpression($node)
463
+ {
464
+ $this->parser->getStream()->next();
465
+
466
+ return $this->parseFilterExpressionRaw($node);
467
+ }
468
+
469
+ public function parseFilterExpressionRaw($node, $tag = null)
470
+ {
471
+ while (true) {
472
+ $token = $this->parser->getStream()->expect(Twig_Token::NAME_TYPE);
473
+
474
+ $name = new Twig_Node_Expression_Constant($token->getValue(), $token->getLine());
475
+ if (!$this->parser->getStream()->test(Twig_Token::PUNCTUATION_TYPE, '(')) {
476
+ $arguments = new Twig_Node();
477
+ } else {
478
+ $arguments = $this->parseArguments(true);
479
+ }
480
+
481
+ $class = $this->getFilterNodeClass($name->getAttribute('value'), $token->getLine());
482
+
483
+ $node = new $class($node, $name, $arguments, $token->getLine(), $tag);
484
+
485
+ if (!$this->parser->getStream()->test(Twig_Token::PUNCTUATION_TYPE, '|')) {
486
+ break;
487
+ }
488
+
489
+ $this->parser->getStream()->next();
490
+ }
491
+
492
+ return $node;
493
+ }
494
+
495
+ /**
496
+ * Parses arguments.
497
+ *
498
+ * @param bool $namedArguments Whether to allow named arguments or not
499
+ * @param bool $definition Whether we are parsing arguments for a function definition
500
+ *
501
+ * @return Twig_Node
502
+ *
503
+ * @throws Twig_Error_Syntax
504
+ */
505
+ public function parseArguments($namedArguments = false, $definition = false)
506
+ {
507
+ $args = array();
508
+ $stream = $this->parser->getStream();
509
+
510
+ $stream->expect(Twig_Token::PUNCTUATION_TYPE, '(', 'A list of arguments must begin with an opening parenthesis');
511
+ while (!$stream->test(Twig_Token::PUNCTUATION_TYPE, ')')) {
512
+ if (!empty($args)) {
513
+ $stream->expect(Twig_Token::PUNCTUATION_TYPE, ',', 'Arguments must be separated by a comma');
514
+ }
515
+
516
+ if ($definition) {
517
+ $token = $stream->expect(Twig_Token::NAME_TYPE, null, 'An argument must be a name');
518
+ $value = new Twig_Node_Expression_Name($token->getValue(), $this->parser->getCurrentToken()->getLine());
519
+ } else {
520
+ $value = $this->parseExpression();
521
+ }
522
+
523
+ $name = null;
524
+ if ($namedArguments && $token = $stream->nextIf(Twig_Token::OPERATOR_TYPE, '=')) {
525
+ if (!$value instanceof Twig_Node_Expression_Name) {
526
+ throw new Twig_Error_Syntax(sprintf('A parameter name must be a string, "%s" given.', get_class($value)), $token->getLine(), $stream->getSourceContext());
527
+ }
528
+ $name = $value->getAttribute('name');
529
+
530
+ if ($definition) {
531
+ $value = $this->parsePrimaryExpression();
532
+
533
+ if (!$this->checkConstantExpression($value)) {
534
+ throw new Twig_Error_Syntax(sprintf('A default value for an argument must be a constant (a boolean, a string, a number, or an array).'), $token->getLine(), $stream->getSourceContext());
535
+ }
536
+ } else {
537
+ $value = $this->parseExpression();
538
+ }
539
+ }
540
+
541
+ if ($definition) {
542
+ if (null === $name) {
543
+ $name = $value->getAttribute('name');
544
+ $value = new Twig_Node_Expression_Constant(null, $this->parser->getCurrentToken()->getLine());
545
+ }
546
+ $args[$name] = $value;
547
+ } else {
548
+ if (null === $name) {
549
+ $args[] = $value;
550
+ } else {
551
+ $args[$name] = $value;
552
+ }
553
+ }
554
+ }
555
+ $stream->expect(Twig_Token::PUNCTUATION_TYPE, ')', 'A list of arguments must be closed by a parenthesis');
556
+
557
+ return new Twig_Node($args);
558
+ }
559
+
560
+ public function parseAssignmentExpression()
561
+ {
562
+ $stream = $this->parser->getStream();
563
+ $targets = array();
564
+ while (true) {
565
+ $token = $stream->expect(Twig_Token::NAME_TYPE, null, 'Only variables can be assigned to');
566
+ $value = $token->getValue();
567
+ if (in_array(strtolower($value), array('true', 'false', 'none', 'null'))) {
568
+ throw new Twig_Error_Syntax(sprintf('You cannot assign a value to "%s".', $value), $token->getLine(), $stream->getSourceContext());
569
+ }
570
+ $targets[] = new Twig_Node_Expression_AssignName($value, $token->getLine());
571
+
572
+ if (!$stream->nextIf(Twig_Token::PUNCTUATION_TYPE, ',')) {
573
+ break;
574
+ }
575
+ }
576
+
577
+ return new Twig_Node($targets);
578
+ }
579
+
580
+ public function parseMultitargetExpression()
581
+ {
582
+ $targets = array();
583
+ while (true) {
584
+ $targets[] = $this->parseExpression();
585
+ if (!$this->parser->getStream()->nextIf(Twig_Token::PUNCTUATION_TYPE, ',')) {
586
+ break;
587
+ }
588
+ }
589
+
590
+ return new Twig_Node($targets);
591
+ }
592
+
593
+ private function parseNotTestExpression(Twig_NodeInterface $node)
594
+ {
595
+ return new Twig_Node_Expression_Unary_Not($this->parseTestExpression($node), $this->parser->getCurrentToken()->getLine());
596
+ }
597
+
598
+ private function parseTestExpression(Twig_NodeInterface $node)
599
+ {
600
+ $stream = $this->parser->getStream();
601
+ list($name, $test) = $this->getTest($node->getTemplateLine());
602
+
603
+ $class = $this->getTestNodeClass($test);
604
+ $arguments = null;
605
+ if ($stream->test(Twig_Token::PUNCTUATION_TYPE, '(')) {
606
+ $arguments = $this->parser->getExpressionParser()->parseArguments(true);
607
+ }
608
+
609
+ return new $class($node, $name, $arguments, $this->parser->getCurrentToken()->getLine());
610
+ }
611
+
612
+ private function getTest($line)
613
+ {
614
+ $stream = $this->parser->getStream();
615
+ $name = $stream->expect(Twig_Token::NAME_TYPE)->getValue();
616
+
617
+ if ($test = $this->env->getTest($name)) {
618
+ return array($name, $test);
619
+ }
620
+
621
+ if ($stream->test(Twig_Token::NAME_TYPE)) {
622
+ // try 2-words tests
623
+ $name = $name.' '.$this->parser->getCurrentToken()->getValue();
624
+
625
+ if ($test = $this->env->getTest($name)) {
626
+ $stream->next();
627
+
628
+ return array($name, $test);
629
+ }
630
+ }
631
+
632
+ $e = new Twig_Error_Syntax(sprintf('Unknown "%s" test.', $name), $line, $stream->getSourceContext());
633
+ $e->addSuggestions($name, array_keys($this->env->getTests()));
634
+
635
+ throw $e;
636
+ }
637
+
638
+ private function getTestNodeClass($test)
639
+ {
640
+ if ($test instanceof Twig_SimpleTest && $test->isDeprecated()) {
641
+ $stream = $this->parser->getStream();
642
+ $message = sprintf('Twig Test "%s" is deprecated', $test->getName());
643
+ if (!is_bool($test->getDeprecatedVersion())) {
644
+ $message .= sprintf(' since version %s', $test->getDeprecatedVersion());
645
+ }
646
+ if ($test->getAlternative()) {
647
+ $message .= sprintf('. Use "%s" instead', $test->getAlternative());
648
+ }
649
+ $src = $stream->getSourceContext();
650
+ $message .= sprintf(' in %s at line %d.', $src->getPath() ? $src->getPath() : $src->getName(), $stream->getCurrent()->getLine());
651
+
652
+ @trigger_error($message, E_USER_DEPRECATED);
653
+ }
654
+
655
+ if ($test instanceof Twig_SimpleTest) {
656
+ return $test->getNodeClass();
657
+ }
658
+
659
+ return $test instanceof Twig_Test_Node ? $test->getClass() : 'Twig_Node_Expression_Test';
660
+ }
661
+
662
+ protected function getFunctionNodeClass($name, $line)
663
+ {
664
+ if (false === $function = $this->env->getFunction($name)) {
665
+ $e = new Twig_Error_Syntax(sprintf('Unknown "%s" function.', $name), $line, $this->parser->getStream()->getSourceContext());
666
+ $e->addSuggestions($name, array_keys($this->env->getFunctions()));
667
+
668
+ throw $e;
669
+ }
670
+
671
+ if ($function instanceof Twig_SimpleFunction && $function->isDeprecated()) {
672
+ $message = sprintf('Twig Function "%s" is deprecated', $function->getName());
673
+ if (!is_bool($function->getDeprecatedVersion())) {
674
+ $message .= sprintf(' since version %s', $function->getDeprecatedVersion());
675
+ }
676
+ if ($function->getAlternative()) {
677
+ $message .= sprintf('. Use "%s" instead', $function->getAlternative());
678
+ }
679
+ $src = $this->parser->getStream()->getSourceContext();
680
+ $message .= sprintf(' in %s at line %d.', $src->getPath() ? $src->getPath() : $src->getName(), $line);
681
+
682
+ @trigger_error($message, E_USER_DEPRECATED);
683
+ }
684
+
685
+ if ($function instanceof Twig_SimpleFunction) {
686
+ return $function->getNodeClass();
687
+ }
688
+
689
+ return $function instanceof Twig_Function_Node ? $function->getClass() : 'Twig_Node_Expression_Function';
690
+ }
691
+
692
+ protected function getFilterNodeClass($name, $line)
693
+ {
694
+ if (false === $filter = $this->env->getFilter($name)) {
695
+ $e = new Twig_Error_Syntax(sprintf('Unknown "%s" filter.', $name), $line, $this->parser->getStream()->getSourceContext());
696
+ $e->addSuggestions($name, array_keys($this->env->getFilters()));
697
+
698
+ throw $e;
699
+ }
700
+
701
+ if ($filter instanceof Twig_SimpleFilter && $filter->isDeprecated()) {
702
+ $message = sprintf('Twig Filter "%s" is deprecated', $filter->getName());
703
+ if (!is_bool($filter->getDeprecatedVersion())) {
704
+ $message .= sprintf(' since version %s', $filter->getDeprecatedVersion());
705
+ }
706
+ if ($filter->getAlternative()) {
707
+ $message .= sprintf('. Use "%s" instead', $filter->getAlternative());
708
+ }
709
+ $src = $this->parser->getStream()->getSourceContext();
710
+ $message .= sprintf(' in %s at line %d.', $src->getPath() ? $src->getPath() : $src->getName(), $line);
711
+
712
+ @trigger_error($message, E_USER_DEPRECATED);
713
+ }
714
+
715
+ if ($filter instanceof Twig_SimpleFilter) {
716
+ return $filter->getNodeClass();
717
+ }
718
+
719
+ return $filter instanceof Twig_Filter_Node ? $filter->getClass() : 'Twig_Node_Expression_Filter';
720
+ }
721
+
722
+ // checks that the node only contains "constant" elements
723
+ protected function checkConstantExpression(Twig_NodeInterface $node)
724
+ {
725
+ if (!($node instanceof Twig_Node_Expression_Constant || $node instanceof Twig_Node_Expression_Array
726
+ || $node instanceof Twig_Node_Expression_Unary_Neg || $node instanceof Twig_Node_Expression_Unary_Pos
727
+ )) {
728
+ return false;
729
+ }
730
+
731
+ foreach ($node as $n) {
732
+ if (!$this->checkConstantExpression($n)) {
733
+ return false;
734
+ }
735
+ }
736
+
737
+ return true;
738
+ }
739
+ }
vendor/twig/twig/lib/Twig/Extension.php ADDED
@@ -0,0 +1,65 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * This file is part of Twig.
5
+ *
6
+ * (c) Fabien Potencier
7
+ *
8
+ * For the full copyright and license information, please view the LICENSE
9
+ * file that was distributed with this source code.
10
+ */
11
+ abstract class Twig_Extension implements Twig_ExtensionInterface
12
+ {
13
+ /**
14
+ * @deprecated since 1.23 (to be removed in 2.0), implement Twig_Extension_InitRuntimeInterface instead
15
+ */
16
+ public function initRuntime(Twig_Environment $environment)
17
+ {
18
+ }
19
+
20
+ public function getTokenParsers()
21
+ {
22
+ return array();
23
+ }
24
+
25
+ public function getNodeVisitors()
26
+ {
27
+ return array();
28
+ }
29
+
30
+ public function getFilters()
31
+ {
32
+ return array();
33
+ }
34
+
35
+ public function getTests()
36
+ {
37
+ return array();
38
+ }
39
+
40
+ public function getFunctions()
41
+ {
42
+ return array();
43
+ }
44
+
45
+ public function getOperators()
46
+ {
47
+ return array();
48
+ }
49
+
50
+ /**
51
+ * @deprecated since 1.23 (to be removed in 2.0), implement Twig_Extension_GlobalsInterface instead
52
+ */
53
+ public function getGlobals()
54
+ {
55
+ return array();
56
+ }
57
+
58
+ /**
59
+ * @deprecated since 1.26 (to be removed in 2.0), not used anymore internally
60
+ */
61
+ public function getName()
62
+ {
63
+ return get_class($this);
64
+ }
65
+ }
vendor/twig/twig/lib/Twig/Extension/Core.php ADDED
@@ -0,0 +1,1578 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ if (!defined('ENT_SUBSTITUTE')) {
4
+ // use 0 as hhvm does not support several flags yet
5
+ define('ENT_SUBSTITUTE', 0);
6
+ }
7
+
8
+ /*
9
+ * This file is part of Twig.
10
+ *
11
+ * (c) Fabien Potencier
12
+ *
13
+ * For the full copyright and license information, please view the LICENSE
14
+ * file that was distributed with this source code.
15
+ */
16
+
17
+ /**
18
+ * @final
19
+ */
20
+ class Twig_Extension_Core extends Twig_Extension
21
+ {
22
+ protected $dateFormats = array('F j, Y H:i', '%d days');
23
+ protected $numberFormat = array(0, '.', ',');
24
+ protected $timezone = null;
25
+ protected $escapers = array();
26
+
27
+ /**
28
+ * Defines a new escaper to be used via the escape filter.
29
+ *
30
+ * @param string $strategy The strategy name that should be used as a strategy in the escape call
31
+ * @param callable $callable A valid PHP callable
32
+ */
33
+ public function setEscaper($strategy, $callable)
34
+ {
35
+ $this->escapers[$strategy] = $callable;
36
+ }
37
+
38
+ /**
39
+ * Gets all defined escapers.
40
+ *
41
+ * @return array An array of escapers
42
+ */
43
+ public function getEscapers()
44
+ {
45
+ return $this->escapers;
46
+ }
47
+
48
+ /**
49
+ * Sets the default format to be used by the date filter.
50
+ *
51
+ * @param string $format The default date format string
52
+ * @param string $dateIntervalFormat The default date interval format string
53
+ */
54
+ public function setDateFormat($format = null, $dateIntervalFormat = null)
55
+ {
56
+ if (null !== $format) {
57
+ $this->dateFormats[0] = $format;
58
+ }
59
+
60
+ if (null !== $dateIntervalFormat) {
61
+ $this->dateFormats[1] = $dateIntervalFormat;
62
+ }
63
+ }
64
+
65
+ /**
66
+ * Gets the default format to be used by the date filter.
67
+ *
68
+ * @return array The default date format string and the default date interval format string
69
+ */
70
+ public function getDateFormat()
71
+ {
72
+ return $this->dateFormats;
73
+ }
74
+
75
+ /**
76
+ * Sets the default timezone to be used by the date filter.
77
+ *
78
+ * @param DateTimeZone|string $timezone The default timezone string or a DateTimeZone object
79
+ */
80
+ public function setTimezone($timezone)
81
+ {
82
+ $this->timezone = $timezone instanceof DateTimeZone ? $timezone : new DateTimeZone($timezone);
83
+ }
84
+
85
+ /**
86
+ * Gets the default timezone to be used by the date filter.
87
+ *
88
+ * @return DateTimeZone The default timezone currently in use
89
+ */
90
+ public function getTimezone()
91
+ {
92
+ if (null === $this->timezone) {
93
+ $this->timezone = new DateTimeZone(date_default_timezone_get());
94
+ }
95
+
96
+ return $this->timezone;
97
+ }
98
+
99
+ /**
100
+ * Sets the default format to be used by the number_format filter.
101
+ *
102
+ * @param int $decimal the number of decimal places to use
103
+ * @param string $decimalPoint the character(s) to use for the decimal point
104
+ * @param string $thousandSep the character(s) to use for the thousands separator
105
+ */
106
+ public function setNumberFormat($decimal, $decimalPoint, $thousandSep)
107
+ {
108
+ $this->numberFormat = array($decimal, $decimalPoint, $thousandSep);
109
+ }
110
+
111
+ /**
112
+ * Get the default format used by the number_format filter.
113
+ *
114
+ * @return array The arguments for number_format()
115
+ */
116
+ public function getNumberFormat()
117
+ {
118
+ return $this->numberFormat;
119
+ }
120
+
121
+ public function getTokenParsers()
122
+ {
123
+ return array(
124
+ new Twig_TokenParser_For(),
125
+ new Twig_TokenParser_If(),
126
+ new Twig_TokenParser_Extends(),
127
+ new Twig_TokenParser_Include(),
128
+ new Twig_TokenParser_Block(),
129
+ new Twig_TokenParser_Use(),
130
+ new Twig_TokenParser_Filter(),
131
+ new Twig_TokenParser_Macro(),
132
+ new Twig_TokenParser_Import(),
133
+ new Twig_TokenParser_From(),
134
+ new Twig_TokenParser_Set(),
135
+ new Twig_TokenParser_Spaceless(),
136
+ new Twig_TokenParser_Flush(),
137
+ new Twig_TokenParser_Do(),
138
+ new Twig_TokenParser_Embed(),
139
+ new Twig_TokenParser_With(),
140
+ );
141
+ }
142
+
143
+ public function getFilters()
144
+ {
145
+ $filters = array(
146
+ // formatting filters
147
+ new Twig_SimpleFilter('date', 'twig_date_format_filter', array('needs_environment' => true)),
148
+ new Twig_SimpleFilter('date_modify', 'twig_date_modify_filter', array('needs_environment' => true)),
149
+ new Twig_SimpleFilter('format', 'sprintf'),
150
+ new Twig_SimpleFilter('replace', 'twig_replace_filter'),
151
+ new Twig_SimpleFilter('number_format', 'twig_number_format_filter', array('needs_environment' => true)),
152
+ new Twig_SimpleFilter('abs', 'abs'),
153
+ new Twig_SimpleFilter('round', 'twig_round'),
154
+
155
+ // encoding
156
+ new Twig_SimpleFilter('url_encode', 'twig_urlencode_filter'),
157
+ new Twig_SimpleFilter('json_encode', 'twig_jsonencode_filter'),
158
+ new Twig_SimpleFilter('convert_encoding', 'twig_convert_encoding'),
159
+
160
+ // string filters
161
+ new Twig_SimpleFilter('title', 'twig_title_string_filter', array('needs_environment' => true)),
162
+ new Twig_SimpleFilter('capitalize', 'twig_capitalize_string_filter', array('needs_environment' => true)),
163
+ new Twig_SimpleFilter('upper', 'strtoupper'),
164
+ new Twig_SimpleFilter('lower', 'strtolower'),
165
+ new Twig_SimpleFilter('striptags', 'strip_tags'),
166
+ new Twig_SimpleFilter('trim', 'twig_trim_filter'),
167
+ new Twig_SimpleFilter('nl2br', 'nl2br', array('pre_escape' => 'html', 'is_safe' => array('html'))),
168
+
169
+ // array helpers
170
+ new Twig_SimpleFilter('join', 'twig_join_filter'),
171
+ new Twig_SimpleFilter('split', 'twig_split_filter', array('needs_environment' => true)),
172
+ new Twig_SimpleFilter('sort', 'twig_sort_filter'),
173
+ new Twig_SimpleFilter('merge', 'twig_array_merge'),
174
+ new Twig_SimpleFilter('batch', 'twig_array_batch'),
175
+
176
+ // string/array filters
177
+ new Twig_SimpleFilter('reverse', 'twig_reverse_filter', array('needs_environment' => true)),
178
+ new Twig_SimpleFilter('length', 'twig_length_filter', array('needs_environment' => true)),
179
+ new Twig_SimpleFilter('slice', 'twig_slice', array('needs_environment' => true)),
180
+ new Twig_SimpleFilter('first', 'twig_first', array('needs_environment' => true)),
181
+ new Twig_SimpleFilter('last', 'twig_last', array('needs_environment' => true)),
182
+
183
+ // iteration and runtime
184
+ new Twig_SimpleFilter('default', '_twig_default_filter', array('node_class' => 'Twig_Node_Expression_Filter_Default')),
185
+ new Twig_SimpleFilter('keys', 'twig_get_array_keys_filter'),
186
+
187
+ // escaping
188
+ new Twig_SimpleFilter('escape', 'twig_escape_filter', array('needs_environment' => true, 'is_safe_callback' => 'twig_escape_filter_is_safe')),
189
+ new Twig_SimpleFilter('e', 'twig_escape_filter', array('needs_environment' => true, 'is_safe_callback' => 'twig_escape_filter_is_safe')),
190
+ );
191
+
192
+ if (function_exists('mb_get_info')) {
193
+ $filters[] = new Twig_SimpleFilter('upper', 'twig_upper_filter', array('needs_environment' => true));
194
+ $filters[] = new Twig_SimpleFilter('lower', 'twig_lower_filter', array('needs_environment' => true));
195
+ }
196
+
197
+ return $filters;
198
+ }
199
+
200
+ public function getFunctions()
201
+ {
202
+ return array(
203
+ new Twig_SimpleFunction('max', 'max'),
204
+ new Twig_SimpleFunction('min', 'min'),
205
+ new Twig_SimpleFunction('range', 'range'),
206
+ new Twig_SimpleFunction('constant', 'twig_constant'),
207
+ new Twig_SimpleFunction('cycle', 'twig_cycle'),
208
+ new Twig_SimpleFunction('random', 'twig_random', array('needs_environment' => true)),
209
+ new Twig_SimpleFunction('date', 'twig_date_converter', array('needs_environment' => true)),
210
+ new Twig_SimpleFunction('include', 'twig_include', array('needs_environment' => true, 'needs_context' => true, 'is_safe' => array('all'))),
211
+ new Twig_SimpleFunction('source', 'twig_source', array('needs_environment' => true, 'is_safe' => array('all'))),
212
+ );
213
+ }
214
+
215
+ public function getTests()
216
+ {
217
+ return array(
218
+ new Twig_SimpleTest('even', null, array('node_class' => 'Twig_Node_Expression_Test_Even')),
219
+ new Twig_SimpleTest('odd', null, array('node_class' => 'Twig_Node_Expression_Test_Odd')),
220
+ new Twig_SimpleTest('defined', null, array('node_class' => 'Twig_Node_Expression_Test_Defined')),
221
+ new Twig_SimpleTest('sameas', null, array('node_class' => 'Twig_Node_Expression_Test_Sameas', 'deprecated' => '1.21', 'alternative' => 'same as')),
222
+ new Twig_SimpleTest('same as', null, array('node_class' => 'Twig_Node_Expression_Test_Sameas')),
223
+ new Twig_SimpleTest('none', null, array('node_class' => 'Twig_Node_Expression_Test_Null')),
224
+ new Twig_SimpleTest('null', null, array('node_class' => 'Twig_Node_Expression_Test_Null')),
225
+ new Twig_SimpleTest('divisibleby', null, array('node_class' => 'Twig_Node_Expression_Test_Divisibleby', 'deprecated' => '1.21', 'alternative' => 'divisible by')),
226
+ new Twig_SimpleTest('divisible by', null, array('node_class' => 'Twig_Node_Expression_Test_Divisibleby')),
227
+ new Twig_SimpleTest('constant', null, array('node_class' => 'Twig_Node_Expression_Test_Constant')),
228
+ new Twig_SimpleTest('empty', 'twig_test_empty'),
229
+ new Twig_SimpleTest('iterable', 'twig_test_iterable'),
230
+ );
231
+ }
232
+
233
+ public function getOperators()
234
+ {
235
+ return array(
236
+ array(
237
+ 'not' => array('precedence' => 50, 'class' => 'Twig_Node_Expression_Unary_Not'),
238
+ '-' => array('precedence' => 500, 'class' => 'Twig_Node_Expression_Unary_Neg'),
239
+ '+' => array('precedence' => 500, 'class' => 'Twig_Node_Expression_Unary_Pos'),
240
+ ),
241
+ array(
242
+ 'or' => array('precedence' => 10, 'class' => 'Twig_Node_Expression_Binary_Or', 'associativity' => Twig_ExpressionParser::OPERATOR_LEFT),
243
+ 'and' => array('precedence' => 15, 'class' => 'Twig_Node_Expression_Binary_And', 'associativity' => Twig_ExpressionParser::OPERATOR_LEFT),
244
+ 'b-or' => array('precedence' => 16, 'class' => 'Twig_Node_Expression_Binary_BitwiseOr', 'associativity' => Twig_ExpressionParser::OPERATOR_LEFT),
245
+ 'b-xor' => array('precedence' => 17, 'class' => 'Twig_Node_Expression_Binary_BitwiseXor', 'associativity' => Twig_ExpressionParser::OPERATOR_LEFT),
246
+ 'b-and' => array('precedence' => 18, 'class' => 'Twig_Node_Expression_Binary_BitwiseAnd', 'associativity' => Twig_ExpressionParser::OPERATOR_LEFT),
247
+ '==' => array('precedence' => 20, 'class' => 'Twig_Node_Expression_Binary_Equal', 'associativity' => Twig_ExpressionParser::OPERATOR_LEFT),
248
+ '!=' => array('precedence' => 20, 'class' => 'Twig_Node_Expression_Binary_NotEqual', 'associativity' => Twig_ExpressionParser::OPERATOR_LEFT),
249
+ '<' => array('precedence' => 20, 'class' => 'Twig_Node_Expression_Binary_Less', 'associativity' => Twig_ExpressionParser::OPERATOR_LEFT),
250
+ '>' => array('precedence' => 20, 'class' => 'Twig_Node_Expression_Binary_Greater', 'associativity' => Twig_ExpressionParser::OPERATOR_LEFT),
251
+ '>=' => array('precedence' => 20, 'class' => 'Twig_Node_Expression_Binary_GreaterEqual', 'associativity' => Twig_ExpressionParser::OPERATOR_LEFT),
252
+ '<=' => array('precedence' => 20, 'class' => 'Twig_Node_Expression_Binary_LessEqual', 'associativity' => Twig_ExpressionParser::OPERATOR_LEFT),
253
+ 'not in' => array('precedence' => 20, 'class' => 'Twig_Node_Expression_Binary_NotIn', 'associativity' => Twig_ExpressionParser::OPERATOR_LEFT),
254
+ 'in' => array('precedence' => 20, 'class' => 'Twig_Node_Expression_Binary_In', 'associativity' => Twig_ExpressionParser::OPERATOR_LEFT),
255
+ 'matches' => array('precedence' => 20, 'class' => 'Twig_Node_Expression_Binary_Matches', 'associativity' => Twig_ExpressionParser::OPERATOR_LEFT),
256
+ 'starts with' => array('precedence' => 20, 'class' => 'Twig_Node_Expression_Binary_StartsWith', 'associativity' => Twig_ExpressionParser::OPERATOR_LEFT),
257
+ 'ends with' => array('precedence' => 20, 'class' => 'Twig_Node_Expression_Binary_EndsWith', 'associativity' => Twig_ExpressionParser::OPERATOR_LEFT),
258
+ '..' => array('precedence' => 25, 'class' => 'Twig_Node_Expression_Binary_Range', 'associativity' => Twig_ExpressionParser::OPERATOR_LEFT),
259
+ '+' => array('precedence' => 30, 'class' => 'Twig_Node_Expression_Binary_Add', 'associativity' => Twig_ExpressionParser::OPERATOR_LEFT),
260
+ '-' => array('precedence' => 30, 'class' => 'Twig_Node_Expression_Binary_Sub', 'associativity' => Twig_ExpressionParser::OPERATOR_LEFT),
261
+ '~' => array('precedence' => 40, 'class' => 'Twig_Node_Expression_Binary_Concat', 'associativity' => Twig_ExpressionParser::OPERATOR_LEFT),
262
+ '*' => array('precedence' => 60, 'class' => 'Twig_Node_Expression_Binary_Mul', 'associativity' => Twig_ExpressionParser::OPERATOR_LEFT),
263
+ '/' => array('precedence' => 60, 'class' => 'Twig_Node_Expression_Binary_Div', 'associativity' => Twig_ExpressionParser::OPERATOR_LEFT),
264
+ '//' => array('precedence' => 60, 'class' => 'Twig_Node_Expression_Binary_FloorDiv', 'associativity' => Twig_ExpressionParser::OPERATOR_LEFT),
265
+ '%' => array('precedence' => 60, 'class' => 'Twig_Node_Expression_Binary_Mod', 'associativity' => Twig_ExpressionParser::OPERATOR_LEFT),
266
+ 'is' => array('precedence' => 100, 'associativity' => Twig_ExpressionParser::OPERATOR_LEFT),
267
+ 'is not' => array('precedence' => 100, 'associativity' => Twig_ExpressionParser::OPERATOR_LEFT),
268
+ '**' => array('precedence' => 200, 'class' => 'Twig_Node_Expression_Binary_Power', 'associativity' => Twig_ExpressionParser::OPERATOR_RIGHT),
269
+ '??' => array('precedence' => 300, 'class' => 'Twig_Node_Expression_NullCoalesce', 'associativity' => Twig_ExpressionParser::OPERATOR_RIGHT),
270
+ ),
271
+ );
272
+ }
273
+
274
+ public function getName()
275
+ {
276
+ return 'core';
277
+ }
278
+ }
279
+
280
+ /**
281
+ * Cycles over a value.
282
+ *
283
+ * @param ArrayAccess|array $values
284
+ * @param int $position The cycle position
285
+ *
286
+ * @return string The next value in the cycle
287
+ */
288
+ function twig_cycle($values, $position)
289
+ {
290
+ if (!is_array($values) && !$values instanceof ArrayAccess) {
291
+ return $values;
292
+ }
293
+
294
+ return $values[$position % count($values)];
295
+ }
296
+
297
+ /**
298
+ * Returns a random value depending on the supplied parameter type:
299
+ * - a random item from a Traversable or array
300
+ * - a random character from a string
301
+ * - a random integer between 0 and the integer parameter.
302
+ *
303
+ * @param Twig_Environment $env
304
+ * @param Traversable|array|int|float|string $values The values to pick a random item from
305
+ *
306
+ * @throws Twig_Error_Runtime when $values is an empty array (does not apply to an empty string which is returned as is)
307
+ *
308
+ * @return mixed A random value from the given sequence
309
+ */
310
+ function twig_random(Twig_Environment $env, $values = null)
311
+ {
312
+ if (null === $values) {
313
+ return mt_rand();
314
+ }
315
+
316
+ if (is_int($values) || is_float($values)) {
317
+ return $values < 0 ? mt_rand($values, 0) : mt_rand(0, $values);
318
+ }
319
+
320
+ if ($values instanceof Traversable) {
321
+ $values = iterator_to_array($values);
322
+ } elseif (is_string($values)) {
323
+ if ('' === $values) {
324
+ return '';
325
+ }
326
+ if (null !== $charset = $env->getCharset()) {
327
+ if ('UTF-8' !== $charset) {
328
+ $values = twig_convert_encoding($values, 'UTF-8', $charset);
329
+ }
330
+
331
+ // unicode version of str_split()
332
+ // split at all positions, but not after the start and not before the end
333
+ $values = preg_split('/(?<!^)(?!$)/u', $values);
334
+
335
+ if ('UTF-8' !== $charset) {
336
+ foreach ($values as $i => $value) {
337
+ $values[$i] = twig_convert_encoding($value, $charset, 'UTF-8');
338
+ }
339
+ }
340
+ } else {
341
+ return $values[mt_rand(0, strlen($values) - 1)];
342
+ }
343
+ }
344
+
345
+ if (!is_array($values)) {
346
+ return $values;
347
+ }
348
+
349
+ if (0 === count($values)) {
350
+ throw new Twig_Error_Runtime('The random function cannot pick from an empty array.');
351
+ }
352
+
353
+ return $values[array_rand($values, 1)];
354
+ }
355
+
356
+ /**
357
+ * Converts a date to the given format.
358
+ *
359
+ * <pre>
360
+ * {{ post.published_at|date("m/d/Y") }}
361
+ * </pre>
362
+ *
363
+ * @param Twig_Environment $env
364
+ * @param DateTime|DateTimeInterface|DateInterval|string $date A date
365
+ * @param string|null $format The target format, null to use the default
366
+ * @param DateTimeZone|string|null|false $timezone The target timezone, null to use the default, false to leave unchanged
367
+ *
368
+ * @return string The formatted date
369
+ */
370
+ function twig_date_format_filter(Twig_Environment $env, $date, $format = null, $timezone = null)
371
+ {
372
+ if (null === $format) {
373
+ $formats = $env->getExtension('Twig_Extension_Core')->getDateFormat();
374
+ $format = $date instanceof DateInterval ? $formats[1] : $formats[0];
375
+ }
376
+
377
+ if ($date instanceof DateInterval) {
378
+ return $date->format($format);
379
+ }
380
+
381
+ return twig_date_converter($env, $date, $timezone)->format($format);
382
+ }
383
+
384
+ /**
385
+ * Returns a new date object modified.
386
+ *
387
+ * <pre>
388
+ * {{ post.published_at|date_modify("-1day")|date("m/d/Y") }}
389
+ * </pre>
390
+ *
391
+ * @param Twig_Environment $env
392
+ * @param DateTime|string $date A date
393
+ * @param string $modifier A modifier string
394
+ *
395
+ * @return DateTime A new date object
396
+ */
397
+ function twig_date_modify_filter(Twig_Environment $env, $date, $modifier)
398
+ {
399
+ $date = twig_date_converter($env, $date, false);
400
+ $resultDate = $date->modify($modifier);
401
+
402
+ // This is a hack to ensure PHP 5.2 support and support for DateTimeImmutable
403
+ // DateTime::modify does not return the modified DateTime object < 5.3.0
404
+ // and DateTimeImmutable does not modify $date.
405
+ return null === $resultDate ? $date : $resultDate;
406
+ }
407
+
408
+ /**
409
+ * Converts an input to a DateTime instance.
410
+ *
411
+ * <pre>
412
+ * {% if date(user.created_at) < date('+2days') %}
413
+ * {# do something #}
414
+ * {% endif %}
415
+ * </pre>
416
+ *
417
+ * @param Twig_Environment $env
418
+ * @param DateTime|DateTimeInterface|string|null $date A date
419
+ * @param DateTimeZone|string|null|false $timezone The target timezone, null to use the default, false to leave unchanged
420
+ *
421
+ * @return DateTime A DateTime instance
422
+ */
423
+ function twig_date_converter(Twig_Environment $env, $date = null, $timezone = null)
424
+ {
425
+ // determine the timezone
426
+ if (false !== $timezone) {
427
+ if (null === $timezone) {
428
+ $timezone = $env->getExtension('Twig_Extension_Core')->getTimezone();
429
+ } elseif (!$timezone instanceof DateTimeZone) {
430
+ $timezone = new DateTimeZone($timezone);
431
+ }
432
+ }
433
+
434
+ // immutable dates
435
+ if ($date instanceof DateTimeImmutable) {
436
+ return false !== $timezone ? $date->setTimezone($timezone) : $date;
437
+ }
438
+
439
+ if ($date instanceof DateTime || $date instanceof DateTimeInterface) {
440
+ $date = clone $date;
441
+ if (false !== $timezone) {
442
+ $date->setTimezone($timezone);
443
+ }
444
+
445
+ return $date;
446
+ }
447
+
448
+ if (null === $date || 'now' === $date) {
449
+ return new DateTime($date, false !== $timezone ? $timezone : $env->getExtension('Twig_Extension_Core')->getTimezone());
450
+ }
451
+
452
+ $asString = (string) $date;
453
+ if (ctype_digit($asString) || (!empty($asString) && '-' === $asString[0] && ctype_digit(substr($asString, 1)))) {
454
+ $date = new DateTime('@'.$date);
455
+ } else {
456
+ $date = new DateTime($date, $env->getExtension('Twig_Extension_Core')->getTimezone());
457
+ }
458
+
459
+ if (false !== $timezone) {
460
+ $date->setTimezone($timezone);
461
+ }
462
+
463
+ return $date;
464
+ }
465
+
466
+ /**
467
+ * Replaces strings within a string.
468
+ *
469
+ * @param string $str String to replace in
470
+ * @param array|Traversable $from Replace values
471
+ * @param string|null $to Replace to, deprecated (@see http://php.net/manual/en/function.strtr.php)
472
+ *
473
+ * @return string
474
+ */
475
+ function twig_replace_filter($str, $from, $to = null)
476
+ {
477
+ if ($from instanceof Traversable) {
478
+ $from = iterator_to_array($from);
479
+ } elseif (is_string($from) && is_string($to)) {
480
+ @trigger_error('Using "replace" with character by character replacement is deprecated since version 1.22 and will be removed in Twig 2.0', E_USER_DEPRECATED);
481
+
482
+ return strtr($str, $from, $to);
483
+ } elseif (!is_array($from)) {
484
+ throw new Twig_Error_Runtime(sprintf('The "replace" filter expects an array or "Traversable" as replace values, got "%s".', is_object($from) ? get_class($from) : gettype($from)));
485
+ }
486
+
487
+ return strtr($str, $from);
488
+ }
489
+
490
+ /**
491
+ * Rounds a number.
492
+ *
493
+ * @param int|float $value The value to round
494
+ * @param int|float $precision The rounding precision
495
+ * @param string $method The method to use for rounding
496
+ *
497
+ * @return int|float The rounded number
498
+ */
499
+ function twig_round($value, $precision = 0, $method = 'common')
500
+ {
501
+ if ('common' == $method) {
502
+ return round($value, $precision);
503
+ }
504
+
505
+ if ('ceil' != $method && 'floor' != $method) {
506
+ throw new Twig_Error_Runtime('The round filter only supports the "common", "ceil", and "floor" methods.');
507
+ }
508
+
509
+ return $method($value * pow(10, $precision)) / pow(10, $precision);
510
+ }
511
+
512
+ /**
513
+ * Number format filter.
514
+ *
515
+ * All of the formatting options can be left null, in that case the defaults will
516
+ * be used. Supplying any of the parameters will override the defaults set in the
517
+ * environment object.
518
+ *
519
+ * @param Twig_Environment $env
520
+ * @param mixed $number A float/int/string of the number to format
521
+ * @param int $decimal the number of decimal points to display
522
+ * @param string $decimalPoint the character(s) to use for the decimal point
523
+ * @param string $thousandSep the character(s) to use for the thousands separator
524
+ *
525
+ * @return string The formatted number
526
+ */
527
+ function twig_number_format_filter(Twig_Environment $env, $number, $decimal = null, $decimalPoint = null, $thousandSep = null)
528
+ {
529
+ $defaults = $env->getExtension('Twig_Extension_Core')->getNumberFormat();
530
+ if (null === $decimal) {
531
+ $decimal = $defaults[0];
532
+ }
533
+
534
+ if (null === $decimalPoint) {
535
+ $decimalPoint = $defaults[1];
536
+ }
537
+
538
+ if (null === $thousandSep) {
539
+ $thousandSep = $defaults[2];
540
+ }
541
+
542
+ return number_format((float) $number, $decimal, $decimalPoint, $thousandSep);
543
+ }
544
+
545
+ /**
546
+ * URL encodes (RFC 3986) a string as a path segment or an array as a query string.
547
+ *
548
+ * @param string|array $url A URL or an array of query parameters
549
+ *
550
+ * @return string The URL encoded value
551
+ */
552
+ function twig_urlencode_filter($url)
553
+ {
554
+ if (is_array($url)) {
555
+ if (defined('PHP_QUERY_RFC3986')) {
556
+ return http_build_query($url, '', '&', PHP_QUERY_RFC3986);
557
+ }
558
+
559
+ return http_build_query($url, '', '&');
560
+ }
561
+
562
+ return rawurlencode($url);
563
+ }
564
+
565
+ if (PHP_VERSION_ID < 50300) {
566
+ /**
567
+ * JSON encodes a variable.
568
+ *
569
+ * @param mixed $value the value to encode
570
+ * @param int $options Not used on PHP 5.2.x
571
+ *
572
+ * @return mixed The JSON encoded value
573
+ */
574
+ function twig_jsonencode_filter($value, $options = 0)
575
+ {
576
+ if ($value instanceof Twig_Markup) {
577
+ $value = (string) $value;
578
+ } elseif (is_array($value)) {
579
+ array_walk_recursive($value, '_twig_markup2string');
580
+ }
581
+
582
+ return json_encode($value);
583
+ }
584
+ } else {
585
+ /**
586
+ * JSON encodes a variable.
587
+ *
588
+ * @param mixed $value the value to encode
589
+ * @param int $options Bitmask consisting of JSON_HEX_QUOT, JSON_HEX_TAG, JSON_HEX_AMP, JSON_HEX_APOS, JSON_NUMERIC_CHECK, JSON_PRETTY_PRINT, JSON_UNESCAPED_SLASHES, JSON_FORCE_OBJECT
590
+ *
591
+ * @return mixed The JSON encoded value
592
+ */
593
+ function twig_jsonencode_filter($value, $options = 0)
594
+ {
595
+ if ($value instanceof Twig_Markup) {
596
+ $value = (string) $value;
597
+ } elseif (is_array($value)) {
598
+ array_walk_recursive($value, '_twig_markup2string');
599
+ }
600
+
601
+ return json_encode($value, $options);
602
+ }
603
+ }
604
+
605
+ function _twig_markup2string(&$value)
606
+ {
607
+ if ($value instanceof Twig_Markup) {
608
+ $value = (string) $value;
609
+ }
610
+ }
611
+
612
+ /**
613
+ * Merges an array with another one.
614
+ *
615
+ * <pre>
616
+ * {% set items = { 'apple': 'fruit', 'orange': 'fruit' } %}
617
+ *
618
+ * {% set items = items|merge({ 'peugeot': 'car' }) %}
619
+ *
620
+ * {# items now contains { 'apple': 'fruit', 'orange': 'fruit', 'peugeot': 'car' } #}
621
+ * </pre>
622
+ *
623
+ * @param array|Traversable $arr1 An array
624
+ * @param array|Traversable $arr2 An array
625
+ *
626
+ * @return array The merged array
627
+ */
628
+ function twig_array_merge($arr1, $arr2)
629
+ {
630
+ if ($arr1 instanceof Traversable) {
631
+ $arr1 = iterator_to_array($arr1);
632
+ } elseif (!is_array($arr1)) {
633
+ throw new Twig_Error_Runtime(sprintf('The merge filter only works with arrays or "Traversable", got "%s" as first argument.', gettype($arr1)));
634
+ }
635
+
636
+ if ($arr2 instanceof Traversable) {
637
+ $arr2 = iterator_to_array($arr2);
638
+ } elseif (!is_array($arr2)) {
639
+ throw new Twig_Error_Runtime(sprintf('The merge filter only works with arrays or "Traversable", got "%s" as second argument.', gettype($arr2)));
640
+ }
641
+
642
+ return array_merge($arr1, $arr2);
643
+ }
644
+
645
+ /**
646
+ * Slices a variable.
647
+ *
648
+ * @param Twig_Environment $env
649
+ * @param mixed $item A variable
650
+ * @param int $start Start of the slice
651
+ * @param int $length Size of the slice
652
+ * @param bool $preserveKeys Whether to preserve key or not (when the input is an array)
653
+ *
654
+ * @return mixed The sliced variable
655
+ */
656
+ function twig_slice(Twig_Environment $env, $item, $start, $length = null, $preserveKeys = false)
657
+ {
658
+ if ($item instanceof Traversable) {
659
+ while ($item instanceof IteratorAggregate) {
660
+ $item = $item->getIterator();
661
+ }
662
+
663
+ if ($start >= 0 && $length >= 0 && $item instanceof Iterator) {
664
+ try {
665
+ return iterator_to_array(new LimitIterator($item, $start, $length === null ? -1 : $length), $preserveKeys);
666
+ } catch (OutOfBoundsException $exception) {
667
+ return array();
668
+ }
669
+ }
670
+
671
+ $item = iterator_to_array($item, $preserveKeys);
672
+ }
673
+
674
+ if (is_array($item)) {
675
+ return array_slice($item, $start, $length, $preserveKeys);
676
+ }
677
+
678
+ $item = (string) $item;
679
+
680
+ if (function_exists('mb_get_info') && null !== $charset = $env->getCharset()) {
681
+ return (string) mb_substr($item, $start, null === $length ? mb_strlen($item, $charset) - $start : $length, $charset);
682
+ }
683
+
684
+ return (string) (null === $length ? substr($item, $start) : substr($item, $start, $length));
685
+ }
686
+
687
+ /**
688
+ * Returns the first element of the item.
689
+ *
690
+ * @param Twig_Environment $env
691
+ * @param mixed $item A variable
692
+ *
693
+ * @return mixed The first element of the item
694
+ */
695
+ function twig_first(Twig_Environment $env, $item)
696
+ {
697
+ $elements = twig_slice($env, $item, 0, 1, false);
698
+
699
+ return is_string($elements) ? $elements : current($elements);
700
+ }
701
+
702
+ /**
703
+ * Returns the last element of the item.
704
+ *
705
+ * @param Twig_Environment $env
706
+ * @param mixed $item A variable
707
+ *
708
+ * @return mixed The last element of the item
709
+ */
710
+ function twig_last(Twig_Environment $env, $item)
711
+ {
712
+ $elements = twig_slice($env, $item, -1, 1, false);
713
+
714
+ return is_string($elements) ? $elements : current($elements);
715
+ }
716
+
717
+ /**
718
+ * Joins the values to a string.
719
+ *
720
+ * The separator between elements is an empty string per default, you can define it with the optional parameter.
721
+ *
722
+ * <pre>
723
+ * {{ [1, 2, 3]|join('|') }}
724
+ * {# returns 1|2|3 #}
725
+ *
726
+ * {{ [1, 2, 3]|join }}
727
+ * {# returns 123 #}
728
+ * </pre>
729
+ *
730
+ * @param array $value An array
731
+ * @param string $glue The separator
732
+ *
733
+ * @return string The concatenated string
734
+ */
735
+ function twig_join_filter($value, $glue = '')
736
+ {
737
+ if ($value instanceof Traversable) {
738
+ $value = iterator_to_array($value, false);
739
+ }
740
+
741
+ return implode($glue, (array) $value);
742
+ }
743
+
744
+ /**
745
+ * Splits the string into an array.
746
+ *
747
+ * <pre>
748
+ * {{ "one,two,three"|split(',') }}
749
+ * {# returns [one, two, three] #}
750
+ *
751
+ * {{ "one,two,three,four,five"|split(',', 3) }}
752
+ * {# returns [one, two, "three,four,five"] #}
753
+ *
754
+ * {{ "123"|split('') }}
755
+ * {# returns [1, 2, 3] #}
756
+ *
757
+ * {{ "aabbcc"|split('', 2) }}
758
+ * {# returns [aa, bb, cc] #}
759
+ * </pre>
760
+ *
761
+ * @param Twig_Environment $env
762
+ * @param string $value A string
763
+ * @param string $delimiter The delimiter
764
+ * @param int $limit The limit
765
+ *
766
+ * @return array The split string as an array
767
+ */
768
+ function twig_split_filter(Twig_Environment $env, $value, $delimiter, $limit = null)
769
+ {
770
+ if (!empty($delimiter)) {
771
+ return null === $limit ? explode($delimiter, $value) : explode($delimiter, $value, $limit);
772
+ }
773
+
774
+ if (!function_exists('mb_get_info') || null === $charset = $env->getCharset()) {
775
+ return str_split($value, null === $limit ? 1 : $limit);
776
+ }
777
+
778
+ if ($limit <= 1) {
779
+ return preg_split('/(?<!^)(?!$)/u', $value);
780
+ }
781
+
782
+ $length = mb_strlen($value, $charset);
783
+ if ($length < $limit) {
784
+ return array($value);
785
+ }
786
+
787
+ $r = array();
788
+ for ($i = 0; $i < $length; $i += $limit) {
789
+ $r[] = mb_substr($value, $i, $limit, $charset);
790
+ }
791
+
792
+ return $r;
793
+ }
794
+
795
+ // The '_default' filter is used internally to avoid using the ternary operator
796
+ // which costs a lot for big contexts (before PHP 5.4). So, on average,
797
+ // a function call is cheaper.
798
+ /**
799
+ * @internal
800
+ */
801
+ function _twig_default_filter($value, $default = '')
802
+ {
803
+ if (twig_test_empty($value)) {
804
+ return $default;
805
+ }
806
+
807
+ return $value;
808
+ }
809
+
810
+ /**
811
+ * Returns the keys for the given array.
812
+ *
813
+ * It is useful when you want to iterate over the keys of an array:
814
+ *
815
+ * <pre>
816
+ * {% for key in array|keys %}
817
+ * {# ... #}
818
+ * {% endfor %}
819
+ * </pre>
820
+ *
821
+ * @param array $array An array
822
+ *
823
+ * @return array The keys
824
+ */
825
+ function twig_get_array_keys_filter($array)
826
+ {
827
+ if ($array instanceof Traversable) {
828
+ while ($array instanceof IteratorAggregate) {
829
+ $array = $array->getIterator();
830
+ }
831
+
832
+ if ($array instanceof Iterator) {
833
+ $keys = array();
834
+ $array->rewind();
835
+ while ($array->valid()) {
836
+ $keys[] = $array->key();
837
+ $array->next();
838
+ }
839
+
840
+ return $keys;
841
+ }
842
+
843
+ $keys = array();
844
+ foreach ($array as $key => $item) {
845
+ $keys[] = $key;
846
+ }
847
+
848
+ return $keys;
849
+ }
850
+
851
+ if (!is_array($array)) {
852
+ return array();
853
+ }
854
+
855
+ return array_keys($array);
856
+ }
857
+
858
+ /**
859
+ * Reverses a variable.
860
+ *
861
+ * @param Twig_Environment $env
862
+ * @param array|Traversable|string $item An array, a Traversable instance, or a string
863
+ * @param bool $preserveKeys Whether to preserve key or not
864
+ *
865
+ * @return mixed The reversed input
866
+ */
867
+ function twig_reverse_filter(Twig_Environment $env, $item, $preserveKeys = false)
868
+ {
869
+ if ($item instanceof Traversable) {
870
+ return array_reverse(iterator_to_array($item), $preserveKeys);
871
+ }
872
+
873
+ if (is_array($item)) {
874
+ return array_reverse($item, $preserveKeys);
875
+ }
876
+
877
+ if (null !== $charset = $env->getCharset()) {
878
+ $string = (string) $item;
879
+
880
+ if ('UTF-8' !== $charset) {
881
+ $item = twig_convert_encoding($string, 'UTF-8', $charset);
882
+ }
883
+
884
+ preg_match_all('/./us', $item, $matches);
885
+
886
+ $string = implode('', array_reverse($matches[0]));
887
+
888
+ if ('UTF-8' !== $charset) {
889
+ $string = twig_convert_encoding($string, $charset, 'UTF-8');
890
+ }
891
+
892
+ return $string;
893
+ }
894
+
895
+ return strrev((string) $item);
896
+ }
897
+
898
+ /**
899
+ * Sorts an array.
900
+ *
901
+ * @param array|Traversable $array
902
+ *
903
+ * @return array
904
+ */
905
+ function twig_sort_filter($array)
906
+ {
907
+ if ($array instanceof Traversable) {
908
+ $array = iterator_to_array($array);
909
+ } elseif (!is_array($array)) {
910
+ throw new Twig_Error_Runtime(sprintf('The sort filter only works with arrays or "Traversable", got "%s".', gettype($array)));
911
+ }
912
+
913
+ asort($array);
914
+
915
+ return $array;
916
+ }
917
+
918
+ /**
919
+ * @internal
920
+ */
921
+ function twig_in_filter($value, $compare)
922
+ {
923
+ if (is_array($compare)) {
924
+ return in_array($value, $compare, is_object($value) || is_resource($value));
925
+ } elseif (is_string($compare) && (is_string($value) || is_int($value) || is_float($value))) {
926
+ return '' === $value || false !== strpos($compare, (string) $value);
927
+ } elseif ($compare instanceof Traversable) {
928
+ if (is_object($value) || is_resource($value)) {
929
+ foreach ($compare as $item) {
930
+ if ($item === $value) {
931
+ return true;
932
+ }
933
+ }
934
+ } else {
935
+ foreach ($compare as $item) {
936
+ if ($item == $value) {
937
+ return true;
938
+ }
939
+ }
940
+ }
941
+
942
+ return false;
943
+ }
944
+
945
+ return false;
946
+ }
947
+
948
+ /**
949
+ * Returns a trimmed string.
950
+ *
951
+ * @return string
952
+ *
953
+ * @throws Twig_Error_Runtime When an invalid trimming side is used (not a string or not 'left', 'right', or 'both')
954
+ */
955
+ function twig_trim_filter($string, $characterMask = null, $side = 'both')
956
+ {
957
+ if (null === $characterMask) {
958
+ $characterMask = " \t\n\r\0\x0B";
959
+ }
960
+
961
+ switch ($side) {
962
+ case 'both':
963
+ return trim($string, $characterMask);
964
+ case 'left':
965
+ return ltrim($string, $characterMask);
966
+ case 'right':
967
+ return rtrim($string, $characterMask);
968
+ default:
969
+ throw new Twig_Error_Runtime('Trimming side must be "left", "right" or "both".');
970
+ }
971
+ }
972
+
973
+ /**
974
+ * Escapes a string.
975
+ *
976
+ * @param Twig_Environment $env
977
+ * @param mixed $string The value to be escaped
978
+ * @param string $strategy The escaping strategy
979
+ * @param string $charset The charset
980
+ * @param bool $autoescape Whether the function is called by the auto-escaping feature (true) or by the developer (false)
981
+ *
982
+ * @return string
983
+ */
984
+ function twig_escape_filter(Twig_Environment $env, $string, $strategy = 'html', $charset = null, $autoescape = false)
985
+ {
986
+ if ($autoescape && $string instanceof Twig_Markup) {
987
+ return $string;
988
+ }
989
+
990
+ if (!is_string($string)) {
991
+ if (is_object($string) && method_exists($string, '__toString')) {
992
+ $string = (string) $string;
993
+ } elseif (in_array($strategy, array('html', 'js', 'css', 'html_attr', 'url'))) {
994
+ return $string;
995
+ }
996
+ }
997
+
998
+ if (null === $charset) {
999
+ $charset = $env->getCharset();
1000
+ }
1001
+
1002
+ switch ($strategy) {
1003
+ case 'html':
1004
+ // see http://php.net/htmlspecialchars
1005
+
1006
+ // Using a static variable to avoid initializing the array
1007
+ // each time the function is called. Moving the declaration on the
1008
+ // top of the function slow downs other escaping strategies.
1009
+ static $htmlspecialcharsCharsets;
1010
+
1011
+ if (null === $htmlspecialcharsCharsets) {
1012
+ if (defined('HHVM_VERSION')) {
1013
+ $htmlspecialcharsCharsets = array('utf-8' => true, 'UTF-8' => true);
1014
+ } else {
1015
+ $htmlspecialcharsCharsets = array(
1016
+ 'ISO-8859-1' => true, 'ISO8859-1' => true,
1017
+ 'ISO-8859-15' => true, 'ISO8859-15' => true,
1018
+ 'utf-8' => true, 'UTF-8' => true,
1019
+ 'CP866' => true, 'IBM866' => true, '866' => true,
1020
+ 'CP1251' => true, 'WINDOWS-1251' => true, 'WIN-1251' => true,
1021
+ '1251' => true,
1022
+ 'CP1252' => true, 'WINDOWS-1252' => true, '1252' => true,
1023
+ 'KOI8-R' => true, 'KOI8-RU' => true, 'KOI8R' => true,
1024
+ 'BIG5' => true, '950' => true,
1025
+ 'GB2312' => true, '936' => true,
1026
+ 'BIG5-HKSCS' => true,
1027
+ 'SHIFT_JIS' => true, 'SJIS' => true, '932' => true,
1028
+ 'EUC-JP' => true, 'EUCJP' => true,
1029
+ 'ISO8859-5' => true, 'ISO-8859-5' => true, 'MACROMAN' => true,
1030
+ );
1031
+ }
1032
+ }
1033
+
1034
+ if (isset($htmlspecialcharsCharsets[$charset])) {
1035
+ return htmlspecialchars($string, ENT_QUOTES | ENT_SUBSTITUTE, $charset);
1036
+ }
1037
+
1038
+ if (isset($htmlspecialcharsCharsets[strtoupper($charset)])) {
1039
+ // cache the lowercase variant for future iterations
1040
+ $htmlspecialcharsCharsets[$charset] = true;
1041
+
1042
+ return htmlspecialchars($string, ENT_QUOTES | ENT_SUBSTITUTE, $charset);
1043
+ }
1044
+
1045
+ $string = twig_convert_encoding($string, 'UTF-8', $charset);
1046
+ $string = htmlspecialchars($string, ENT_QUOTES | ENT_SUBSTITUTE, 'UTF-8');
1047
+
1048
+ return twig_convert_encoding($string, $charset, 'UTF-8');
1049
+
1050
+ case 'js':
1051
+ // escape all non-alphanumeric characters
1052
+ // into their \xHH or \uHHHH representations
1053
+ if ('UTF-8' !== $charset) {
1054
+ $string = twig_convert_encoding($string, 'UTF-8', $charset);
1055
+ }
1056
+
1057
+ if (0 == strlen($string) ? false : 1 !== preg_match('/^./su', $string)) {
1058
+ throw new Twig_Error_Runtime('The string to escape is not a valid UTF-8 string.');
1059
+ }
1060
+
1061
+ $string = preg_replace_callback('#[^a-zA-Z0-9,\._]#Su', '_twig_escape_js_callback', $string);
1062
+
1063
+ if ('UTF-8' !== $charset) {
1064
+ $string = twig_convert_encoding($string, $charset, 'UTF-8');
1065
+ }
1066
+
1067
+ return $string;
1068
+
1069
+ case 'css':
1070
+ if ('UTF-8' !== $charset) {
1071
+ $string = twig_convert_encoding($string, 'UTF-8', $charset);
1072
+ }
1073
+
1074
+ if (0 == strlen($string) ? false : 1 !== preg_match('/^./su', $string)) {
1075
+ throw new Twig_Error_Runtime('The string to escape is not a valid UTF-8 string.');
1076
+ }
1077
+
1078
+ $string = preg_replace_callback('#[^a-zA-Z0-9]#Su', '_twig_escape_css_callback', $string);
1079
+
1080
+ if ('UTF-8' !== $charset) {
1081
+ $string = twig_convert_encoding($string, $charset, 'UTF-8');
1082
+ }
1083
+
1084
+ return $string;
1085
+
1086
+ case 'html_attr':
1087
+ if ('UTF-8' !== $charset) {
1088
+ $string = twig_convert_encoding($string, 'UTF-8', $charset);
1089
+ }
1090
+
1091
+ if (0 == strlen($string) ? false : 1 !== preg_match('/^./su', $string)) {
1092
+ throw new Twig_Error_Runtime('The string to escape is not a valid UTF-8 string.');
1093
+ }
1094
+
1095
+ $string = preg_replace_callback('#[^a-zA-Z0-9,\.\-_]#Su', '_twig_escape_html_attr_callback', $string);
1096
+
1097
+ if ('UTF-8' !== $charset) {
1098
+ $string = twig_convert_encoding($string, $charset, 'UTF-8');
1099
+ }
1100
+
1101
+ return $string;
1102
+
1103
+ case 'url':
1104
+ if (PHP_VERSION_ID < 50300) {
1105
+ return str_replace('%7E', '~', rawurlencode($string));
1106
+ }
1107
+
1108
+ return rawurlencode($string);
1109
+
1110
+ default:
1111
+ static $escapers;
1112
+
1113
+ if (null === $escapers) {
1114
+ $escapers = $env->getExtension('Twig_Extension_Core')->getEscapers();
1115
+ }
1116
+
1117
+ if (isset($escapers[$strategy])) {
1118
+ return call_user_func($escapers[$strategy], $env, $string, $charset);
1119
+ }
1120
+
1121
+ $validStrategies = implode(', ', array_merge(array('html', 'js', 'url', 'css', 'html_attr'), array_keys($escapers)));
1122
+
1123
+ throw new Twig_Error_Runtime(sprintf('Invalid escaping strategy "%s" (valid ones: %s).', $strategy, $validStrategies));
1124
+ }
1125
+ }
1126
+
1127
+ /**
1128
+ * @internal
1129
+ */
1130
+ function twig_escape_filter_is_safe(Twig_Node $filterArgs)
1131
+ {
1132
+ foreach ($filterArgs as $arg) {
1133
+ if ($arg instanceof Twig_Node_Expression_Constant) {
1134
+ return array($arg->getAttribute('value'));
1135
+ }
1136
+
1137
+ return array();
1138
+ }
1139
+
1140
+ return array('html');
1141
+ }
1142
+
1143
+ if (function_exists('mb_convert_encoding')) {
1144
+ function twig_convert_encoding($string, $to, $from)
1145
+ {
1146
+ return mb_convert_encoding($string, $to, $from);
1147
+ }
1148
+ } elseif (function_exists('iconv')) {
1149
+ function twig_convert_encoding($string, $to, $from)
1150
+ {
1151
+ return iconv($from, $to, $string);
1152
+ }
1153
+ } else {
1154
+ function twig_convert_encoding($string, $to, $from)
1155
+ {
1156
+ throw new Twig_Error_Runtime('No suitable convert encoding function (use UTF-8 as your encoding or install the iconv or mbstring extension).');
1157
+ }
1158
+ }
1159
+
1160
+ function _twig_escape_js_callback($matches)
1161
+ {
1162
+ $char = $matches[0];
1163
+
1164
+ // \xHH
1165
+ if (!isset($char[1])) {
1166
+ return '\\x'.strtoupper(substr('00'.bin2hex($char), -2));
1167
+ }
1168
+
1169
+ // \uHHHH
1170
+ $char = twig_convert_encoding($char, 'UTF-16BE', 'UTF-8');
1171
+
1172
+ return '\\u'.strtoupper(substr('0000'.bin2hex($char), -4));
1173
+ }
1174
+
1175
+ function _twig_escape_css_callback($matches)
1176
+ {
1177
+ $char = $matches[0];
1178
+
1179
+ // \xHH
1180
+ if (!isset($char[1])) {
1181
+ $hex = ltrim(strtoupper(bin2hex($char)), '0');
1182
+ if (0 === strlen($hex)) {
1183
+ $hex = '0';
1184
+ }
1185
+
1186
+ return '\\'.$hex.' ';
1187
+ }
1188
+
1189
+ // \uHHHH
1190
+ $char = twig_convert_encoding($char, 'UTF-16BE', 'UTF-8');
1191
+
1192
+ return '\\'.ltrim(strtoupper(bin2hex($char)), '0').' ';
1193
+ }
1194
+
1195
+ /**
1196
+ * This function is adapted from code coming from Zend Framework.
1197
+ *
1198
+ * @copyright Copyright (c) 2005-2012 Zend Technologies USA Inc. (http://www.zend.com)
1199
+ * @license http://framework.zend.com/license/new-bsd New BSD License
1200
+ */
1201
+ function _twig_escape_html_attr_callback($matches)
1202
+ {
1203
+ /*
1204
+ * While HTML supports far more named entities, the lowest common denominator
1205
+ * has become HTML5's XML Serialisation which is restricted to the those named
1206
+ * entities that XML supports. Using HTML entities would result in this error:
1207
+ * XML Parsing Error: undefined entity
1208
+ */
1209
+ static $entityMap = array(
1210
+ 34 => 'quot', /* quotation mark */
1211
+ 38 => 'amp', /* ampersand */
1212
+ 60 => 'lt', /* less-than sign */
1213
+ 62 => 'gt', /* greater-than sign */
1214
+ );
1215
+
1216
+ $chr = $matches[0];
1217
+ $ord = ord($chr);
1218
+
1219
+ /*
1220
+ * The following replaces characters undefined in HTML with the
1221
+ * hex entity for the Unicode replacement character.
1222
+ */
1223
+ if (($ord <= 0x1f && $chr != "\t" && $chr != "\n" && $chr != "\r") || ($ord >= 0x7f && $ord <= 0x9f)) {
1224
+ return '&#xFFFD;';
1225
+ }
1226
+
1227
+ /*
1228
+ * Check if the current character to escape has a name entity we should
1229
+ * replace it with while grabbing the hex value of the character.
1230
+ */
1231
+ if (strlen($chr) == 1) {
1232
+ $hex = strtoupper(substr('00'.bin2hex($chr), -2));
1233
+ } else {
1234
+ $chr = twig_convert_encoding($chr, 'UTF-16BE', 'UTF-8');
1235
+ $hex = strtoupper(substr('0000'.bin2hex($chr), -4));
1236
+ }
1237
+
1238
+ $int = hexdec($hex);
1239
+ if (array_key_exists($int, $entityMap)) {
1240
+ return sprintf('&%s;', $entityMap[$int]);
1241
+ }
1242
+
1243
+ /*
1244
+ * Per OWASP recommendations, we'll use hex entities for any other
1245
+ * characters where a named entity does not exist.
1246
+ */
1247
+ return sprintf('&#x%s;', $hex);
1248
+ }
1249
+
1250
+ // add multibyte extensions if possible
1251
+ if (function_exists('mb_get_info')) {
1252
+ /**
1253
+ * Returns the length of a variable.
1254
+ *
1255
+ * @param Twig_Environment $env
1256
+ * @param mixed $thing A variable
1257
+ *
1258
+ * @return int The length of the value
1259
+ */
1260
+ function twig_length_filter(Twig_Environment $env, $thing)
1261
+ {
1262
+ return is_scalar($thing) ? mb_strlen($thing, $env->getCharset()) : count($thing);
1263
+ }
1264
+
1265
+ /**
1266
+ * Converts a string to uppercase.
1267
+ *
1268
+ * @param Twig_Environment $env
1269
+ * @param string $string A string
1270
+ *
1271
+ * @return string The uppercased string
1272
+ */
1273
+ function twig_upper_filter(Twig_Environment $env, $string)
1274
+ {
1275
+ if (null !== $charset = $env->getCharset()) {
1276
+ return mb_strtoupper($string, $charset);
1277
+ }
1278
+
1279
+ return strtoupper($string);
1280
+ }
1281
+
1282
+ /**
1283
+ * Converts a string to lowercase.
1284
+ *
1285
+ * @param Twig_Environment $env
1286
+ * @param string $string A string
1287
+ *
1288
+ * @return string The lowercased string
1289
+ */
1290
+ function twig_lower_filter(Twig_Environment $env, $string)
1291
+ {
1292
+ if (null !== $charset = $env->getCharset()) {
1293
+ return mb_strtolower($string, $charset);
1294
+ }
1295
+
1296
+ return strtolower($string);
1297
+ }
1298
+
1299
+ /**
1300
+ * Returns a titlecased string.
1301
+ *
1302
+ * @param Twig_Environment $env
1303
+ * @param string $string A string
1304
+ *
1305
+ * @return string The titlecased string
1306
+ */
1307
+ function twig_title_string_filter(Twig_Environment $env, $string)
1308
+ {
1309
+ if (null !== $charset = $env->getCharset()) {
1310
+ return mb_convert_case($string, MB_CASE_TITLE, $charset);
1311
+ }
1312
+
1313
+ return ucwords(strtolower($string));
1314
+ }
1315
+
1316
+ /**
1317
+ * Returns a capitalized string.
1318
+ *
1319
+ * @param Twig_Environment $env
1320
+ * @param string $string A string
1321
+ *
1322
+ * @return string The capitalized string
1323
+ */
1324
+ function twig_capitalize_string_filter(Twig_Environment $env, $string)
1325
+ {
1326
+ if (null !== $charset = $env->getCharset()) {
1327
+ return mb_strtoupper(mb_substr($string, 0, 1, $charset), $charset).mb_strtolower(mb_substr($string, 1, mb_strlen($string, $charset), $charset), $charset);
1328
+ }
1329
+
1330
+ return ucfirst(strtolower($string));
1331
+ }
1332
+ }
1333
+ // and byte fallback
1334
+ else {
1335
+ /**
1336
+ * Returns the length of a variable.
1337
+ *
1338
+ * @param Twig_Environment $env
1339
+ * @param mixed $thing A variable
1340
+ *
1341
+ * @return int The length of the value
1342
+ */
1343
+ function twig_length_filter(Twig_Environment $env, $thing)
1344
+ {
1345
+ return is_scalar($thing) ? strlen($thing) : count($thing);
1346
+ }
1347
+
1348
+ /**
1349
+ * Returns a titlecased string.
1350
+ *
1351
+ * @param Twig_Environment $env
1352
+ * @param string $string A string
1353
+ *
1354
+ * @return string The titlecased string
1355
+ */
1356
+ function twig_title_string_filter(Twig_Environment $env, $string)
1357
+ {
1358
+ return ucwords(strtolower($string));
1359
+ }
1360
+
1361
+ /**
1362
+ * Returns a capitalized string.
1363
+ *
1364
+ * @param Twig_Environment $env
1365
+ * @param string $string A string
1366
+ *
1367
+ * @return string The capitalized string
1368
+ */
1369
+ function twig_capitalize_string_filter(Twig_Environment $env, $string)
1370
+ {
1371
+ return ucfirst(strtolower($string));
1372
+ }
1373
+ }
1374
+
1375
+ /**
1376
+ * @internal
1377
+ */
1378
+ function twig_ensure_traversable($seq)
1379
+ {
1380
+ if ($seq instanceof Traversable || is_array($seq)) {
1381
+ return $seq;
1382
+ }
1383
+
1384
+ return array();
1385
+ }
1386
+
1387
+ /**
1388
+ * Checks if a variable is empty.
1389
+ *
1390
+ * <pre>
1391
+ * {# evaluates to true if the foo variable is null, false, or the empty string #}
1392
+ * {% if foo is empty %}
1393
+ * {# ... #}
1394
+ * {% endif %}
1395
+ * </pre>
1396
+ *
1397
+ * @param mixed $value A variable
1398
+ *
1399
+ * @return bool true if the value is empty, false otherwise
1400
+ */
1401
+ function twig_test_empty($value)
1402
+ {
1403
+ if ($value instanceof Countable) {
1404
+ return 0 == count($value);
1405
+ }
1406
+
1407
+ return '' === $value || false === $value || null === $value || array() === $value;
1408
+ }
1409
+
1410
+ /**
1411
+ * Checks if a variable is traversable.
1412
+ *
1413
+ * <pre>
1414
+ * {# evaluates to true if the foo variable is an array or a traversable object #}
1415
+ * {% if foo is traversable %}
1416
+ * {# ... #}
1417
+ * {% endif %}
1418
+ * </pre>
1419
+ *
1420
+ * @param mixed $value A variable
1421
+ *
1422
+ * @return bool true if the value is traversable
1423
+ */
1424
+ function twig_test_iterable($value)
1425
+ {
1426
+ return $value instanceof Traversable || is_array($value);
1427
+ }
1428
+
1429
+ /**
1430
+ * Renders a template.
1431
+ *
1432
+ * @param Twig_Environment $env
1433
+ * @param array $context
1434
+ * @param string|array $template The template to render or an array of templates to try consecutively
1435
+ * @param array $variables The variables to pass to the template
1436
+ * @param bool $withContext
1437
+ * @param bool $ignoreMissing Whether to ignore missing templates or not
1438
+ * @param bool $sandboxed Whether to sandbox the template or not
1439
+ *
1440
+ * @return string The rendered template
1441
+ */
1442
+ function twig_include(Twig_Environment $env, $context, $template, $variables = array(), $withContext = true, $ignoreMissing = false, $sandboxed = false)
1443
+ {
1444
+ $alreadySandboxed = false;
1445
+ $sandbox = null;
1446
+ if ($withContext) {
1447
+ $variables = array_merge($context, $variables);
1448
+ }
1449
+
1450
+ if ($isSandboxed = $sandboxed && $env->hasExtension('Twig_Extension_Sandbox')) {
1451
+ $sandbox = $env->getExtension('Twig_Extension_Sandbox');
1452
+ if (!$alreadySandboxed = $sandbox->isSandboxed()) {
1453
+ $sandbox->enableSandbox();
1454
+ }
1455
+ }
1456
+
1457
+ $result = null;
1458
+ try {
1459
+ $result = $env->resolveTemplate($template)->render($variables);
1460
+ } catch (Twig_Error_Loader $e) {
1461
+ if (!$ignoreMissing) {
1462
+ if ($isSandboxed && !$alreadySandboxed) {
1463
+ $sandbox->disableSandbox();
1464
+ }
1465
+
1466
+ throw $e;
1467
+ }
1468
+ } catch (Throwable $e) {
1469
+ if ($isSandboxed && !$alreadySandboxed) {
1470
+ $sandbox->disableSandbox();
1471
+ }
1472
+
1473
+ throw $e;
1474
+ } catch (Exception $e) {
1475
+ if ($isSandboxed && !$alreadySandboxed) {
1476
+ $sandbox->disableSandbox();
1477
+ }
1478
+
1479
+ throw $e;
1480
+ }
1481
+
1482
+ if ($isSandboxed && !$alreadySandboxed) {
1483
+ $sandbox->disableSandbox();
1484
+ }
1485
+
1486
+ return $result;
1487
+ }
1488
+
1489
+ /**
1490
+ * Returns a template content without rendering it.
1491
+ *
1492
+ * @param Twig_Environment $env
1493
+ * @param string $name The template name
1494
+ * @param bool $ignoreMissing Whether to ignore missing templates or not
1495
+ *
1496
+ * @return string The template source
1497
+ */
1498
+ function twig_source(Twig_Environment $env, $name, $ignoreMissing = false)
1499
+ {
1500
+ $loader = $env->getLoader();
1501
+ try {
1502
+ if (!$loader instanceof Twig_SourceContextLoaderInterface) {
1503
+ return $loader->getSource($name);
1504
+ } else {
1505
+ return $loader->getSourceContext($name)->getCode();
1506
+ }
1507
+ } catch (Twig_Error_Loader $e) {
1508
+ if (!$ignoreMissing) {
1509
+ throw $e;
1510
+ }
1511
+ }
1512
+ }
1513
+
1514
+ /**
1515
+ * Provides the ability to get constants from instances as well as class/global constants.
1516
+ *
1517
+ * @param string $constant The name of the constant
1518
+ * @param null|object $object The object to get the constant from
1519
+ *
1520
+ * @return string
1521
+ */
1522
+ function twig_constant($constant, $object = null)
1523
+ {
1524
+ if (null !== $object) {
1525
+ $constant = get_class($object).'::'.$constant;
1526
+ }
1527
+
1528
+ return constant($constant);
1529
+ }
1530
+
1531
+ /**
1532
+ * Checks if a constant exists.
1533
+ *
1534
+ * @param string $constant The name of the constant
1535
+ * @param null|object $object The object to get the constant from
1536
+ *
1537
+ * @return bool
1538
+ */
1539
+ function twig_constant_is_defined($constant, $object = null)
1540
+ {
1541
+ if (null !== $object) {
1542
+ $constant = get_class($object).'::'.$constant;
1543
+ }
1544
+
1545
+ return defined($constant);
1546
+ }
1547
+
1548
+ /**
1549
+ * Batches item.
1550
+ *
1551
+ * @param array $items An array of items
1552
+ * @param int $size The size of the batch
1553
+ * @param mixed $fill A value used to fill missing items
1554
+ *
1555
+ * @return array
1556
+ */
1557
+ function twig_array_batch($items, $size, $fill = null)
1558
+ {
1559
+ if ($items instanceof Traversable) {
1560
+ $items = iterator_to_array($items, false);
1561
+ }
1562
+
1563
+ $size = ceil($size);
1564
+
1565
+ $result = array_chunk($items, $size, true);
1566
+
1567
+ if (null !== $fill && !empty($result)) {
1568
+ $last = count($result) - 1;
1569
+ if ($fillCount = $size - count($result[$last])) {
1570
+ $result[$last] = array_merge(
1571
+ $result[$last],
1572
+ array_fill(0, $fillCount, $fill)
1573
+ );
1574
+ }
1575
+ }
1576
+
1577
+ return $result;
1578
+ }
vendor/twig/twig/lib/Twig/Extension/Debug.php ADDED
@@ -0,0 +1,65 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * This file is part of Twig.
5
+ *
6
+ * (c) Fabien Potencier
7
+ *
8
+ * For the full copyright and license information, please view the LICENSE
9
+ * file that was distributed with this source code.
10
+ */
11
+
12
+ /**
13
+ * @final
14
+ */
15
+ class Twig_Extension_Debug extends Twig_Extension
16
+ {
17
+ public function getFunctions()
18
+ {
19
+ // dump is safe if var_dump is overridden by xdebug
20
+ $isDumpOutputHtmlSafe = extension_loaded('xdebug')
21
+ // false means that it was not set (and the default is on) or it explicitly enabled
22
+ && (false === ini_get('xdebug.overload_var_dump') || ini_get('xdebug.overload_var_dump'))
23
+ // false means that it was not set (and the default is on) or it explicitly enabled
24
+ // xdebug.overload_var_dump produces HTML only when html_errors is also enabled
25
+ && (false === ini_get('html_errors') || ini_get('html_errors'))
26
+ || 'cli' === php_sapi_name()
27
+ ;
28
+
29
+ return array(
30
+ new Twig_SimpleFunction('dump', 'twig_var_dump', array('is_safe' => $isDumpOutputHtmlSafe ? array('html') : array(), 'needs_context' => true, 'needs_environment' => true)),
31
+ );
32
+ }
33
+
34
+ public function getName()
35
+ {
36
+ return 'debug';
37
+ }
38
+ }
39
+
40
+ function twig_var_dump(Twig_Environment $env, $context)
41
+ {
42
+ if (!$env->isDebug()) {
43
+ return;
44
+ }
45
+
46
+ ob_start();
47
+
48
+ $count = func_num_args();
49
+ if (2 === $count) {
50
+ $vars = array();
51
+ foreach ($context as $key => $value) {
52
+ if (!$value instanceof Twig_Template) {
53
+ $vars[$key] = $value;
54
+ }
55
+ }
56
+
57
+ var_dump($vars);
58
+ } else {
59
+ for ($i = 2; $i < $count; ++$i) {
60
+ var_dump(func_get_arg($i));
61
+ }
62
+ }
63
+
64
+ return ob_get_clean();
65
+ }
vendor/twig/twig/lib/Twig/Extension/Escaper.php ADDED
@@ -0,0 +1,110 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * This file is part of Twig.
5
+ *
6
+ * (c) Fabien Potencier
7
+ *
8
+ * For the full copyright and license information, please view the LICENSE
9
+ * file that was distributed with this source code.
10
+ */
11
+
12
+ /**
13
+ * @final
14
+ */
15
+ class Twig_Extension_Escaper extends Twig_Extension
16
+ {
17
+ protected $defaultStrategy;
18
+
19
+ /**
20
+ * @param string|false|callable $defaultStrategy An escaping strategy
21
+ *
22
+ * @see setDefaultStrategy()
23
+ */
24
+ public function __construct($defaultStrategy = 'html')
25
+ {
26
+ $this->setDefaultStrategy($defaultStrategy);
27
+ }
28
+
29
+ public function getTokenParsers()
30
+ {
31
+ return array(new Twig_TokenParser_AutoEscape());
32
+ }
33
+
34
+ public function getNodeVisitors()
35
+ {
36
+ return array(new Twig_NodeVisitor_Escaper());
37
+ }
38
+
39
+ public function getFilters()
40
+ {
41
+ return array(
42
+ new Twig_SimpleFilter('raw', 'twig_raw_filter', array('is_safe' => array('all'))),
43
+ );
44
+ }
45
+
46
+ /**
47
+ * Sets the default strategy to use when not defined by the user.
48
+ *
49
+ * The strategy can be a valid PHP callback that takes the template
50
+ * name as an argument and returns the strategy to use.
51
+ *
52
+ * @param string|false|callable $defaultStrategy An escaping strategy
53
+ */
54
+ public function setDefaultStrategy($defaultStrategy)
55
+ {
56
+ // for BC
57
+ if (true === $defaultStrategy) {
58
+ @trigger_error('Using "true" as the default strategy is deprecated since version 1.21. Use "html" instead.', E_USER_DEPRECATED);
59
+
60
+ $defaultStrategy = 'html';
61
+ }
62
+
63
+ if ('filename' === $defaultStrategy) {
64
+ @trigger_error('Using "filename" as the default strategy is deprecated since version 1.27. Use "name" instead.', E_USER_DEPRECATED);
65
+
66
+ $defaultStrategy = 'name';
67
+ }
68
+
69
+ if ('name' === $defaultStrategy) {
70
+ $defaultStrategy = array('Twig_FileExtensionEscapingStrategy', 'guess');
71
+ }
72
+
73
+ $this->defaultStrategy = $defaultStrategy;
74
+ }
75
+
76
+ /**
77
+ * Gets the default strategy to use when not defined by the user.
78
+ *
79
+ * @param string $name The template name
80
+ *
81
+ * @return string|false The default strategy to use for the template
82
+ */
83
+ public function getDefaultStrategy($name)
84
+ {
85
+ // disable string callables to avoid calling a function named html or js,
86
+ // or any other upcoming escaping strategy
87
+ if (!is_string($this->defaultStrategy) && false !== $this->defaultStrategy) {
88
+ return call_user_func($this->defaultStrategy, $name);
89
+ }
90
+
91
+ return $this->defaultStrategy;
92
+ }
93
+
94
+ public function getName()
95
+ {
96
+ return 'escaper';
97
+ }
98
+ }
99
+
100
+ /**
101
+ * Marks a variable as being safe.
102
+ *
103
+ * @param string $string A PHP variable
104
+ *
105
+ * @return string
106
+ */
107
+ function twig_raw_filter($string)
108
+ {
109
+ return $string;
110
+ }
vendor/twig/twig/lib/Twig/Extension/GlobalsInterface.php ADDED
@@ -0,0 +1,22 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * This file is part of Twig.
5
+ *
6
+ * (c) Fabien Potencier
7
+ *
8
+ * For the full copyright and license information, please view the LICENSE
9
+ * file that was distributed with this source code.
10
+ */
11
+
12
+ /**
13
+ * Enables usage of the deprecated Twig_Extension::getGlobals() method.
14
+ *
15
+ * Explicitly implement this interface if you really need to implement the
16
+ * deprecated getGlobals() method in your extensions.
17
+ *
18
+ * @author Fabien Potencier <fabien@symfony.com>
19
+ */
20
+ interface Twig_Extension_GlobalsInterface
21
+ {
22
+ }
vendor/twig/twig/lib/Twig/Extension/InitRuntimeInterface.php ADDED
@@ -0,0 +1,22 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * This file is part of Twig.
5
+ *
6
+ * (c) Fabien Potencier
7
+ *
8
+ * For the full copyright and license information, please view the LICENSE
9
+ * file that was distributed with this source code.
10
+ */
11
+
12
+ /**
13
+ * Enables usage of the deprecated Twig_Extension::initRuntime() method.
14
+ *
15
+ * Explicitly implement this interface if you really need to implement the
16
+ * deprecated initRuntime() method in your extensions.
17
+ *
18
+ * @author Fabien Potencier <fabien@symfony.com>
19
+ */
20
+ interface Twig_Extension_InitRuntimeInterface
21
+ {
22
+ }
vendor/twig/twig/lib/Twig/Extension/Optimizer.php ADDED
@@ -0,0 +1,33 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * This file is part of Twig.
5
+ *
6
+ * (c) Fabien Potencier
7
+ *
8
+ * For the full copyright and license information, please view the LICENSE
9
+ * file that was distributed with this source code.
10
+ */
11
+
12
+ /**
13
+ * @final
14
+ */
15
+ class Twig_Extension_Optimizer extends Twig_Extension
16
+ {
17
+ protected $optimizers;
18
+
19
+ public function __construct($optimizers = -1)
20
+ {
21
+ $this->optimizers = $optimizers;
22
+ }
23
+
24
+ public function getNodeVisitors()
25
+ {
26
+ return array(new Twig_NodeVisitor_Optimizer($this->optimizers));
27
+ }
28
+
29
+ public function getName()
30
+ {
31
+ return 'optimizer';
32
+ }
33
+ }
vendor/twig/twig/lib/Twig/Extension/Profiler.php ADDED
@@ -0,0 +1,46 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * This file is part of Twig.
5
+ *
6
+ * (c) Fabien Potencier
7
+ *
8
+ * For the full copyright and license information, please view the LICENSE
9
+ * file that was distributed with this source code.
10
+ */
11
+
12
+ class Twig_Extension_Profiler extends Twig_Extension
13
+ {
14
+ private $actives = array();
15
+
16
+ public function __construct(Twig_Profiler_Profile $profile)
17
+ {
18
+ $this->actives[] = $profile;
19
+ }
20
+
21
+ public function enter(Twig_Profiler_Profile $profile)
22
+ {
23
+ $this->actives[0]->addProfile($profile);
24
+ array_unshift($this->actives, $profile);
25
+ }
26
+
27
+ public function leave(Twig_Profiler_Profile $profile)
28
+ {
29
+ $profile->leave();
30
+ array_shift($this->actives);
31
+
32
+ if (1 === count($this->actives)) {
33
+ $this->actives[0]->leave();
34
+ }
35
+ }
36
+
37
+ public function getNodeVisitors()
38
+ {
39
+ return array(new Twig_Profiler_NodeVisitor_Profiler(get_class($this)));
40
+ }
41
+
42
+ public function getName()
43
+ {
44
+ return 'profiler';
45
+ }
46
+ }
vendor/twig/twig/lib/Twig/Extension/Sandbox.php ADDED
@@ -0,0 +1,101 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * This file is part of Twig.
5
+ *
6
+ * (c) Fabien Potencier
7
+ *
8
+ * For the full copyright and license information, please view the LICENSE
9
+ * file that was distributed with this source code.
10
+ */
11
+
12
+ /**
13
+ * @final
14
+ */
15
+ class Twig_Extension_Sandbox extends Twig_Extension
16
+ {
17
+ protected $sandboxedGlobally;
18
+ protected $sandboxed;
19
+ protected $policy;
20
+
21
+ public function __construct(Twig_Sandbox_SecurityPolicyInterface $policy, $sandboxed = false)
22
+ {
23
+ $this->policy = $policy;
24
+ $this->sandboxedGlobally = $sandboxed;
25
+ }
26
+
27
+ public function getTokenParsers()
28
+ {
29
+ return array(new Twig_TokenParser_Sandbox());
30
+ }
31
+
32
+ public function getNodeVisitors()
33
+ {
34
+ return array(new Twig_NodeVisitor_Sandbox());
35
+ }
36
+
37
+ public function enableSandbox()
38
+ {
39
+ $this->sandboxed = true;
40
+ }
41
+
42
+ public function disableSandbox()
43
+ {
44
+ $this->sandboxed = false;
45
+ }
46
+
47
+ public function isSandboxed()
48
+ {
49
+ return $this->sandboxedGlobally || $this->sandboxed;
50
+ }
51
+
52
+ public function isSandboxedGlobally()
53
+ {
54
+ return $this->sandboxedGlobally;
55
+ }
56
+
57
+ public function setSecurityPolicy(Twig_Sandbox_SecurityPolicyInterface $policy)
58
+ {
59
+ $this->policy = $policy;
60
+ }
61
+
62
+ public function getSecurityPolicy()
63
+ {
64
+ return $this->policy;
65
+ }
66
+
67
+ public function checkSecurity($tags, $filters, $functions)
68
+ {
69
+ if ($this->isSandboxed()) {
70
+ $this->policy->checkSecurity($tags, $filters, $functions);
71
+ }
72
+ }
73
+
74
+ public function checkMethodAllowed($obj, $method)
75
+ {
76
+ if ($this->isSandboxed()) {
77
+ $this->policy->checkMethodAllowed($obj, $method);
78
+ }
79
+ }
80
+
81
+ public function checkPropertyAllowed($obj, $method)
82
+ {
83
+ if ($this->isSandboxed()) {
84
+ $this->policy->checkPropertyAllowed($obj, $method);
85
+ }
86
+ }
87
+
88
+ public function ensureToStringAllowed($obj)
89
+ {
90
+ if ($this->isSandboxed() && is_object($obj)) {
91
+ $this->policy->checkMethodAllowed($obj, '__toString');
92
+ }
93
+
94
+ return $obj;
95
+ }
96
+
97
+ public function getName()
98
+ {
99
+ return 'sandbox';
100
+ }
101
+ }
vendor/twig/twig/lib/Twig/Extension/Staging.php ADDED
@@ -0,0 +1,110 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * This file is part of Twig.
5
+ *
6
+ * (c) Fabien Potencier
7
+ *
8
+ * For the full copyright and license information, please view the LICENSE
9
+ * file that was distributed with this source code.
10
+ */
11
+
12
+ /**
13
+ * Internal class.
14
+ *
15
+ * This class is used by Twig_Environment as a staging area and must not be used directly.
16
+ *
17
+ * @author Fabien Potencier <fabien@symfony.com>
18
+ *
19
+ * @internal
20
+ */
21
+ class Twig_Extension_Staging extends Twig_Extension
22
+ {
23
+ protected $functions = array();
24
+ protected $filters = array();
25
+ protected $visitors = array();
26
+ protected $tokenParsers = array();
27
+ protected $globals = array();
28
+ protected $tests = array();
29
+
30
+ public function addFunction($name, $function)
31
+ {
32
+ if (isset($this->functions[$name])) {
33
+ @trigger_error(sprintf('Overriding function "%s" that is already registered is deprecated since version 1.30 and won\'t be possible anymore in 2.0.', $name), E_USER_DEPRECATED);
34
+ }
35
+
36
+ $this->functions[$name] = $function;
37
+ }
38
+
39
+ public function getFunctions()
40
+ {
41
+ return $this->functions;
42
+ }
43
+
44
+ public function addFilter($name, $filter)
45
+ {
46
+ if (isset($this->filters[$name])) {
47
+ @trigger_error(sprintf('Overriding filter "%s" that is already registered is deprecated since version 1.30 and won\'t be possible anymore in 2.0.', $name), E_USER_DEPRECATED);
48
+ }
49
+
50
+ $this->filters[$name] = $filter;
51
+ }
52
+
53
+ public function getFilters()
54
+ {
55
+ return $this->filters;
56
+ }
57
+
58
+ public function addNodeVisitor(Twig_NodeVisitorInterface $visitor)
59
+ {
60
+ $this->visitors[] = $visitor;
61
+ }
62
+
63
+ public function getNodeVisitors()
64
+ {
65
+ return $this->visitors;
66
+ }
67
+
68
+ public function addTokenParser(Twig_TokenParserInterface $parser)
69
+ {
70
+ if (isset($this->tokenParsers[$parser->getTag()])) {
71
+ @trigger_error(sprintf('Overriding tag "%s" that is already registered is deprecated since version 1.30 and won\'t be possible anymore in 2.0.', $parser->getTag()), E_USER_DEPRECATED);
72
+ }
73
+
74
+ $this->tokenParsers[$parser->getTag()] = $parser;
75
+ }
76
+
77
+ public function getTokenParsers()
78
+ {
79
+ return $this->tokenParsers;
80
+ }
81
+
82
+ public function addGlobal($name, $value)
83
+ {
84
+ $this->globals[$name] = $value;
85
+ }
86
+
87
+ public function getGlobals()
88
+ {
89
+ return $this->globals;
90
+ }
91
+
92
+ public function addTest($name, $test)
93
+ {
94
+ if (isset($this->tests[$name])) {
95
+ @trigger_error(sprintf('Overriding test "%s" that is already registered is deprecated since version 1.30 and won\'t be possible anymore in 2.0.', $name), E_USER_DEPRECATED);
96
+ }
97
+
98
+ $this->tests[$name] = $test;
99
+ }
100
+
101
+ public function getTests()
102
+ {
103
+ return $this->tests;
104
+ }
105
+
106
+ public function getName()
107
+ {
108
+ return 'staging';
109
+ }
110
+ }
vendor/twig/twig/lib/Twig/Extension/StringLoader.php ADDED
@@ -0,0 +1,45 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * This file is part of Twig.
5
+ *
6
+ * (c) Fabien Potencier
7
+ *
8
+ * For the full copyright and license information, please view the LICENSE
9
+ * file that was distributed with this source code.
10
+ */
11
+
12
+ /**
13
+ * @final
14
+ */
15
+ class Twig_Extension_StringLoader extends Twig_Extension
16
+ {
17
+ public function getFunctions()
18
+ {
19
+ return array(
20
+ new Twig_SimpleFunction('template_from_string', 'twig_template_from_string', array('needs_environment' => true)),
21
+ );
22
+ }
23
+
24
+ public function getName()
25
+ {
26
+ return 'string_loader';
27
+ }
28
+ }
29
+
30
+ /**
31
+ * Loads a template from a string.
32
+ *
33
+ * <pre>
34
+ * {{ include(template_from_string("Hello {{ name }}")) }}
35
+ * </pre>
36
+ *
37
+ * @param Twig_Environment $env A Twig_Environment instance
38
+ * @param string $template A template as a string or object implementing __toString()
39
+ *
40
+ * @return Twig_Template
41
+ */
42
+ function twig_template_from_string(Twig_Environment $env, $template)
43
+ {
44
+ return $env->createTemplate((string) $template);
45
+ }
vendor/twig/twig/lib/Twig/ExtensionInterface.php ADDED
@@ -0,0 +1,87 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * This file is part of Twig.
5
+ *
6
+ * (c) Fabien Potencier
7
+ *
8
+ * For the full copyright and license information, please view the LICENSE
9
+ * file that was distributed with this source code.
10
+ */
11
+
12
+ /**
13
+ * Interface implemented by extension classes.
14
+ *
15
+ * @author Fabien Potencier <fabien@symfony.com>
16
+ */
17
+ interface Twig_ExtensionInterface
18
+ {
19
+ /**
20
+ * Initializes the runtime environment.
21
+ *
22
+ * This is where you can load some file that contains filter functions for instance.
23
+ *
24
+ * @deprecated since 1.23 (to be removed in 2.0), implement Twig_Extension_InitRuntimeInterface instead
25
+ */
26
+ public function initRuntime(Twig_Environment $environment);
27
+
28
+ /**
29
+ * Returns the token parser instances to add to the existing list.
30
+ *
31
+ * @return Twig_TokenParserInterface[]
32
+ */
33
+ public function getTokenParsers();
34
+
35
+ /**
36
+ * Returns the node visitor instances to add to the existing list.
37
+ *
38
+ * @return Twig_NodeVisitorInterface[]
39
+ */
40
+ public function getNodeVisitors();
41
+
42
+ /**
43
+ * Returns a list of filters to add to the existing list.
44
+ *
45
+ * @return Twig_SimpleFilter[]
46
+ */
47
+ public function getFilters();
48
+
49
+ /**
50
+ * Returns a list of tests to add to the existing list.
51
+ *
52
+ * @return Twig_SimpleTest[]
53
+ */
54
+ public function getTests();
55
+
56
+ /**
57
+ * Returns a list of functions to add to the existing list.
58
+ *
59
+ * @return Twig_SimpleFunction[]
60
+ */
61
+ public function getFunctions();
62
+
63
+ /**
64
+ * Returns a list of operators to add to the existing list.
65
+ *
66
+ * @return array<array> First array of unary operators, second array of binary operators
67
+ */
68
+ public function getOperators();
69
+
70
+ /**
71
+ * Returns a list of global variables to add to the existing list.
72
+ *
73
+ * @return array An array of global variables
74
+ *
75
+ * @deprecated since 1.23 (to be removed in 2.0), implement Twig_Extension_GlobalsInterface instead
76
+ */
77
+ public function getGlobals();
78
+
79
+ /**
80
+ * Returns the name of the extension.
81
+ *
82
+ * @return string The extension name
83
+ *
84
+ * @deprecated since 1.26 (to be removed in 2.0), not used anymore internally
85
+ */
86
+ public function getName();
87
+ }
vendor/twig/twig/lib/Twig/FactoryRuntimeLoader.php ADDED
@@ -0,0 +1,37 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * This file is part of Twig.
5
+ *
6
+ * (c) Fabien Potencier
7
+ *
8
+ * For the full copyright and license information, please view the LICENSE
9
+ * file that was distributed with this source code.
10
+ */
11
+
12
+ /**
13
+ * Lazy loads the runtime implementations for a Twig element.
14
+ *
15
+ * @author Robin Chalas <robin.chalas@gmail.com>
16
+ */
17
+ class Twig_FactoryRuntimeLoader implements Twig_RuntimeLoaderInterface
18
+ {
19
+ private $map;
20
+
21
+ /**
22
+ * @param array $map An array where keys are class names and values factory callables
23
+ */
24
+ public function __construct($map = array())
25
+ {
26
+ $this->map = $map;
27
+ }
28
+
29
+ public function load($class)
30
+ {
31
+ if (isset($this->map[$class])) {
32
+ $runtimeFactory = $this->map[$class];
33
+
34
+ return $runtimeFactory();
35
+ }
36
+ }
37
+ }
vendor/twig/twig/lib/Twig/FileExtensionEscapingStrategy.php ADDED
@@ -0,0 +1,58 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * This file is part of Twig.
5
+ *
6
+ * (c) Fabien Potencier
7
+ *
8
+ * For the full copyright and license information, please view the LICENSE
9
+ * file that was distributed with this source code.
10
+ */
11
+
12
+ /**
13
+ * Default autoescaping strategy based on file names.
14
+ *
15
+ * This strategy sets the HTML as the default autoescaping strategy,
16
+ * but changes it based on the template name.
17
+ *
18
+ * Note that there is no runtime performance impact as the
19
+ * default autoescaping strategy is set at compilation time.
20
+ *
21
+ * @author Fabien Potencier <fabien@symfony.com>
22
+ */
23
+ class Twig_FileExtensionEscapingStrategy
24
+ {
25
+ /**
26
+ * Guesses the best autoescaping strategy based on the file name.
27
+ *
28
+ * @param string $name The template name
29
+ *
30
+ * @return string|false The escaping strategy name to use or false to disable
31
+ */
32
+ public static function guess($name)
33
+ {
34
+ if (in_array(substr($name, -1), array('/', '\\'))) {
35
+ return 'html'; // return html for directories
36
+ }
37
+
38
+ if ('.twig' === substr($name, -5)) {
39
+ $name = substr($name, 0, -5);
40
+ }
41
+
42
+ $extension = pathinfo($name, PATHINFO_EXTENSION);
43
+
44
+ switch ($extension) {
45
+ case 'js':
46
+ return 'js';
47
+
48
+ case 'css':
49
+ return 'css';
50
+
51
+ case 'txt':
52
+ return false;
53
+
54
+ default:
55
+ return 'html';
56
+ }
57
+ }
58
+ }
vendor/twig/twig/lib/Twig/Filter.php ADDED
@@ -0,0 +1,84 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * This file is part of Twig.
5
+ *
6
+ * (c) Fabien Potencier
7
+ *
8
+ * For the full copyright and license information, please view the LICENSE
9
+ * file that was distributed with this source code.
10
+ */
11
+
12
+ @trigger_error('The Twig_Filter class is deprecated since version 1.12 and will be removed in 2.0. Use Twig_SimpleFilter instead.', E_USER_DEPRECATED);
13
+
14
+ /**
15
+ * Represents a template filter.
16
+ *
17
+ * Use Twig_SimpleFilter instead.
18
+ *
19
+ * @author Fabien Potencier <fabien@symfony.com>
20
+ *
21
+ * @deprecated since 1.12 (to be removed in 2.0)
22
+ */
23
+ abstract class Twig_Filter implements Twig_FilterInterface, Twig_FilterCallableInterface
24
+ {
25
+ protected $options;
26
+ protected $arguments = array();
27
+
28
+ public function __construct(array $options = array())
29
+ {
30
+ $this->options = array_merge(array(
31
+ 'needs_environment' => false,
32
+ 'needs_context' => false,
33
+ 'pre_escape' => null,
34
+ 'preserves_safety' => null,
35
+ 'callable' => null,
36
+ ), $options);
37
+ }
38
+
39
+ public function setArguments($arguments)
40
+ {
41
+ $this->arguments = $arguments;
42
+ }
43
+
44
+ public function getArguments()
45
+ {
46
+ return $this->arguments;
47
+ }
48
+
49
+ public function needsEnvironment()
50
+ {
51
+ return $this->options['needs_environment'];
52
+ }
53
+
54
+ public function needsContext()
55
+ {
56
+ return $this->options['needs_context'];
57
+ }
58
+
59
+ public function getSafe(Twig_Node $filterArgs)
60
+ {
61
+ if (isset($this->options['is_safe'])) {
62
+ return $this->options['is_safe'];
63
+ }
64
+
65
+ if (isset($this->options['is_safe_callback'])) {
66
+ return call_user_func($this->options['is_safe_callback'], $filterArgs);
67
+ }
68
+ }
69
+
70
+ public function getPreservesSafety()
71
+ {
72
+ return $this->options['preserves_safety'];
73
+ }
74
+
75
+ public function getPreEscape()
76
+ {
77
+ return $this->options['pre_escape'];
78
+ }
79
+
80
+ public function getCallable()
81
+ {
82
+ return $this->options['callable'];
83
+ }
84
+ }
vendor/twig/twig/lib/Twig/Filter/Function.php ADDED
@@ -0,0 +1,40 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * This file is part of Twig.
5
+ *
6
+ * (c) Fabien Potencier
7
+ *
8
+ * For the full copyright and license information, please view the LICENSE
9
+ * file that was distributed with this source code.
10
+ */
11
+
12
+ @trigger_error('The Twig_Filter_Function class is deprecated since version 1.12 and will be removed in 2.0. Use Twig_SimpleFilter instead.', E_USER_DEPRECATED);
13
+
14
+ /**
15
+ * Represents a function template filter.
16
+ *
17
+ * Use Twig_SimpleFilter instead.
18
+ *
19
+ * @author Fabien Potencier <fabien@symfony.com>
20
+ *
21
+ * @deprecated since 1.12 (to be removed in 2.0)
22
+ */
23
+ class Twig_Filter_Function extends Twig_Filter
24
+ {
25
+ protected $function;
26
+
27
+ public function __construct($function, array $options = array())
28
+ {
29
+ $options['callable'] = $function;
30
+
31
+ parent::__construct($options);
32
+
33
+ $this->function = $function;
34
+ }
35
+
36
+ public function compile()
37
+ {
38
+ return $this->function;
39
+ }
40
+ }
vendor/twig/twig/lib/Twig/Filter/Method.php ADDED
@@ -0,0 +1,42 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * This file is part of Twig.
5
+ *
6
+ * (c) Fabien Potencier
7
+ *
8
+ * For the full copyright and license information, please view the LICENSE
9
+ * file that was distributed with this source code.
10
+ */
11
+
12
+ @trigger_error('The Twig_Filter_Method class is deprecated since version 1.12 and will be removed in 2.0. Use Twig_SimpleFilter instead.', E_USER_DEPRECATED);
13
+
14
+ /**
15
+ * Represents a method template filter.
16
+ *
17
+ * Use Twig_SimpleFilter instead.
18
+ *
19
+ * @author Fabien Potencier <fabien@symfony.com>
20
+ *
21
+ * @deprecated since 1.12 (to be removed in 2.0)
22
+ */
23
+ class Twig_Filter_Method extends Twig_Filter
24
+ {
25
+ protected $extension;
26
+ protected $method;
27
+
28
+ public function __construct(Twig_ExtensionInterface $extension, $method, array $options = array())
29
+ {
30
+ $options['callable'] = array($extension, $method);
31
+
32
+ parent::__construct($options);
33
+
34
+ $this->extension = $extension;
35
+ $this->method = $method;
36
+ }
37
+
38
+ public function compile()
39
+ {
40
+ return sprintf('$this->env->getExtension(\'%s\')->%s', get_class($this->extension), $this->method);
41
+ }
42
+ }
vendor/twig/twig/lib/Twig/Filter/Node.php ADDED
@@ -0,0 +1,42 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * This file is part of Twig.
5
+ *
6
+ * (c) Fabien Potencier
7
+ *
8
+ * For the full copyright and license information, please view the LICENSE
9
+ * file that was distributed with this source code.
10
+ */
11
+
12
+ @trigger_error('The Twig_Filter_Node class is deprecated since version 1.12 and will be removed in 2.0. Use Twig_SimpleFilter instead.', E_USER_DEPRECATED);
13
+
14
+ /**
15
+ * Represents a template filter as a node.
16
+ *
17
+ * Use Twig_SimpleFilter instead.
18
+ *
19
+ * @author Fabien Potencier <fabien@symfony.com>
20
+ *
21
+ * @deprecated since 1.12 (to be removed in 2.0)
22
+ */
23
+ class Twig_Filter_Node extends Twig_Filter
24
+ {
25
+ protected $class;
26
+
27
+ public function __construct($class, array $options = array())
28
+ {
29
+ parent::__construct($options);
30
+
31
+ $this->class = $class;
32
+ }
33
+
34
+ public function getClass()
35
+ {
36
+ return $this->class;
37
+ }
38
+
39
+ public function compile()
40
+ {
41
+ }
42
+ }
vendor/twig/twig/lib/Twig/FilterCallableInterface.php ADDED
@@ -0,0 +1,24 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * This file is part of Twig.
5
+ *
6
+ * (c) Fabien Potencier
7
+ *
8
+ * For the full copyright and license information, please view the LICENSE
9
+ * file that was distributed with this source code.
10
+ */
11
+
12
+ /**
13
+ * Represents a callable template filter.
14
+ *
15
+ * Use Twig_SimpleFilter instead.
16
+ *
17
+ * @author Fabien Potencier <fabien@symfony.com>
18
+ *
19
+ * @deprecated since 1.12 (to be removed in 2.0)
20
+ */
21
+ interface Twig_FilterCallableInterface
22
+ {
23
+ public function getCallable();
24
+ }
vendor/twig/twig/lib/Twig/FilterInterface.php ADDED
@@ -0,0 +1,43 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * This file is part of Twig.
5
+ *
6
+ * (c) Fabien Potencier
7
+ *
8
+ * For the full copyright and license information, please view the LICENSE
9
+ * file that was distributed with this source code.
10
+ */
11
+
12
+ /**
13
+ * Represents a template filter.
14
+ *
15
+ * Use Twig_SimpleFilter instead.
16
+ *
17
+ * @author Fabien Potencier <fabien@symfony.com>
18
+ *
19
+ * @deprecated since 1.12 (to be removed in 2.0)
20
+ */
21
+ interface Twig_FilterInterface
22
+ {
23
+ /**
24
+ * Compiles a filter.
25
+ *
26
+ * @return string The PHP code for the filter
27
+ */
28
+ public function compile();
29
+
30
+ public function needsEnvironment();
31
+
32
+ public function needsContext();
33
+
34
+ public function getSafe(Twig_Node $filterArgs);
35
+
36
+ public function getPreservesSafety();
37
+
38
+ public function getPreEscape();
39
+
40
+ public function setArguments($arguments);
41
+
42
+ public function getArguments();
43
+ }
vendor/twig/twig/lib/Twig/Function.php ADDED
@@ -0,0 +1,74 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * This file is part of Twig.
5
+ *
6
+ * (c) Fabien Potencier
7
+ *
8
+ * For the full copyright and license information, please view the LICENSE
9
+ * file that was distributed with this source code.
10
+ */
11
+
12
+ @trigger_error('The Twig_Function class is deprecated since version 1.12 and will be removed in 2.0. Use Twig_SimpleFunction instead.', E_USER_DEPRECATED);
13
+
14
+ /**
15
+ * Represents a template function.
16
+ *
17
+ * Use Twig_SimpleFunction instead.
18
+ *
19
+ * @author Fabien Potencier <fabien@symfony.com>
20
+ *
21
+ * @deprecated since 1.12 (to be removed in 2.0)
22
+ */
23
+ abstract class Twig_Function implements Twig_FunctionInterface, Twig_FunctionCallableInterface
24
+ {
25
+ protected $options;
26
+ protected $arguments = array();
27
+
28
+ public function __construct(array $options = array())
29
+ {
30
+ $this->options = array_merge(array(
31
+ 'needs_environment' => false,
32
+ 'needs_context' => false,
33
+ 'callable' => null,
34
+ ), $options);
35
+ }
36
+
37
+ public function setArguments($arguments)
38
+ {
39
+ $this->arguments = $arguments;
40
+ }
41
+
42
+ public function getArguments()
43
+ {
44
+ return $this->arguments;
45
+ }
46
+
47
+ public function needsEnvironment()
48
+ {
49
+ return $this->options['needs_environment'];
50
+ }
51
+
52
+ public function needsContext()
53
+ {
54
+ return $this->options['needs_context'];
55
+ }
56
+
57
+ public function getSafe(Twig_Node $functionArgs)
58
+ {
59
+ if (isset($this->options['is_safe'])) {
60
+ return $this->options['is_safe'];
61
+ }
62
+
63
+ if (isset($this->options['is_safe_callback'])) {
64
+ return call_user_func($this->options['is_safe_callback'], $functionArgs);
65
+ }
66
+
67
+ return array();
68
+ }
69
+
70
+ public function getCallable()
71
+ {
72
+ return $this->options['callable'];
73
+ }
74
+ }
vendor/twig/twig/lib/Twig/Function/Function.php ADDED
@@ -0,0 +1,41 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * This file is part of Twig.
5
+ *
6
+ * (c) Fabien Potencier
7
+ * (c) Arnaud Le Blanc
8
+ *
9
+ * For the full copyright and license information, please view the LICENSE
10
+ * file that was distributed with this source code.
11
+ */
12
+
13
+ @trigger_error('The Twig_Function_Function class is deprecated since version 1.12 and will be removed in 2.0. Use Twig_SimpleFunction instead.', E_USER_DEPRECATED);
14
+
15
+ /**
16
+ * Represents a function template function.
17
+ *
18
+ * Use Twig_SimpleFunction instead.
19
+ *
20
+ * @author Arnaud Le Blanc <arnaud.lb@gmail.com>
21
+ *
22
+ * @deprecated since 1.12 (to be removed in 2.0)
23
+ */
24
+ class Twig_Function_Function extends Twig_Function
25
+ {
26
+ protected $function;
27
+
28
+ public function __construct($function, array $options = array())
29
+ {
30
+ $options['callable'] = $function;
31
+
32
+ parent::__construct($options);
33
+
34
+ $this->function = $function;
35
+ }
36
+
37
+ public function compile()
38
+ {
39
+ return $this->function;
40
+ }
41
+ }
vendor/twig/twig/lib/Twig/Function/Method.php ADDED
@@ -0,0 +1,43 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * This file is part of Twig.
5
+ *
6
+ * (c) Fabien Potencier
7
+ * (c) Arnaud Le Blanc
8
+ *
9
+ * For the full copyright and license information, please view the LICENSE
10
+ * file that was distributed with this source code.
11
+ */
12
+
13
+ @trigger_error('The Twig_Function_Method class is deprecated since version 1.12 and will be removed in 2.0. Use Twig_SimpleFunction instead.', E_USER_DEPRECATED);
14
+
15
+ /**
16
+ * Represents a method template function.
17
+ *
18
+ * Use Twig_SimpleFunction instead.
19
+ *
20
+ * @author Arnaud Le Blanc <arnaud.lb@gmail.com>
21
+ *
22
+ * @deprecated since 1.12 (to be removed in 2.0)
23
+ */
24
+ class Twig_Function_Method extends Twig_Function
25
+ {
26
+ protected $extension;
27
+ protected $method;
28
+
29
+ public function __construct(Twig_ExtensionInterface $extension, $method, array $options = array())
30
+ {
31
+ $options['callable'] = array($extension, $method);
32
+
33
+ parent::__construct($options);
34
+
35
+ $this->extension = $extension;
36
+ $this->method = $method;
37
+ }
38
+
39
+ public function compile()
40
+ {
41
+ return sprintf('$this->env->getExtension(\'%s\')->%s', get_class($this->extension), $this->method);
42
+ }
43
+ }
vendor/twig/twig/lib/Twig/Function/Node.php ADDED
@@ -0,0 +1,42 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * This file is part of Twig.
5
+ *
6
+ * (c) Fabien Potencier
7
+ *
8
+ * For the full copyright and license information, please view the LICENSE
9
+ * file that was distributed with this source code.
10
+ */
11
+
12
+ @trigger_error('The Twig_Function_Node class is deprecated since version 1.12 and will be removed in 2.0. Use Twig_SimpleFunction instead.', E_USER_DEPRECATED);
13
+
14
+ /**
15
+ * Represents a template function as a node.
16
+ *
17
+ * Use Twig_SimpleFunction instead.
18
+ *
19
+ * @author Fabien Potencier <fabien@symfony.com>
20
+ *
21
+ * @deprecated since 1.12 (to be removed in 2.0)
22
+ */
23
+ class Twig_Function_Node extends Twig_Function
24
+ {
25
+ protected $class;
26
+
27
+ public function __construct($class, array $options = array())
28
+ {
29
+ parent::__construct($options);
30
+
31
+ $this->class = $class;
32
+ }
33
+
34
+ public function getClass()
35
+ {
36
+ return $this->class;
37
+ }
38
+
39
+ public function compile()
40
+ {
41
+ }
42
+ }
vendor/twig/twig/lib/Twig/FunctionCallableInterface.php ADDED
@@ -0,0 +1,24 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * This file is part of Twig.
5
+ *
6
+ * (c) Fabien Potencier
7
+ *
8
+ * For the full copyright and license information, please view the LICENSE
9
+ * file that was distributed with this source code.
10
+ */
11
+
12
+ /**
13
+ * Represents a callable template function.
14
+ *
15
+ * Use Twig_SimpleFunction instead.
16
+ *
17
+ * @author Fabien Potencier <fabien@symfony.com>
18
+ *
19
+ * @deprecated since 1.12 (to be removed in 2.0)
20
+ */
21
+ interface Twig_FunctionCallableInterface
22
+ {
23
+ public function getCallable();
24
+ }
vendor/twig/twig/lib/Twig/FunctionInterface.php ADDED
@@ -0,0 +1,40 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * This file is part of Twig.
5
+ *
6
+ * (c) Fabien Potencier
7
+ * (c) Arnaud Le Blanc
8
+ *
9
+ * For the full copyright and license information, please view the LICENSE
10
+ * file that was distributed with this source code.
11
+ */
12
+
13
+ /**
14
+ * Represents a template function.
15
+ *
16
+ * Use Twig_SimpleFunction instead.
17
+ *
18
+ * @author Arnaud Le Blanc <arnaud.lb@gmail.com>
19
+ *
20
+ * @deprecated since 1.12 (to be removed in 2.0)
21
+ */
22
+ interface Twig_FunctionInterface
23
+ {
24
+ /**
25
+ * Compiles a function.
26
+ *
27
+ * @return string The PHP code for the function
28
+ */
29
+ public function compile();
30
+
31
+ public function needsEnvironment();
32
+
33
+ public function needsContext();
34
+
35
+ public function getSafe(Twig_Node $filterArgs);
36
+
37
+ public function setArguments($arguments);
38
+
39
+ public function getArguments();
40
+ }
vendor/twig/twig/lib/Twig/Lexer.php ADDED
@@ -0,0 +1,422 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * This file is part of Twig.
5
+ *
6
+ * (c) Fabien Potencier
7
+ * (c) Armin Ronacher
8
+ *
9
+ * For the full copyright and license information, please view the LICENSE
10
+ * file that was distributed with this source code.
11
+ */
12
+
13
+ /**
14
+ * Lexes a template string.
15
+ *
16
+ * @author Fabien Potencier <fabien@symfony.com>
17
+ */
18
+ class Twig_Lexer implements Twig_LexerInterface
19
+ {
20
+ protected $tokens;
21
+ protected $code;
22
+ protected $cursor;
23
+ protected $lineno;
24
+ protected $end;
25
+ protected $state;
26
+ protected $states;
27
+ protected $brackets;
28
+ protected $env;
29
+ // to be renamed to $name in 2.0 (where it is private)
30
+ protected $filename;
31
+ protected $options;
32
+ protected $regexes;
33
+ protected $position;
34
+ protected $positions;
35
+ protected $currentVarBlockLine;
36
+
37
+ private $source;
38
+
39
+ const STATE_DATA = 0;
40
+ const STATE_BLOCK = 1;
41
+ const STATE_VAR = 2;
42
+ const STATE_STRING = 3;
43
+ const STATE_INTERPOLATION = 4;
44
+
45
+ const REGEX_NAME = '/[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*/A';
46
+ const REGEX_NUMBER = '/[0-9]+(?:\.[0-9]+)?/A';
47
+ const REGEX_STRING = '/"([^#"\\\\]*(?:\\\\.[^#"\\\\]*)*)"|\'([^\'\\\\]*(?:\\\\.[^\'\\\\]*)*)\'/As';
48
+ const REGEX_DQ_STRING_DELIM = '/"/A';
49
+ const REGEX_DQ_STRING_PART = '/[^#"\\\\]*(?:(?:\\\\.|#(?!\{))[^#"\\\\]*)*/As';
50
+ const PUNCTUATION = '()[]{}?:.,|';
51
+
52
+ public function __construct(Twig_Environment $env, array $options = array())
53
+ {
54
+ $this->env = $env;
55
+
56
+ $this->options = array_merge(array(
57
+ 'tag_comment' => array('{#', '#}'),
58
+ 'tag_block' => array('{%', '%}'),
59
+ 'tag_variable' => array('{{', '}}'),
60
+ 'whitespace_trim' => '-',
61
+ 'interpolation' => array('#{', '}'),
62
+ ), $options);
63
+
64
+ $this->regexes = array(
65
+ 'lex_var' => '/\s*'.preg_quote($this->options['whitespace_trim'].$this->options['tag_variable'][1], '/').'\s*|\s*'.preg_quote($this->options['tag_variable'][1], '/').'/A',
66
+ 'lex_block' => '/\s*(?:'.preg_quote($this->options['whitespace_trim'].$this->options['tag_block'][1], '/').'\s*|\s*'.preg_quote($this->options['tag_block'][1], '/').')\n?/A',
67
+ 'lex_raw_data' => '/('.preg_quote($this->options['tag_block'][0].$this->options['whitespace_trim'], '/').'|'.preg_quote($this->options['tag_block'][0], '/').')\s*(?:end%s)\s*(?:'.preg_quote($this->options['whitespace_trim'].$this->options['tag_block'][1], '/').'\s*|\s*'.preg_quote($this->options['tag_block'][1], '/').')/s',
68
+ 'operator' => $this->getOperatorRegex(),
69
+ 'lex_comment' => '/(?:'.preg_quote($this->options['whitespace_trim'], '/').preg_quote($this->options['tag_comment'][1], '/').'\s*|'.preg_quote($this->options['tag_comment'][1], '/').')\n?/s',
70
+ 'lex_block_raw' => '/\s*(raw|verbatim)\s*(?:'.preg_quote($this->options['whitespace_trim'].$this->options['tag_block'][1], '/').'\s*|\s*'.preg_quote($this->options['tag_block'][1], '/').')/As',
71
+ 'lex_block_line' => '/\s*line\s+(\d+)\s*'.preg_quote($this->options['tag_block'][1], '/').'/As',
72
+ 'lex_tokens_start' => '/('.preg_quote($this->options['tag_variable'][0], '/').'|'.preg_quote($this->options['tag_block'][0], '/').'|'.preg_quote($this->options['tag_comment'][0], '/').')('.preg_quote($this->options['whitespace_trim'], '/').')?/s',
73
+ 'interpolation_start' => '/'.preg_quote($this->options['interpolation'][0], '/').'\s*/A',
74
+ 'interpolation_end' => '/\s*'.preg_quote($this->options['interpolation'][1], '/').'/A',
75
+ );
76
+ }
77
+
78
+ public function tokenize($code, $name = null)
79
+ {
80
+ if (!$code instanceof Twig_Source) {
81
+ @trigger_error(sprintf('Passing a string as the $code argument of %s() is deprecated since version 1.27 and will be removed in 2.0. Pass a Twig_Source instance instead.', __METHOD__), E_USER_DEPRECATED);
82
+ $this->source = new Twig_Source($code, $name);
83
+ } else {
84
+ $this->source = $code;
85
+ }
86
+
87
+ if (((int) ini_get('mbstring.func_overload')) & 2) {
88
+ @trigger_error('Support for having "mbstring.func_overload" different from 0 is deprecated version 1.29 and will be removed in 2.0.', E_USER_DEPRECATED);
89
+ }
90
+
91
+ if (function_exists('mb_internal_encoding') && ((int) ini_get('mbstring.func_overload')) & 2) {
92
+ $mbEncoding = mb_internal_encoding();
93
+ mb_internal_encoding('ASCII');
94
+ } else {
95
+ $mbEncoding = null;
96
+ }
97
+
98
+ $this->code = str_replace(array("\r\n", "\r"), "\n", $this->source->getCode());
99
+ $this->filename = $this->source->getName();
100
+ $this->cursor = 0;
101
+ $this->lineno = 1;
102
+ $this->end = strlen($this->code);
103
+ $this->tokens = array();
104
+ $this->state = self::STATE_DATA;
105
+ $this->states = array();
106
+ $this->brackets = array();
107
+ $this->position = -1;
108
+
109
+ // find all token starts in one go
110
+ preg_match_all($this->regexes['lex_tokens_start'], $this->code, $matches, PREG_OFFSET_CAPTURE);
111
+ $this->positions = $matches;
112
+
113
+ while ($this->cursor < $this->end) {
114
+ // dispatch to the lexing functions depending
115
+ // on the current state
116
+ switch ($this->state) {
117
+ case self::STATE_DATA:
118
+ $this->lexData();
119
+ break;
120
+
121
+ case self::STATE_BLOCK:
122
+ $this->lexBlock();
123
+ break;
124
+
125
+ case self::STATE_VAR:
126
+ $this->lexVar();
127
+ break;
128
+
129
+ case self::STATE_STRING:
130
+ $this->lexString();
131
+ break;
132
+
133
+ case self::STATE_INTERPOLATION:
134
+ $this->lexInterpolation();
135
+ break;
136
+ }
137
+ }
138
+
139
+ $this->pushToken(Twig_Token::EOF_TYPE);
140
+
141
+ if (!empty($this->brackets)) {
142
+ list($expect, $lineno) = array_pop($this->brackets);
143
+ throw new Twig_Error_Syntax(sprintf('Unclosed "%s".', $expect), $lineno, $this->source);
144
+ }
145
+
146
+ if ($mbEncoding) {
147
+ mb_internal_encoding($mbEncoding);
148
+ }
149
+
150
+ return new Twig_TokenStream($this->tokens, $this->source);
151
+ }
152
+
153
+ protected function lexData()
154
+ {
155
+ // if no matches are left we return the rest of the template as simple text token
156
+ if ($this->position == count($this->positions[0]) - 1) {
157
+ $this->pushToken(Twig_Token::TEXT_TYPE, substr($this->code, $this->cursor));
158
+ $this->cursor = $this->end;
159
+
160
+ return;
161
+ }
162
+
163
+ // Find the first token after the current cursor
164
+ $position = $this->positions[0][++$this->position];
165
+ while ($position[1] < $this->cursor) {
166
+ if ($this->position == count($this->positions[0]) - 1) {
167
+ return;
168
+ }
169
+ $position = $this->positions[0][++$this->position];
170
+ }
171
+
172
+ // push the template text first
173
+ $text = $textContent = substr($this->code, $this->cursor, $position[1] - $this->cursor);
174
+ if (isset($this->positions[2][$this->position][0])) {
175
+ $text = rtrim($text);
176
+ }
177
+ $this->pushToken(Twig_Token::TEXT_TYPE, $text);
178
+ $this->moveCursor($textContent.$position[0]);
179
+
180
+ switch ($this->positions[1][$this->position][0]) {
181
+ case $this->options['tag_comment'][0]:
182
+ $this->lexComment();
183
+ break;
184
+
185
+ case $this->options['tag_block'][0]:
186
+ // raw data?
187
+ if (preg_match($this->regexes['lex_block_raw'], $this->code, $match, null, $this->cursor)) {
188
+ $this->moveCursor($match[0]);
189
+ $this->lexRawData($match[1]);
190
+ // {% line \d+ %}
191
+ } elseif (preg_match($this->regexes['lex_block_line'], $this->code, $match, null, $this->cursor)) {
192
+ $this->moveCursor($match[0]);
193
+ $this->lineno = (int) $match[1];
194
+ } else {
195
+ $this->pushToken(Twig_Token::BLOCK_START_TYPE);
196
+ $this->pushState(self::STATE_BLOCK);
197
+ $this->currentVarBlockLine = $this->lineno;
198
+ }
199
+ break;
200
+
201
+ case $this->options['tag_variable'][0]:
202
+ $this->pushToken(Twig_Token::VAR_START_TYPE);
203
+ $this->pushState(self::STATE_VAR);
204
+ $this->currentVarBlockLine = $this->lineno;
205
+ break;
206
+ }
207
+ }
208
+
209
+ protected function lexBlock()
210
+ {
211
+ if (empty($this->brackets) && preg_match($this->regexes['lex_block'], $this->code, $match, null, $this->cursor)) {
212
+ $this->pushToken(Twig_Token::BLOCK_END_TYPE);
213
+ $this->moveCursor($match[0]);
214
+ $this->popState();
215
+ } else {
216
+ $this->lexExpression();
217
+ }
218
+ }
219
+
220
+ protected function lexVar()
221
+ {
222
+ if (empty($this->brackets) && preg_match($this->regexes['lex_var'], $this->code, $match, null, $this->cursor)) {
223
+ $this->pushToken(Twig_Token::VAR_END_TYPE);
224
+ $this->moveCursor($match[0]);
225
+ $this->popState();
226
+ } else {
227
+ $this->lexExpression();
228
+ }
229
+ }
230
+
231
+ protected function lexExpression()
232
+ {
233
+ // whitespace
234
+ if (preg_match('/\s+/A', $this->code, $match, null, $this->cursor)) {
235
+ $this->moveCursor($match[0]);
236
+
237
+ if ($this->cursor >= $this->end) {
238
+ throw new Twig_Error_Syntax(sprintf('Unclosed "%s".', $this->state === self::STATE_BLOCK ? 'block' : 'variable'), $this->currentVarBlockLine, $this->source);
239
+ }
240
+ }
241
+
242
+ // operators
243
+ if (preg_match($this->regexes['operator'], $this->code, $match, null, $this->cursor)) {
244
+ $this->pushToken(Twig_Token::OPERATOR_TYPE, preg_replace('/\s+/', ' ', $match[0]));
245
+ $this->moveCursor($match[0]);
246
+ }
247
+ // names
248
+ elseif (preg_match(self::REGEX_NAME, $this->code, $match, null, $this->cursor)) {
249
+ $this->pushToken(Twig_Token::NAME_TYPE, $match[0]);
250
+ $this->moveCursor($match[0]);
251
+ }
252
+ // numbers
253
+ elseif (preg_match(self::REGEX_NUMBER, $this->code, $match, null, $this->cursor)) {
254
+ $number = (float) $match[0]; // floats
255
+ if (ctype_digit($match[0]) && $number <= PHP_INT_MAX) {
256
+ $number = (int) $match[0]; // integers lower than the maximum
257
+ }
258
+ $this->pushToken(Twig_Token::NUMBER_TYPE, $number);
259
+ $this->moveCursor($match[0]);
260
+ }
261
+ // punctuation
262
+ elseif (false !== strpos(self::PUNCTUATION, $this->code[$this->cursor])) {
263
+ // opening bracket
264
+ if (false !== strpos('([{', $this->code[$this->cursor])) {
265
+ $this->brackets[] = array($this->code[$this->cursor], $this->lineno);
266
+ }
267
+ // closing bracket
268
+ elseif (false !== strpos(')]}', $this->code[$this->cursor])) {
269
+ if (empty($this->brackets)) {
270
+ throw new Twig_Error_Syntax(sprintf('Unexpected "%s".', $this->code[$this->cursor]), $this->lineno, $this->source);
271
+ }
272
+
273
+ list($expect, $lineno) = array_pop($this->brackets);
274
+ if ($this->code[$this->cursor] != strtr($expect, '([{', ')]}')) {
275
+ throw new Twig_Error_Syntax(sprintf('Unclosed "%s".', $expect), $lineno, $this->source);
276
+ }
277
+ }
278
+
279
+ $this->pushToken(Twig_Token::PUNCTUATION_TYPE, $this->code[$this->cursor]);
280
+ ++$this->cursor;
281
+ }
282
+ // strings
283
+ elseif (preg_match(self::REGEX_STRING, $this->code, $match, null, $this->cursor)) {
284
+ $this->pushToken(Twig_Token::STRING_TYPE, stripcslashes(substr($match[0], 1, -1)));
285
+ $this->moveCursor($match[0]);
286
+ }
287
+ // opening double quoted string
288
+ elseif (preg_match(self::REGEX_DQ_STRING_DELIM, $this->code, $match, null, $this->cursor)) {
289
+ $this->brackets[] = array('"', $this->lineno);
290
+ $this->pushState(self::STATE_STRING);
291
+ $this->moveCursor($match[0]);
292
+ }
293
+ // unlexable
294
+ else {
295
+ throw new Twig_Error_Syntax(sprintf('Unexpected character "%s".', $this->code[$this->cursor]), $this->lineno, $this->source);
296
+ }
297
+ }
298
+
299
+ protected function lexRawData($tag)
300
+ {
301
+ if ('raw' === $tag) {
302
+ @trigger_error(sprintf('Twig Tag "raw" is deprecated since version 1.21. Use "verbatim" instead in %s at line %d.', $this->filename, $this->lineno), E_USER_DEPRECATED);
303
+ }
304
+
305
+ if (!preg_match(str_replace('%s', $tag, $this->regexes['lex_raw_data']), $this->code, $match, PREG_OFFSET_CAPTURE, $this->cursor)) {
306
+ throw new Twig_Error_Syntax(sprintf('Unexpected end of file: Unclosed "%s" block.', $tag), $this->lineno, $this->source);
307
+ }
308
+
309
+ $text = substr($this->code, $this->cursor, $match[0][1] - $this->cursor);
310
+ $this->moveCursor($text.$match[0][0]);
311
+
312
+ if (false !== strpos($match[1][0], $this->options['whitespace_trim'])) {
313
+ $text = rtrim($text);
314
+ }
315
+
316
+ $this->pushToken(Twig_Token::TEXT_TYPE, $text);
317
+ }
318
+
319
+ protected function lexComment()
320
+ {
321
+ if (!preg_match($this->regexes['lex_comment'], $this->code, $match, PREG_OFFSET_CAPTURE, $this->cursor)) {
322
+ throw new Twig_Error_Syntax('Unclosed comment.', $this->lineno, $this->source);
323
+ }
324
+
325
+ $this->moveCursor(substr($this->code, $this->cursor, $match[0][1] - $this->cursor).$match[0][0]);
326
+ }
327
+
328
+ protected function lexString()
329
+ {
330
+ if (preg_match($this->regexes['interpolation_start'], $this->code, $match, null, $this->cursor)) {
331
+ $this->brackets[] = array($this->options['interpolation'][0], $this->lineno);
332
+ $this->pushToken(Twig_Token::INTERPOLATION_START_TYPE);
333
+ $this->moveCursor($match[0]);
334
+ $this->pushState(self::STATE_INTERPOLATION);
335
+ } elseif (preg_match(self::REGEX_DQ_STRING_PART, $this->code, $match, null, $this->cursor) && strlen($match[0]) > 0) {
336
+ $this->pushToken(Twig_Token::STRING_TYPE, stripcslashes($match[0]));
337
+ $this->moveCursor($match[0]);
338
+ } elseif (preg_match(self::REGEX_DQ_STRING_DELIM, $this->code, $match, null, $this->cursor)) {
339
+ list($expect, $lineno) = array_pop($this->brackets);
340
+ if ($this->code[$this->cursor] != '"') {
341
+ throw new Twig_Error_Syntax(sprintf('Unclosed "%s".', $expect), $lineno, $this->source);
342
+ }
343
+
344
+ $this->popState();
345
+ ++$this->cursor;
346
+ }
347
+ }
348
+
349
+ protected function lexInterpolation()
350
+ {
351
+ $bracket = end($this->brackets);
352
+ if ($this->options['interpolation'][0] === $bracket[0] && preg_match($this->regexes['interpolation_end'], $this->code, $match, null, $this->cursor)) {
353
+ array_pop($this->brackets);
354
+ $this->pushToken(Twig_Token::INTERPOLATION_END_TYPE);
355
+ $this->moveCursor($match[0]);
356
+ $this->popState();
357
+ } else {
358
+ $this->lexExpression();
359
+ }
360
+ }
361
+
362
+ protected function pushToken($type, $value = '')
363
+ {
364
+ // do not push empty text tokens
365
+ if (Twig_Token::TEXT_TYPE === $type && '' === $value) {
366
+ return;
367
+ }
368
+
369
+ $this->tokens[] = new Twig_Token($type, $value, $this->lineno);
370
+ }
371
+
372
+ protected function moveCursor($text)
373
+ {
374
+ $this->cursor += strlen($text);
375
+ $this->lineno += substr_count($text, "\n");
376
+ }
377
+
378
+ protected function getOperatorRegex()
379
+ {
380
+ $operators = array_merge(
381
+ array('='),
382
+ array_keys($this->env->getUnaryOperators()),
383
+ array_keys($this->env->getBinaryOperators())
384
+ );
385
+
386
+ $operators = array_combine($operators, array_map('strlen', $operators));
387
+ arsort($operators);
388
+
389
+ $regex = array();
390
+ foreach ($operators as $operator => $length) {
391
+ // an operator that ends with a character must be followed by
392
+ // a whitespace or a parenthesis
393
+ if (ctype_alpha($operator[$length - 1])) {
394
+ $r = preg_quote($operator, '/').'(?=[\s()])';
395
+ } else {
396
+ $r = preg_quote($operator, '/');
397
+ }
398
+
399
+ // an operator with a space can be any amount of whitespaces
400
+ $r = preg_replace('/\s+/', '\s+', $r);
401
+
402
+ $regex[] = $r;
403
+ }
404
+
405
+ return '/'.implode('|', $regex).'/A';
406
+ }
407
+
408
+ protected function pushState($state)
409
+ {
410
+ $this->states[] = $this->state;
411
+ $this->state = $state;
412
+ }
413
+
414
+ protected function popState()
415
+ {
416
+ if (0 === count($this->states)) {
417
+ throw new Exception('Cannot pop state without a previous state.');
418
+ }
419
+
420
+ $this->state = array_pop($this->states);
421
+ }
422
+ }
vendor/twig/twig/lib/Twig/LexerInterface.php ADDED
@@ -0,0 +1,32 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * This file is part of Twig.
5
+ *
6
+ * (c) Fabien Potencier
7
+ *
8
+ * For the full copyright and license information, please view the LICENSE
9
+ * file that was distributed with this source code.
10
+ */
11
+
12
+ /**
13
+ * Interface implemented by lexer classes.
14
+ *
15
+ * @author Fabien Potencier <fabien@symfony.com>
16
+ *
17
+ * @deprecated since 1.12 (to be removed in 3.0)
18
+ */
19
+ interface Twig_LexerInterface
20
+ {
21
+ /**
22
+ * Tokenizes a source code.
23
+ *
24
+ * @param string|Twig_Source $code The source code
25
+ * @param string $name A unique identifier for the source code
26
+ *
27
+ * @return Twig_TokenStream
28
+ *
29
+ * @throws Twig_Error_Syntax When the code is syntactically wrong
30
+ */
31
+ public function tokenize($code, $name = null);
32
+ }
vendor/twig/twig/lib/Twig/Loader/Array.php ADDED
@@ -0,0 +1,95 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * This file is part of Twig.
5
+ *
6
+ * (c) Fabien Potencier
7
+ *
8
+ * For the full copyright and license information, please view the LICENSE
9
+ * file that was distributed with this source code.
10
+ */
11
+
12
+ /**
13
+ * Loads a template from an array.
14
+ *
15
+ * When using this loader with a cache mechanism, you should know that a new cache
16
+ * key is generated each time a template content "changes" (the cache key being the
17
+ * source code of the template). If you don't want to see your cache grows out of
18
+ * control, you need to take care of clearing the old cache file by yourself.
19
+ *
20
+ * This loader should only be used for unit testing.
21
+ *
22
+ * @final
23
+ *
24
+ * @author Fabien Potencier <fabien@symfony.com>
25
+ */
26
+ class Twig_Loader_Array implements Twig_LoaderInterface, Twig_ExistsLoaderInterface, Twig_SourceContextLoaderInterface
27
+ {
28
+ protected $templates = array();
29
+
30
+ /**
31
+ * @param array $templates An array of templates (keys are the names, and values are the source code)
32
+ */
33
+ public function __construct(array $templates = array())
34
+ {
35
+ $this->templates = $templates;
36
+ }
37
+
38
+ /**
39
+ * Adds or overrides a template.
40
+ *
41
+ * @param string $name The template name
42
+ * @param string $template The template source
43
+ */
44
+ public function setTemplate($name, $template)
45
+ {
46
+ $this->templates[(string) $name] = $template;
47
+ }
48
+
49
+ public function getSource($name)
50
+ {
51
+ @trigger_error(sprintf('Calling "getSource" on "%s" is deprecated since 1.27. Use getSourceContext() instead.', get_class($this)), E_USER_DEPRECATED);
52
+
53
+ $name = (string) $name;
54
+ if (!isset($this->templates[$name])) {
55
+ throw new Twig_Error_Loader(sprintf('Template "%s" is not defined.', $name));
56
+ }
57
+
58
+ return $this->templates[$name];
59
+ }
60
+
61
+ public function getSourceContext($name)
62
+ {
63
+ $name = (string) $name;
64
+ if (!isset($this->templates[$name])) {
65
+ throw new Twig_Error_Loader(sprintf('Template "%s" is not defined.', $name));
66
+ }
67
+
68
+ return new Twig_Source($this->templates[$name], $name);
69
+ }
70
+
71
+ public function exists($name)
72
+ {
73
+ return isset($this->templates[(string) $name]);
74
+ }
75
+
76
+ public function getCacheKey($name)
77
+ {
78
+ $name = (string) $name;
79
+ if (!isset($this->templates[$name])) {
80
+ throw new Twig_Error_Loader(sprintf('Template "%s" is not defined.', $name));
81
+ }
82
+
83
+ return $this->templates[$name];
84
+ }
85
+
86
+ public function isFresh($name, $time)
87
+ {
88
+ $name = (string) $name;
89
+ if (!isset($this->templates[$name])) {
90
+ throw new Twig_Error_Loader(sprintf('Template "%s" is not defined.', $name));
91
+ }
92
+
93
+ return true;
94
+ }
95
+ }
vendor/twig/twig/lib/Twig/Loader/Chain.php ADDED
@@ -0,0 +1,149 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * This file is part of Twig.
5
+ *
6
+ * (c) Fabien Potencier
7
+ *
8
+ * For the full copyright and license information, please view the LICENSE
9
+ * file that was distributed with this source code.
10
+ */
11
+
12
+ /**
13
+ * Loads templates from other loaders.
14
+ *
15
+ * @final
16
+ *
17
+ * @author Fabien Potencier <fabien@symfony.com>
18
+ */
19
+ class Twig_Loader_Chain implements Twig_LoaderInterface, Twig_ExistsLoaderInterface, Twig_SourceContextLoaderInterface
20
+ {
21
+ private $hasSourceCache = array();
22
+ protected $loaders = array();
23
+
24
+ /**
25
+ * @param Twig_LoaderInterface[] $loaders
26
+ */
27
+ public function __construct(array $loaders = array())
28
+ {
29
+ foreach ($loaders as $loader) {
30
+ $this->addLoader($loader);
31
+ }
32
+ }
33
+
34
+ public function addLoader(Twig_LoaderInterface $loader)
35
+ {
36
+ $this->loaders[] = $loader;
37
+ $this->hasSourceCache = array();
38
+ }
39
+
40
+ public function getSource($name)
41
+ {
42
+ @trigger_error(sprintf('Calling "getSource" on "%s" is deprecated since 1.27. Use getSourceContext() instead.', get_class($this)), E_USER_DEPRECATED);
43
+
44
+ $exceptions = array();
45
+ foreach ($this->loaders as $loader) {
46
+ if ($loader instanceof Twig_ExistsLoaderInterface && !$loader->exists($name)) {
47
+ continue;
48
+ }
49
+
50
+ try {
51
+ return $loader->getSource($name);
52
+ } catch (Twig_Error_Loader $e) {
53
+ $exceptions[] = $e->getMessage();
54
+ }
55
+ }
56
+
57
+ throw new Twig_Error_Loader(sprintf('Template "%s" is not defined%s.', $name, $exceptions ? ' ('.implode(', ', $exceptions).')' : ''));
58
+ }
59
+
60
+ public function getSourceContext($name)
61
+ {
62
+ $exceptions = array();
63
+ foreach ($this->loaders as $loader) {
64
+ if ($loader instanceof Twig_ExistsLoaderInterface && !$loader->exists($name)) {
65
+ continue;
66
+ }
67
+
68
+ try {
69
+ if ($loader instanceof Twig_SourceContextLoaderInterface) {
70
+ return $loader->getSourceContext($name);
71
+ }
72
+
73
+ return new Twig_Source($loader->getSource($name), $name);
74
+ } catch (Twig_Error_Loader $e) {
75
+ $exceptions[] = $e->getMessage();
76
+ }
77
+ }
78
+
79
+ throw new Twig_Error_Loader(sprintf('Template "%s" is not defined%s.', $name, $exceptions ? ' ('.implode(', ', $exceptions).')' : ''));
80
+ }
81
+
82
+ public function exists($name)
83
+ {
84
+ $name = (string) $name;
85
+
86
+ if (isset($this->hasSourceCache[$name])) {
87
+ return $this->hasSourceCache[$name];
88
+ }
89
+
90
+ foreach ($this->loaders as $loader) {
91
+ if ($loader instanceof Twig_ExistsLoaderInterface) {
92
+ if ($loader->exists($name)) {
93
+ return $this->hasSourceCache[$name] = true;
94
+ }
95
+
96
+ continue;
97
+ }
98
+
99
+ try {
100
+ if ($loader instanceof Twig_SourceContextLoaderInterface) {
101
+ $loader->getSourceContext($name);
102
+ } else {
103
+ $loader->getSource($name);
104
+ }
105
+
106
+ return $this->hasSourceCache[$name] = true;
107
+ } catch (Twig_Error_Loader $e) {
108
+ }
109
+ }
110
+
111
+ return $this->hasSourceCache[$name] = false;
112
+ }
113
+
114
+ public function getCacheKey($name)
115
+ {
116
+ $exceptions = array();
117
+ foreach ($this->loaders as $loader) {
118
+ if ($loader instanceof Twig_ExistsLoaderInterface && !$loader->exists($name)) {
119
+ continue;
120
+ }
121
+
122
+ try {
123
+ return $loader->getCacheKey($name);
124
+ } catch (Twig_Error_Loader $e) {
125
+ $exceptions[] = get_class($loader).': '.$e->getMessage();
126
+ }
127
+ }
128
+
129
+ throw new Twig_Error_Loader(sprintf('Template "%s" is not defined%s.', $name, $exceptions ? ' ('.implode(', ', $exceptions).')' : ''));
130
+ }
131
+
132
+ public function isFresh($name, $time)
133
+ {
134
+ $exceptions = array();
135
+ foreach ($this->loaders as $loader) {
136
+ if ($loader instanceof Twig_ExistsLoaderInterface && !$loader->exists($name)) {
137
+ continue;
138
+ }
139
+
140
+ try {
141
+ return $loader->isFresh($name, $time);
142
+ } catch (Twig_Error_Loader $e) {
143
+ $exceptions[] = get_class($loader).': '.$e->getMessage();
144
+ }
145
+ }
146
+
147
+ throw new Twig_Error_Loader(sprintf('Template "%s" is not defined%s.', $name, $exceptions ? ' ('.implode(', ', $exceptions).')' : ''));
148
+ }
149
+ }
vendor/twig/twig/lib/Twig/Loader/Filesystem.php ADDED
@@ -0,0 +1,288 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * This file is part of Twig.
5
+ *
6
+ * (c) Fabien Potencier
7
+ *
8
+ * For the full copyright and license information, please view the LICENSE
9
+ * file that was distributed with this source code.
10
+ */
11
+
12
+ /**
13
+ * Loads template from the filesystem.
14
+ *
15
+ * @author Fabien Potencier <fabien@symfony.com>
16
+ */
17
+ class Twig_Loader_Filesystem implements Twig_LoaderInterface, Twig_ExistsLoaderInterface, Twig_SourceContextLoaderInterface
18
+ {
19
+ /** Identifier of the main namespace. */
20
+ const MAIN_NAMESPACE = '__main__';
21
+
22
+ protected $paths = array();
23
+ protected $cache = array();
24
+ protected $errorCache = array();
25
+
26
+ private $rootPath;
27
+
28
+ /**
29
+ * @param string|array $paths A path or an array of paths where to look for templates
30
+ * @param string|null $rootPath The root path common to all relative paths (null for getcwd())
31
+ */
32
+ public function __construct($paths = array(), $rootPath = null)
33
+ {
34
+ $this->rootPath = (null === $rootPath ? getcwd() : $rootPath).DIRECTORY_SEPARATOR;
35
+ if (false !== $realPath = realpath($rootPath)) {
36
+ $this->rootPath = $realPath.DIRECTORY_SEPARATOR;
37
+ }
38
+
39
+ if ($paths) {
40
+ $this->setPaths($paths);
41
+ }
42
+ }
43
+
44
+ /**
45
+ * Returns the paths to the templates.
46
+ *
47
+ * @param string $namespace A path namespace
48
+ *
49
+ * @return array The array of paths where to look for templates
50
+ */
51
+ public function getPaths($namespace = self::MAIN_NAMESPACE)
52
+ {
53
+ return isset($this->paths[$namespace]) ? $this->paths[$namespace] : array();
54
+ }
55
+
56
+ /**
57
+ * Returns the path namespaces.
58
+ *
59
+ * The main namespace is always defined.
60
+ *
61
+ * @return array The array of defined namespaces
62
+ */
63
+ public function getNamespaces()
64
+ {
65
+ return array_keys($this->paths);
66
+ }
67
+
68
+ /**
69
+ * Sets the paths where templates are stored.
70
+ *
71
+ * @param string|array $paths A path or an array of paths where to look for templates
72
+ * @param string $namespace A path namespace
73
+ */
74
+ public function setPaths($paths, $namespace = self::MAIN_NAMESPACE)
75
+ {
76
+ if (!is_array($paths)) {
77
+ $paths = array($paths);
78
+ }
79
+
80
+ $this->paths[$namespace] = array();
81
+ foreach ($paths as $path) {
82
+ $this->addPath($path, $namespace);
83
+ }
84
+ }
85
+
86
+ /**
87
+ * Adds a path where templates are stored.
88
+ *
89
+ * @param string $path A path where to look for templates
90
+ * @param string $namespace A path namespace
91
+ *
92
+ * @throws Twig_Error_Loader
93
+ */
94
+ public function addPath($path, $namespace = self::MAIN_NAMESPACE)
95
+ {
96
+ // invalidate the cache
97
+ $this->cache = $this->errorCache = array();
98
+
99
+ $checkPath = $this->isAbsolutePath($path) ? $path : $this->rootPath.$path;
100
+ if (!is_dir($checkPath)) {
101
+ throw new Twig_Error_Loader(sprintf('The "%s" directory does not exist ("%s").', $path, $checkPath));
102
+ }
103
+
104
+ $this->paths[$namespace][] = rtrim($path, '/\\');
105
+ }
106
+
107
+ /**
108
+ * Prepends a path where templates are stored.
109
+ *
110
+ * @param string $path A path where to look for templates
111
+ * @param string $namespace A path namespace
112
+ *
113
+ * @throws Twig_Error_Loader
114
+ */
115
+ public function prependPath($path, $namespace = self::MAIN_NAMESPACE)
116
+ {
117
+ // invalidate the cache
118
+ $this->cache = $this->errorCache = array();
119
+
120
+ $checkPath = $this->isAbsolutePath($path) ? $path : $this->rootPath.$path;
121
+ if (!is_dir($checkPath)) {
122
+ throw new Twig_Error_Loader(sprintf('The "%s" directory does not exist ("%s").', $path, $checkPath));
123
+ }
124
+
125
+ $path = rtrim($path, '/\\');
126
+
127
+ if (!isset($this->paths[$namespace])) {
128
+ $this->paths[$namespace][] = $path;
129
+ } else {
130
+ array_unshift($this->paths[$namespace], $path);
131
+ }
132
+ }
133
+
134
+ public function getSource($name)
135
+ {
136
+ @trigger_error(sprintf('Calling "getSource" on "%s" is deprecated since 1.27. Use getSourceContext() instead.', get_class($this)), E_USER_DEPRECATED);
137
+
138
+ return file_get_contents($this->findTemplate($name));
139
+ }
140
+
141
+ public function getSourceContext($name)
142
+ {
143
+ $path = $this->findTemplate($name);
144
+
145
+ return new Twig_Source(file_get_contents($path), $name, $path);
146
+ }
147
+
148
+ public function getCacheKey($name)
149
+ {
150
+ $path = $this->findTemplate($name);
151
+ $len = strlen($this->rootPath);
152
+ if (0 === strncmp($this->rootPath, $path, $len)) {
153
+ return substr($path, $len);
154
+ }
155
+
156
+ return $path;
157
+ }
158
+
159
+ public function exists($name)
160
+ {
161
+ $name = $this->normalizeName($name);
162
+
163
+ if (isset($this->cache[$name])) {
164
+ return true;
165
+ }
166
+
167
+ try {
168
+ return false !== $this->findTemplate($name, false);
169
+ } catch (Twig_Error_Loader $exception) {
170
+ @trigger_error(sprintf('In %s::findTemplate(), you must accept a second argument that when set to "false" returns "false" instead of throwing an exception. Not supporting this argument is deprecated since version 1.27.', get_class($this)), E_USER_DEPRECATED);
171
+
172
+ return false;
173
+ }
174
+ }
175
+
176
+ public function isFresh($name, $time)
177
+ {
178
+ return filemtime($this->findTemplate($name)) <= $time;
179
+ }
180
+
181
+ protected function findTemplate($name)
182
+ {
183
+ $throw = func_num_args() > 1 ? func_get_arg(1) : true;
184
+ $name = $this->normalizeName($name);
185
+
186
+ if (isset($this->cache[$name])) {
187
+ return $this->cache[$name];
188
+ }
189
+
190
+ if (isset($this->errorCache[$name])) {
191
+ if (!$throw) {
192
+ return false;
193
+ }
194
+
195
+ throw new Twig_Error_Loader($this->errorCache[$name]);
196
+ }
197
+
198
+ $this->validateName($name);
199
+
200
+ list($namespace, $shortname) = $this->parseName($name);
201
+
202
+ if (!isset($this->paths[$namespace])) {
203
+ $this->errorCache[$name] = sprintf('There are no registered paths for namespace "%s".', $namespace);
204
+
205
+ if (!$throw) {
206
+ return false;
207
+ }
208
+
209
+ throw new Twig_Error_Loader($this->errorCache[$name]);
210
+ }
211
+
212
+ foreach ($this->paths[$namespace] as $path) {
213
+ if (!$this->isAbsolutePath($path)) {
214
+ $path = $this->rootPath.'/'.$path;
215
+ }
216
+
217
+ if (is_file($path.'/'.$shortname)) {
218
+ if (false !== $realpath = realpath($path.'/'.$shortname)) {
219
+ return $this->cache[$name] = $realpath;
220
+ }
221
+
222
+ return $this->cache[$name] = $path.'/'.$shortname;
223
+ }
224
+ }
225
+
226
+ $this->errorCache[$name] = sprintf('Unable to find template "%s" (looked into: %s).', $name, implode(', ', $this->paths[$namespace]));
227
+
228
+ if (!$throw) {
229
+ return false;
230
+ }
231
+
232
+ throw new Twig_Error_Loader($this->errorCache[$name]);
233
+ }
234
+
235
+ protected function parseName($name, $default = self::MAIN_NAMESPACE)
236
+ {
237
+ if (isset($name[0]) && '@' == $name[0]) {
238
+ if (false === $pos = strpos($name, '/')) {
239
+ throw new Twig_Error_Loader(sprintf('Malformed namespaced template name "%s" (expecting "@namespace/template_name").', $name));
240
+ }
241
+
242
+ $namespace = substr($name, 1, $pos - 1);
243
+ $shortname = substr($name, $pos + 1);
244
+
245
+ return array($namespace, $shortname);
246
+ }
247
+
248
+ return array($default, $name);
249
+ }
250
+
251
+ protected function normalizeName($name)
252
+ {
253
+ return preg_replace('#/{2,}#', '/', str_replace('\\', '/', (string) $name));
254
+ }
255
+
256
+ protected function validateName($name)
257
+ {
258
+ if (false !== strpos($name, "\0")) {
259
+ throw new Twig_Error_Loader('A template name cannot contain NUL bytes.');
260
+ }
261
+
262
+ $name = ltrim($name, '/');
263
+ $parts = explode('/', $name);
264
+ $level = 0;
265
+ foreach ($parts as $part) {
266
+ if ('..' === $part) {
267
+ --$level;
268
+ } elseif ('.' !== $part) {
269
+ ++$level;
270
+ }
271
+
272
+ if ($level < 0) {
273
+ throw new Twig_Error_Loader(sprintf('Looks like you try to load a template outside configured directories (%s).', $name));
274
+ }
275
+ }
276
+ }
277
+
278
+ private function isAbsolutePath($file)
279
+ {
280
+ return strspn($file, '/\\', 0, 1)
281
+ || (strlen($file) > 3 && ctype_alpha($file[0])
282
+ && substr($file, 1, 1) === ':'
283
+ && strspn($file, '/\\', 2, 1)
284
+ )
285
+ || null !== parse_url($file, PHP_URL_SCHEME)
286
+ ;
287
+ }
288
+ }
vendor/twig/twig/lib/Twig/Loader/String.php ADDED
@@ -0,0 +1,58 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * This file is part of Twig.
5
+ *
6
+ * (c) Fabien Potencier
7
+ *
8
+ * For the full copyright and license information, please view the LICENSE
9
+ * file that was distributed with this source code.
10
+ */
11
+
12
+ @trigger_error('The Twig_Loader_String class is deprecated since version 1.18.1 and will be removed in 2.0. Use Twig_Loader_Array instead or Twig_Environment::createTemplate().', E_USER_DEPRECATED);
13
+
14
+ /**
15
+ * Loads a template from a string.
16
+ *
17
+ * This loader should NEVER be used. It only exists for Twig internal purposes.
18
+ *
19
+ * When using this loader with a cache mechanism, you should know that a new cache
20
+ * key is generated each time a template content "changes" (the cache key being the
21
+ * source code of the template). If you don't want to see your cache grows out of
22
+ * control, you need to take care of clearing the old cache file by yourself.
23
+ *
24
+ * @deprecated since 1.18.1 (to be removed in 2.0)
25
+ *
26
+ * @internal
27
+ *
28
+ * @author Fabien Potencier <fabien@symfony.com>
29
+ */
30
+ class Twig_Loader_String implements Twig_LoaderInterface, Twig_ExistsLoaderInterface, Twig_SourceContextLoaderInterface
31
+ {
32
+ public function getSource($name)
33
+ {
34
+ @trigger_error(sprintf('Calling "getSource" on "%s" is deprecated since 1.27. Use getSourceContext() instead.', get_class($this)), E_USER_DEPRECATED);
35
+
36
+ return $name;
37
+ }
38
+
39
+ public function getSourceContext($name)
40
+ {
41
+ return new Twig_Source($name, $name);
42
+ }
43
+
44
+ public function exists($name)
45
+ {
46
+ return true;
47
+ }
48
+
49
+ public function getCacheKey($name)
50
+ {
51
+ return $name;
52
+ }
53
+
54
+ public function isFresh($name, $time)
55
+ {
56
+ return true;
57
+ }
58
+ }
vendor/twig/twig/lib/Twig/LoaderInterface.php ADDED
@@ -0,0 +1,55 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * This file is part of Twig.
5
+ *
6
+ * (c) Fabien Potencier
7
+ *
8
+ * For the full copyright and license information, please view the LICENSE
9
+ * file that was distributed with this source code.
10
+ */
11
+
12
+ /**
13
+ * Interface all loaders must implement.
14
+ *
15
+ * @author Fabien Potencier <fabien@symfony.com>
16
+ */
17
+ interface Twig_LoaderInterface
18
+ {
19
+ /**
20
+ * Gets the source code of a template, given its name.
21
+ *
22
+ * @param string $name The name of the template to load
23
+ *
24
+ * @return string The template source code
25
+ *
26
+ * @throws Twig_Error_Loader When $name is not found
27
+ *
28
+ * @deprecated since 1.27 (to be removed in 2.0), implement Twig_SourceContextLoaderInterface
29
+ */
30
+ public function getSource($name);
31
+
32
+ /**
33
+ * Gets the cache key to use for the cache for a given template name.
34
+ *
35
+ * @param string $name The name of the template to load
36
+ *
37
+ * @return string The cache key
38
+ *
39
+ * @throws Twig_Error_Loader When $name is not found
40
+ */
41
+ public function getCacheKey($name);
42
+
43
+ /**
44
+ * Returns true if the template is still fresh.
45
+ *
46
+ * @param string $name The template name
47
+ * @param int $time Timestamp of the last modification time of the
48
+ * cached template
49
+ *
50
+ * @return bool true if the template is fresh, false otherwise
51
+ *
52
+ * @throws Twig_Error_Loader When $name is not found
53
+ */
54
+ public function isFresh($name, $time);
55
+ }
vendor/twig/twig/lib/Twig/Markup.php ADDED
@@ -0,0 +1,37 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * This file is part of Twig.
5
+ *
6
+ * (c) Fabien Potencier
7
+ *
8
+ * For the full copyright and license information, please view the LICENSE
9
+ * file that was distributed with this source code.
10
+ */
11
+
12
+ /**
13
+ * Marks a content as safe.
14
+ *
15
+ * @author Fabien Potencier <fabien@symfony.com>
16
+ */
17
+ class Twig_Markup implements Countable
18
+ {
19
+ protected $content;
20
+ protected $charset;
21
+
22
+ public function __construct($content, $charset)
23
+ {
24
+ $this->content = (string) $content;
25
+ $this->charset = $charset;
26
+ }
27
+
28
+ public function __toString()
29
+ {
30
+ return $this->content;
31
+ }
32
+
33
+ public function count()
34
+ {
35
+ return function_exists('mb_get_info') ? mb_strlen($this->content, $this->charset) : strlen($this->content);
36
+ }
37
+ }
vendor/twig/twig/lib/Twig/Node.php ADDED
@@ -0,0 +1,253 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * This file is part of Twig.
5
+ *
6
+ * (c) Fabien Potencier
7
+ * (c) Armin Ronacher
8
+ *
9
+ * For the full copyright and license information, please view the LICENSE
10
+ * file that was distributed with this source code.
11
+ */
12
+
13
+ /**
14
+ * Represents a node in the AST.
15
+ *
16
+ * @author Fabien Potencier <fabien@symfony.com>
17
+ */
18
+ class Twig_Node implements Twig_NodeInterface
19
+ {
20
+ protected $nodes;
21
+ protected $attributes;
22
+ protected $lineno;
23
+ protected $tag;
24
+
25
+ private $name;
26
+
27
+ /**
28
+ * Constructor.
29
+ *
30
+ * The nodes are automatically made available as properties ($this->node).
31
+ * The attributes are automatically made available as array items ($this['name']).
32
+ *
33
+ * @param array $nodes An array of named nodes
34
+ * @param array $attributes An array of attributes (should not be nodes)
35
+ * @param int $lineno The line number
36
+ * @param string $tag The tag name associated with the Node
37
+ */
38
+ public function __construct(array $nodes = array(), array $attributes = array(), $lineno = 0, $tag = null)
39
+ {
40
+ foreach ($nodes as $name => $node) {
41
+ if (!$node instanceof Twig_NodeInterface) {
42
+ @trigger_error(sprintf('Using "%s" for the value of node "%s" of "%s" is deprecated since version 1.25 and will be removed in 2.0.', is_object($node) ? get_class($node) : null === $node ? 'null' : gettype($node), $name, get_class($this)), E_USER_DEPRECATED);
43
+ }
44
+ }
45
+ $this->nodes = $nodes;
46
+ $this->attributes = $attributes;
47
+ $this->lineno = $lineno;
48
+ $this->tag = $tag;
49
+ }
50
+
51
+ public function __toString()
52
+ {
53
+ $attributes = array();
54
+ foreach ($this->attributes as $name => $value) {
55
+ $attributes[] = sprintf('%s: %s', $name, str_replace("\n", '', var_export($value, true)));
56
+ }
57
+
58
+ $repr = array(get_class($this).'('.implode(', ', $attributes));
59
+
60
+ if (count($this->nodes)) {
61
+ foreach ($this->nodes as $name => $node) {
62
+ $len = strlen($name) + 4;
63
+ $noderepr = array();
64
+ foreach (explode("\n", (string) $node) as $line) {
65
+ $noderepr[] = str_repeat(' ', $len).$line;
66
+ }
67
+
68
+ $repr[] = sprintf(' %s: %s', $name, ltrim(implode("\n", $noderepr)));
69
+ }
70
+
71
+ $repr[] = ')';
72
+ } else {
73
+ $repr[0] .= ')';
74
+ }
75
+
76
+ return implode("\n", $repr);
77
+ }
78
+
79
+ /**
80
+ * @deprecated since 1.16.1 (to be removed in 2.0)
81
+ */
82
+ public function toXml($asDom = false)
83
+ {
84
+ @trigger_error(sprintf('%s is deprecated since version 1.16.1 and will be removed in 2.0.', __METHOD__), E_USER_DEPRECATED);
85
+
86
+ $dom = new DOMDocument('1.0', 'UTF-8');
87
+ $dom->formatOutput = true;
88
+ $dom->appendChild($xml = $dom->createElement('twig'));
89
+
90
+ $xml->appendChild($node = $dom->createElement('node'));
91
+ $node->setAttribute('class', get_class($this));
92
+
93
+ foreach ($this->attributes as $name => $value) {
94
+ $node->appendChild($attribute = $dom->createElement('attribute'));
95
+ $attribute->setAttribute('name', $name);
96
+ $attribute->appendChild($dom->createTextNode($value));
97
+ }
98
+
99
+ foreach ($this->nodes as $name => $n) {
100
+ if (null === $n) {
101
+ continue;
102
+ }
103
+
104
+ $child = $n->toXml(true)->getElementsByTagName('node')->item(0);
105
+ $child = $dom->importNode($child, true);
106
+ $child->setAttribute('name', $name);
107
+
108
+ $node->appendChild($child);
109
+ }
110
+
111
+ return $asDom ? $dom : $dom->saveXML();
112
+ }
113
+
114
+ public function compile(Twig_Compiler $compiler)
115
+ {
116
+ foreach ($this->nodes as $node) {
117
+ $node->compile($compiler);
118
+ }
119
+ }
120
+
121
+ public function getTemplateLine()
122
+ {
123
+ return $this->lineno;
124
+ }
125
+
126
+ /**
127
+ * @deprecated since 1.27 (to be removed in 2.0)
128
+ */
129
+ public function getLine()
130
+ {
131
+ @trigger_error('The '.__METHOD__.' method is deprecated since version 1.27 and will be removed in 2.0. Use getTemplateLine() instead.', E_USER_DEPRECATED);
132
+
133
+ return $this->lineno;
134
+ }
135
+
136
+ public function getNodeTag()
137
+ {
138
+ return $this->tag;
139
+ }
140
+
141
+ /**
142
+ * @return bool
143
+ */
144
+ public function hasAttribute($name)
145
+ {
146
+ return array_key_exists($name, $this->attributes);
147
+ }
148
+
149
+ /**
150
+ * @return mixed
151
+ */
152
+ public function getAttribute($name)
153
+ {
154
+ if (!array_key_exists($name, $this->attributes)) {
155
+ throw new LogicException(sprintf('Attribute "%s" does not exist for Node "%s".', $name, get_class($this)));
156
+ }
157
+
158
+ return $this->attributes[$name];
159
+ }
160
+
161
+ /**
162
+ * @param string $name
163
+ * @param mixed $value
164
+ */
165
+ public function setAttribute($name, $value)
166
+ {
167
+ $this->attributes[$name] = $value;
168
+ }
169
+
170
+ public function removeAttribute($name)
171
+ {
172
+ unset($this->attributes[$name]);
173
+ }
174
+
175
+ /**
176
+ * @return bool
177
+ */
178
+ public function hasNode($name)
179
+ {
180
+ return array_key_exists($name, $this->nodes);
181
+ }
182
+
183
+ /**
184
+ * @return Twig_Node
185
+ */
186
+ public function getNode($name)
187
+ {
188
+ if (!array_key_exists($name, $this->nodes)) {
189
+ throw new LogicException(sprintf('Node "%s" does not exist for Node "%s".', $name, get_class($this)));
190
+ }
191
+
192
+ return $this->nodes[$name];
193
+ }
194
+
195
+ public function setNode($name, $node = null)
196
+ {
197
+ if (!$node instanceof Twig_NodeInterface) {
198
+ @trigger_error(sprintf('Using "%s" for the value of node "%s" of "%s" is deprecated since version 1.25 and will be removed in 2.0.', is_object($node) ? get_class($node) : null === $node ? 'null' : gettype($node), $name, get_class($this)), E_USER_DEPRECATED);
199
+ }
200
+
201
+ $this->nodes[$name] = $node;
202
+ }
203
+
204
+ public function removeNode($name)
205
+ {
206
+ unset($this->nodes[$name]);
207
+ }
208
+
209
+ public function count()
210
+ {
211
+ return count($this->nodes);
212
+ }
213
+
214
+ public function getIterator()
215
+ {
216
+ return new ArrayIterator($this->nodes);
217
+ }
218
+
219
+ public function setTemplateName($name)
220
+ {
221
+ $this->name = $name;
222
+ foreach ($this->nodes as $node) {
223
+ if (null !== $node) {
224
+ $node->setTemplateName($name);
225
+ }
226
+ }
227
+ }
228
+
229
+ public function getTemplateName()
230
+ {
231
+ return $this->name;
232
+ }
233
+
234
+ /**
235
+ * @deprecated since 1.27 (to be removed in 2.0)
236
+ */
237
+ public function setFilename($name)
238
+ {
239
+ @trigger_error('The '.__METHOD__.' method is deprecated since version 1.27 and will be removed in 2.0. Use setTemplateName() instead.', E_USER_DEPRECATED);
240
+
241
+ $this->setTemplateName($name);
242
+ }
243
+
244
+ /**
245
+ * @deprecated since 1.27 (to be removed in 2.0)
246
+ */
247
+ public function getFilename()
248
+ {
249
+ @trigger_error('The '.__METHOD__.' method is deprecated since version 1.27 and will be removed in 2.0. Use getTemplateName() instead.', E_USER_DEPRECATED);
250
+
251
+ return $this->name;
252
+ }
253
+ }
vendor/twig/twig/lib/Twig/Node/AutoEscape.php ADDED
@@ -0,0 +1,34 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * This file is part of Twig.
5
+ *
6
+ * (c) Fabien Potencier
7
+ *
8
+ * For the full copyright and license information, please view the LICENSE
9
+ * file that was distributed with this source code.
10
+ */
11
+
12
+ /**
13
+ * Represents an autoescape node.
14
+ *
15
+ * The value is the escaping strategy (can be html, js, ...)
16
+ *
17
+ * The true value is equivalent to html.
18
+ *
19
+ * If autoescaping is disabled, then the value is false.
20
+ *
21
+ * @author Fabien Potencier <fabien@symfony.com>
22
+ */
23
+ class Twig_Node_AutoEscape extends Twig_Node
24
+ {
25
+ public function __construct($value, Twig_NodeInterface $body, $lineno, $tag = 'autoescape')
26
+ {
27
+ parent::__construct(array('body' => $body), array('value' => $value), $lineno, $tag);
28
+ }
29
+
30
+ public function compile(Twig_Compiler $compiler)
31
+ {
32
+ $compiler->subcompile($this->getNode('body'));
33
+ }
34
+ }
vendor/twig/twig/lib/Twig/Node/Block.php ADDED
@@ -0,0 +1,39 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * This file is part of Twig.
5
+ *
6
+ * (c) Fabien Potencier
7
+ * (c) Armin Ronacher
8
+ *
9
+ * For the full copyright and license information, please view the LICENSE
10
+ * file that was distributed with this source code.
11
+ */
12
+
13
+ /**
14
+ * Represents a block node.
15
+ *
16
+ * @author Fabien Potencier <fabien@symfony.com>
17
+ */
18
+ class Twig_Node_Block extends Twig_Node
19
+ {
20
+ public function __construct($name, Twig_NodeInterface $body, $lineno, $tag = null)
21
+ {
22
+ parent::__construct(array('body' => $body), array('name' => $name), $lineno, $tag);
23
+ }
24
+
25
+ public function compile(Twig_Compiler $compiler)
26
+ {
27
+ $compiler
28
+ ->addDebugInfo($this)
29
+ ->write(sprintf("public function block_%s(\$context, array \$blocks = array())\n", $this->getAttribute('name')), "{\n")
30
+ ->indent()
31
+ ;
32
+
33
+ $compiler
34
+ ->subcompile($this->getNode('body'))
35
+ ->outdent()
36
+ ->write("}\n\n")
37
+ ;
38
+ }
39
+ }
vendor/twig/twig/lib/Twig/Node/BlockReference.php ADDED
@@ -0,0 +1,32 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * This file is part of Twig.
5
+ *
6
+ * (c) Fabien Potencier
7
+ * (c) Armin Ronacher
8
+ *
9
+ * For the full copyright and license information, please view the LICENSE
10
+ * file that was distributed with this source code.
11
+ */
12
+
13
+ /**
14
+ * Represents a block call node.
15
+ *
16
+ * @author Fabien Potencier <fabien@symfony.com>
17
+ */
18
+ class Twig_Node_BlockReference extends Twig_Node implements Twig_NodeOutputInterface
19
+ {
20
+ public function __construct($name, $lineno, $tag = null)
21
+ {
22
+ parent::__construct(array(), array('name' => $name), $lineno, $tag);
23
+ }
24
+
25
+ public function compile(Twig_Compiler $compiler)
26
+ {
27
+ $compiler
28
+ ->addDebugInfo($this)
29
+ ->write(sprintf("\$this->displayBlock('%s', \$context, \$blocks);\n", $this->getAttribute('name')))
30
+ ;
31
+ }
32
+ }
vendor/twig/twig/lib/Twig/Node/Body.php ADDED
@@ -0,0 +1,19 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * This file is part of Twig.
5
+ *
6
+ * (c) Fabien Potencier
7
+ *
8
+ * For the full copyright and license information, please view the LICENSE
9
+ * file that was distributed with this source code.
10
+ */
11
+
12
+ /**
13
+ * Represents a body node.
14
+ *
15
+ * @author Fabien Potencier <fabien@symfony.com>
16
+ */
17
+ class Twig_Node_Body extends Twig_Node
18
+ {
19
+ }
vendor/twig/twig/lib/Twig/Node/CheckSecurity.php ADDED
@@ -0,0 +1,78 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * This file is part of Twig.
5
+ *
6
+ * (c) Fabien Potencier
7
+ *
8
+ * For the full copyright and license information, please view the LICENSE
9
+ * file that was distributed with this source code.
10
+ */
11
+
12
+ /**
13
+ * @author Fabien Potencier <fabien@symfony.com>
14
+ */
15
+ class Twig_Node_CheckSecurity extends Twig_Node
16
+ {
17
+ protected $usedFilters;
18
+ protected $usedTags;
19
+ protected $usedFunctions;
20
+
21
+ public function __construct(array $usedFilters, array $usedTags, array $usedFunctions)
22
+ {
23
+ $this->usedFilters = $usedFilters;
24
+ $this->usedTags = $usedTags;
25
+ $this->usedFunctions = $usedFunctions;
26
+
27
+ parent::__construct();
28
+ }
29
+
30
+ public function compile(Twig_Compiler $compiler)
31
+ {
32
+ $tags = $filters = $functions = array();
33
+ foreach (array('tags', 'filters', 'functions') as $type) {
34
+ foreach ($this->{'used'.ucfirst($type)} as $name => $node) {
35
+ if ($node instanceof Twig_Node) {
36
+ ${$type}[$name] = $node->getTemplateLine();
37
+ } else {
38
+ ${$type}[$node] = null;
39
+ }
40
+ }
41
+ }
42
+
43
+ $compiler
44
+ ->write('$tags = ')->repr(array_filter($tags))->raw(";\n")
45
+ ->write('$filters = ')->repr(array_filter($filters))->raw(";\n")
46
+ ->write('$functions = ')->repr(array_filter($functions))->raw(";\n\n")
47
+ ->write("try {\n")
48
+ ->indent()
49
+ ->write("\$this->env->getExtension('Twig_Extension_Sandbox')->checkSecurity(\n")
50
+ ->indent()
51
+ ->write(!$tags ? "array(),\n" : "array('".implode("', '", array_keys($tags))."'),\n")
52
+ ->write(!$filters ? "array(),\n" : "array('".implode("', '", array_keys($filters))."'),\n")
53
+ ->write(!$functions ? "array()\n" : "array('".implode("', '", array_keys($functions))."')\n")
54
+ ->outdent()
55
+ ->write(");\n")
56
+ ->outdent()
57
+ ->write("} catch (Twig_Sandbox_SecurityError \$e) {\n")
58
+ ->indent()
59
+ ->write("\$e->setSourceContext(\$this->getSourceContext());\n\n")
60
+ ->write("if (\$e instanceof Twig_Sandbox_SecurityNotAllowedTagError && isset(\$tags[\$e->getTagName()])) {\n")
61
+ ->indent()
62
+ ->write("\$e->setTemplateLine(\$tags[\$e->getTagName()]);\n")
63
+ ->outdent()
64
+ ->write("} elseif (\$e instanceof Twig_Sandbox_SecurityNotAllowedFilterError && isset(\$filters[\$e->getFilterName()])) {\n")
65
+ ->indent()
66
+ ->write("\$e->setTemplateLine(\$filters[\$e->getFilterName()]);\n")
67
+ ->outdent()
68
+ ->write("} elseif (\$e instanceof Twig_Sandbox_SecurityNotAllowedFunctionError && isset(\$functions[\$e->getFunctionName()])) {\n")
69
+ ->indent()
70
+ ->write("\$e->setTemplateLine(\$functions[\$e->getFunctionName()]);\n")
71
+ ->outdent()
72
+ ->write("}\n\n")
73
+ ->write("throw \$e;\n")
74
+ ->outdent()
75
+ ->write("}\n\n")
76
+ ;
77
+ }
78
+ }
vendor/twig/twig/lib/Twig/Node/Do.php ADDED
@@ -0,0 +1,33 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * This file is part of Twig.
5
+ *
6
+ * (c) Fabien Potencier
7
+ *
8
+ * For the full copyright and license information, please view the LICENSE
9
+ * file that was distributed with this source code.
10
+ */
11
+
12
+ /**
13
+ * Represents a do node.
14
+ *
15
+ * @author Fabien Potencier <fabien@symfony.com>
16
+ */
17
+ class Twig_Node_Do extends Twig_Node
18
+ {
19
+ public function __construct(Twig_Node_Expression $expr, $lineno, $tag = null)
20
+ {
21
+ parent::__construct(array('expr' => $expr), array(), $lineno, $tag);
22
+ }
23
+
24
+ public function compile(Twig_Compiler $compiler)
25
+ {
26
+ $compiler
27
+ ->addDebugInfo($this)
28
+ ->write('')
29
+ ->subcompile($this->getNode('expr'))
30
+ ->raw(";\n")
31
+ ;
32
+ }
33
+ }
vendor/twig/twig/lib/Twig/Node/Embed.php ADDED
@@ -0,0 +1,44 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * This file is part of Twig.
5
+ *
6
+ * (c) Fabien Potencier
7
+ *
8
+ * For the full copyright and license information, please view the LICENSE
9
+ * file that was distributed with this source code.
10
+ */
11
+
12
+ /**
13
+ * Represents an embed node.
14
+ *
15
+ * @author Fabien Potencier <fabien@symfony.com>
16
+ */
17
+ class Twig_Node_Embed extends Twig_Node_Include
18
+ {
19
+ // we don't inject the module to avoid node visitors to traverse it twice (as it will be already visited in the main module)
20
+ public function __construct($name, $index, Twig_Node_Expression $variables = null, $only = false, $ignoreMissing = false, $lineno, $tag = null)
21
+ {
22
+ parent::__construct(new Twig_Node_Expression_Constant('not_used', $lineno), $variables, $only, $ignoreMissing, $lineno, $tag);
23
+
24
+ $this->setAttribute('name', $name);
25
+ // to be removed in 2.0, used name instead
26
+ $this->setAttribute('filename', $name);
27
+ $this->setAttribute('index', $index);
28
+ }
29
+
30
+ protected function addGetTemplate(Twig_Compiler $compiler)
31
+ {
32
+ $compiler
33
+ ->write('$this->loadTemplate(')
34
+ ->string($this->getAttribute('name'))
35
+ ->raw(', ')
36
+ ->repr($this->getTemplateName())
37
+ ->raw(', ')
38
+ ->repr($this->getTemplateLine())
39
+ ->raw(', ')
40
+ ->string($this->getAttribute('index'))
41
+ ->raw(')')
42
+ ;
43
+ }
44
+ }
vendor/twig/twig/lib/Twig/Node/Expression.php ADDED
@@ -0,0 +1,20 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * This file is part of Twig.
5
+ *
6
+ * (c) Fabien Potencier
7
+ * (c) Armin Ronacher
8
+ *
9
+ * For the full copyright and license information, please view the LICENSE
10
+ * file that was distributed with this source code.
11
+ */
12
+
13
+ /**
14
+ * Abstract class for all nodes that represents an expression.
15
+ *
16
+ * @author Fabien Potencier <fabien@symfony.com>
17
+ */
18
+ abstract class Twig_Node_Expression extends Twig_Node
19
+ {
20
+ }
vendor/twig/twig/lib/Twig/Node/Expression/Array.php ADDED
@@ -0,0 +1,81 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * This file is part of Twig.
5
+ *
6
+ * (c) Fabien Potencier
7
+ *
8
+ * For the full copyright and license information, please view the LICENSE
9
+ * file that was distributed with this source code.
10
+ */
11
+ class Twig_Node_Expression_Array extends Twig_Node_Expression
12
+ {
13
+ protected $index;
14
+
15
+ public function __construct(array $elements, $lineno)
16
+ {
17
+ parent::__construct($elements, array(), $lineno);
18
+
19
+ $this->index = -1;
20
+ foreach ($this->getKeyValuePairs() as $pair) {
21
+ if ($pair['key'] instanceof Twig_Node_Expression_Constant && ctype_digit((string) $pair['key']->getAttribute('value')) && $pair['key']->getAttribute('value') > $this->index) {
22
+ $this->index = $pair['key']->getAttribute('value');
23
+ }
24
+ }
25
+ }
26
+
27
+ public function getKeyValuePairs()
28
+ {
29
+ $pairs = array();
30
+
31
+ foreach (array_chunk($this->nodes, 2) as $pair) {
32
+ $pairs[] = array(
33
+ 'key' => $pair[0],
34
+ 'value' => $pair[1],
35
+ );
36
+ }
37
+
38
+ return $pairs;
39
+ }
40
+
41
+ public function hasElement(Twig_Node_Expression $key)
42
+ {
43
+ foreach ($this->getKeyValuePairs() as $pair) {
44
+ // we compare the string representation of the keys
45
+ // to avoid comparing the line numbers which are not relevant here.
46
+ if ((string) $key === (string) $pair['key']) {
47
+ return true;
48
+ }
49
+ }
50
+
51
+ return false;
52
+ }
53
+
54
+ public function addElement(Twig_Node_Expression $value, Twig_Node_Expression $key = null)
55
+ {
56
+ if (null === $key) {
57
+ $key = new Twig_Node_Expression_Constant(++$this->index, $value->getTemplateLine());
58
+ }
59
+
60
+ array_push($this->nodes, $key, $value);
61
+ }
62
+
63
+ public function compile(Twig_Compiler $compiler)
64
+ {
65
+ $compiler->raw('array(');
66
+ $first = true;
67
+ foreach ($this->getKeyValuePairs() as $pair) {
68
+ if (!$first) {
69
+ $compiler->raw(', ');
70
+ }
71
+ $first = false;
72
+
73
+ $compiler
74
+ ->subcompile($pair['key'])
75
+ ->raw(' => ')
76
+ ->subcompile($pair['value'])
77
+ ;
78
+ }
79
+ $compiler->raw(')');
80
+ }
81
+ }
vendor/twig/twig/lib/Twig/Node/Expression/AssignName.php ADDED
@@ -0,0 +1,23 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * This file is part of Twig.
5
+ *
6
+ * (c) Fabien Potencier
7
+ * (c) Armin Ronacher
8
+ *
9
+ * For the full copyright and license information, please view the LICENSE
10
+ * file that was distributed with this source code.
11
+ */
12
+
13
+ class Twig_Node_Expression_AssignName extends Twig_Node_Expression_Name
14
+ {
15
+ public function compile(Twig_Compiler $compiler)
16
+ {
17
+ $compiler
18
+ ->raw('$context[')
19
+ ->string($this->getAttribute('name'))
20
+ ->raw(']')
21
+ ;
22
+ }
23
+ }
vendor/twig/twig/lib/Twig/Node/Expression/Binary.php ADDED
@@ -0,0 +1,35 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * This file is part of Twig.
5
+ *
6
+ * (c) Fabien Potencier
7
+ * (c) Armin Ronacher
8
+ *
9
+ * For the full copyright and license information, please view the LICENSE
10
+ * file that was distributed with this source code.
11
+ */
12
+ abstract class Twig_Node_Expression_Binary extends Twig_Node_Expression
13
+ {
14
+ public function __construct(Twig_NodeInterface $left, Twig_NodeInterface $right, $lineno)
15
+ {
16
+ parent::__construct(array('left' => $left, 'right' => $right), array(), $lineno);
17
+ }
18
+
19
+ public function compile(Twig_Compiler $compiler)
20
+ {
21
+ $compiler
22
+ ->raw('(')
23
+ ->subcompile($this->getNode('left'))
24
+ ->raw(' ')
25
+ ;
26
+ $this->operator($compiler);
27
+ $compiler
28
+ ->raw(' ')
29
+ ->subcompile($this->getNode('right'))
30
+ ->raw(')')
31
+ ;
32
+ }
33
+
34
+ abstract public function operator(Twig_Compiler $compiler);
35
+ }
vendor/twig/twig/lib/Twig/Node/Expression/Binary/Add.php ADDED
@@ -0,0 +1,18 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * This file is part of Twig.
5
+ *
6
+ * (c) Fabien Potencier
7
+ * (c) Armin Ronacher
8
+ *
9
+ * For the full copyright and license information, please view the LICENSE
10
+ * file that was distributed with this source code.
11
+ */
12
+ class Twig_Node_Expression_Binary_Add extends Twig_Node_Expression_Binary
13
+ {
14
+ public function operator(Twig_Compiler $compiler)
15
+ {
16
+ return $compiler->raw('+');
17
+ }
18
+ }
vendor/twig/twig/lib/Twig/Node/Expression/Binary/And.php ADDED
@@ -0,0 +1,18 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * This file is part of Twig.
5
+ *
6
+ * (c) Fabien Potencier
7
+ * (c) Armin Ronacher
8
+ *
9
+ * For the full copyright and license information, please view the LICENSE
10
+ * file that was distributed with this source code.
11
+ */
12
+ class Twig_Node_Expression_Binary_And extends Twig_Node_Expression_Binary
13
+ {
14
+ public function operator(Twig_Compiler $compiler)
15
+ {
16
+ return $compiler->raw('&&');
17
+ }
18
+ }
vendor/twig/twig/lib/Twig/Node/Expression/Binary/BitwiseAnd.php ADDED
@@ -0,0 +1,18 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * This file is part of Twig.
5
+ *
6
+ * (c) Fabien Potencier
7
+ * (c) Armin Ronacher
8
+ *
9
+ * For the full copyright and license information, please view the LICENSE
10
+ * file that was distributed with this source code.
11
+ */
12
+ class Twig_Node_Expression_Binary_BitwiseAnd extends Twig_Node_Expression_Binary
13
+ {
14
+ public function operator(Twig_Compiler $compiler)
15
+ {
16
+ return $compiler->raw('&');
17
+ }
18
+ }
vendor/twig/twig/lib/Twig/Node/Expression/Binary/BitwiseOr.php ADDED
@@ -0,0 +1,18 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * This file is part of Twig.
5
+ *
6
+ * (c) Fabien Potencier
7
+ * (c) Armin Ronacher
8
+ *
9
+ * For the full copyright and license information, please view the LICENSE
10
+ * file that was distributed with this source code.
11
+ */
12
+ class Twig_Node_Expression_Binary_BitwiseOr extends Twig_Node_Expression_Binary
13
+ {
14
+ public function operator(Twig_Compiler $compiler)
15
+ {
16
+ return $compiler->raw('|');
17
+ }
18
+ }
vendor/twig/twig/lib/Twig/Node/Expression/Binary/BitwiseXor.php ADDED
@@ -0,0 +1,18 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * This file is part of Twig.
5
+ *
6
+ * (c) Fabien Potencier
7
+ * (c) Armin Ronacher
8
+ *
9
+ * For the full copyright and license information, please view the LICENSE
10
+ * file that was distributed with this source code.
11
+ */
12
+ class Twig_Node_Expression_Binary_BitwiseXor extends Twig_Node_Expression_Binary
13
+ {
14
+ public function operator(Twig_Compiler $compiler)
15
+ {
16
+ return $compiler->raw('^');
17
+ }
18
+ }
vendor/twig/twig/lib/Twig/Node/Expression/Binary/Concat.php ADDED
@@ -0,0 +1,18 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * This file is part of Twig.
5
+ *
6
+ * (c) Fabien Potencier
7
+ * (c) Armin Ronacher
8
+ *
9
+ * For the full copyright and license information, please view the LICENSE
10
+ * file that was distributed with this source code.
11
+ */
12
+ class Twig_Node_Expression_Binary_Concat extends Twig_Node_Expression_Binary
13
+ {
14
+ public function operator(Twig_Compiler $compiler)
15
+ {
16
+ return $compiler->raw('.');
17
+ }
18
+ }
vendor/twig/twig/lib/Twig/Node/Expression/Binary/Div.php ADDED
@@ -0,0 +1,18 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * This file is part of Twig.
5
+ *
6
+ * (c) Fabien Potencier
7
+ * (c) Armin Ronacher
8
+ *
9
+ * For the full copyright and license information, please view the LICENSE
10
+ * file that was distributed with this source code.
11
+ */
12
+ class Twig_Node_Expression_Binary_Div extends Twig_Node_Expression_Binary
13
+ {
14
+ public function operator(Twig_Compiler $compiler)
15
+ {
16
+ return $compiler->raw('/');
17
+ }
18
+ }
vendor/twig/twig/lib/Twig/Node/Expression/Binary/EndsWith.php ADDED
@@ -0,0 +1,30 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * This file is part of Twig.
5
+ *
6
+ * (c) Fabien Potencier
7
+ *
8
+ * For the full copyright and license information, please view the LICENSE
9
+ * file that was distributed with this source code.
10
+ */
11
+ class Twig_Node_Expression_Binary_EndsWith extends Twig_Node_Expression_Binary
12
+ {
13
+ public function compile(Twig_Compiler $compiler)
14
+ {
15
+ $left = $compiler->getVarName();
16
+ $right = $compiler->getVarName();
17
+ $compiler
18
+ ->raw(sprintf('(is_string($%s = ', $left))
19
+ ->subcompile($this->getNode('left'))
20
+ ->raw(sprintf(') && is_string($%s = ', $right))
21
+ ->subcompile($this->getNode('right'))
22
+ ->raw(sprintf(') && (\'\' === $%2$s || $%2$s === substr($%1$s, -strlen($%2$s))))', $left, $right))
23
+ ;
24
+ }
25
+
26
+ public function operator(Twig_Compiler $compiler)
27
+ {
28
+ return $compiler->raw('');
29
+ }
30
+ }
vendor/twig/twig/lib/Twig/Node/Expression/Binary/Equal.php ADDED
@@ -0,0 +1,17 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * This file is part of Twig.
5
+ *
6
+ * (c) Fabien Potencier
7
+ *
8
+ * For the full copyright and license information, please view the LICENSE
9
+ * file that was distributed with this source code.
10
+ */
11
+ class Twig_Node_Expression_Binary_Equal extends Twig_Node_Expression_Binary
12
+ {
13
+ public function operator(Twig_Compiler $compiler)
14
+ {
15
+ return $compiler->raw('==');
16
+ }
17
+ }
vendor/twig/twig/lib/Twig/Node/Expression/Binary/FloorDiv.php ADDED
@@ -0,0 +1,24 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * This file is part of Twig.
5
+ *
6
+ * (c) Fabien Potencier
7
+ *
8
+ * For the full copyright and license information, please view the LICENSE
9
+ * file that was distributed with this source code.
10
+ */
11
+ class Twig_Node_Expression_Binary_FloorDiv extends Twig_Node_Expression_Binary
12
+ {
13
+ public function compile(Twig_Compiler $compiler)
14
+ {
15
+ $compiler->raw('(int) floor(');
16
+ parent::compile($compiler);
17
+ $compiler->raw(')');
18
+ }
19
+
20
+ public function operator(Twig_Compiler $compiler)
21
+ {
22
+ return $compiler->raw('/');
23
+ }
24
+ }
vendor/twig/twig/lib/Twig/Node/Expression/Binary/Greater.php ADDED
@@ -0,0 +1,17 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * This file is part of Twig.
5
+ *
6
+ * (c) Fabien Potencier
7
+ *
8
+ * For the full copyright and license information, please view the LICENSE
9
+ * file that was distributed with this source code.
10
+ */
11
+ class Twig_Node_Expression_Binary_Greater extends Twig_Node_Expression_Binary
12
+ {
13
+ public function operator(Twig_Compiler $compiler)
14
+ {
15
+ return $compiler->raw('>');
16
+ }
17
+ }
vendor/twig/twig/lib/Twig/Node/Expression/Binary/GreaterEqual.php ADDED
@@ -0,0 +1,17 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * This file is part of Twig.
5
+ *
6
+ * (c) Fabien Potencier
7
+ *
8
+ * For the full copyright and license information, please view the LICENSE
9
+ * file that was distributed with this source code.
10
+ */
11
+ class Twig_Node_Expression_Binary_GreaterEqual extends Twig_Node_Expression_Binary
12
+ {
13
+ public function operator(Twig_Compiler $compiler)
14
+ {
15
+ return $compiler->raw('>=');
16
+ }
17
+ }
vendor/twig/twig/lib/Twig/Node/Expression/Binary/In.php ADDED
@@ -0,0 +1,28 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * This file is part of Twig.
5
+ *
6
+ * (c) Fabien Potencier
7
+ *
8
+ * For the full copyright and license information, please view the LICENSE
9
+ * file that was distributed with this source code.
10
+ */
11
+ class Twig_Node_Expression_Binary_In extends Twig_Node_Expression_Binary
12
+ {
13
+ public function compile(Twig_Compiler $compiler)
14
+ {
15
+ $compiler
16
+ ->raw('twig_in_filter(')
17
+ ->subcompile($this->getNode('left'))
18
+ ->raw(', ')
19
+ ->subcompile($this->getNode('right'))
20
+ ->raw(')')
21
+ ;
22
+ }
23
+
24
+ public function operator(Twig_Compiler $compiler)
25
+ {
26
+ return $compiler->raw('in');
27
+ }
28
+ }
vendor/twig/twig/lib/Twig/Node/Expression/Binary/Less.php ADDED
@@ -0,0 +1,17 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * This file is part of Twig.
5
+ *
6
+ * (c) Fabien Potencier
7
+ *
8
+ * For the full copyright and license information, please view the LICENSE
9
+ * file that was distributed with this source code.
10
+ */
11
+ class Twig_Node_Expression_Binary_Less extends Twig_Node_Expression_Binary
12
+ {
13
+ public function operator(Twig_Compiler $compiler)
14
+ {
15
+ return $compiler->raw('<');
16
+ }
17
+ }
vendor/twig/twig/lib/Twig/Node/Expression/Binary/LessEqual.php ADDED
@@ -0,0 +1,17 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * This file is part of Twig.
5
+ *
6
+ * (c) Fabien Potencier
7
+ *
8
+ * For the full copyright and license information, please view the LICENSE
9
+ * file that was distributed with this source code.
10
+ */
11
+ class Twig_Node_Expression_Binary_LessEqual extends Twig_Node_Expression_Binary
12
+ {
13
+ public function operator(Twig_Compiler $compiler)
14
+ {
15
+ return $compiler->raw('<=');
16
+ }
17
+ }
vendor/twig/twig/lib/Twig/Node/Expression/Binary/Matches.php ADDED
@@ -0,0 +1,28 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * This file is part of Twig.
5
+ *
6
+ * (c) Fabien Potencier
7
+ *
8
+ * For the full copyright and license information, please view the LICENSE
9
+ * file that was distributed with this source code.
10
+ */
11
+ class Twig_Node_Expression_Binary_Matches extends Twig_Node_Expression_Binary
12
+ {
13
+ public function compile(Twig_Compiler $compiler)
14
+ {
15
+ $compiler
16
+ ->raw('preg_match(')
17
+ ->subcompile($this->getNode('right'))
18
+ ->raw(', ')
19
+ ->subcompile($this->getNode('left'))
20
+ ->raw(')')
21
+ ;
22
+ }
23
+
24
+ public function operator(Twig_Compiler $compiler)
25
+ {
26
+ return $compiler->raw('');
27
+ }
28
+ }
vendor/twig/twig/lib/Twig/Node/Expression/Binary/Mod.php ADDED
@@ -0,0 +1,18 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * This file is part of Twig.
5
+ *
6
+ * (c) Fabien Potencier
7
+ * (c) Armin Ronacher
8
+ *
9
+ * For the full copyright and license information, please view the LICENSE
10
+ * file that was distributed with this source code.
11
+ */
12
+ class Twig_Node_Expression_Binary_Mod extends Twig_Node_Expression_Binary
13
+ {
14
+ public function operator(Twig_Compiler $compiler)
15
+ {
16
+ return $compiler->raw('%');
17
+ }
18
+ }
vendor/twig/twig/lib/Twig/Node/Expression/Binary/Mul.php ADDED
@@ -0,0 +1,18 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * This file is part of Twig.
5
+ *
6
+ * (c) Fabien Potencier
7
+ * (c) Armin Ronacher
8
+ *
9
+ * For the full copyright and license information, please view the LICENSE
10
+ * file that was distributed with this source code.
11
+ */
12
+ class Twig_Node_Expression_Binary_Mul extends Twig_Node_Expression_Binary
13
+ {
14
+ public function operator(Twig_Compiler $compiler)
15
+ {
16
+ return $compiler->raw('*');
17
+ }
18
+ }
vendor/twig/twig/lib/Twig/Node/Expression/Binary/NotEqual.php ADDED
@@ -0,0 +1,17 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * This file is part of Twig.
5
+ *
6
+ * (c) Fabien Potencier
7
+ *
8
+ * For the full copyright and license information, please view the LICENSE
9
+ * file that was distributed with this source code.
10
+ */
11
+ class Twig_Node_Expression_Binary_NotEqual extends Twig_Node_Expression_Binary
12
+ {
13
+ public function operator(Twig_Compiler $compiler)
14
+ {
15
+ return $compiler->raw('!=');
16
+ }
17
+ }
vendor/twig/twig/lib/Twig/Node/Expression/Binary/NotIn.php ADDED
@@ -0,0 +1,28 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * This file is part of Twig.
5
+ *
6
+ * (c) Fabien Potencier
7
+ *
8
+ * For the full copyright and license information, please view the LICENSE
9
+ * file that was distributed with this source code.
10
+ */
11
+ class Twig_Node_Expression_Binary_NotIn extends Twig_Node_Expression_Binary
12
+ {
13
+ public function compile(Twig_Compiler $compiler)
14
+ {
15
+ $compiler
16
+ ->raw('!twig_in_filter(')
17
+ ->subcompile($this->getNode('left'))
18
+ ->raw(', ')
19
+ ->subcompile($this->getNode('right'))
20
+ ->raw(')')
21
+ ;
22
+ }
23
+
24
+ public function operator(Twig_Compiler $compiler)
25
+ {
26
+ return $compiler->raw('not in');
27
+ }
28
+ }
vendor/twig/twig/lib/Twig/Node/Expression/Binary/Or.php ADDED
@@ -0,0 +1,18 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * This file is part of Twig.
5
+ *
6
+ * (c) Fabien Potencier
7
+ * (c) Armin Ronacher
8
+ *
9
+ * For the full copyright and license information, please view the LICENSE
10
+ * file that was distributed with this source code.
11
+ */
12
+ class Twig_Node_Expression_Binary_Or extends Twig_Node_Expression_Binary
13
+ {
14
+ public function operator(Twig_Compiler $compiler)
15
+ {
16
+ return $compiler->raw('||');
17
+ }
18
+ }
vendor/twig/twig/lib/Twig/Node/Expression/Binary/Power.php ADDED
@@ -0,0 +1,32 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * This file is part of Twig.
5
+ *
6
+ * (c) Fabien Potencier
7
+ *
8
+ * For the full copyright and license information, please view the LICENSE
9
+ * file that was distributed with this source code.
10
+ */
11
+ class Twig_Node_Expression_Binary_Power extends Twig_Node_Expression_Binary
12
+ {
13
+ public function compile(Twig_Compiler $compiler)
14
+ {
15
+ if (PHP_VERSION_ID >= 50600) {
16
+ return parent::compile($compiler);
17
+ }
18
+
19
+ $compiler
20
+ ->raw('pow(')
21
+ ->subcompile($this->getNode('left'))
22
+ ->raw(', ')
23
+ ->subcompile($this->getNode('right'))
24
+ ->raw(')')
25
+ ;
26
+ }
27
+
28
+ public function operator(Twig_Compiler $compiler)
29
+ {
30
+ return $compiler->raw('**');
31
+ }
32
+ }
vendor/twig/twig/lib/Twig/Node/Expression/Binary/Range.php ADDED
@@ -0,0 +1,28 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * This file is part of Twig.
5
+ *
6
+ * (c) Fabien Potencier
7
+ *
8
+ * For the full copyright and license information, please view the LICENSE
9
+ * file that was distributed with this source code.
10
+ */
11
+ class Twig_Node_Expression_Binary_Range extends Twig_Node_Expression_Binary
12
+ {
13
+ public function compile(Twig_Compiler $compiler)
14
+ {
15
+ $compiler
16
+ ->raw('range(')
17
+ ->subcompile($this->getNode('left'))
18
+ ->raw(', ')
19
+ ->subcompile($this->getNode('right'))
20
+ ->raw(')')
21
+ ;
22
+ }
23
+
24
+ public function operator(Twig_Compiler $compiler)
25
+ {
26
+ return $compiler->raw('..');
27
+ }
28
+ }
vendor/twig/twig/lib/Twig/Node/Expression/Binary/StartsWith.php ADDED
@@ -0,0 +1,30 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * This file is part of Twig.
5
+ *
6
+ * (c) Fabien Potencier
7
+ *
8
+ * For the full copyright and license information, please view the LICENSE
9
+ * file that was distributed with this source code.
10
+ */
11
+ class Twig_Node_Expression_Binary_StartsWith extends Twig_Node_Expression_Binary
12
+ {
13
+ public function compile(Twig_Compiler $compiler)
14
+ {
15
+ $left = $compiler->getVarName();
16
+ $right = $compiler->getVarName();
17
+ $compiler
18
+ ->raw(sprintf('(is_string($%s = ', $left))
19
+ ->subcompile($this->getNode('left'))
20
+ ->raw(sprintf(') && is_string($%s = ', $right))
21
+ ->subcompile($this->getNode('right'))
22
+ ->raw(sprintf(') && (\'\' === $%2$s || 0 === strpos($%1$s, $%2$s)))', $left, $right))
23
+ ;
24
+ }
25
+
26
+ public function operator(Twig_Compiler $compiler)
27
+ {
28
+ return $compiler->raw('');
29
+ }
30
+ }
vendor/twig/twig/lib/Twig/Node/Expression/Binary/Sub.php ADDED
@@ -0,0 +1,18 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * This file is part of Twig.
5
+ *
6
+ * (c) Fabien Potencier
7
+ * (c) Armin Ronacher
8
+ *
9
+ * For the full copyright and license information, please view the LICENSE
10
+ * file that was distributed with this source code.
11
+ */
12
+ class Twig_Node_Expression_Binary_Sub extends Twig_Node_Expression_Binary
13
+ {
14
+ public function operator(Twig_Compiler $compiler)
15
+ {
16
+ return $compiler->raw('-');
17
+ }
18
+ }
vendor/twig/twig/lib/Twig/Node/Expression/BlockReference.php ADDED
@@ -0,0 +1,91 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * This file is part of Twig.
5
+ *
6
+ * (c) Fabien Potencier
7
+ * (c) Armin Ronacher
8
+ *
9
+ * For the full copyright and license information, please view the LICENSE
10
+ * file that was distributed with this source code.
11
+ */
12
+
13
+ /**
14
+ * Represents a block call node.
15
+ *
16
+ * @author Fabien Potencier <fabien@symfony.com>
17
+ */
18
+ class Twig_Node_Expression_BlockReference extends Twig_Node_Expression
19
+ {
20
+ /**
21
+ * @param Twig_Node|null $template
22
+ */
23
+ public function __construct(Twig_NodeInterface $name, $template = null, $lineno, $tag = null)
24
+ {
25
+ if (is_bool($template)) {
26
+ @trigger_error(sprintf('The %s method "$asString" argument is deprecated since version 1.28 and will be removed in 2.0.', __METHOD__), E_USER_DEPRECATED);
27
+
28
+ $template = null;
29
+ }
30
+
31
+ $nodes = array('name' => $name);
32
+ if (null !== $template) {
33
+ $nodes['template'] = $template;
34
+ }
35
+
36
+ parent::__construct($nodes, array('is_defined_test' => false, 'output' => false), $lineno, $tag);
37
+ }
38
+
39
+ public function compile(Twig_Compiler $compiler)
40
+ {
41
+ if ($this->getAttribute('is_defined_test')) {
42
+ $this->compileTemplateCall($compiler, 'hasBlock');
43
+ } else {
44
+ if ($this->getAttribute('output')) {
45
+ $compiler->addDebugInfo($this);
46
+
47
+ $this
48
+ ->compileTemplateCall($compiler, 'displayBlock')
49
+ ->raw(";\n");
50
+ } else {
51
+ $this->compileTemplateCall($compiler, 'renderBlock');
52
+ }
53
+ }
54
+ }
55
+
56
+ private function compileTemplateCall(Twig_Compiler $compiler, $method)
57
+ {
58
+ if (!$this->hasNode('template')) {
59
+ $compiler->write('$this');
60
+ } else {
61
+ $compiler
62
+ ->write('$this->loadTemplate(')
63
+ ->subcompile($this->getNode('template'))
64
+ ->raw(', ')
65
+ ->repr($this->getTemplateName())
66
+ ->raw(', ')
67
+ ->repr($this->getTemplateLine())
68
+ ->raw(')')
69
+ ;
70
+ }
71
+
72
+ $compiler->raw(sprintf('->%s', $method));
73
+ $this->compileBlockArguments($compiler);
74
+
75
+ return $compiler;
76
+ }
77
+
78
+ private function compileBlockArguments(Twig_Compiler $compiler)
79
+ {
80
+ $compiler
81
+ ->raw('(')
82
+ ->subcompile($this->getNode('name'))
83
+ ->raw(', $context');
84
+
85
+ if (!$this->hasNode('template')) {
86
+ $compiler->raw(', $blocks');
87
+ }
88
+
89
+ return $compiler->raw(')');
90
+ }
91
+ }
vendor/twig/twig/lib/Twig/Node/Expression/Call.php ADDED
@@ -0,0 +1,289 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * This file is part of Twig.
5
+ *
6
+ * (c) Fabien Potencier
7
+ *
8
+ * For the full copyright and license information, please view the LICENSE
9
+ * file that was distributed with this source code.
10
+ */
11
+ abstract class Twig_Node_Expression_Call extends Twig_Node_Expression
12
+ {
13
+ private $reflector;
14
+
15
+ protected function compileCallable(Twig_Compiler $compiler)
16
+ {
17
+ $closingParenthesis = false;
18
+ if ($this->hasAttribute('callable') && $callable = $this->getAttribute('callable')) {
19
+ if (is_string($callable) && false === strpos($callable, '::')) {
20
+ $compiler->raw($callable);
21
+ } else {
22
+ list($r, $callable) = $this->reflectCallable($callable);
23
+ if ($r instanceof ReflectionMethod && is_string($callable[0])) {
24
+ if ($r->isStatic()) {
25
+ $compiler->raw(sprintf('%s::%s', $callable[0], $callable[1]));
26
+ } else {
27
+ $compiler->raw(sprintf('$this->env->getRuntime(\'%s\')->%s', $callable[0], $callable[1]));
28
+ }
29
+ } elseif ($r instanceof ReflectionMethod && $callable[0] instanceof Twig_ExtensionInterface) {
30
+ $compiler->raw(sprintf('$this->env->getExtension(\'%s\')->%s', get_class($callable[0]), $callable[1]));
31
+ } else {
32
+ $type = ucfirst($this->getAttribute('type'));
33
+ $compiler->raw(sprintf('call_user_func_array($this->env->get%s(\'%s\')->getCallable(), array', $type, $this->getAttribute('name')));
34
+ $closingParenthesis = true;
35
+ }
36
+ }
37
+ } else {
38
+ $compiler->raw($this->getAttribute('thing')->compile());
39
+ }
40
+
41
+ $this->compileArguments($compiler);
42
+
43
+ if ($closingParenthesis) {
44
+ $compiler->raw(')');
45
+ }
46
+ }
47
+
48
+ protected function compileArguments(Twig_Compiler $compiler)
49
+ {
50
+ $compiler->raw('(');
51
+
52
+ $first = true;
53
+
54
+ if ($this->hasAttribute('needs_environment') && $this->getAttribute('needs_environment')) {
55
+ $compiler->raw('$this->env');
56
+ $first = false;
57
+ }
58
+
59
+ if ($this->hasAttribute('needs_context') && $this->getAttribute('needs_context')) {
60
+ if (!$first) {
61
+ $compiler->raw(', ');
62
+ }
63
+ $compiler->raw('$context');
64
+ $first = false;
65
+ }
66
+
67
+ if ($this->hasAttribute('arguments')) {
68
+ foreach ($this->getAttribute('arguments') as $argument) {
69
+ if (!$first) {
70
+ $compiler->raw(', ');
71
+ }
72
+ $compiler->string($argument);
73
+ $first = false;
74
+ }
75
+ }
76
+
77
+ if ($this->hasNode('node')) {
78
+ if (!$first) {
79
+ $compiler->raw(', ');
80
+ }
81
+ $compiler->subcompile($this->getNode('node'));
82
+ $first = false;
83
+ }
84
+
85
+ if ($this->hasNode('arguments')) {
86
+ $callable = $this->hasAttribute('callable') ? $this->getAttribute('callable') : null;
87
+
88
+ $arguments = $this->getArguments($callable, $this->getNode('arguments'));
89
+
90
+ foreach ($arguments as $node) {
91
+ if (!$first) {
92
+ $compiler->raw(', ');
93
+ }
94
+ $compiler->subcompile($node);
95
+ $first = false;
96
+ }
97
+ }
98
+
99
+ $compiler->raw(')');
100
+ }
101
+
102
+ protected function getArguments($callable, $arguments)
103
+ {
104
+ $callType = $this->getAttribute('type');
105
+ $callName = $this->getAttribute('name');
106
+
107
+ $parameters = array();
108
+ $named = false;
109
+ foreach ($arguments as $name => $node) {
110
+ if (!is_int($name)) {
111
+ $named = true;
112
+ $name = $this->normalizeName($name);
113
+ } elseif ($named) {
114
+ throw new Twig_Error_Syntax(sprintf('Positional arguments cannot be used after named arguments for %s "%s".', $callType, $callName));
115
+ }
116
+
117
+ $parameters[$name] = $node;
118
+ }
119
+
120
+ $isVariadic = $this->hasAttribute('is_variadic') && $this->getAttribute('is_variadic');
121
+ if (!$named && !$isVariadic) {
122
+ return $parameters;
123
+ }
124
+
125
+ if (!$callable) {
126
+ if ($named) {
127
+ $message = sprintf('Named arguments are not supported for %s "%s".', $callType, $callName);
128
+ } else {
129
+ $message = sprintf('Arbitrary positional arguments are not supported for %s "%s".', $callType, $callName);
130
+ }
131
+
132
+ throw new LogicException($message);
133
+ }
134
+
135
+ $callableParameters = $this->getCallableParameters($callable, $isVariadic);
136
+ $arguments = array();
137
+ $names = array();
138
+ $missingArguments = array();
139
+ $optionalArguments = array();
140
+ $pos = 0;
141
+ foreach ($callableParameters as $callableParameter) {
142
+ $names[] = $name = $this->normalizeName($callableParameter->name);
143
+
144
+ if (array_key_exists($name, $parameters)) {
145
+ if (array_key_exists($pos, $parameters)) {
146
+ throw new Twig_Error_Syntax(sprintf('Argument "%s" is defined twice for %s "%s".', $name, $callType, $callName));
147
+ }
148
+
149
+ if (count($missingArguments)) {
150
+ throw new Twig_Error_Syntax(sprintf(
151
+ 'Argument "%s" could not be assigned for %s "%s(%s)" because it is mapped to an internal PHP function which cannot determine default value for optional argument%s "%s".',
152
+ $name, $callType, $callName, implode(', ', $names), count($missingArguments) > 1 ? 's' : '', implode('", "', $missingArguments))
153
+ );
154
+ }
155
+
156
+ $arguments = array_merge($arguments, $optionalArguments);
157
+ $arguments[] = $parameters[$name];
158
+ unset($parameters[$name]);
159
+ $optionalArguments = array();
160
+ } elseif (array_key_exists($pos, $parameters)) {
161
+ $arguments = array_merge($arguments, $optionalArguments);
162
+ $arguments[] = $parameters[$pos];
163
+ unset($parameters[$pos]);
164
+ $optionalArguments = array();
165
+ ++$pos;
166
+ } elseif ($callableParameter->isDefaultValueAvailable()) {
167
+ $optionalArguments[] = new Twig_Node_Expression_Constant($callableParameter->getDefaultValue(), -1);
168
+ } elseif ($callableParameter->isOptional()) {
169
+ if (empty($parameters)) {
170
+ break;
171
+ } else {
172
+ $missingArguments[] = $name;
173
+ }
174
+ } else {
175
+ throw new Twig_Error_Syntax(sprintf('Value for argument "%s" is required for %s "%s".', $name, $callType, $callName));
176
+ }
177
+ }
178
+
179
+ if ($isVariadic) {
180
+ $arbitraryArguments = new Twig_Node_Expression_Array(array(), -1);
181
+ foreach ($parameters as $key => $value) {
182
+ if (is_int($key)) {
183
+ $arbitraryArguments->addElement($value);
184
+ } else {
185
+ $arbitraryArguments->addElement($value, new Twig_Node_Expression_Constant($key, -1));
186
+ }
187
+ unset($parameters[$key]);
188
+ }
189
+
190
+ if ($arbitraryArguments->count()) {
191
+ $arguments = array_merge($arguments, $optionalArguments);
192
+ $arguments[] = $arbitraryArguments;
193
+ }
194
+ }
195
+
196
+ if (!empty($parameters)) {
197
+ $unknownParameter = null;
198
+ foreach ($parameters as $parameter) {
199
+ if ($parameter instanceof Twig_Node) {
200
+ $unknownParameter = $parameter;
201
+ break;
202
+ }
203
+ }
204
+
205
+ throw new Twig_Error_Syntax(sprintf(
206
+ 'Unknown argument%s "%s" for %s "%s(%s)".',
207
+ count($parameters) > 1 ? 's' : '', implode('", "', array_keys($parameters)), $callType, $callName, implode(', ', $names)
208
+ ), $unknownParameter ? $unknownParameter->getTemplateLine() : -1);
209
+ }
210
+
211
+ return $arguments;
212
+ }
213
+
214
+ protected function normalizeName($name)
215
+ {
216
+ return strtolower(preg_replace(array('/([A-Z]+)([A-Z][a-z])/', '/([a-z\d])([A-Z])/'), array('\\1_\\2', '\\1_\\2'), $name));
217
+ }
218
+
219
+ private function getCallableParameters($callable, $isVariadic)
220
+ {
221
+ list($r) = $this->reflectCallable($callable);
222
+ if (null === $r) {
223
+ return array();
224
+ }
225
+
226
+ $parameters = $r->getParameters();
227
+ if ($this->hasNode('node')) {
228
+ array_shift($parameters);
229
+ }
230
+ if ($this->hasAttribute('needs_environment') && $this->getAttribute('needs_environment')) {
231
+ array_shift($parameters);
232
+ }
233
+ if ($this->hasAttribute('needs_context') && $this->getAttribute('needs_context')) {
234
+ array_shift($parameters);
235
+ }
236
+ if ($this->hasAttribute('arguments') && null !== $this->getAttribute('arguments')) {
237
+ foreach ($this->getAttribute('arguments') as $argument) {
238
+ array_shift($parameters);
239
+ }
240
+ }
241
+ if ($isVariadic) {
242
+ $argument = end($parameters);
243
+ if ($argument && $argument->isArray() && $argument->isDefaultValueAvailable() && array() === $argument->getDefaultValue()) {
244
+ array_pop($parameters);
245
+ } else {
246
+ $callableName = $r->name;
247
+ if ($r instanceof ReflectionMethod) {
248
+ $callableName = $r->getDeclaringClass()->name.'::'.$callableName;
249
+ }
250
+
251
+ throw new LogicException(sprintf('The last parameter of "%s" for %s "%s" must be an array with default value, eg. "array $arg = array()".', $callableName, $this->getAttribute('type'), $this->getAttribute('name')));
252
+ }
253
+ }
254
+
255
+ return $parameters;
256
+ }
257
+
258
+ private function reflectCallable($callable)
259
+ {
260
+ if (null !== $this->reflector) {
261
+ return $this->reflector;
262
+ }
263
+
264
+ if (is_array($callable)) {
265
+ if (!method_exists($callable[0], $callable[1])) {
266
+ // __call()
267
+ return array(null, array());
268
+ }
269
+ $r = new ReflectionMethod($callable[0], $callable[1]);
270
+ } elseif (is_object($callable) && !$callable instanceof Closure) {
271
+ $r = new ReflectionObject($callable);
272
+ $r = $r->getMethod('__invoke');
273
+ $callable = array($callable, '__invoke');
274
+ } elseif (is_string($callable) && false !== $pos = strpos($callable, '::')) {
275
+ $class = substr($callable, 0, $pos);
276
+ $method = substr($callable, $pos + 2);
277
+ if (!method_exists($class, $method)) {
278
+ // __staticCall()
279
+ return array(null, array());
280
+ }
281
+ $r = new ReflectionMethod($callable);
282
+ $callable = array($class, $method);
283
+ } else {
284
+ $r = new ReflectionFunction($callable);
285
+ }
286
+
287
+ return $this->reflector = array($r, $callable);
288
+ }
289
+ }
vendor/twig/twig/lib/Twig/Node/Expression/Conditional.php ADDED
@@ -0,0 +1,31 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * This file is part of Twig.
5
+ *
6
+ * (c) Fabien Potencier
7
+ * (c) Armin Ronacher
8
+ *
9
+ * For the full copyright and license information, please view the LICENSE
10
+ * file that was distributed with this source code.
11
+ */
12
+ class Twig_Node_Expression_Conditional extends Twig_Node_Expression
13
+ {
14
+ public function __construct(Twig_Node_Expression $expr1, Twig_Node_Expression $expr2, Twig_Node_Expression $expr3, $lineno)
15
+ {
16
+ parent::__construct(array('expr1' => $expr1, 'expr2' => $expr2, 'expr3' => $expr3), array(), $lineno);
17
+ }
18
+
19
+ public function compile(Twig_Compiler $compiler)
20
+ {
21
+ $compiler
22
+ ->raw('((')
23
+ ->subcompile($this->getNode('expr1'))
24
+ ->raw(') ? (')
25
+ ->subcompile($this->getNode('expr2'))
26
+ ->raw(') : (')
27
+ ->subcompile($this->getNode('expr3'))
28
+ ->raw('))')
29
+ ;
30
+ }
31
+ }
vendor/twig/twig/lib/Twig/Node/Expression/Constant.php ADDED
@@ -0,0 +1,23 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * This file is part of Twig.
5
+ *
6
+ * (c) Fabien Potencier
7
+ * (c) Armin Ronacher
8
+ *
9
+ * For the full copyright and license information, please view the LICENSE
10
+ * file that was distributed with this source code.
11
+ */
12
+ class Twig_Node_Expression_Constant extends Twig_Node_Expression
13
+ {
14
+ public function __construct($value, $lineno)
15
+ {
16
+ parent::__construct(array(), array('value' => $value), $lineno);
17
+ }
18
+
19
+ public function compile(Twig_Compiler $compiler)
20
+ {
21
+ $compiler->repr($this->getAttribute('value'));
22
+ }
23
+ }
vendor/twig/twig/lib/Twig/Node/Expression/ExtensionReference.php ADDED
@@ -0,0 +1,32 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * This file is part of Twig.
5
+ *
6
+ * (c) Fabien Potencier
7
+ *
8
+ * For the full copyright and license information, please view the LICENSE
9
+ * file that was distributed with this source code.
10
+ */
11
+
12
+ @trigger_error('The Twig_Node_Expression_ExtensionReference class is deprecated since version 1.23 and will be removed in 2.0.', E_USER_DEPRECATED);
13
+
14
+ /**
15
+ * Represents an extension call node.
16
+ *
17
+ * @author Fabien Potencier <fabien@symfony.com>
18
+ *
19
+ * @deprecated since 1.23 and will be removed in 2.0.
20
+ */
21
+ class Twig_Node_Expression_ExtensionReference extends Twig_Node_Expression
22
+ {
23
+ public function __construct($name, $lineno, $tag = null)
24
+ {
25
+ parent::__construct(array(), array('name' => $name), $lineno, $tag);
26
+ }
27
+
28
+ public function compile(Twig_Compiler $compiler)
29
+ {
30
+ $compiler->raw(sprintf("\$this->env->getExtension('%s')", $this->getAttribute('name')));
31
+ }
32
+ }
vendor/twig/twig/lib/Twig/Node/Expression/Filter.php ADDED
@@ -0,0 +1,39 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * This file is part of Twig.
5
+ *
6
+ * (c) Fabien Potencier
7
+ * (c) Armin Ronacher
8
+ *
9
+ * For the full copyright and license information, please view the LICENSE
10
+ * file that was distributed with this source code.
11
+ */
12
+ class Twig_Node_Expression_Filter extends Twig_Node_Expression_Call
13
+ {
14
+ public function __construct(Twig_NodeInterface $node, Twig_Node_Expression_Constant $filterName, Twig_NodeInterface $arguments, $lineno, $tag = null)
15
+ {
16
+ parent::__construct(array('node' => $node, 'filter' => $filterName, 'arguments' => $arguments), array(), $lineno, $tag);
17
+ }
18
+
19
+ public function compile(Twig_Compiler $compiler)
20
+ {
21
+ $name = $this->getNode('filter')->getAttribute('value');
22
+ $filter = $compiler->getEnvironment()->getFilter($name);
23
+
24
+ $this->setAttribute('name', $name);
25
+ $this->setAttribute('type', 'filter');
26
+ $this->setAttribute('thing', $filter);
27
+ $this->setAttribute('needs_environment', $filter->needsEnvironment());
28
+ $this->setAttribute('needs_context', $filter->needsContext());
29
+ $this->setAttribute('arguments', $filter->getArguments());
30
+ if ($filter instanceof Twig_FilterCallableInterface || $filter instanceof Twig_SimpleFilter) {
31
+ $this->setAttribute('callable', $filter->getCallable());
32
+ }
33
+ if ($filter instanceof Twig_SimpleFilter) {
34
+ $this->setAttribute('is_variadic', $filter->isVariadic());
35
+ }
36
+
37
+ $this->compileCallable($compiler);
38
+ }
39
+ }
vendor/twig/twig/lib/Twig/Node/Expression/Filter/Default.php ADDED
@@ -0,0 +1,43 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * This file is part of Twig.
5
+ *
6
+ * (c) Fabien Potencier
7
+ *
8
+ * For the full copyright and license information, please view the LICENSE
9
+ * file that was distributed with this source code.
10
+ */
11
+
12
+ /**
13
+ * Returns the value or the default value when it is undefined or empty.
14
+ *
15
+ * <pre>
16
+ * {{ var.foo|default('foo item on var is not defined') }}
17
+ * </pre>
18
+ *
19
+ * @author Fabien Potencier <fabien@symfony.com>
20
+ */
21
+ class Twig_Node_Expression_Filter_Default extends Twig_Node_Expression_Filter
22
+ {
23
+ public function __construct(Twig_NodeInterface $node, Twig_Node_Expression_Constant $filterName, Twig_NodeInterface $arguments, $lineno, $tag = null)
24
+ {
25
+ $default = new Twig_Node_Expression_Filter($node, new Twig_Node_Expression_Constant('default', $node->getTemplateLine()), $arguments, $node->getTemplateLine());
26
+
27
+ if ('default' === $filterName->getAttribute('value') && ($node instanceof Twig_Node_Expression_Name || $node instanceof Twig_Node_Expression_GetAttr)) {
28
+ $test = new Twig_Node_Expression_Test_Defined(clone $node, 'defined', new Twig_Node(), $node->getTemplateLine());
29
+ $false = count($arguments) ? $arguments->getNode(0) : new Twig_Node_Expression_Constant('', $node->getTemplateLine());
30
+
31
+ $node = new Twig_Node_Expression_Conditional($test, $default, $false, $node->getTemplateLine());
32
+ } else {
33
+ $node = $default;
34
+ }
35
+
36
+ parent::__construct($node, $filterName, $arguments, $lineno, $tag);
37
+ }
38
+
39
+ public function compile(Twig_Compiler $compiler)
40
+ {
41
+ $compiler->subcompile($this->getNode('node'));
42
+ }
43
+ }
vendor/twig/twig/lib/Twig/Node/Expression/Function.php ADDED
@@ -0,0 +1,43 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * This file is part of Twig.
5
+ *
6
+ * (c) Fabien Potencier
7
+ *
8
+ * For the full copyright and license information, please view the LICENSE
9
+ * file that was distributed with this source code.
10
+ */
11
+ class Twig_Node_Expression_Function extends Twig_Node_Expression_Call
12
+ {
13
+ public function __construct($name, Twig_NodeInterface $arguments, $lineno)
14
+ {
15
+ parent::__construct(array('arguments' => $arguments), array('name' => $name, 'is_defined_test' => false), $lineno);
16
+ }
17
+
18
+ public function compile(Twig_Compiler $compiler)
19
+ {
20
+ $name = $this->getAttribute('name');
21
+ $function = $compiler->getEnvironment()->getFunction($name);
22
+
23
+ $this->setAttribute('name', $name);
24
+ $this->setAttribute('type', 'function');
25
+ $this->setAttribute('thing', $function);
26
+ $this->setAttribute('needs_environment', $function->needsEnvironment());
27
+ $this->setAttribute('needs_context', $function->needsContext());
28
+ $this->setAttribute('arguments', $function->getArguments());
29
+ if ($function instanceof Twig_FunctionCallableInterface || $function instanceof Twig_SimpleFunction) {
30
+ $callable = $function->getCallable();
31
+ if ('constant' === $name && $this->getAttribute('is_defined_test')) {
32
+ $callable = 'twig_constant_is_defined';
33
+ }
34
+
35
+ $this->setAttribute('callable', $callable);
36
+ }
37
+ if ($function instanceof Twig_SimpleFunction) {
38
+ $this->setAttribute('is_variadic', $function->isVariadic());
39
+ }
40
+
41
+ $this->compileCallable($compiler);
42
+ }
43
+ }
vendor/twig/twig/lib/Twig/Node/Expression/GetAttr.php ADDED
@@ -0,0 +1,72 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * This file is part of Twig.
5
+ *
6
+ * (c) Fabien Potencier
7
+ * (c) Armin Ronacher
8
+ *
9
+ * For the full copyright and license information, please view the LICENSE
10
+ * file that was distributed with this source code.
11
+ */
12
+ class Twig_Node_Expression_GetAttr extends Twig_Node_Expression
13
+ {
14
+ public function __construct(Twig_Node_Expression $node, Twig_Node_Expression $attribute, Twig_Node_Expression $arguments = null, $type, $lineno)
15
+ {
16
+ $nodes = array('node' => $node, 'attribute' => $attribute);
17
+ if (null !== $arguments) {
18
+ $nodes['arguments'] = $arguments;
19
+ }
20
+
21
+ parent::__construct($nodes, array('type' => $type, 'is_defined_test' => false, 'ignore_strict_check' => false, 'disable_c_ext' => false), $lineno);
22
+ }
23
+
24
+ public function compile(Twig_Compiler $compiler)
25
+ {
26
+ if ($this->getAttribute('disable_c_ext')) {
27
+ @trigger_error(sprintf('Using the "disable_c_ext" attribute on %s is deprecated since version 1.30 and will be removed in 2.0.', __CLASS__), E_USER_DEPRECATED);
28
+ }
29
+
30
+ if (function_exists('twig_template_get_attributes') && !$this->getAttribute('disable_c_ext')) {
31
+ $compiler->raw('twig_template_get_attributes($this, ');
32
+ } else {
33
+ $compiler->raw('$this->getAttribute(');
34
+ }
35
+
36
+ if ($this->getAttribute('ignore_strict_check')) {
37
+ $this->getNode('node')->setAttribute('ignore_strict_check', true);
38
+ }
39
+
40
+ $compiler->subcompile($this->getNode('node'));
41
+
42
+ $compiler->raw(', ')->subcompile($this->getNode('attribute'));
43
+
44
+ // only generate optional arguments when needed (to make generated code more readable)
45
+ $needFourth = $this->getAttribute('ignore_strict_check');
46
+ $needThird = $needFourth || $this->getAttribute('is_defined_test');
47
+ $needSecond = $needThird || Twig_Template::ANY_CALL !== $this->getAttribute('type');
48
+ $needFirst = $needSecond || $this->hasNode('arguments');
49
+
50
+ if ($needFirst) {
51
+ if ($this->hasNode('arguments')) {
52
+ $compiler->raw(', ')->subcompile($this->getNode('arguments'));
53
+ } else {
54
+ $compiler->raw(', array()');
55
+ }
56
+ }
57
+
58
+ if ($needSecond) {
59
+ $compiler->raw(', ')->repr($this->getAttribute('type'));
60
+ }
61
+
62
+ if ($needThird) {
63
+ $compiler->raw(', ')->repr($this->getAttribute('is_defined_test'));
64
+ }
65
+
66
+ if ($needFourth) {
67
+ $compiler->raw(', ')->repr($this->getAttribute('ignore_strict_check'));
68
+ }
69
+
70
+ $compiler->raw(')');
71
+ }
72
+ }
vendor/twig/twig/lib/Twig/Node/Expression/MethodCall.php ADDED
@@ -0,0 +1,41 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * This file is part of Twig.
5
+ *
6
+ * (c) Fabien Potencier
7
+ *
8
+ * For the full copyright and license information, please view the LICENSE
9
+ * file that was distributed with this source code.
10
+ */
11
+ class Twig_Node_Expression_MethodCall extends Twig_Node_Expression
12
+ {
13
+ public function __construct(Twig_Node_Expression $node, $method, Twig_Node_Expression_Array $arguments, $lineno)
14
+ {
15
+ parent::__construct(array('node' => $node, 'arguments' => $arguments), array('method' => $method, 'safe' => false), $lineno);
16
+
17
+ if ($node instanceof Twig_Node_Expression_Name) {
18
+ $node->setAttribute('always_defined', true);
19
+ }
20
+ }
21
+
22
+ public function compile(Twig_Compiler $compiler)
23
+ {
24
+ $compiler
25
+ ->subcompile($this->getNode('node'))
26
+ ->raw('->')
27
+ ->raw($this->getAttribute('method'))
28
+ ->raw('(')
29
+ ;
30
+ $first = true;
31
+ foreach ($this->getNode('arguments')->getKeyValuePairs() as $pair) {
32
+ if (!$first) {
33
+ $compiler->raw(', ');
34
+ }
35
+ $first = false;
36
+
37
+ $compiler->subcompile($pair['value']);
38
+ }
39
+ $compiler->raw(')');
40
+ }
41
+ }
vendor/twig/twig/lib/Twig/Node/Expression/Name.php ADDED
@@ -0,0 +1,100 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * This file is part of Twig.
5
+ *
6
+ * (c) Fabien Potencier
7
+ * (c) Armin Ronacher
8
+ *
9
+ * For the full copyright and license information, please view the LICENSE
10
+ * file that was distributed with this source code.
11
+ */
12
+ class Twig_Node_Expression_Name extends Twig_Node_Expression
13
+ {
14
+ protected $specialVars = array(
15
+ '_self' => '$this',
16
+ '_context' => '$context',
17
+ '_charset' => '$this->env->getCharset()',
18
+ );
19
+
20
+ public function __construct($name, $lineno)
21
+ {
22
+ parent::__construct(array(), array('name' => $name, 'is_defined_test' => false, 'ignore_strict_check' => false, 'always_defined' => false), $lineno);
23
+ }
24
+
25
+ public function compile(Twig_Compiler $compiler)
26
+ {
27
+ $name = $this->getAttribute('name');
28
+
29
+ $compiler->addDebugInfo($this);
30
+
31
+ if ($this->getAttribute('is_defined_test')) {
32
+ if ($this->isSpecial()) {
33
+ $compiler->repr(true);
34
+ } else {
35
+ $compiler->raw('array_key_exists(')->repr($name)->raw(', $context)');
36
+ }
37
+ } elseif ($this->isSpecial()) {
38
+ $compiler->raw($this->specialVars[$name]);
39
+ } elseif ($this->getAttribute('always_defined')) {
40
+ $compiler
41
+ ->raw('$context[')
42
+ ->string($name)
43
+ ->raw(']')
44
+ ;
45
+ } else {
46
+ if (PHP_VERSION_ID >= 70000) {
47
+ // use PHP 7 null coalescing operator
48
+ $compiler
49
+ ->raw('($context[')
50
+ ->string($name)
51
+ ->raw('] ?? ')
52
+ ;
53
+
54
+ if ($this->getAttribute('ignore_strict_check') || !$compiler->getEnvironment()->isStrictVariables()) {
55
+ $compiler->raw('null)');
56
+ } else {
57
+ $compiler->raw('$this->getContext($context, ')->string($name)->raw('))');
58
+ }
59
+ } elseif (PHP_VERSION_ID >= 50400) {
60
+ // PHP 5.4 ternary operator performance was optimized
61
+ $compiler
62
+ ->raw('(isset($context[')
63
+ ->string($name)
64
+ ->raw(']) ? $context[')
65
+ ->string($name)
66
+ ->raw('] : ')
67
+ ;
68
+
69
+ if ($this->getAttribute('ignore_strict_check') || !$compiler->getEnvironment()->isStrictVariables()) {
70
+ $compiler->raw('null)');
71
+ } else {
72
+ $compiler->raw('$this->getContext($context, ')->string($name)->raw('))');
73
+ }
74
+ } else {
75
+ $compiler
76
+ ->raw('$this->getContext($context, ')
77
+ ->string($name)
78
+ ;
79
+
80
+ if ($this->getAttribute('ignore_strict_check')) {
81
+ $compiler->raw(', true');
82
+ }
83
+
84
+ $compiler
85
+ ->raw(')')
86
+ ;
87
+ }
88
+ }
89
+ }
90
+
91
+ public function isSpecial()
92
+ {
93
+ return isset($this->specialVars[$this->getAttribute('name')]);
94
+ }
95
+
96
+ public function isSimple()
97
+ {
98
+ return !$this->isSpecial() && !$this->getAttribute('is_defined_test');
99
+ }
100
+ }
vendor/twig/twig/lib/Twig/Node/Expression/NullCoalesce.php ADDED
@@ -0,0 +1,46 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * This file is part of Twig.
5
+ *
6
+ * (c) Fabien Potencier
7
+ *
8
+ * For the full copyright and license information, please view the LICENSE
9
+ * file that was distributed with this source code.
10
+ */
11
+ class Twig_Node_Expression_NullCoalesce extends Twig_Node_Expression_Conditional
12
+ {
13
+ public function __construct(Twig_NodeInterface $left, Twig_NodeInterface $right, $lineno)
14
+ {
15
+ $test = new Twig_Node_Expression_Binary_And(
16
+ new Twig_Node_Expression_Test_Defined(clone $left, 'defined', new Twig_Node(), $left->getTemplateLine()),
17
+ new Twig_Node_Expression_Unary_Not(new Twig_Node_Expression_Test_Null($left, 'null', new Twig_Node(), $left->getTemplateLine()), $left->getTemplateLine()),
18
+ $left->getTemplateLine()
19
+ );
20
+
21
+ parent::__construct($test, $left, $right, $lineno);
22
+ }
23
+
24
+ public function compile(Twig_Compiler $compiler)
25
+ {
26
+ /*
27
+ * This optimizes only one case. PHP 7 also supports more complex expressions
28
+ * that can return null. So, for instance, if log is defined, log("foo") ?? "..." works,
29
+ * but log($a["foo"]) ?? "..." does not if $a["foo"] is not defined. More advanced
30
+ * cases might be implemented as an optimizer node visitor, but has not been done
31
+ * as benefits are probably not worth the added complexity.
32
+ */
33
+ if (PHP_VERSION_ID >= 70000 && $this->getNode('expr2') instanceof Twig_Node_Expression_Name) {
34
+ $this->getNode('expr2')->setAttribute('always_defined', true);
35
+ $compiler
36
+ ->raw('((')
37
+ ->subcompile($this->getNode('expr2'))
38
+ ->raw(') ?? (')
39
+ ->subcompile($this->getNode('expr3'))
40
+ ->raw('))')
41
+ ;
42
+ } else {
43
+ parent::compile($compiler);
44
+ }
45
+ }
46
+ }
vendor/twig/twig/lib/Twig/Node/Expression/Parent.php ADDED
@@ -0,0 +1,42 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * This file is part of Twig.
5
+ *
6
+ * (c) Fabien Potencier
7
+ * (c) Armin Ronacher
8
+ *
9
+ * For the full copyright and license information, please view the LICENSE
10
+ * file that was distributed with this source code.
11
+ */
12
+
13
+ /**
14
+ * Represents a parent node.
15
+ *
16
+ * @author Fabien Potencier <fabien@symfony.com>
17
+ */
18
+ class Twig_Node_Expression_Parent extends Twig_Node_Expression
19
+ {
20
+ public function __construct($name, $lineno, $tag = null)
21
+ {
22
+ parent::__construct(array(), array('output' => false, 'name' => $name), $lineno, $tag);
23
+ }
24
+
25
+ public function compile(Twig_Compiler $compiler)
26
+ {
27
+ if ($this->getAttribute('output')) {
28
+ $compiler
29
+ ->addDebugInfo($this)
30
+ ->write('$this->displayParentBlock(')
31
+ ->string($this->getAttribute('name'))
32
+ ->raw(", \$context, \$blocks);\n")
33
+ ;
34
+ } else {
35
+ $compiler
36
+ ->raw('$this->renderParentBlock(')
37
+ ->string($this->getAttribute('name'))
38
+ ->raw(', $context, $blocks)')
39
+ ;
40
+ }
41
+ }
42
+ }
vendor/twig/twig/lib/Twig/Node/Expression/TempName.php ADDED
@@ -0,0 +1,26 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * This file is part of Twig.
5
+ *
6
+ * (c) Fabien Potencier
7
+ *
8
+ * For the full copyright and license information, please view the LICENSE
9
+ * file that was distributed with this source code.
10
+ */
11
+ class Twig_Node_Expression_TempName extends Twig_Node_Expression
12
+ {
13
+ public function __construct($name, $lineno)
14
+ {
15
+ parent::__construct(array(), array('name' => $name), $lineno);
16
+ }
17
+
18
+ public function compile(Twig_Compiler $compiler)
19
+ {
20
+ $compiler
21
+ ->raw('$_')
22
+ ->raw($this->getAttribute('name'))
23
+ ->raw('_')
24
+ ;
25
+ }
26
+ }
vendor/twig/twig/lib/Twig/Node/Expression/Test.php ADDED
@@ -0,0 +1,40 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * This file is part of Twig.
5
+ *
6
+ * (c) Fabien Potencier
7
+ *
8
+ * For the full copyright and license information, please view the LICENSE
9
+ * file that was distributed with this source code.
10
+ */
11
+ class Twig_Node_Expression_Test extends Twig_Node_Expression_Call
12
+ {
13
+ public function __construct(Twig_NodeInterface $node, $name, Twig_NodeInterface $arguments = null, $lineno)
14
+ {
15
+ $nodes = array('node' => $node);
16
+ if (null !== $arguments) {
17
+ $nodes['arguments'] = $arguments;
18
+ }
19
+
20
+ parent::__construct($nodes, array('name' => $name), $lineno);
21
+ }
22
+
23
+ public function compile(Twig_Compiler $compiler)
24
+ {
25
+ $name = $this->getAttribute('name');
26
+ $test = $compiler->getEnvironment()->getTest($name);
27
+
28
+ $this->setAttribute('name', $name);
29
+ $this->setAttribute('type', 'test');
30
+ $this->setAttribute('thing', $test);
31
+ if ($test instanceof Twig_TestCallableInterface || $test instanceof Twig_SimpleTest) {
32
+ $this->setAttribute('callable', $test->getCallable());
33
+ }
34
+ if ($test instanceof Twig_SimpleTest) {
35
+ $this->setAttribute('is_variadic', $test->isVariadic());
36
+ }
37
+
38
+ $this->compileCallable($compiler);
39
+ }
40
+ }
vendor/twig/twig/lib/Twig/Node/Expression/Test/Constant.php ADDED
@@ -0,0 +1,46 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * This file is part of Twig.
5
+ *
6
+ * (c) Fabien Potencier
7
+ *
8
+ * For the full copyright and license information, please view the LICENSE
9
+ * file that was distributed with this source code.
10
+ */
11
+
12
+ /**
13
+ * Checks if a variable is the exact same value as a constant.
14
+ *
15
+ * <pre>
16
+ * {% if post.status is constant('Post::PUBLISHED') %}
17
+ * the status attribute is exactly the same as Post::PUBLISHED
18
+ * {% endif %}
19
+ * </pre>
20
+ *
21
+ * @author Fabien Potencier <fabien@symfony.com>
22
+ */
23
+ class Twig_Node_Expression_Test_Constant extends Twig_Node_Expression_Test
24
+ {
25
+ public function compile(Twig_Compiler $compiler)
26
+ {
27
+ $compiler
28
+ ->raw('(')
29
+ ->subcompile($this->getNode('node'))
30
+ ->raw(' === constant(')
31
+ ;
32
+
33
+ if ($this->getNode('arguments')->hasNode(1)) {
34
+ $compiler
35
+ ->raw('get_class(')
36
+ ->subcompile($this->getNode('arguments')->getNode(1))
37
+ ->raw(')."::".')
38
+ ;
39
+ }
40
+
41
+ $compiler
42
+ ->subcompile($this->getNode('arguments')->getNode(0))
43
+ ->raw('))')
44
+ ;
45
+ }
46
+ }
vendor/twig/twig/lib/Twig/Node/Expression/Test/Defined.php ADDED
@@ -0,0 +1,59 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * This file is part of Twig.
5
+ *
6
+ * (c) Fabien Potencier
7
+ *
8
+ * For the full copyright and license information, please view the LICENSE
9
+ * file that was distributed with this source code.
10
+ */
11
+
12
+ /**
13
+ * Checks if a variable is defined in the current context.
14
+ *
15
+ * <pre>
16
+ * {# defined works with variable names and variable attributes #}
17
+ * {% if foo is defined %}
18
+ * {# ... #}
19
+ * {% endif %}
20
+ * </pre>
21
+ *
22
+ * @author Fabien Potencier <fabien@symfony.com>
23
+ */
24
+ class Twig_Node_Expression_Test_Defined extends Twig_Node_Expression_Test
25
+ {
26
+ public function __construct(Twig_NodeInterface $node, $name, Twig_NodeInterface $arguments = null, $lineno)
27
+ {
28
+ if ($node instanceof Twig_Node_Expression_Name) {
29
+ $node->setAttribute('is_defined_test', true);
30
+ } elseif ($node instanceof Twig_Node_Expression_GetAttr) {
31
+ $node->setAttribute('is_defined_test', true);
32
+ $this->changeIgnoreStrictCheck($node);
33
+ } elseif ($node instanceof Twig_Node_Expression_BlockReference) {
34
+ $node->setAttribute('is_defined_test', true);
35
+ } elseif ($node instanceof Twig_Node_Expression_Function && 'constant' === $node->getAttribute('name')) {
36
+ $node->setAttribute('is_defined_test', true);
37
+ } elseif ($node instanceof Twig_Node_Expression_Constant || $node instanceof Twig_Node_Expression_Array) {
38
+ $node = new Twig_Node_Expression_Constant(true, $node->getTemplateLine());
39
+ } else {
40
+ throw new Twig_Error_Syntax('The "defined" test only works with simple variables.', $this->getTemplateLine());
41
+ }
42
+
43
+ parent::__construct($node, $name, $arguments, $lineno);
44
+ }
45
+
46
+ protected function changeIgnoreStrictCheck(Twig_Node_Expression_GetAttr $node)
47
+ {
48
+ $node->setAttribute('ignore_strict_check', true);
49
+
50
+ if ($node->getNode('node') instanceof Twig_Node_Expression_GetAttr) {
51
+ $this->changeIgnoreStrictCheck($node->getNode('node'));
52
+ }
53
+ }
54
+
55
+ public function compile(Twig_Compiler $compiler)
56
+ {
57
+ $compiler->subcompile($this->getNode('node'));
58
+ }
59
+ }
vendor/twig/twig/lib/Twig/Node/Expression/Test/Divisibleby.php ADDED
@@ -0,0 +1,33 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * This file is part of Twig.
5
+ *
6
+ * (c) Fabien Potencier
7
+ *
8
+ * For the full copyright and license information, please view the LICENSE
9
+ * file that was distributed with this source code.
10
+ */
11
+
12
+ /**
13
+ * Checks if a variable is divisible by a number.
14
+ *
15
+ * <pre>
16
+ * {% if loop.index is divisible by(3) %}
17
+ * </pre>
18
+ *
19
+ * @author Fabien Potencier <fabien@symfony.com>
20
+ */
21
+ class Twig_Node_Expression_Test_Divisibleby extends Twig_Node_Expression_Test
22
+ {
23
+ public function compile(Twig_Compiler $compiler)
24
+ {
25
+ $compiler
26
+ ->raw('(0 == ')
27
+ ->subcompile($this->getNode('node'))
28
+ ->raw(' % ')
29
+ ->subcompile($this->getNode('arguments')->getNode(0))
30
+ ->raw(')')
31
+ ;
32
+ }
33
+ }
vendor/twig/twig/lib/Twig/Node/Expression/Test/Even.php ADDED
@@ -0,0 +1,32 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * This file is part of Twig.
5
+ *
6
+ * (c) Fabien Potencier
7
+ *
8
+ * For the full copyright and license information, please view the LICENSE
9
+ * file that was distributed with this source code.
10
+ */
11
+
12
+ /**
13
+ * Checks if a number is even.
14
+ *
15
+ * <pre>
16
+ * {{ var is even }}
17
+ * </pre>
18
+ *
19
+ * @author Fabien Potencier <fabien@symfony.com>
20
+ */
21
+ class Twig_Node_Expression_Test_Even extends Twig_Node_Expression_Test
22
+ {
23
+ public function compile(Twig_Compiler $compiler)
24
+ {
25
+ $compiler
26
+ ->raw('(')
27
+ ->subcompile($this->getNode('node'))
28
+ ->raw(' % 2 == 0')
29
+ ->raw(')')
30
+ ;
31
+ }
32
+ }
vendor/twig/twig/lib/Twig/Node/Expression/Test/Null.php ADDED
@@ -0,0 +1,31 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * This file is part of Twig.
5
+ *
6
+ * (c) Fabien Potencier
7
+ *
8
+ * For the full copyright and license information, please view the LICENSE
9
+ * file that was distributed with this source code.
10
+ */
11
+
12
+ /**
13
+ * Checks that a variable is null.
14
+ *
15
+ * <pre>
16
+ * {{ var is none }}
17
+ * </pre>
18
+ *
19
+ * @author Fabien Potencier <fabien@symfony.com>
20
+ */
21
+ class Twig_Node_Expression_Test_Null extends Twig_Node_Expression_Test
22
+ {
23
+ public function compile(Twig_Compiler $compiler)
24
+ {
25
+ $compiler
26
+ ->raw('(null === ')
27
+ ->subcompile($this->getNode('node'))
28
+ ->raw(')')
29
+ ;
30
+ }
31
+ }
vendor/twig/twig/lib/Twig/Node/Expression/Test/Odd.php ADDED
@@ -0,0 +1,32 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * This file is part of Twig.
5
+ *
6
+ * (c) Fabien Potencier
7
+ *
8
+ * For the full copyright and license information, please view the LICENSE
9
+ * file that was distributed with this source code.
10
+ */
11
+
12
+ /**
13
+ * Checks if a number is odd.
14
+ *
15
+ * <pre>
16
+ * {{ var is odd }}
17
+ * </pre>
18
+ *
19
+ * @author Fabien Potencier <fabien@symfony.com>
20
+ */
21
+ class Twig_Node_Expression_Test_Odd extends Twig_Node_Expression_Test
22
+ {
23
+ public function compile(Twig_Compiler $compiler)
24
+ {
25
+ $compiler
26
+ ->raw('(')
27
+ ->subcompile($this->getNode('node'))
28
+ ->raw(' % 2 == 1')
29
+ ->raw(')')
30
+ ;
31
+ }
32
+ }
vendor/twig/twig/lib/Twig/Node/Expression/Test/Sameas.php ADDED
@@ -0,0 +1,29 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * This file is part of Twig.
5
+ *
6
+ * (c) Fabien Potencier
7
+ *
8
+ * For the full copyright and license information, please view the LICENSE
9
+ * file that was distributed with this source code.
10
+ */
11
+
12
+ /**
13
+ * Checks if a variable is the same as another one (=== in PHP).
14
+ *
15
+ * @author Fabien Potencier <fabien@symfony.com>
16
+ */
17
+ class Twig_Node_Expression_Test_Sameas extends Twig_Node_Expression_Test
18
+ {
19
+ public function compile(Twig_Compiler $compiler)
20
+ {
21
+ $compiler
22
+ ->raw('(')
23
+ ->subcompile($this->getNode('node'))
24
+ ->raw(' === ')
25
+ ->subcompile($this->getNode('arguments')->getNode(0))
26
+ ->raw(')')
27
+ ;
28
+ }
29
+ }
vendor/twig/twig/lib/Twig/Node/Expression/Unary.php ADDED
@@ -0,0 +1,27 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * This file is part of Twig.
5
+ *
6
+ * (c) Fabien Potencier
7
+ * (c) Armin Ronacher
8
+ *
9
+ * For the full copyright and license information, please view the LICENSE
10
+ * file that was distributed with this source code.
11
+ */
12
+ abstract class Twig_Node_Expression_Unary extends Twig_Node_Expression
13
+ {
14
+ public function __construct(Twig_NodeInterface $node, $lineno)
15
+ {
16
+ parent::__construct(array('node' => $node), array(), $lineno);
17
+ }
18
+
19
+ public function compile(Twig_Compiler $compiler)
20
+ {
21
+ $compiler->raw(' ');
22
+ $this->operator($compiler);
23
+ $compiler->subcompile($this->getNode('node'));
24
+ }
25
+
26
+ abstract public function operator(Twig_Compiler $compiler);
27
+ }
vendor/twig/twig/lib/Twig/Node/Expression/Unary/Neg.php ADDED
@@ -0,0 +1,18 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * This file is part of Twig.
5
+ *
6
+ * (c) Fabien Potencier
7
+ * (c) Armin Ronacher
8
+ *
9
+ * For the full copyright and license information, please view the LICENSE
10
+ * file that was distributed with this source code.
11
+ */
12
+ class Twig_Node_Expression_Unary_Neg extends Twig_Node_Expression_Unary
13
+ {
14
+ public function operator(Twig_Compiler $compiler)
15
+ {
16
+ $compiler->raw('-');
17
+ }
18
+ }
vendor/twig/twig/lib/Twig/Node/Expression/Unary/Not.php ADDED
@@ -0,0 +1,18 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * This file is part of Twig.
5
+ *
6
+ * (c) Fabien Potencier
7
+ * (c) Armin Ronacher
8
+ *
9
+ * For the full copyright and license information, please view the LICENSE
10
+ * file that was distributed with this source code.
11
+ */
12
+ class Twig_Node_Expression_Unary_Not extends Twig_Node_Expression_Unary
13
+ {
14
+ public function operator(Twig_Compiler $compiler)
15
+ {
16
+ $compiler->raw('!');
17
+ }
18
+ }
vendor/twig/twig/lib/Twig/Node/Expression/Unary/Pos.php ADDED
@@ -0,0 +1,18 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * This file is part of Twig.
5
+ *
6
+ * (c) Fabien Potencier
7
+ * (c) Armin Ronacher
8
+ *
9
+ * For the full copyright and license information, please view the LICENSE
10
+ * file that was distributed with this source code.
11
+ */
12
+ class Twig_Node_Expression_Unary_Pos extends Twig_Node_Expression_Unary
13
+ {
14
+ public function operator(Twig_Compiler $compiler)
15
+ {
16
+ $compiler->raw('+');
17
+ }
18
+ }
vendor/twig/twig/lib/Twig/Node/Flush.php ADDED
@@ -0,0 +1,31 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * This file is part of Twig.
5
+ *
6
+ * (c) Fabien Potencier
7
+ *
8
+ * For the full copyright and license information, please view the LICENSE
9
+ * file that was distributed with this source code.
10
+ */
11
+
12
+ /**
13
+ * Represents a flush node.
14
+ *
15
+ * @author Fabien Potencier <fabien@symfony.com>
16
+ */
17
+ class Twig_Node_Flush extends Twig_Node
18
+ {
19
+ public function __construct($lineno, $tag)
20
+ {
21
+ parent::__construct(array(), array(), $lineno, $tag);
22
+ }
23
+
24
+ public function compile(Twig_Compiler $compiler)
25
+ {
26
+ $compiler
27
+ ->addDebugInfo($this)
28
+ ->write("flush();\n")
29
+ ;
30
+ }
31
+ }
vendor/twig/twig/lib/Twig/Node/For.php ADDED
@@ -0,0 +1,111 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * This file is part of Twig.
5
+ *
6
+ * (c) Fabien Potencier
7
+ * (c) Armin Ronacher
8
+ *
9
+ * For the full copyright and license information, please view the LICENSE
10
+ * file that was distributed with this source code.
11
+ */
12
+
13
+ /**
14
+ * Represents a for node.
15
+ *
16
+ * @author Fabien Potencier <fabien@symfony.com>
17
+ */
18
+ class Twig_Node_For extends Twig_Node
19
+ {
20
+ protected $loop;
21
+
22
+ public function __construct(Twig_Node_Expression_AssignName $keyTarget, Twig_Node_Expression_AssignName $valueTarget, Twig_Node_Expression $seq, Twig_Node_Expression $ifexpr = null, Twig_NodeInterface $body, Twig_NodeInterface $else = null, $lineno, $tag = null)
23
+ {
24
+ $body = new Twig_Node(array($body, $this->loop = new Twig_Node_ForLoop($lineno, $tag)));
25
+
26
+ if (null !== $ifexpr) {
27
+ $body = new Twig_Node_If(new Twig_Node(array($ifexpr, $body)), null, $lineno, $tag);
28
+ }
29
+
30
+ $nodes = array('key_target' => $keyTarget, 'value_target' => $valueTarget, 'seq' => $seq, 'body' => $body);
31
+ if (null !== $else) {
32
+ $nodes['else'] = $else;
33
+ }
34
+
35
+ parent::__construct($nodes, array('with_loop' => true, 'ifexpr' => null !== $ifexpr), $lineno, $tag);
36
+ }
37
+
38
+ public function compile(Twig_Compiler $compiler)
39
+ {
40
+ $compiler
41
+ ->addDebugInfo($this)
42
+ ->write("\$context['_parent'] = \$context;\n")
43
+ ->write("\$context['_seq'] = twig_ensure_traversable(")
44
+ ->subcompile($this->getNode('seq'))
45
+ ->raw(");\n")
46
+ ;
47
+
48
+ if ($this->hasNode('else')) {
49
+ $compiler->write("\$context['_iterated'] = false;\n");
50
+ }
51
+
52
+ if ($this->getAttribute('with_loop')) {
53
+ $compiler
54
+ ->write("\$context['loop'] = array(\n")
55
+ ->write(" 'parent' => \$context['_parent'],\n")
56
+ ->write(" 'index0' => 0,\n")
57
+ ->write(" 'index' => 1,\n")
58
+ ->write(" 'first' => true,\n")
59
+ ->write(");\n")
60
+ ;
61
+
62
+ if (!$this->getAttribute('ifexpr')) {
63
+ $compiler
64
+ ->write("if (is_array(\$context['_seq']) || (is_object(\$context['_seq']) && \$context['_seq'] instanceof Countable)) {\n")
65
+ ->indent()
66
+ ->write("\$length = count(\$context['_seq']);\n")
67
+ ->write("\$context['loop']['revindex0'] = \$length - 1;\n")
68
+ ->write("\$context['loop']['revindex'] = \$length;\n")
69
+ ->write("\$context['loop']['length'] = \$length;\n")
70
+ ->write("\$context['loop']['last'] = 1 === \$length;\n")
71
+ ->outdent()
72
+ ->write("}\n")
73
+ ;
74
+ }
75
+ }
76
+
77
+ $this->loop->setAttribute('else', $this->hasNode('else'));
78
+ $this->loop->setAttribute('with_loop', $this->getAttribute('with_loop'));
79
+ $this->loop->setAttribute('ifexpr', $this->getAttribute('ifexpr'));
80
+
81
+ $compiler
82
+ ->write("foreach (\$context['_seq'] as ")
83
+ ->subcompile($this->getNode('key_target'))
84
+ ->raw(' => ')
85
+ ->subcompile($this->getNode('value_target'))
86
+ ->raw(") {\n")
87
+ ->indent()
88
+ ->subcompile($this->getNode('body'))
89
+ ->outdent()
90
+ ->write("}\n")
91
+ ;
92
+
93
+ if ($this->hasNode('else')) {
94
+ $compiler
95
+ ->write("if (!\$context['_iterated']) {\n")
96
+ ->indent()
97
+ ->subcompile($this->getNode('else'))
98
+ ->outdent()
99
+ ->write("}\n")
100
+ ;
101
+ }
102
+
103
+ $compiler->write("\$_parent = \$context['_parent'];\n");
104
+
105
+ // remove some "private" loop variables (needed for nested loops)
106
+ $compiler->write('unset($context[\'_seq\'], $context[\'_iterated\'], $context[\''.$this->getNode('key_target')->getAttribute('name').'\'], $context[\''.$this->getNode('value_target')->getAttribute('name').'\'], $context[\'_parent\'], $context[\'loop\']);'."\n");
107
+
108
+ // keep the values set in the inner context for variables defined in the outer context
109
+ $compiler->write("\$context = array_intersect_key(\$context, \$_parent) + \$_parent;\n");
110
+ }
111
+ }
vendor/twig/twig/lib/Twig/Node/ForLoop.php ADDED
@@ -0,0 +1,50 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * This file is part of Twig.
5
+ *
6
+ * (c) Fabien Potencier
7
+ *
8
+ * For the full copyright and license information, please view the LICENSE
9
+ * file that was distributed with this source code.
10
+ */
11
+
12
+ /**
13
+ * Internal node used by the for node.
14
+ *
15
+ * @author Fabien Potencier <fabien@symfony.com>
16
+ */
17
+ class Twig_Node_ForLoop extends Twig_Node
18
+ {
19
+ public function __construct($lineno, $tag = null)
20
+ {
21
+ parent::__construct(array(), array('with_loop' => false, 'ifexpr' => false, 'else' => false), $lineno, $tag);
22
+ }
23
+
24
+ public function compile(Twig_Compiler $compiler)
25
+ {
26
+ if ($this->getAttribute('else')) {
27
+ $compiler->write("\$context['_iterated'] = true;\n");
28
+ }
29
+
30
+ if ($this->getAttribute('with_loop')) {
31
+ $compiler
32
+ ->write("++\$context['loop']['index0'];\n")
33
+ ->write("++\$context['loop']['index'];\n")
34
+ ->write("\$context['loop']['first'] = false;\n")
35
+ ;
36
+
37
+ if (!$this->getAttribute('ifexpr')) {
38
+ $compiler
39
+ ->write("if (isset(\$context['loop']['length'])) {\n")
40
+ ->indent()
41
+ ->write("--\$context['loop']['revindex0'];\n")
42
+ ->write("--\$context['loop']['revindex'];\n")
43
+ ->write("\$context['loop']['last'] = 0 === \$context['loop']['revindex0'];\n")
44
+ ->outdent()
45
+ ->write("}\n")
46
+ ;
47
+ }
48
+ }
49
+ }
50
+ }
vendor/twig/twig/lib/Twig/Node/If.php ADDED
@@ -0,0 +1,66 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * This file is part of Twig.
5
+ *
6
+ * (c) Fabien Potencier
7
+ * (c) Armin Ronacher
8
+ *
9
+ * For the full copyright and license information, please view the LICENSE
10
+ * file that was distributed with this source code.
11
+ */
12
+
13
+ /**
14
+ * Represents an if node.
15
+ *
16
+ * @author Fabien Potencier <fabien@symfony.com>
17
+ */
18
+ class Twig_Node_If extends Twig_Node
19
+ {
20
+ public function __construct(Twig_NodeInterface $tests, Twig_NodeInterface $else = null, $lineno, $tag = null)
21
+ {
22
+ $nodes = array('tests' => $tests);
23
+ if (null !== $else) {
24
+ $nodes['else'] = $else;
25
+ }
26
+
27
+ parent::__construct($nodes, array(), $lineno, $tag);
28
+ }
29
+
30
+ public function compile(Twig_Compiler $compiler)
31
+ {
32
+ $compiler->addDebugInfo($this);
33
+ for ($i = 0, $count = count($this->getNode('tests')); $i < $count; $i += 2) {
34
+ if ($i > 0) {
35
+ $compiler
36
+ ->outdent()
37
+ ->write('} elseif (')
38
+ ;
39
+ } else {
40
+ $compiler
41
+ ->write('if (')
42
+ ;
43
+ }
44
+
45
+ $compiler
46
+ ->subcompile($this->getNode('tests')->getNode($i))
47
+ ->raw(") {\n")
48
+ ->indent()
49
+ ->subcompile($this->getNode('tests')->getNode($i + 1))
50
+ ;
51
+ }
52
+
53
+ if ($this->hasNode('else')) {
54
+ $compiler
55
+ ->outdent()
56
+ ->write("} else {\n")
57
+ ->indent()
58
+ ->subcompile($this->getNode('else'))
59
+ ;
60
+ }
61
+
62
+ $compiler
63
+ ->outdent()
64
+ ->write("}\n");
65
+ }
66
+ }
vendor/twig/twig/lib/Twig/Node/Import.php ADDED
@@ -0,0 +1,49 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * This file is part of Twig.
5
+ *
6
+ * (c) Fabien Potencier
7
+ *
8
+ * For the full copyright and license information, please view the LICENSE
9
+ * file that was distributed with this source code.
10
+ */
11
+
12
+ /**
13
+ * Represents an import node.
14
+ *
15
+ * @author Fabien Potencier <fabien@symfony.com>
16
+ */
17
+ class Twig_Node_Import extends Twig_Node
18
+ {
19
+ public function __construct(Twig_Node_Expression $expr, Twig_Node_Expression $var, $lineno, $tag = null)
20
+ {
21
+ parent::__construct(array('expr' => $expr, 'var' => $var), array(), $lineno, $tag);
22
+ }
23
+
24
+ public function compile(Twig_Compiler $compiler)
25
+ {
26
+ $compiler
27
+ ->addDebugInfo($this)
28
+ ->write('')
29
+ ->subcompile($this->getNode('var'))
30
+ ->raw(' = ')
31
+ ;
32
+
33
+ if ($this->getNode('expr') instanceof Twig_Node_Expression_Name && '_self' === $this->getNode('expr')->getAttribute('name')) {
34
+ $compiler->raw('$this');
35
+ } else {
36
+ $compiler
37
+ ->raw('$this->loadTemplate(')
38
+ ->subcompile($this->getNode('expr'))
39
+ ->raw(', ')
40
+ ->repr($this->getTemplateName())
41
+ ->raw(', ')
42
+ ->repr($this->getTemplateLine())
43
+ ->raw(')')
44
+ ;
45
+ }
46
+
47
+ $compiler->raw(";\n");
48
+ }
49
+ }
vendor/twig/twig/lib/Twig/Node/Include.php ADDED
@@ -0,0 +1,88 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * This file is part of Twig.
5
+ *
6
+ * (c) Fabien Potencier
7
+ * (c) Armin Ronacher
8
+ *
9
+ * For the full copyright and license information, please view the LICENSE
10
+ * file that was distributed with this source code.
11
+ */
12
+
13
+ /**
14
+ * Represents an include node.
15
+ *
16
+ * @author Fabien Potencier <fabien@symfony.com>
17
+ */
18
+ class Twig_Node_Include extends Twig_Node implements Twig_NodeOutputInterface
19
+ {
20
+ public function __construct(Twig_Node_Expression $expr, Twig_Node_Expression $variables = null, $only = false, $ignoreMissing = false, $lineno, $tag = null)
21
+ {
22
+ $nodes = array('expr' => $expr);
23
+ if (null !== $variables) {
24
+ $nodes['variables'] = $variables;
25
+ }
26
+
27
+ parent::__construct($nodes, array('only' => (bool) $only, 'ignore_missing' => (bool) $ignoreMissing), $lineno, $tag);
28
+ }
29
+
30
+ public function compile(Twig_Compiler $compiler)
31
+ {
32
+ $compiler->addDebugInfo($this);
33
+
34
+ if ($this->getAttribute('ignore_missing')) {
35
+ $compiler
36
+ ->write("try {\n")
37
+ ->indent()
38
+ ;
39
+ }
40
+
41
+ $this->addGetTemplate($compiler);
42
+
43
+ $compiler->raw('->display(');
44
+
45
+ $this->addTemplateArguments($compiler);
46
+
47
+ $compiler->raw(");\n");
48
+
49
+ if ($this->getAttribute('ignore_missing')) {
50
+ $compiler
51
+ ->outdent()
52
+ ->write("} catch (Twig_Error_Loader \$e) {\n")
53
+ ->indent()
54
+ ->write("// ignore missing template\n")
55
+ ->outdent()
56
+ ->write("}\n\n")
57
+ ;
58
+ }
59
+ }
60
+
61
+ protected function addGetTemplate(Twig_Compiler $compiler)
62
+ {
63
+ $compiler
64
+ ->write('$this->loadTemplate(')
65
+ ->subcompile($this->getNode('expr'))
66
+ ->raw(', ')
67
+ ->repr($this->getTemplateName())
68
+ ->raw(', ')
69
+ ->repr($this->getTemplateLine())
70
+ ->raw(')')
71
+ ;
72
+ }
73
+
74
+ protected function addTemplateArguments(Twig_Compiler $compiler)
75
+ {
76
+ if (!$this->hasNode('variables')) {
77
+ $compiler->raw(false === $this->getAttribute('only') ? '$context' : 'array()');
78
+ } elseif (false === $this->getAttribute('only')) {
79
+ $compiler
80
+ ->raw('array_merge($context, ')
81
+ ->subcompile($this->getNode('variables'))
82
+ ->raw(')')
83
+ ;
84
+ } else {
85
+ $compiler->subcompile($this->getNode('variables'));
86
+ }
87
+ }
88
+ }
vendor/twig/twig/lib/Twig/Node/Macro.php ADDED
@@ -0,0 +1,123 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * This file is part of Twig.
5
+ *
6
+ * (c) Fabien Potencier
7
+ *
8
+ * For the full copyright and license information, please view the LICENSE
9
+ * file that was distributed with this source code.
10
+ */
11
+
12
+ /**
13
+ * Represents a macro node.
14
+ *
15
+ * @author Fabien Potencier <fabien@symfony.com>
16
+ */
17
+ class Twig_Node_Macro extends Twig_Node
18
+ {
19
+ const VARARGS_NAME = 'varargs';
20
+
21
+ public function __construct($name, Twig_NodeInterface $body, Twig_NodeInterface $arguments, $lineno, $tag = null)
22
+ {
23
+ foreach ($arguments as $argumentName => $argument) {
24
+ if (self::VARARGS_NAME === $argumentName) {
25
+ throw new Twig_Error_Syntax(sprintf('The argument "%s" in macro "%s" cannot be defined because the variable "%s" is reserved for arbitrary arguments.', self::VARARGS_NAME, $name, self::VARARGS_NAME), $argument->getTemplateLine());
26
+ }
27
+ }
28
+
29
+ parent::__construct(array('body' => $body, 'arguments' => $arguments), array('name' => $name), $lineno, $tag);
30
+ }
31
+
32
+ public function compile(Twig_Compiler $compiler)
33
+ {
34
+ $compiler
35
+ ->addDebugInfo($this)
36
+ ->write(sprintf('public function get%s(', $this->getAttribute('name')))
37
+ ;
38
+
39
+ $count = count($this->getNode('arguments'));
40
+ $pos = 0;
41
+ foreach ($this->getNode('arguments') as $name => $default) {
42
+ $compiler
43
+ ->raw('$__'.$name.'__ = ')
44
+ ->subcompile($default)
45
+ ;
46
+
47
+ if (++$pos < $count) {
48
+ $compiler->raw(', ');
49
+ }
50
+ }
51
+
52
+ if (PHP_VERSION_ID >= 50600) {
53
+ if ($count) {
54
+ $compiler->raw(', ');
55
+ }
56
+
57
+ $compiler->raw('...$__varargs__');
58
+ }
59
+
60
+ $compiler
61
+ ->raw(")\n")
62
+ ->write("{\n")
63
+ ->indent()
64
+ ;
65
+
66
+ $compiler
67
+ ->write("\$context = \$this->env->mergeGlobals(array(\n")
68
+ ->indent()
69
+ ;
70
+
71
+ foreach ($this->getNode('arguments') as $name => $default) {
72
+ $compiler
73
+ ->write('')
74
+ ->string($name)
75
+ ->raw(' => $__'.$name.'__')
76
+ ->raw(",\n")
77
+ ;
78
+ }
79
+
80
+ $compiler
81
+ ->write('')
82
+ ->string(self::VARARGS_NAME)
83
+ ->raw(' => ')
84
+ ;
85
+
86
+ if (PHP_VERSION_ID >= 50600) {
87
+ $compiler->raw("\$__varargs__,\n");
88
+ } else {
89
+ $compiler
90
+ ->raw('func_num_args() > ')
91
+ ->repr($count)
92
+ ->raw(' ? array_slice(func_get_args(), ')
93
+ ->repr($count)
94
+ ->raw(") : array(),\n")
95
+ ;
96
+ }
97
+
98
+ $compiler
99
+ ->outdent()
100
+ ->write("));\n\n")
101
+ ->write("\$blocks = array();\n\n")
102
+ ->write("ob_start();\n")
103
+ ->write("try {\n")
104
+ ->indent()
105
+ ->subcompile($this->getNode('body'))
106
+ ->outdent()
107
+ ->write("} catch (Exception \$e) {\n")
108
+ ->indent()
109
+ ->write("ob_end_clean();\n\n")
110
+ ->write("throw \$e;\n")
111
+ ->outdent()
112
+ ->write("} catch (Throwable \$e) {\n")
113
+ ->indent()
114
+ ->write("ob_end_clean();\n\n")
115
+ ->write("throw \$e;\n")
116
+ ->outdent()
117
+ ->write("}\n\n")
118
+ ->write("return ('' === \$tmp = ob_get_clean()) ? '' : new Twig_Markup(\$tmp, \$this->env->getCharset());\n")
119
+ ->outdent()
120
+ ->write("}\n\n")
121
+ ;
122
+ }
123
+ }
vendor/twig/twig/lib/Twig/Node/Module.php ADDED
@@ -0,0 +1,459 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * This file is part of Twig.
5
+ *
6
+ * (c) Fabien Potencier
7
+ * (c) Armin Ronacher
8
+ *
9
+ * For the full copyright and license information, please view the LICENSE
10
+ * file that was distributed with this source code.
11
+ */
12
+
13
+ /**
14
+ * Represents a module node.
15
+ *
16
+ * Consider this class as being final. If you need to customize the behavior of
17
+ * the generated class, consider adding nodes to the following nodes: display_start,
18
+ * display_end, constructor_start, constructor_end, and class_end.
19
+ *
20
+ * @author Fabien Potencier <fabien@symfony.com>
21
+ */
22
+ class Twig_Node_Module extends Twig_Node
23
+ {
24
+ private $source;
25
+
26
+ public function __construct(Twig_NodeInterface $body, Twig_Node_Expression $parent = null, Twig_NodeInterface $blocks, Twig_NodeInterface $macros, Twig_NodeInterface $traits, $embeddedTemplates, $name, $source = '')
27
+ {
28
+ if (!$name instanceof Twig_Source) {
29
+ @trigger_error(sprintf('Passing a string as the $name argument of %s() is deprecated since version 1.27. Pass a Twig_Source instance instead.', __METHOD__), E_USER_DEPRECATED);
30
+ $this->source = new Twig_Source($source, $name);
31
+ } else {
32
+ $this->source = $name;
33
+ }
34
+
35
+ $nodes = array(
36
+ 'body' => $body,
37
+ 'blocks' => $blocks,
38
+ 'macros' => $macros,
39
+ 'traits' => $traits,
40
+ 'display_start' => new Twig_Node(),
41
+ 'display_end' => new Twig_Node(),
42
+ 'constructor_start' => new Twig_Node(),
43
+ 'constructor_end' => new Twig_Node(),
44
+ 'class_end' => new Twig_Node(),
45
+ );
46
+ if (null !== $parent) {
47
+ $nodes['parent'] = $parent;
48
+ }
49
+
50
+ // embedded templates are set as attributes so that they are only visited once by the visitors
51
+ parent::__construct($nodes, array(
52
+ // source to be remove in 2.0
53
+ 'source' => $this->source->getCode(),
54
+ // filename to be remove in 2.0 (use getTemplateName() instead)
55
+ 'filename' => $this->source->getName(),
56
+ 'index' => null,
57
+ 'embedded_templates' => $embeddedTemplates,
58
+ ), 1);
59
+
60
+ // populate the template name of all node children
61
+ $this->setTemplateName($this->source->getName());
62
+ }
63
+
64
+ public function setIndex($index)
65
+ {
66
+ $this->setAttribute('index', $index);
67
+ }
68
+
69
+ public function compile(Twig_Compiler $compiler)
70
+ {
71
+ $this->compileTemplate($compiler);
72
+
73
+ foreach ($this->getAttribute('embedded_templates') as $template) {
74
+ $compiler->subcompile($template);
75
+ }
76
+ }
77
+
78
+ protected function compileTemplate(Twig_Compiler $compiler)
79
+ {
80
+ if (!$this->getAttribute('index')) {
81
+ $compiler->write('<?php');
82
+ }
83
+
84
+ $this->compileClassHeader($compiler);
85
+
86
+ if (
87
+ count($this->getNode('blocks'))
88
+ || count($this->getNode('traits'))
89
+ || !$this->hasNode('parent')
90
+ || $this->getNode('parent') instanceof Twig_Node_Expression_Constant
91
+ || count($this->getNode('constructor_start'))
92
+ || count($this->getNode('constructor_end'))
93
+ ) {
94
+ $this->compileConstructor($compiler);
95
+ }
96
+
97
+ $this->compileGetParent($compiler);
98
+
99
+ $this->compileDisplay($compiler);
100
+
101
+ $compiler->subcompile($this->getNode('blocks'));
102
+
103
+ $this->compileMacros($compiler);
104
+
105
+ $this->compileGetTemplateName($compiler);
106
+
107
+ $this->compileIsTraitable($compiler);
108
+
109
+ $this->compileDebugInfo($compiler);
110
+
111
+ $this->compileGetSource($compiler);
112
+
113
+ $this->compileGetSourceContext($compiler);
114
+
115
+ $this->compileClassFooter($compiler);
116
+ }
117
+
118
+ protected function compileGetParent(Twig_Compiler $compiler)
119
+ {
120
+ if (!$this->hasNode('parent')) {
121
+ return;
122
+ }
123
+ $parent = $this->getNode('parent');
124
+
125
+ $compiler
126
+ ->write("protected function doGetParent(array \$context)\n", "{\n")
127
+ ->indent()
128
+ ->addDebugInfo($parent)
129
+ ->write('return ')
130
+ ;
131
+
132
+ if ($parent instanceof Twig_Node_Expression_Constant) {
133
+ $compiler->subcompile($parent);
134
+ } else {
135
+ $compiler
136
+ ->raw('$this->loadTemplate(')
137
+ ->subcompile($parent)
138
+ ->raw(', ')
139
+ ->repr($this->source->getName())
140
+ ->raw(', ')
141
+ ->repr($parent->getTemplateLine())
142
+ ->raw(')')
143
+ ;
144
+ }
145
+
146
+ $compiler
147
+ ->raw(";\n")
148
+ ->outdent()
149
+ ->write("}\n\n")
150
+ ;
151
+ }
152
+
153
+ protected function compileClassHeader(Twig_Compiler $compiler)
154
+ {
155
+ $compiler
156
+ ->write("\n\n")
157
+ // if the template name contains */, add a blank to avoid a PHP parse error
158
+ ->write('/* '.str_replace('*/', '* /', $this->source->getName())." */\n")
159
+ ->write('class '.$compiler->getEnvironment()->getTemplateClass($this->source->getName(), $this->getAttribute('index')))
160
+ ->raw(sprintf(" extends %s\n", $compiler->getEnvironment()->getBaseTemplateClass()))
161
+ ->write("{\n")
162
+ ->indent()
163
+ ;
164
+ }
165
+
166
+ protected function compileConstructor(Twig_Compiler $compiler)
167
+ {
168
+ $compiler
169
+ ->write("public function __construct(Twig_Environment \$env)\n", "{\n")
170
+ ->indent()
171
+ ->subcompile($this->getNode('constructor_start'))
172
+ ->write("parent::__construct(\$env);\n\n")
173
+ ;
174
+
175
+ // parent
176
+ if (!$this->hasNode('parent')) {
177
+ $compiler->write("\$this->parent = false;\n\n");
178
+ } elseif (($parent = $this->getNode('parent')) && $parent instanceof Twig_Node_Expression_Constant) {
179
+ $compiler
180
+ ->addDebugInfo($parent)
181
+ ->write('$this->parent = $this->loadTemplate(')
182
+ ->subcompile($parent)
183
+ ->raw(', ')
184
+ ->repr($this->source->getName())
185
+ ->raw(', ')
186
+ ->repr($parent->getTemplateLine())
187
+ ->raw(");\n")
188
+ ;
189
+ }
190
+
191
+ $countTraits = count($this->getNode('traits'));
192
+ if ($countTraits) {
193
+ // traits
194
+ foreach ($this->getNode('traits') as $i => $trait) {
195
+ $this->compileLoadTemplate($compiler, $trait->getNode('template'), sprintf('$_trait_%s', $i));
196
+
197
+ $compiler
198
+ ->addDebugInfo($trait->getNode('template'))
199
+ ->write(sprintf("if (!\$_trait_%s->isTraitable()) {\n", $i))
200
+ ->indent()
201
+ ->write("throw new Twig_Error_Runtime('Template \"'.")
202
+ ->subcompile($trait->getNode('template'))
203
+ ->raw(".'\" cannot be used as a trait.');\n")
204
+ ->outdent()
205
+ ->write("}\n")
206
+ ->write(sprintf("\$_trait_%s_blocks = \$_trait_%s->getBlocks();\n\n", $i, $i))
207
+ ;
208
+
209
+ foreach ($trait->getNode('targets') as $key => $value) {
210
+ $compiler
211
+ ->write(sprintf('if (!isset($_trait_%s_blocks[', $i))
212
+ ->string($key)
213
+ ->raw("])) {\n")
214
+ ->indent()
215
+ ->write("throw new Twig_Error_Runtime(sprintf('Block ")
216
+ ->string($key)
217
+ ->raw(' is not defined in trait ')
218
+ ->subcompile($trait->getNode('template'))
219
+ ->raw(".'));\n")
220
+ ->outdent()
221
+ ->write("}\n\n")
222
+
223
+ ->write(sprintf('$_trait_%s_blocks[', $i))
224
+ ->subcompile($value)
225
+ ->raw(sprintf('] = $_trait_%s_blocks[', $i))
226
+ ->string($key)
227
+ ->raw(sprintf(']; unset($_trait_%s_blocks[', $i))
228
+ ->string($key)
229
+ ->raw("]);\n\n")
230
+ ;
231
+ }
232
+ }
233
+
234
+ if ($countTraits > 1) {
235
+ $compiler
236
+ ->write("\$this->traits = array_merge(\n")
237
+ ->indent()
238
+ ;
239
+
240
+ for ($i = 0; $i < $countTraits; ++$i) {
241
+ $compiler
242
+ ->write(sprintf('$_trait_%s_blocks'.($i == $countTraits - 1 ? '' : ',')."\n", $i))
243
+ ;
244
+ }
245
+
246
+ $compiler
247
+ ->outdent()
248
+ ->write(");\n\n")
249
+ ;
250
+ } else {
251
+ $compiler
252
+ ->write("\$this->traits = \$_trait_0_blocks;\n\n")
253
+ ;
254
+ }
255
+
256
+ $compiler
257
+ ->write("\$this->blocks = array_merge(\n")
258
+ ->indent()
259
+ ->write("\$this->traits,\n")
260
+ ->write("array(\n")
261
+ ;
262
+ } else {
263
+ $compiler
264
+ ->write("\$this->blocks = array(\n")
265
+ ;
266
+ }
267
+
268
+ // blocks
269
+ $compiler
270
+ ->indent()
271
+ ;
272
+
273
+ foreach ($this->getNode('blocks') as $name => $node) {
274
+ $compiler
275
+ ->write(sprintf("'%s' => array(\$this, 'block_%s'),\n", $name, $name))
276
+ ;
277
+ }
278
+
279
+ if ($countTraits) {
280
+ $compiler
281
+ ->outdent()
282
+ ->write(")\n")
283
+ ;
284
+ }
285
+
286
+ $compiler
287
+ ->outdent()
288
+ ->write(");\n")
289
+ ->outdent()
290
+ ->subcompile($this->getNode('constructor_end'))
291
+ ->write("}\n\n")
292
+ ;
293
+ }
294
+
295
+ protected function compileDisplay(Twig_Compiler $compiler)
296
+ {
297
+ $compiler
298
+ ->write("protected function doDisplay(array \$context, array \$blocks = array())\n", "{\n")
299
+ ->indent()
300
+ ->subcompile($this->getNode('display_start'))
301
+ ->subcompile($this->getNode('body'))
302
+ ;
303
+
304
+ if ($this->hasNode('parent')) {
305
+ $parent = $this->getNode('parent');
306
+ $compiler->addDebugInfo($parent);
307
+ if ($parent instanceof Twig_Node_Expression_Constant) {
308
+ $compiler->write('$this->parent');
309
+ } else {
310
+ $compiler->write('$this->getParent($context)');
311
+ }
312
+ $compiler->raw("->display(\$context, array_merge(\$this->blocks, \$blocks));\n");
313
+ }
314
+
315
+ $compiler
316
+ ->subcompile($this->getNode('display_end'))
317
+ ->outdent()
318
+ ->write("}\n\n")
319
+ ;
320
+ }
321
+
322
+ protected function compileClassFooter(Twig_Compiler $compiler)
323
+ {
324
+ $compiler
325
+ ->subcompile($this->getNode('class_end'))
326
+ ->outdent()
327
+ ->write("}\n")
328
+ ;
329
+ }
330
+
331
+ protected function compileMacros(Twig_Compiler $compiler)
332
+ {
333
+ $compiler->subcompile($this->getNode('macros'));
334
+ }
335
+
336
+ protected function compileGetTemplateName(Twig_Compiler $compiler)
337
+ {
338
+ $compiler
339
+ ->write("public function getTemplateName()\n", "{\n")
340
+ ->indent()
341
+ ->write('return ')
342
+ ->repr($this->source->getName())
343
+ ->raw(";\n")
344
+ ->outdent()
345
+ ->write("}\n\n")
346
+ ;
347
+ }
348
+
349
+ protected function compileIsTraitable(Twig_Compiler $compiler)
350
+ {
351
+ // A template can be used as a trait if:
352
+ // * it has no parent
353
+ // * it has no macros
354
+ // * it has no body
355
+ //
356
+ // Put another way, a template can be used as a trait if it
357
+ // only contains blocks and use statements.
358
+ $traitable = !$this->hasNode('parent') && 0 === count($this->getNode('macros'));
359
+ if ($traitable) {
360
+ if ($this->getNode('body') instanceof Twig_Node_Body) {
361
+ $nodes = $this->getNode('body')->getNode(0);
362
+ } else {
363
+ $nodes = $this->getNode('body');
364
+ }
365
+
366
+ if (!count($nodes)) {
367
+ $nodes = new Twig_Node(array($nodes));
368
+ }
369
+
370
+ foreach ($nodes as $node) {
371
+ if (!count($node)) {
372
+ continue;
373
+ }
374
+
375
+ if ($node instanceof Twig_Node_Text && ctype_space($node->getAttribute('data'))) {
376
+ continue;
377
+ }
378
+
379
+ if ($node instanceof Twig_Node_BlockReference) {
380
+ continue;
381
+ }
382
+
383
+ $traitable = false;
384
+ break;
385
+ }
386
+ }
387
+
388
+ if ($traitable) {
389
+ return;
390
+ }
391
+
392
+ $compiler
393
+ ->write("public function isTraitable()\n", "{\n")
394
+ ->indent()
395
+ ->write(sprintf("return %s;\n", $traitable ? 'true' : 'false'))
396
+ ->outdent()
397
+ ->write("}\n\n")
398
+ ;
399
+ }
400
+
401
+ protected function compileDebugInfo(Twig_Compiler $compiler)
402
+ {
403
+ $compiler
404
+ ->write("public function getDebugInfo()\n", "{\n")
405
+ ->indent()
406
+ ->write(sprintf("return %s;\n", str_replace("\n", '', var_export(array_reverse($compiler->getDebugInfo(), true), true))))
407
+ ->outdent()
408
+ ->write("}\n\n")
409
+ ;
410
+ }
411
+
412
+ protected function compileGetSource(Twig_Compiler $compiler)
413
+ {
414
+ $compiler
415
+ ->write("/** @deprecated since 1.27 (to be removed in 2.0). Use getSourceContext() instead */\n")
416
+ ->write("public function getSource()\n", "{\n")
417
+ ->indent()
418
+ ->write("@trigger_error('The '.__METHOD__.' method is deprecated since version 1.27 and will be removed in 2.0. Use getSourceContext() instead.', E_USER_DEPRECATED);\n\n")
419
+ ->write('return $this->getSourceContext()->getCode();')
420
+ ->raw("\n")
421
+ ->outdent()
422
+ ->write("}\n\n")
423
+ ;
424
+ }
425
+
426
+ protected function compileGetSourceContext(Twig_Compiler $compiler)
427
+ {
428
+ $compiler
429
+ ->write("public function getSourceContext()\n", "{\n")
430
+ ->indent()
431
+ ->write('return new Twig_Source(')
432
+ ->string($compiler->getEnvironment()->isDebug() ? $this->source->getCode() : '')
433
+ ->raw(', ')
434
+ ->string($this->source->getName())
435
+ ->raw(', ')
436
+ ->string($this->source->getPath())
437
+ ->raw(");\n")
438
+ ->outdent()
439
+ ->write("}\n")
440
+ ;
441
+ }
442
+
443
+ protected function compileLoadTemplate(Twig_Compiler $compiler, $node, $var)
444
+ {
445
+ if ($node instanceof Twig_Node_Expression_Constant) {
446
+ $compiler
447
+ ->write(sprintf('%s = $this->loadTemplate(', $var))
448
+ ->subcompile($node)
449
+ ->raw(', ')
450
+ ->repr($node->getTemplateName())
451
+ ->raw(', ')
452
+ ->repr($node->getTemplateLine())
453
+ ->raw(");\n")
454
+ ;
455
+ } else {
456
+ throw new LogicException('Trait templates can only be constant nodes.');
457
+ }
458
+ }
459
+ }
vendor/twig/twig/lib/Twig/Node/Print.php ADDED
@@ -0,0 +1,34 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * This file is part of Twig.
5
+ *
6
+ * (c) Fabien Potencier
7
+ * (c) Armin Ronacher
8
+ *
9
+ * For the full copyright and license information, please view the LICENSE
10
+ * file that was distributed with this source code.
11
+ */
12
+
13
+ /**
14
+ * Represents a node that outputs an expression.
15
+ *
16
+ * @author Fabien Potencier <fabien@symfony.com>
17
+ */
18
+ class Twig_Node_Print extends Twig_Node implements Twig_NodeOutputInterface
19
+ {
20
+ public function __construct(Twig_Node_Expression $expr, $lineno, $tag = null)
21
+ {
22
+ parent::__construct(array('expr' => $expr), array(), $lineno, $tag);
23
+ }
24
+
25
+ public function compile(Twig_Compiler $compiler)
26
+ {
27
+ $compiler
28
+ ->addDebugInfo($this)
29
+ ->write('echo ')
30
+ ->subcompile($this->getNode('expr'))
31
+ ->raw(";\n")
32
+ ;
33
+ }
34
+ }
vendor/twig/twig/lib/Twig/Node/Sandbox.php ADDED
@@ -0,0 +1,42 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * This file is part of Twig.
5
+ *
6
+ * (c) Fabien Potencier
7
+ *
8
+ * For the full copyright and license information, please view the LICENSE
9
+ * file that was distributed with this source code.
10
+ */
11
+
12
+ /**
13
+ * Represents a sandbox node.
14
+ *
15
+ * @author Fabien Potencier <fabien@symfony.com>
16
+ */
17
+ class Twig_Node_Sandbox extends Twig_Node
18
+ {
19
+ public function __construct(Twig_NodeInterface $body, $lineno, $tag = null)
20
+ {
21
+ parent::__construct(array('body' => $body), array(), $lineno, $tag);
22
+ }
23
+
24
+ public function compile(Twig_Compiler $compiler)
25
+ {
26
+ $compiler
27
+ ->addDebugInfo($this)
28
+ ->write("\$sandbox = \$this->env->getExtension('Twig_Extension_Sandbox');\n")
29
+ ->write("if (!\$alreadySandboxed = \$sandbox->isSandboxed()) {\n")
30
+ ->indent()
31
+ ->write("\$sandbox->enableSandbox();\n")
32
+ ->outdent()
33
+ ->write("}\n")
34
+ ->subcompile($this->getNode('body'))
35
+ ->write("if (!\$alreadySandboxed) {\n")
36
+ ->indent()
37
+ ->write("\$sandbox->disableSandbox();\n")
38
+ ->outdent()
39
+ ->write("}\n")
40
+ ;
41
+ }
42
+ }
vendor/twig/twig/lib/Twig/Node/SandboxedPrint.php ADDED
@@ -0,0 +1,49 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * This file is part of Twig.
5
+ *
6
+ * (c) Fabien Potencier
7
+ *
8
+ * For the full copyright and license information, please view the LICENSE
9
+ * file that was distributed with this source code.
10
+ */
11
+
12
+ /**
13
+ * Twig_Node_SandboxedPrint adds a check for the __toString() method
14
+ * when the variable is an object and the sandbox is activated.
15
+ *
16
+ * When there is a simple Print statement, like {{ article }},
17
+ * and if the sandbox is enabled, we need to check that the __toString()
18
+ * method is allowed if 'article' is an object.
19
+ *
20
+ * @author Fabien Potencier <fabien@symfony.com>
21
+ */
22
+ class Twig_Node_SandboxedPrint extends Twig_Node_Print
23
+ {
24
+ public function compile(Twig_Compiler $compiler)
25
+ {
26
+ $compiler
27
+ ->addDebugInfo($this)
28
+ ->write('echo $this->env->getExtension(\'Twig_Extension_Sandbox\')->ensureToStringAllowed(')
29
+ ->subcompile($this->getNode('expr'))
30
+ ->raw(");\n")
31
+ ;
32
+ }
33
+
34
+ /**
35
+ * Removes node filters.
36
+ *
37
+ * This is mostly needed when another visitor adds filters (like the escaper one).
38
+ *
39
+ * @return Twig_Node
40
+ */
41
+ protected function removeNodeFilter(Twig_Node $node)
42
+ {
43
+ if ($node instanceof Twig_Node_Expression_Filter) {
44
+ return $this->removeNodeFilter($node->getNode('node'));
45
+ }
46
+
47
+ return $node;
48
+ }
49
+ }
vendor/twig/twig/lib/Twig/Node/Set.php ADDED
@@ -0,0 +1,96 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * This file is part of Twig.
5
+ *
6
+ * (c) Fabien Potencier
7
+ *
8
+ * For the full copyright and license information, please view the LICENSE
9
+ * file that was distributed with this source code.
10
+ */
11
+
12
+ /**
13
+ * Represents a set node.
14
+ *
15
+ * @author Fabien Potencier <fabien@symfony.com>
16
+ */
17
+ class Twig_Node_Set extends Twig_Node implements Twig_NodeCaptureInterface
18
+ {
19
+ public function __construct($capture, Twig_NodeInterface $names, Twig_NodeInterface $values, $lineno, $tag = null)
20
+ {
21
+ parent::__construct(array('names' => $names, 'values' => $values), array('capture' => $capture, 'safe' => false), $lineno, $tag);
22
+
23
+ /*
24
+ * Optimizes the node when capture is used for a large block of text.
25
+ *
26
+ * {% set foo %}foo{% endset %} is compiled to $context['foo'] = new Twig_Markup("foo");
27
+ */
28
+ if ($this->getAttribute('capture')) {
29
+ $this->setAttribute('safe', true);
30
+
31
+ $values = $this->getNode('values');
32
+ if ($values instanceof Twig_Node_Text) {
33
+ $this->setNode('values', new Twig_Node_Expression_Constant($values->getAttribute('data'), $values->getTemplateLine()));
34
+ $this->setAttribute('capture', false);
35
+ }
36
+ }
37
+ }
38
+
39
+ public function compile(Twig_Compiler $compiler)
40
+ {
41
+ $compiler->addDebugInfo($this);
42
+
43
+ if (count($this->getNode('names')) > 1) {
44
+ $compiler->write('list(');
45
+ foreach ($this->getNode('names') as $idx => $node) {
46
+ if ($idx) {
47
+ $compiler->raw(', ');
48
+ }
49
+
50
+ $compiler->subcompile($node);
51
+ }
52
+ $compiler->raw(')');
53
+ } else {
54
+ if ($this->getAttribute('capture')) {
55
+ $compiler
56
+ ->write("ob_start();\n")
57
+ ->subcompile($this->getNode('values'))
58
+ ;
59
+ }
60
+
61
+ $compiler->subcompile($this->getNode('names'), false);
62
+
63
+ if ($this->getAttribute('capture')) {
64
+ $compiler->raw(" = ('' === \$tmp = ob_get_clean()) ? '' : new Twig_Markup(\$tmp, \$this->env->getCharset())");
65
+ }
66
+ }
67
+
68
+ if (!$this->getAttribute('capture')) {
69
+ $compiler->raw(' = ');
70
+
71
+ if (count($this->getNode('names')) > 1) {
72
+ $compiler->write('array(');
73
+ foreach ($this->getNode('values') as $idx => $value) {
74
+ if ($idx) {
75
+ $compiler->raw(', ');
76
+ }
77
+
78
+ $compiler->subcompile($value);
79
+ }
80
+ $compiler->raw(')');
81
+ } else {
82
+ if ($this->getAttribute('safe')) {
83
+ $compiler
84
+ ->raw("('' === \$tmp = ")
85
+ ->subcompile($this->getNode('values'))
86
+ ->raw(") ? '' : new Twig_Markup(\$tmp, \$this->env->getCharset())")
87
+ ;
88
+ } else {
89
+ $compiler->subcompile($this->getNode('values'));
90
+ }
91
+ }
92
+ }
93
+
94
+ $compiler->raw(";\n");
95
+ }
96
+ }
vendor/twig/twig/lib/Twig/Node/SetTemp.php ADDED
@@ -0,0 +1,35 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * This file is part of Twig.
5
+ *
6
+ * (c) Fabien Potencier
7
+ *
8
+ * For the full copyright and license information, please view the LICENSE
9
+ * file that was distributed with this source code.
10
+ */
11
+
12
+ class Twig_Node_SetTemp extends Twig_Node
13
+ {
14
+ public function __construct($name, $lineno)
15
+ {
16
+ parent::__construct(array(), array('name' => $name), $lineno);
17
+ }
18
+
19
+ public function compile(Twig_Compiler $compiler)
20
+ {
21
+ $name = $this->getAttribute('name');
22
+ $compiler
23
+ ->addDebugInfo($this)
24
+ ->write('if (isset($context[')
25
+ ->string($name)
26
+ ->raw('])) { $_')
27
+ ->raw($name)
28
+ ->raw('_ = $context[')
29
+ ->repr($name)
30
+ ->raw(']; } else { $_')
31
+ ->raw($name)
32
+ ->raw("_ = null; }\n")
33
+ ;
34
+ }
35
+ }
vendor/twig/twig/lib/Twig/Node/Spaceless.php ADDED
@@ -0,0 +1,35 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * This file is part of Twig.
5
+ *
6
+ * (c) Fabien Potencier
7
+ *
8
+ * For the full copyright and license information, please view the LICENSE
9
+ * file that was distributed with this source code.
10
+ */
11
+
12
+ /**
13
+ * Represents a spaceless node.
14
+ *
15
+ * It removes spaces between HTML tags.
16
+ *
17
+ * @author Fabien Potencier <fabien@symfony.com>
18
+ */
19
+ class Twig_Node_Spaceless extends Twig_Node
20
+ {
21
+ public function __construct(Twig_NodeInterface $body, $lineno, $tag = 'spaceless')
22
+ {
23
+ parent::__construct(array('body' => $body), array(), $lineno, $tag);
24
+ }
25
+
26
+ public function compile(Twig_Compiler $compiler)
27
+ {
28
+ $compiler
29
+ ->addDebugInfo($this)
30
+ ->write("ob_start();\n")
31
+ ->subcompile($this->getNode('body'))
32
+ ->write("echo trim(preg_replace('/>\s+</', '><', ob_get_clean()));\n")
33
+ ;
34
+ }
35
+ }
vendor/twig/twig/lib/Twig/Node/Text.php ADDED
@@ -0,0 +1,34 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * This file is part of Twig.
5
+ *
6
+ * (c) Fabien Potencier
7
+ * (c) Armin Ronacher
8
+ *
9
+ * For the full copyright and license information, please view the LICENSE
10
+ * file that was distributed with this source code.
11
+ */
12
+
13
+ /**
14
+ * Represents a text node.
15
+ *
16
+ * @author Fabien Potencier <fabien@symfony.com>
17
+ */
18
+ class Twig_Node_Text extends Twig_Node implements Twig_NodeOutputInterface
19
+ {
20
+ public function __construct($data, $lineno)
21
+ {
22
+ parent::__construct(array(), array('data' => $data), $lineno);
23
+ }
24
+
25
+ public function compile(Twig_Compiler $compiler)
26
+ {
27
+ $compiler
28
+ ->addDebugInfo($this)
29
+ ->write('echo ')
30
+ ->string($this->getAttribute('data'))
31
+ ->raw(";\n")
32
+ ;
33
+ }
34
+ }
vendor/twig/twig/lib/Twig/Node/With.php ADDED
@@ -0,0 +1,62 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * This file is part of Twig.
5
+ *
6
+ * (c) Fabien Potencier
7
+ *
8
+ * For the full copyright and license information, please view the LICENSE
9
+ * file that was distributed with this source code.
10
+ */
11
+
12
+ /**
13
+ * Represents a nested "with" scope.
14
+ *
15
+ * @author Fabien Potencier <fabien@symfony.com>
16
+ */
17
+ class Twig_Node_With extends Twig_Node
18
+ {
19
+ public function __construct(Twig_Node $body, Twig_Node $variables = null, $only = false, $lineno, $tag = null)
20
+ {
21
+ $nodes = array('body' => $body);
22
+ if (null !== $variables) {
23
+ $nodes['variables'] = $variables;
24
+ }
25
+
26
+ parent::__construct($nodes, array('only' => (bool) $only), $lineno, $tag);
27
+ }
28
+
29
+ public function compile(Twig_Compiler $compiler)
30
+ {
31
+ $compiler->addDebugInfo($this);
32
+
33
+ if ($this->hasNode('variables')) {
34
+ $varsName = $compiler->getVarName();
35
+ $compiler
36
+ ->write(sprintf('$%s = ', $varsName))
37
+ ->subcompile($this->getNode('variables'))
38
+ ->raw(";\n")
39
+ ->write(sprintf("if (!is_array(\$%s)) {\n", $varsName))
40
+ ->indent()
41
+ ->write("throw new Twig_Error_Runtime('Variables passed to the \"with\" tag must be a hash.');\n")
42
+ ->outdent()
43
+ ->write("}\n")
44
+ ;
45
+
46
+ if ($this->getAttribute('only')) {
47
+ $compiler->write("\$context = array('_parent' => \$context);\n");
48
+ } else {
49
+ $compiler->write("\$context['_parent'] = \$context;\n");
50
+ }
51
+
52
+ $compiler->write(sprintf("\$context = array_merge(\$context, \$%s);\n", $varsName));
53
+ } else {
54
+ $compiler->write("\$context['_parent'] = \$context;\n");
55
+ }
56
+
57
+ $compiler
58
+ ->subcompile($this->getNode('body'))
59
+ ->write("\$context = \$context['_parent'];\n")
60
+ ;
61
+ }
62
+ }
vendor/twig/twig/lib/Twig/NodeCaptureInterface.php ADDED
@@ -0,0 +1,19 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * This file is part of Twig.
5
+ *
6
+ * (c) Fabien Potencier
7
+ *
8
+ * For the full copyright and license information, please view the LICENSE
9
+ * file that was distributed with this source code.
10
+ */
11
+
12
+ /**
13
+ * Represents a node that captures any nested displayable nodes.
14
+ *
15
+ * @author Fabien Potencier <fabien@symfony.com>
16
+ */
17
+ interface Twig_NodeCaptureInterface
18
+ {
19
+ }
vendor/twig/twig/lib/Twig/NodeInterface.php ADDED
@@ -0,0 +1,32 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * This file is part of Twig.
5
+ *
6
+ * (c) Fabien Potencier
7
+ *
8
+ * For the full copyright and license information, please view the LICENSE
9
+ * file that was distributed with this source code.
10
+ */
11
+
12
+ /**
13
+ * Represents a node in the AST.
14
+ *
15
+ * @author Fabien Potencier <fabien@symfony.com>
16
+ *
17
+ * @deprecated since 1.12 (to be removed in 3.0)
18
+ */
19
+ interface Twig_NodeInterface extends Countable, IteratorAggregate
20
+ {
21
+ /**
22
+ * Compiles the node to PHP.
23
+ */
24
+ public function compile(Twig_Compiler $compiler);
25
+
26
+ /**
27
+ * @deprecated since 1.27 (to be removed in 2.0)
28
+ */
29
+ public function getLine();
30
+
31
+ public function getNodeTag();
32
+ }
vendor/twig/twig/lib/Twig/NodeOutputInterface.php ADDED
@@ -0,0 +1,19 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * This file is part of Twig.
5
+ *
6
+ * (c) Fabien Potencier
7
+ *
8
+ * For the full copyright and license information, please view the LICENSE
9
+ * file that was distributed with this source code.
10
+ */
11
+
12
+ /**
13
+ * Represents a displayable node in the AST.
14
+ *
15
+ * @author Fabien Potencier <fabien@symfony.com>
16
+ */
17
+ interface Twig_NodeOutputInterface
18
+ {
19
+ }
vendor/twig/twig/lib/Twig/NodeTraverser.php ADDED
@@ -0,0 +1,82 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * This file is part of Twig.
5
+ *
6
+ * (c) Fabien Potencier
7
+ *
8
+ * For the full copyright and license information, please view the LICENSE
9
+ * file that was distributed with this source code.
10
+ */
11
+
12
+ /**
13
+ * Twig_NodeTraverser is a node traverser.
14
+ *
15
+ * It visits all nodes and their children and calls the given visitor for each.
16
+ *
17
+ * @final
18
+ *
19
+ * @author Fabien Potencier <fabien@symfony.com>
20
+ */
21
+ class Twig_NodeTraverser
22
+ {
23
+ protected $env;
24
+ protected $visitors = array();
25
+
26
+ /**
27
+ * @param Twig_Environment $env
28
+ * @param Twig_NodeVisitorInterface[] $visitors
29
+ */
30
+ public function __construct(Twig_Environment $env, array $visitors = array())
31
+ {
32
+ $this->env = $env;
33
+ foreach ($visitors as $visitor) {
34
+ $this->addVisitor($visitor);
35
+ }
36
+ }
37
+
38
+ public function addVisitor(Twig_NodeVisitorInterface $visitor)
39
+ {
40
+ if (!isset($this->visitors[$visitor->getPriority()])) {
41
+ $this->visitors[$visitor->getPriority()] = array();
42
+ }
43
+
44
+ $this->visitors[$visitor->getPriority()][] = $visitor;
45
+ }
46
+
47
+ /**
48
+ * Traverses a node and calls the registered visitors.
49
+ *
50
+ * @return Twig_NodeInterface
51
+ */
52
+ public function traverse(Twig_NodeInterface $node)
53
+ {
54
+ ksort($this->visitors);
55
+ foreach ($this->visitors as $visitors) {
56
+ foreach ($visitors as $visitor) {
57
+ $node = $this->traverseForVisitor($visitor, $node);
58
+ }
59
+ }
60
+
61
+ return $node;
62
+ }
63
+
64
+ protected function traverseForVisitor(Twig_NodeVisitorInterface $visitor, Twig_NodeInterface $node = null)
65
+ {
66
+ if (null === $node) {
67
+ return;
68
+ }
69
+
70
+ $node = $visitor->enterNode($node, $this->env);
71
+
72
+ foreach ($node as $k => $n) {
73
+ if (false !== $n = $this->traverseForVisitor($visitor, $n)) {
74
+ $node->setNode($k, $n);
75
+ } else {
76
+ $node->removeNode($k);
77
+ }
78
+ }
79
+
80
+ return $visitor->leaveNode($node, $this->env);
81
+ }
82
+ }
vendor/twig/twig/lib/Twig/NodeVisitor/Escaper.php ADDED
@@ -0,0 +1,152 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * This file is part of Twig.
5
+ *
6
+ * (c) Fabien Potencier
7
+ *
8
+ * For the full copyright and license information, please view the LICENSE
9
+ * file that was distributed with this source code.
10
+ */
11
+
12
+ /**
13
+ * Twig_NodeVisitor_Escaper implements output escaping.
14
+ *
15
+ * @final
16
+ *
17
+ * @author Fabien Potencier <fabien@symfony.com>
18
+ */
19
+ class Twig_NodeVisitor_Escaper extends Twig_BaseNodeVisitor
20
+ {
21
+ protected $statusStack = array();
22
+ protected $blocks = array();
23
+ protected $safeAnalysis;
24
+ protected $traverser;
25
+ protected $defaultStrategy = false;
26
+ protected $safeVars = array();
27
+
28
+ public function __construct()
29
+ {
30
+ $this->safeAnalysis = new Twig_NodeVisitor_SafeAnalysis();
31
+ }
32
+
33
+ protected function doEnterNode(Twig_Node $node, Twig_Environment $env)
34
+ {
35
+ if ($node instanceof Twig_Node_Module) {
36
+ if ($env->hasExtension('Twig_Extension_Escaper') && $defaultStrategy = $env->getExtension('Twig_Extension_Escaper')->getDefaultStrategy($node->getTemplateName())) {
37
+ $this->defaultStrategy = $defaultStrategy;
38
+ }
39
+ $this->safeVars = array();
40
+ $this->blocks = array();
41
+ } elseif ($node instanceof Twig_Node_AutoEscape) {
42
+ $this->statusStack[] = $node->getAttribute('value');
43
+ } elseif ($node instanceof Twig_Node_Block) {
44
+ $this->statusStack[] = isset($this->blocks[$node->getAttribute('name')]) ? $this->blocks[$node->getAttribute('name')] : $this->needEscaping($env);
45
+ } elseif ($node instanceof Twig_Node_Import) {
46
+ $this->safeVars[] = $node->getNode('var')->getAttribute('name');
47
+ }
48
+
49
+ return $node;
50
+ }
51
+
52
+ protected function doLeaveNode(Twig_Node $node, Twig_Environment $env)
53
+ {
54
+ if ($node instanceof Twig_Node_Module) {
55
+ $this->defaultStrategy = false;
56
+ $this->safeVars = array();
57
+ $this->blocks = array();
58
+ } elseif ($node instanceof Twig_Node_Expression_Filter) {
59
+ return $this->preEscapeFilterNode($node, $env);
60
+ } elseif ($node instanceof Twig_Node_Print) {
61
+ return $this->escapePrintNode($node, $env, $this->needEscaping($env));
62
+ }
63
+
64
+ if ($node instanceof Twig_Node_AutoEscape || $node instanceof Twig_Node_Block) {
65
+ array_pop($this->statusStack);
66
+ } elseif ($node instanceof Twig_Node_BlockReference) {
67
+ $this->blocks[$node->getAttribute('name')] = $this->needEscaping($env);
68
+ }
69
+
70
+ return $node;
71
+ }
72
+
73
+ protected function escapePrintNode(Twig_Node_Print $node, Twig_Environment $env, $type)
74
+ {
75
+ if (false === $type) {
76
+ return $node;
77
+ }
78
+
79
+ $expression = $node->getNode('expr');
80
+
81
+ if ($this->isSafeFor($type, $expression, $env)) {
82
+ return $node;
83
+ }
84
+
85
+ $class = get_class($node);
86
+
87
+ return new $class(
88
+ $this->getEscaperFilter($type, $expression),
89
+ $node->getTemplateLine()
90
+ );
91
+ }
92
+
93
+ protected function preEscapeFilterNode(Twig_Node_Expression_Filter $filter, Twig_Environment $env)
94
+ {
95
+ $name = $filter->getNode('filter')->getAttribute('value');
96
+
97
+ $type = $env->getFilter($name)->getPreEscape();
98
+ if (null === $type) {
99
+ return $filter;
100
+ }
101
+
102
+ $node = $filter->getNode('node');
103
+ if ($this->isSafeFor($type, $node, $env)) {
104
+ return $filter;
105
+ }
106
+
107
+ $filter->setNode('node', $this->getEscaperFilter($type, $node));
108
+
109
+ return $filter;
110
+ }
111
+
112
+ protected function isSafeFor($type, Twig_NodeInterface $expression, $env)
113
+ {
114
+ $safe = $this->safeAnalysis->getSafe($expression);
115
+
116
+ if (null === $safe) {
117
+ if (null === $this->traverser) {
118
+ $this->traverser = new Twig_NodeTraverser($env, array($this->safeAnalysis));
119
+ }
120
+
121
+ $this->safeAnalysis->setSafeVars($this->safeVars);
122
+
123
+ $this->traverser->traverse($expression);
124
+ $safe = $this->safeAnalysis->getSafe($expression);
125
+ }
126
+
127
+ return in_array($type, $safe) || in_array('all', $safe);
128
+ }
129
+
130
+ protected function needEscaping(Twig_Environment $env)
131
+ {
132
+ if (count($this->statusStack)) {
133
+ return $this->statusStack[count($this->statusStack) - 1];
134
+ }
135
+
136
+ return $this->defaultStrategy ? $this->defaultStrategy : false;
137
+ }
138
+
139
+ protected function getEscaperFilter($type, Twig_NodeInterface $node)
140
+ {
141
+ $line = $node->getTemplateLine();
142
+ $name = new Twig_Node_Expression_Constant('escape', $line);
143
+ $args = new Twig_Node(array(new Twig_Node_Expression_Constant((string) $type, $line), new Twig_Node_Expression_Constant(null, $line), new Twig_Node_Expression_Constant(true, $line)));
144
+
145
+ return new Twig_Node_Expression_Filter($node, $name, $args, $line);
146
+ }
147
+
148
+ public function getPriority()
149
+ {
150
+ return 0;
151
+ }
152
+ }
vendor/twig/twig/lib/Twig/NodeVisitor/Optimizer.php ADDED
@@ -0,0 +1,251 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * This file is part of Twig.
5
+ *
6
+ * (c) Fabien Potencier
7
+ *
8
+ * For the full copyright and license information, please view the LICENSE
9
+ * file that was distributed with this source code.
10
+ */
11
+
12
+ /**
13
+ * Twig_NodeVisitor_Optimizer tries to optimizes the AST.
14
+ *
15
+ * This visitor is always the last registered one.
16
+ *
17
+ * You can configure which optimizations you want to activate via the
18
+ * optimizer mode.
19
+ *
20
+ * @final
21
+ *
22
+ * @author Fabien Potencier <fabien@symfony.com>
23
+ */
24
+ class Twig_NodeVisitor_Optimizer extends Twig_BaseNodeVisitor
25
+ {
26
+ const OPTIMIZE_ALL = -1;
27
+ const OPTIMIZE_NONE = 0;
28
+ const OPTIMIZE_FOR = 2;
29
+ const OPTIMIZE_RAW_FILTER = 4;
30
+ const OPTIMIZE_VAR_ACCESS = 8;
31
+
32
+ protected $loops = array();
33
+ protected $loopsTargets = array();
34
+ protected $optimizers;
35
+ protected $prependedNodes = array();
36
+ protected $inABody = false;
37
+
38
+ /**
39
+ * @param int $optimizers The optimizer mode
40
+ */
41
+ public function __construct($optimizers = -1)
42
+ {
43
+ if (!is_int($optimizers) || $optimizers > (self::OPTIMIZE_FOR | self::OPTIMIZE_RAW_FILTER | self::OPTIMIZE_VAR_ACCESS)) {
44
+ throw new InvalidArgumentException(sprintf('Optimizer mode "%s" is not valid.', $optimizers));
45
+ }
46
+
47
+ $this->optimizers = $optimizers;
48
+ }
49
+
50
+ protected function doEnterNode(Twig_Node $node, Twig_Environment $env)
51
+ {
52
+ if (self::OPTIMIZE_FOR === (self::OPTIMIZE_FOR & $this->optimizers)) {
53
+ $this->enterOptimizeFor($node, $env);
54
+ }
55
+
56
+ if (PHP_VERSION_ID < 50400 && self::OPTIMIZE_VAR_ACCESS === (self::OPTIMIZE_VAR_ACCESS & $this->optimizers) && !$env->isStrictVariables() && !$env->hasExtension('Twig_Extension_Sandbox')) {
57
+ if ($this->inABody) {
58
+ if (!$node instanceof Twig_Node_Expression) {
59
+ if (get_class($node) !== 'Twig_Node') {
60
+ array_unshift($this->prependedNodes, array());
61
+ }
62
+ } else {
63
+ $node = $this->optimizeVariables($node, $env);
64
+ }
65
+ } elseif ($node instanceof Twig_Node_Body) {
66
+ $this->inABody = true;
67
+ }
68
+ }
69
+
70
+ return $node;
71
+ }
72
+
73
+ protected function doLeaveNode(Twig_Node $node, Twig_Environment $env)
74
+ {
75
+ $expression = $node instanceof Twig_Node_Expression;
76
+
77
+ if (self::OPTIMIZE_FOR === (self::OPTIMIZE_FOR & $this->optimizers)) {
78
+ $this->leaveOptimizeFor($node, $env);
79
+ }
80
+
81
+ if (self::OPTIMIZE_RAW_FILTER === (self::OPTIMIZE_RAW_FILTER & $this->optimizers)) {
82
+ $node = $this->optimizeRawFilter($node, $env);
83
+ }
84
+
85
+ $node = $this->optimizePrintNode($node, $env);
86
+
87
+ if (self::OPTIMIZE_VAR_ACCESS === (self::OPTIMIZE_VAR_ACCESS & $this->optimizers) && !$env->isStrictVariables() && !$env->hasExtension('Twig_Extension_Sandbox')) {
88
+ if ($node instanceof Twig_Node_Body) {
89
+ $this->inABody = false;
90
+ } elseif ($this->inABody) {
91
+ if (!$expression && get_class($node) !== 'Twig_Node' && $prependedNodes = array_shift($this->prependedNodes)) {
92
+ $nodes = array();
93
+ foreach (array_unique($prependedNodes) as $name) {
94
+ $nodes[] = new Twig_Node_SetTemp($name, $node->getTemplateLine());
95
+ }
96
+
97
+ $nodes[] = $node;
98
+ $node = new Twig_Node($nodes);
99
+ }
100
+ }
101
+ }
102
+
103
+ return $node;
104
+ }
105
+
106
+ protected function optimizeVariables(Twig_NodeInterface $node, Twig_Environment $env)
107
+ {
108
+ if ('Twig_Node_Expression_Name' === get_class($node) && $node->isSimple()) {
109
+ $this->prependedNodes[0][] = $node->getAttribute('name');
110
+
111
+ return new Twig_Node_Expression_TempName($node->getAttribute('name'), $node->getTemplateLine());
112
+ }
113
+
114
+ return $node;
115
+ }
116
+
117
+ /**
118
+ * Optimizes print nodes.
119
+ *
120
+ * It replaces:
121
+ *
122
+ * * "echo $this->render(Parent)Block()" with "$this->display(Parent)Block()"
123
+ *
124
+ * @return Twig_NodeInterface
125
+ */
126
+ protected function optimizePrintNode(Twig_NodeInterface $node, Twig_Environment $env)
127
+ {
128
+ if (!$node instanceof Twig_Node_Print) {
129
+ return $node;
130
+ }
131
+
132
+ $exprNode = $node->getNode('expr');
133
+ if (
134
+ $exprNode instanceof Twig_Node_Expression_BlockReference ||
135
+ $exprNode instanceof Twig_Node_Expression_Parent
136
+ ) {
137
+ $exprNode->setAttribute('output', true);
138
+
139
+ return $exprNode;
140
+ }
141
+
142
+ return $node;
143
+ }
144
+
145
+ /**
146
+ * Removes "raw" filters.
147
+ *
148
+ * @return Twig_NodeInterface
149
+ */
150
+ protected function optimizeRawFilter(Twig_NodeInterface $node, Twig_Environment $env)
151
+ {
152
+ if ($node instanceof Twig_Node_Expression_Filter && 'raw' == $node->getNode('filter')->getAttribute('value')) {
153
+ return $node->getNode('node');
154
+ }
155
+
156
+ return $node;
157
+ }
158
+
159
+ /**
160
+ * Optimizes "for" tag by removing the "loop" variable creation whenever possible.
161
+ */
162
+ protected function enterOptimizeFor(Twig_NodeInterface $node, Twig_Environment $env)
163
+ {
164
+ if ($node instanceof Twig_Node_For) {
165
+ // disable the loop variable by default
166
+ $node->setAttribute('with_loop', false);
167
+ array_unshift($this->loops, $node);
168
+ array_unshift($this->loopsTargets, $node->getNode('value_target')->getAttribute('name'));
169
+ array_unshift($this->loopsTargets, $node->getNode('key_target')->getAttribute('name'));
170
+ } elseif (!$this->loops) {
171
+ // we are outside a loop
172
+ return;
173
+ }
174
+
175
+ // when do we need to add the loop variable back?
176
+
177
+ // the loop variable is referenced for the current loop
178
+ elseif ($node instanceof Twig_Node_Expression_Name && 'loop' === $node->getAttribute('name')) {
179
+ $node->setAttribute('always_defined', true);
180
+ $this->addLoopToCurrent();
181
+ }
182
+
183
+ // optimize access to loop targets
184
+ elseif ($node instanceof Twig_Node_Expression_Name && in_array($node->getAttribute('name'), $this->loopsTargets)) {
185
+ $node->setAttribute('always_defined', true);
186
+ }
187
+
188
+ // block reference
189
+ elseif ($node instanceof Twig_Node_BlockReference || $node instanceof Twig_Node_Expression_BlockReference) {
190
+ $this->addLoopToCurrent();
191
+ }
192
+
193
+ // include without the only attribute
194
+ elseif ($node instanceof Twig_Node_Include && !$node->getAttribute('only')) {
195
+ $this->addLoopToAll();
196
+ }
197
+
198
+ // include function without the with_context=false parameter
199
+ elseif ($node instanceof Twig_Node_Expression_Function
200
+ && 'include' === $node->getAttribute('name')
201
+ && (!$node->getNode('arguments')->hasNode('with_context')
202
+ || false !== $node->getNode('arguments')->getNode('with_context')->getAttribute('value')
203
+ )
204
+ ) {
205
+ $this->addLoopToAll();
206
+ }
207
+
208
+ // the loop variable is referenced via an attribute
209
+ elseif ($node instanceof Twig_Node_Expression_GetAttr
210
+ && (!$node->getNode('attribute') instanceof Twig_Node_Expression_Constant
211
+ || 'parent' === $node->getNode('attribute')->getAttribute('value')
212
+ )
213
+ && (true === $this->loops[0]->getAttribute('with_loop')
214
+ || ($node->getNode('node') instanceof Twig_Node_Expression_Name
215
+ && 'loop' === $node->getNode('node')->getAttribute('name')
216
+ )
217
+ )
218
+ ) {
219
+ $this->addLoopToAll();
220
+ }
221
+ }
222
+
223
+ /**
224
+ * Optimizes "for" tag by removing the "loop" variable creation whenever possible.
225
+ */
226
+ protected function leaveOptimizeFor(Twig_NodeInterface $node, Twig_Environment $env)
227
+ {
228
+ if ($node instanceof Twig_Node_For) {
229
+ array_shift($this->loops);
230
+ array_shift($this->loopsTargets);
231
+ array_shift($this->loopsTargets);
232
+ }
233
+ }
234
+
235
+ protected function addLoopToCurrent()
236
+ {
237
+ $this->loops[0]->setAttribute('with_loop', true);
238
+ }
239
+
240
+ protected function addLoopToAll()
241
+ {
242
+ foreach ($this->loops as $loop) {
243
+ $loop->setAttribute('with_loop', true);
244
+ }
245
+ }
246
+
247
+ public function getPriority()
248
+ {
249
+ return 255;
250
+ }
251
+ }
vendor/twig/twig/lib/Twig/NodeVisitor/SafeAnalysis.php ADDED
@@ -0,0 +1,148 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * This file is part of Twig.
5
+ *
6
+ * (c) Fabien Potencier
7
+ *
8
+ * For the full copyright and license information, please view the LICENSE
9
+ * file that was distributed with this source code.
10
+ */
11
+
12
+ /**
13
+ * @final
14
+ */
15
+ class Twig_NodeVisitor_SafeAnalysis extends Twig_BaseNodeVisitor
16
+ {
17
+ protected $data = array();
18
+ protected $safeVars = array();
19
+
20
+ public function setSafeVars($safeVars)
21
+ {
22
+ $this->safeVars = $safeVars;
23
+ }
24
+
25
+ public function getSafe(Twig_NodeInterface $node)
26
+ {
27
+ $hash = spl_object_hash($node);
28
+ if (!isset($this->data[$hash])) {
29
+ return;
30
+ }
31
+
32
+ foreach ($this->data[$hash] as $bucket) {
33
+ if ($bucket['key'] !== $node) {
34
+ continue;
35
+ }
36
+
37
+ if (in_array('html_attr', $bucket['value'])) {
38
+ $bucket['value'][] = 'html';
39
+ }
40
+
41
+ return $bucket['value'];
42
+ }
43
+ }
44
+
45
+ protected function setSafe(Twig_NodeInterface $node, array $safe)
46
+ {
47
+ $hash = spl_object_hash($node);
48
+ if (isset($this->data[$hash])) {
49
+ foreach ($this->data[$hash] as &$bucket) {
50
+ if ($bucket['key'] === $node) {
51
+ $bucket['value'] = $safe;
52
+
53
+ return;
54
+ }
55
+ }
56
+ }
57
+ $this->data[$hash][] = array(
58
+ 'key' => $node,
59
+ 'value' => $safe,
60
+ );
61
+ }
62
+
63
+ protected function doEnterNode(Twig_Node $node, Twig_Environment $env)
64
+ {
65
+ return $node;
66
+ }
67
+
68
+ protected function doLeaveNode(Twig_Node $node, Twig_Environment $env)
69
+ {
70
+ if ($node instanceof Twig_Node_Expression_Constant) {
71
+ // constants are marked safe for all
72
+ $this->setSafe($node, array('all'));
73
+ } elseif ($node instanceof Twig_Node_Expression_BlockReference) {
74
+ // blocks are safe by definition
75
+ $this->setSafe($node, array('all'));
76
+ } elseif ($node instanceof Twig_Node_Expression_Parent) {
77
+ // parent block is safe by definition
78
+ $this->setSafe($node, array('all'));
79
+ } elseif ($node instanceof Twig_Node_Expression_Conditional) {
80
+ // intersect safeness of both operands
81
+ $safe = $this->intersectSafe($this->getSafe($node->getNode('expr2')), $this->getSafe($node->getNode('expr3')));
82
+ $this->setSafe($node, $safe);
83
+ } elseif ($node instanceof Twig_Node_Expression_Filter) {
84
+ // filter expression is safe when the filter is safe
85
+ $name = $node->getNode('filter')->getAttribute('value');
86
+ $args = $node->getNode('arguments');
87
+ if (false !== $filter = $env->getFilter($name)) {
88
+ $safe = $filter->getSafe($args);
89
+ if (null === $safe) {
90
+ $safe = $this->intersectSafe($this->getSafe($node->getNode('node')), $filter->getPreservesSafety());
91
+ }
92
+ $this->setSafe($node, $safe);
93
+ } else {
94
+ $this->setSafe($node, array());
95
+ }
96
+ } elseif ($node instanceof Twig_Node_Expression_Function) {
97
+ // function expression is safe when the function is safe
98
+ $name = $node->getAttribute('name');
99
+ $args = $node->getNode('arguments');
100
+ $function = $env->getFunction($name);
101
+ if (false !== $function) {
102
+ $this->setSafe($node, $function->getSafe($args));
103
+ } else {
104
+ $this->setSafe($node, array());
105
+ }
106
+ } elseif ($node instanceof Twig_Node_Expression_MethodCall) {
107
+ if ($node->getAttribute('safe')) {
108
+ $this->setSafe($node, array('all'));
109
+ } else {
110
+ $this->setSafe($node, array());
111
+ }
112
+ } elseif ($node instanceof Twig_Node_Expression_GetAttr && $node->getNode('node') instanceof Twig_Node_Expression_Name) {
113
+ $name = $node->getNode('node')->getAttribute('name');
114
+ // attributes on template instances are safe
115
+ if ('_self' == $name || in_array($name, $this->safeVars)) {
116
+ $this->setSafe($node, array('all'));
117
+ } else {
118
+ $this->setSafe($node, array());
119
+ }
120
+ } else {
121
+ $this->setSafe($node, array());
122
+ }
123
+
124
+ return $node;
125
+ }
126
+
127
+ protected function intersectSafe(array $a = null, array $b = null)
128
+ {
129
+ if (null === $a || null === $b) {
130
+ return array();
131
+ }
132
+
133
+ if (in_array('all', $a)) {
134
+ return $b;
135
+ }
136
+
137
+ if (in_array('all', $b)) {
138
+ return $a;
139
+ }
140
+
141
+ return array_intersect($a, $b);
142
+ }
143
+
144
+ public function getPriority()
145
+ {
146
+ return 0;
147
+ }
148
+ }
vendor/twig/twig/lib/Twig/NodeVisitor/Sandbox.php ADDED
@@ -0,0 +1,75 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * This file is part of Twig.
5
+ *
6
+ * (c) Fabien Potencier
7
+ *
8
+ * For the full copyright and license information, please view the LICENSE
9
+ * file that was distributed with this source code.
10
+ */
11
+
12
+ /**
13
+ * Twig_NodeVisitor_Sandbox implements sandboxing.
14
+ *
15
+ * @final
16
+ *
17
+ * @author Fabien Potencier <fabien@symfony.com>
18
+ */
19
+ class Twig_NodeVisitor_Sandbox extends Twig_BaseNodeVisitor
20
+ {
21
+ protected $inAModule = false;
22
+ protected $tags;
23
+ protected $filters;
24
+ protected $functions;
25
+
26
+ protected function doEnterNode(Twig_Node $node, Twig_Environment $env)
27
+ {
28
+ if ($node instanceof Twig_Node_Module) {
29
+ $this->inAModule = true;
30
+ $this->tags = array();
31
+ $this->filters = array();
32
+ $this->functions = array();
33
+
34
+ return $node;
35
+ } elseif ($this->inAModule) {
36
+ // look for tags
37
+ if ($node->getNodeTag() && !isset($this->tags[$node->getNodeTag()])) {
38
+ $this->tags[$node->getNodeTag()] = $node;
39
+ }
40
+
41
+ // look for filters
42
+ if ($node instanceof Twig_Node_Expression_Filter && !isset($this->filters[$node->getNode('filter')->getAttribute('value')])) {
43
+ $this->filters[$node->getNode('filter')->getAttribute('value')] = $node;
44
+ }
45
+
46
+ // look for functions
47
+ if ($node instanceof Twig_Node_Expression_Function && !isset($this->functions[$node->getAttribute('name')])) {
48
+ $this->functions[$node->getAttribute('name')] = $node;
49
+ }
50
+
51
+ // wrap print to check __toString() calls
52
+ if ($node instanceof Twig_Node_Print) {
53
+ return new Twig_Node_SandboxedPrint($node->getNode('expr'), $node->getTemplateLine(), $node->getNodeTag());
54
+ }
55
+ }
56
+
57
+ return $node;
58
+ }
59
+
60
+ protected function doLeaveNode(Twig_Node $node, Twig_Environment $env)
61
+ {
62
+ if ($node instanceof Twig_Node_Module) {
63
+ $this->inAModule = false;
64
+
65
+ $node->setNode('display_start', new Twig_Node(array(new Twig_Node_CheckSecurity($this->filters, $this->tags, $this->functions), $node->getNode('display_start'))));
66
+ }
67
+
68
+ return $node;
69
+ }
70
+
71
+ public function getPriority()
72
+ {
73
+ return 0;
74
+ }
75
+ }
vendor/twig/twig/lib/Twig/NodeVisitorInterface.php ADDED
@@ -0,0 +1,41 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * This file is part of Twig.
5
+ *
6
+ * (c) Fabien Potencier
7
+ *
8
+ * For the full copyright and license information, please view the LICENSE
9
+ * file that was distributed with this source code.
10
+ */
11
+
12
+ /**
13
+ * Twig_NodeVisitorInterface is the interface the all node visitor classes must implement.
14
+ *
15
+ * @author Fabien Potencier <fabien@symfony.com>
16
+ */
17
+ interface Twig_NodeVisitorInterface
18
+ {
19
+ /**
20
+ * Called before child nodes are visited.
21
+ *
22
+ * @return Twig_NodeInterface The modified node
23
+ */
24
+ public function enterNode(Twig_NodeInterface $node, Twig_Environment $env);
25
+
26
+ /**
27
+ * Called after child nodes are visited.
28
+ *
29
+ * @return Twig_NodeInterface|false The modified node or false if the node must be removed
30
+ */
31
+ public function leaveNode(Twig_NodeInterface $node, Twig_Environment $env);
32
+
33
+ /**
34
+ * Returns the priority for this visitor.
35
+ *
36
+ * Priority should be between -10 and 10 (0 is the default).
37
+ *
38
+ * @return int The priority level
39
+ */
40
+ public function getPriority();
41
+ }
vendor/twig/twig/lib/Twig/Parser.php ADDED
@@ -0,0 +1,406 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * This file is part of Twig.
5
+ *
6
+ * (c) Fabien Potencier
7
+ * (c) Armin Ronacher
8
+ *
9
+ * For the full copyright and license information, please view the LICENSE
10
+ * file that was distributed with this source code.
11
+ */
12
+
13
+ /**
14
+ * Default parser implementation.
15
+ *
16
+ * @author Fabien Potencier <fabien@symfony.com>
17
+ */
18
+ class Twig_Parser implements Twig_ParserInterface
19
+ {
20
+ protected $stack = array();
21
+ protected $stream;
22
+ protected $parent;
23
+ protected $handlers;
24
+ protected $visitors;
25
+ protected $expressionParser;
26
+ protected $blocks;
27
+ protected $blockStack;
28
+ protected $macros;
29
+ protected $env;
30
+ protected $reservedMacroNames;
31
+ protected $importedSymbols;
32
+ protected $traits;
33
+ protected $embeddedTemplates = array();
34
+
35
+ public function __construct(Twig_Environment $env)
36
+ {
37
+ $this->env = $env;
38
+ }
39
+
40
+ /**
41
+ * @deprecated since 1.27 (to be removed in 2.0)
42
+ */
43
+ public function getEnvironment()
44
+ {
45
+ @trigger_error('The '.__METHOD__.' method is deprecated since version 1.27 and will be removed in 2.0.', E_USER_DEPRECATED);
46
+
47
+ return $this->env;
48
+ }
49
+
50
+ public function getVarName()
51
+ {
52
+ return sprintf('__internal_%s', hash('sha256', uniqid(mt_rand(), true), false));
53
+ }
54
+
55
+ /**
56
+ * @deprecated since 1.27 (to be removed in 2.0). Use $parser->getStream()->getSourceContext()->getPath() instead.
57
+ */
58
+ public function getFilename()
59
+ {
60
+ @trigger_error(sprintf('The "%s" method is deprecated since version 1.27 and will be removed in 2.0. Use $parser->getStream()->getSourceContext()->getPath() instead.', __METHOD__), E_USER_DEPRECATED);
61
+
62
+ return $this->stream->getSourceContext()->getName();
63
+ }
64
+
65
+ public function parse(Twig_TokenStream $stream, $test = null, $dropNeedle = false)
66
+ {
67
+ // push all variables into the stack to keep the current state of the parser
68
+ // using get_object_vars() instead of foreach would lead to https://bugs.php.net/71336
69
+ // This hack can be removed when min version if PHP 7.0
70
+ $vars = array();
71
+ foreach ($this as $k => $v) {
72
+ $vars[$k] = $v;
73
+ }
74
+
75
+ unset($vars['stack'], $vars['env'], $vars['handlers'], $vars['visitors'], $vars['expressionParser'], $vars['reservedMacroNames']);
76
+ $this->stack[] = $vars;
77
+
78
+ // tag handlers
79
+ if (null === $this->handlers) {
80
+ $this->handlers = $this->env->getTokenParsers();
81
+ $this->handlers->setParser($this);
82
+ }
83
+
84
+ // node visitors
85
+ if (null === $this->visitors) {
86
+ $this->visitors = $this->env->getNodeVisitors();
87
+ }
88
+
89
+ if (null === $this->expressionParser) {
90
+ $this->expressionParser = new Twig_ExpressionParser($this, $this->env);
91
+ }
92
+
93
+ $this->stream = $stream;
94
+ $this->parent = null;
95
+ $this->blocks = array();
96
+ $this->macros = array();
97
+ $this->traits = array();
98
+ $this->blockStack = array();
99
+ $this->importedSymbols = array(array());
100
+ $this->embeddedTemplates = array();
101
+
102
+ try {
103
+ $body = $this->subparse($test, $dropNeedle);
104
+
105
+ if (null !== $this->parent && null === $body = $this->filterBodyNodes($body)) {
106
+ $body = new Twig_Node();
107
+ }
108
+ } catch (Twig_Error_Syntax $e) {
109
+ if (!$e->getSourceContext()) {
110
+ $e->setSourceContext($this->stream->getSourceContext());
111
+ }
112
+
113
+ if (!$e->getTemplateLine()) {
114
+ $e->setTemplateLine($this->stream->getCurrent()->getLine());
115
+ }
116
+
117
+ throw $e;
118
+ }
119
+
120
+ $node = new Twig_Node_Module(new Twig_Node_Body(array($body)), $this->parent, new Twig_Node($this->blocks), new Twig_Node($this->macros), new Twig_Node($this->traits), $this->embeddedTemplates, $stream->getSourceContext());
121
+
122
+ $traverser = new Twig_NodeTraverser($this->env, $this->visitors);
123
+
124
+ $node = $traverser->traverse($node);
125
+
126
+ // restore previous stack so previous parse() call can resume working
127
+ foreach (array_pop($this->stack) as $key => $val) {
128
+ $this->$key = $val;
129
+ }
130
+
131
+ return $node;
132
+ }
133
+
134
+ public function subparse($test, $dropNeedle = false)
135
+ {
136
+ $lineno = $this->getCurrentToken()->getLine();
137
+ $rv = array();
138
+ while (!$this->stream->isEOF()) {
139
+ switch ($this->getCurrentToken()->getType()) {
140
+ case Twig_Token::TEXT_TYPE:
141
+ $token = $this->stream->next();
142
+ $rv[] = new Twig_Node_Text($token->getValue(), $token->getLine());
143
+ break;
144
+
145
+ case Twig_Token::VAR_START_TYPE:
146
+ $token = $this->stream->next();
147
+ $expr = $this->expressionParser->parseExpression();
148
+ $this->stream->expect(Twig_Token::VAR_END_TYPE);
149
+ $rv[] = new Twig_Node_Print($expr, $token->getLine());
150
+