PageRenderTime 25ms CodeModel.GetById 28ms RepoModel.GetById 0ms app.codeStats 0ms

/vendor/Twig/Template.php

https://gitlab.com/dcnf/dcbase.org
PHP | 424 lines | 229 code | 54 blank | 141 comment | 39 complexity | fa7234fe1ed881c8524e980b0e63db09 MD5 | raw file
  1. <?php
  2. /*
  3. * This file is part of Twig.
  4. *
  5. * (c) Fabien Potencier
  6. * (c) Armin Ronacher
  7. *
  8. * For the full copyright and license information, please view the LICENSE
  9. * file that was distributed with this source code.
  10. */
  11. namespace Twig;
  12. use Twig\Error\Error;
  13. use Twig\Error\LoaderError;
  14. use Twig\Error\RuntimeError;
  15. /**
  16. * Default base class for compiled templates.
  17. *
  18. * This class is an implementation detail of how template compilation currently
  19. * works, which might change. It should never be used directly. Use $twig->load()
  20. * instead, which returns an instance of \Twig\TemplateWrapper.
  21. *
  22. * @author Fabien Potencier <fabien@symfony.com>
  23. *
  24. * @internal
  25. */
  26. abstract class Template
  27. {
  28. const ANY_CALL = 'any';
  29. const ARRAY_CALL = 'array';
  30. const METHOD_CALL = 'method';
  31. protected $parent;
  32. protected $parents = [];
  33. protected $env;
  34. protected $blocks = [];
  35. protected $traits = [];
  36. protected $extensions = [];
  37. protected $sandbox;
  38. public function __construct(Environment $env)
  39. {
  40. $this->env = $env;
  41. $this->extensions = $env->getExtensions();
  42. }
  43. /**
  44. * Returns the template name.
  45. *
  46. * @return string The template name
  47. */
  48. abstract public function getTemplateName();
  49. /**
  50. * Returns debug information about the template.
  51. *
  52. * @return array Debug information
  53. */
  54. abstract public function getDebugInfo();
  55. /**
  56. * Returns information about the original template source code.
  57. *
  58. * @return Source
  59. */
  60. abstract public function getSourceContext();
  61. /**
  62. * Returns the parent template.
  63. *
  64. * This method is for internal use only and should never be called
  65. * directly.
  66. *
  67. * @param array $context
  68. *
  69. * @return Template|TemplateWrapper|false The parent template or false if there is no parent
  70. */
  71. public function getParent(array $context)
  72. {
  73. if (null !== $this->parent) {
  74. return $this->parent;
  75. }
  76. try {
  77. $parent = $this->doGetParent($context);
  78. if (false === $parent) {
  79. return false;
  80. }
  81. if ($parent instanceof self || $parent instanceof TemplateWrapper) {
  82. return $this->parents[$parent->getSourceContext()->getName()] = $parent;
  83. }
  84. if (!isset($this->parents[$parent])) {
  85. $this->parents[$parent] = $this->loadTemplate($parent);
  86. }
  87. } catch (LoaderError $e) {
  88. $e->setSourceContext(null);
  89. $e->guess();
  90. throw $e;
  91. }
  92. return $this->parents[$parent];
  93. }
  94. protected function doGetParent(array $context)
  95. {
  96. return false;
  97. }
  98. public function isTraitable()
  99. {
  100. return true;
  101. }
  102. /**
  103. * Displays a parent block.
  104. *
  105. * This method is for internal use only and should never be called
  106. * directly.
  107. *
  108. * @param string $name The block name to display from the parent
  109. * @param array $context The context
  110. * @param array $blocks The current set of blocks
  111. */
  112. public function displayParentBlock($name, array $context, array $blocks = [])
  113. {
  114. if (isset($this->traits[$name])) {
  115. $this->traits[$name][0]->displayBlock($name, $context, $blocks, false);
  116. } elseif (false !== $parent = $this->getParent($context)) {
  117. $parent->displayBlock($name, $context, $blocks, false);
  118. } else {
  119. throw new RuntimeError(sprintf('The template has no parent and no traits defining the "%s" block.', $name), -1, $this->getSourceContext());
  120. }
  121. }
  122. /**
  123. * Displays a block.
  124. *
  125. * This method is for internal use only and should never be called
  126. * directly.
  127. *
  128. * @param string $name The block name to display
  129. * @param array $context The context
  130. * @param array $blocks The current set of blocks
  131. * @param bool $useBlocks Whether to use the current set of blocks
  132. */
  133. public function displayBlock($name, array $context, array $blocks = [], $useBlocks = true, self $templateContext = null)
  134. {
  135. if ($useBlocks && isset($blocks[$name])) {
  136. $template = $blocks[$name][0];
  137. $block = $blocks[$name][1];
  138. } elseif (isset($this->blocks[$name])) {
  139. $template = $this->blocks[$name][0];
  140. $block = $this->blocks[$name][1];
  141. } else {
  142. $template = null;
  143. $block = null;
  144. }
  145. // avoid RCEs when sandbox is enabled
  146. if (null !== $template && !$template instanceof self) {
  147. throw new \LogicException('A block must be a method on a \Twig\Template instance.');
  148. }
  149. if (null !== $template) {
  150. try {
  151. $template->$block($context, $blocks);
  152. } catch (Error $e) {
  153. if (!$e->getSourceContext()) {
  154. $e->setSourceContext($template->getSourceContext());
  155. }
  156. // this is mostly useful for \Twig\Error\LoaderError exceptions
  157. // see \Twig\Error\LoaderError
  158. if (-1 === $e->getTemplateLine()) {
  159. $e->guess();
  160. }
  161. throw $e;
  162. } catch (\Exception $e) {
  163. $e = new RuntimeError(sprintf('An exception has been thrown during the rendering of a template ("%s").', $e->getMessage()), -1, $template->getSourceContext(), $e);
  164. $e->guess();
  165. throw $e;
  166. }
  167. } elseif (false !== $parent = $this->getParent($context)) {
  168. $parent->displayBlock($name, $context, array_merge($this->blocks, $blocks), false, $templateContext ?? $this);
  169. } elseif (isset($blocks[$name])) {
  170. throw new RuntimeError(sprintf('Block "%s" should not call parent() in "%s" as the block does not exist in the parent template "%s".', $name, $blocks[$name][0]->getTemplateName(), $this->getTemplateName()), -1, $blocks[$name][0]->getSourceContext());
  171. } else {
  172. throw new RuntimeError(sprintf('Block "%s" on template "%s" does not exist.', $name, $this->getTemplateName()), -1, ($templateContext ?? $this)->getSourceContext());
  173. }
  174. }
  175. /**
  176. * Renders a parent block.
  177. *
  178. * This method is for internal use only and should never be called
  179. * directly.
  180. *
  181. * @param string $name The block name to render from the parent
  182. * @param array $context The context
  183. * @param array $blocks The current set of blocks
  184. *
  185. * @return string The rendered block
  186. */
  187. public function renderParentBlock($name, array $context, array $blocks = [])
  188. {
  189. if ($this->env->isDebug()) {
  190. ob_start();
  191. } else {
  192. ob_start(function () { return ''; });
  193. }
  194. $this->displayParentBlock($name, $context, $blocks);
  195. return ob_get_clean();
  196. }
  197. /**
  198. * Renders a block.
  199. *
  200. * This method is for internal use only and should never be called
  201. * directly.
  202. *
  203. * @param string $name The block name to render
  204. * @param array $context The context
  205. * @param array $blocks The current set of blocks
  206. * @param bool $useBlocks Whether to use the current set of blocks
  207. *
  208. * @return string The rendered block
  209. */
  210. public function renderBlock($name, array $context, array $blocks = [], $useBlocks = true)
  211. {
  212. if ($this->env->isDebug()) {
  213. ob_start();
  214. } else {
  215. ob_start(function () { return ''; });
  216. }
  217. $this->displayBlock($name, $context, $blocks, $useBlocks);
  218. return ob_get_clean();
  219. }
  220. /**
  221. * Returns whether a block exists or not in the current context of the template.
  222. *
  223. * This method checks blocks defined in the current template
  224. * or defined in "used" traits or defined in parent templates.
  225. *
  226. * @param string $name The block name
  227. * @param array $context The context
  228. * @param array $blocks The current set of blocks
  229. *
  230. * @return bool true if the block exists, false otherwise
  231. */
  232. public function hasBlock($name, array $context, array $blocks = [])
  233. {
  234. if (isset($blocks[$name])) {
  235. return $blocks[$name][0] instanceof self;
  236. }
  237. if (isset($this->blocks[$name])) {
  238. return true;
  239. }
  240. if (false !== $parent = $this->getParent($context)) {
  241. return $parent->hasBlock($name, $context);
  242. }
  243. return false;
  244. }
  245. /**
  246. * Returns all block names in the current context of the template.
  247. *
  248. * This method checks blocks defined in the current template
  249. * or defined in "used" traits or defined in parent templates.
  250. *
  251. * @param array $context The context
  252. * @param array $blocks The current set of blocks
  253. *
  254. * @return array An array of block names
  255. */
  256. public function getBlockNames(array $context, array $blocks = [])
  257. {
  258. $names = array_merge(array_keys($blocks), array_keys($this->blocks));
  259. if (false !== $parent = $this->getParent($context)) {
  260. $names = array_merge($names, $parent->getBlockNames($context));
  261. }
  262. return array_unique($names);
  263. }
  264. /**
  265. * @return Template|TemplateWrapper
  266. */
  267. protected function loadTemplate($template, $templateName = null, $line = null, $index = null)
  268. {
  269. try {
  270. if (\is_array($template)) {
  271. return $this->env->resolveTemplate($template);
  272. }
  273. if ($template instanceof self || $template instanceof TemplateWrapper) {
  274. return $template;
  275. }
  276. if ($template === $this->getTemplateName()) {
  277. $class = \get_class($this);
  278. if (false !== $pos = strrpos($class, '___', -1)) {
  279. $class = substr($class, 0, $pos);
  280. }
  281. } else {
  282. $class = $this->env->getTemplateClass($template);
  283. }
  284. return $this->env->loadTemplate($class, $template, $index);
  285. } catch (Error $e) {
  286. if (!$e->getSourceContext()) {
  287. $e->setSourceContext($templateName ? new Source('', $templateName) : $this->getSourceContext());
  288. }
  289. if ($e->getTemplateLine() > 0) {
  290. throw $e;
  291. }
  292. if (!$line) {
  293. $e->guess();
  294. } else {
  295. $e->setTemplateLine($line);
  296. }
  297. throw $e;
  298. }
  299. }
  300. /**
  301. * @internal
  302. *
  303. * @return Template
  304. */
  305. protected function unwrap()
  306. {
  307. return $this;
  308. }
  309. /**
  310. * Returns all blocks.
  311. *
  312. * This method is for internal use only and should never be called
  313. * directly.
  314. *
  315. * @return array An array of blocks
  316. */
  317. public function getBlocks()
  318. {
  319. return $this->blocks;
  320. }
  321. public function display(array $context, array $blocks = [])
  322. {
  323. $this->displayWithErrorHandling($this->env->mergeGlobals($context), array_merge($this->blocks, $blocks));
  324. }
  325. public function render(array $context)
  326. {
  327. $level = ob_get_level();
  328. if ($this->env->isDebug()) {
  329. ob_start();
  330. } else {
  331. ob_start(function () { return ''; });
  332. }
  333. try {
  334. $this->display($context);
  335. } catch (\Throwable $e) {
  336. while (ob_get_level() > $level) {
  337. ob_end_clean();
  338. }
  339. throw $e;
  340. }
  341. return ob_get_clean();
  342. }
  343. protected function displayWithErrorHandling(array $context, array $blocks = [])
  344. {
  345. try {
  346. $this->doDisplay($context, $blocks);
  347. } catch (Error $e) {
  348. if (!$e->getSourceContext()) {
  349. $e->setSourceContext($this->getSourceContext());
  350. }
  351. // this is mostly useful for \Twig\Error\LoaderError exceptions
  352. // see \Twig\Error\LoaderError
  353. if (-1 === $e->getTemplateLine()) {
  354. $e->guess();
  355. }
  356. throw $e;
  357. } catch (\Exception $e) {
  358. $e = new RuntimeError(sprintf('An exception has been thrown during the rendering of a template ("%s").', $e->getMessage()), -1, $this->getSourceContext(), $e);
  359. $e->guess();
  360. throw $e;
  361. }
  362. }
  363. /**
  364. * Auto-generated method to display the template with the given context.
  365. *
  366. * @param array $context An array of parameters to pass to the template
  367. * @param array $blocks An array of blocks to pass to the template
  368. */
  369. abstract protected function doDisplay(array $context, array $blocks = []);
  370. }