PageRenderTime 27ms CodeModel.GetById 21ms RepoModel.GetById 0ms app.codeStats 0ms

/third-party/twig/lib/Twig/Template.php

https://bitbucket.org/allfields/wave
PHP | 413 lines | 204 code | 47 blank | 162 comment | 35 complexity | e7f5b44574638193e6f7503cfe11768e MD5 | raw file
Possible License(s): LGPL-2.1, LGPL-3.0, BSD-3-Clause
  1. <?php
  2. /*
  3. * This file is part of Twig.
  4. *
  5. * (c) 2009 Fabien Potencier
  6. * (c) 2009 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. /**
  12. * Default base class for compiled templates.
  13. *
  14. * @package twig
  15. * @author Fabien Potencier <fabien@symfony.com>
  16. */
  17. abstract class Twig_Template implements Twig_TemplateInterface
  18. {
  19. static protected $cache = array();
  20. protected $parents;
  21. protected $env;
  22. protected $blocks;
  23. protected $traits;
  24. /**
  25. * Constructor.
  26. *
  27. * @param Twig_Environment $env A Twig_Environment instance
  28. */
  29. public function __construct(Twig_Environment $env)
  30. {
  31. $this->env = $env;
  32. $this->blocks = array();
  33. $this->traits = array();
  34. }
  35. /**
  36. * Returns the template name.
  37. *
  38. * @return string The template name
  39. */
  40. abstract public function getTemplateName();
  41. /**
  42. * Returns the Twig environment.
  43. *
  44. * @return Twig_Environment The Twig environment
  45. */
  46. public function getEnvironment()
  47. {
  48. return $this->env;
  49. }
  50. /**
  51. * Returns the parent template.
  52. *
  53. * This method is for internal use only and should never be called
  54. * directly.
  55. *
  56. * @return Twig_TemplateInterface|false The parent template or false if there is no parent
  57. */
  58. public function getParent(array $context)
  59. {
  60. $parent = $this->doGetParent($context);
  61. if (false === $parent) {
  62. return false;
  63. } elseif ($parent instanceof Twig_Template) {
  64. $name = $parent->getTemplateName();
  65. $this->parents[$name] = $parent;
  66. $parent = $name;
  67. } elseif (!isset($this->parents[$parent])) {
  68. $this->parents[$parent] = $this->env->loadTemplate($parent);
  69. }
  70. return $this->parents[$parent];
  71. }
  72. abstract protected function doGetParent(array $context);
  73. /**
  74. * Displays a parent block.
  75. *
  76. * This method is for internal use only and should never be called
  77. * directly.
  78. *
  79. * @param string $name The block name to display from the parent
  80. * @param array $context The context
  81. * @param array $blocks The current set of blocks
  82. */
  83. public function displayParentBlock($name, array $context, array $blocks = array())
  84. {
  85. if (isset($this->traits[$name])) {
  86. $this->traits[$name][0]->displayBlock($name, $context, $blocks);
  87. } elseif (false !== $parent = $this->getParent($context)) {
  88. $parent->displayBlock($name, $context, $blocks);
  89. } else {
  90. throw new Twig_Error_Runtime(sprintf('The template has no parent and no traits defining the "%s" block', $name), -1, $this->getTemplateName());
  91. }
  92. }
  93. /**
  94. * Displays a block.
  95. *
  96. * This method is for internal use only and should never be called
  97. * directly.
  98. *
  99. * @param string $name The block name to display
  100. * @param array $context The context
  101. * @param array $blocks The current set of blocks
  102. */
  103. public function displayBlock($name, array $context, array $blocks = array())
  104. {
  105. if (isset($blocks[$name])) {
  106. $b = $blocks;
  107. unset($b[$name]);
  108. call_user_func($blocks[$name], $context, $b);
  109. } elseif (isset($this->blocks[$name])) {
  110. call_user_func($this->blocks[$name], $context, $blocks);
  111. } elseif (false !== $parent = $this->getParent($context)) {
  112. $parent->displayBlock($name, $context, array_merge($this->blocks, $blocks));
  113. }
  114. }
  115. /**
  116. * Renders a parent block.
  117. *
  118. * This method is for internal use only and should never be called
  119. * directly.
  120. *
  121. * @param string $name The block name to render from the parent
  122. * @param array $context The context
  123. * @param array $blocks The current set of blocks
  124. *
  125. * @return string The rendered block
  126. */
  127. public function renderParentBlock($name, array $context, array $blocks = array())
  128. {
  129. ob_start();
  130. $this->displayParentBlock($name, $context, $blocks);
  131. return ob_get_clean();
  132. }
  133. /**
  134. * Renders a block.
  135. *
  136. * This method is for internal use only and should never be called
  137. * directly.
  138. *
  139. * @param string $name The block name to render
  140. * @param array $context The context
  141. * @param array $blocks The current set of blocks
  142. *
  143. * @return string The rendered block
  144. */
  145. public function renderBlock($name, array $context, array $blocks = array())
  146. {
  147. ob_start();
  148. $this->displayBlock($name, $context, $blocks);
  149. return ob_get_clean();
  150. }
  151. /**
  152. * Returns whether a block exists or not.
  153. *
  154. * This method is for internal use only and should never be called
  155. * directly.
  156. *
  157. * This method does only return blocks defined in the current template
  158. * or defined in "used" traits.
  159. *
  160. * It does not return blocks from parent templates as the parent
  161. * template name can be dynamic, which is only known based on the
  162. * current context.
  163. *
  164. * @param string $name The block name
  165. *
  166. * @return Boolean true if the block exists, false otherwise
  167. */
  168. public function hasBlock($name)
  169. {
  170. return isset($this->blocks[$name]);
  171. }
  172. /**
  173. * Returns all block names.
  174. *
  175. * This method is for internal use only and should never be called
  176. * directly.
  177. *
  178. * @return array An array of block names
  179. *
  180. * @see hasBlock
  181. */
  182. public function getBlockNames()
  183. {
  184. return array_keys($this->blocks);
  185. }
  186. /**
  187. * Returns all blocks.
  188. *
  189. * This method is for internal use only and should never be called
  190. * directly.
  191. *
  192. * @return array An array of blocks
  193. *
  194. * @see hasBlock
  195. */
  196. public function getBlocks()
  197. {
  198. return $this->blocks;
  199. }
  200. /**
  201. * Displays the template with the given context.
  202. *
  203. * @param array $context An array of parameters to pass to the template
  204. * @param array $blocks An array of blocks to pass to the template
  205. */
  206. public function display(array $context, array $blocks = array())
  207. {
  208. try {
  209. $this->doDisplay($context, $blocks);
  210. } catch (Twig_Error $e) {
  211. throw $e;
  212. } catch (Exception $e) {
  213. throw new Twig_Error_Runtime(sprintf('An exception has been thrown during the rendering of a template ("%s").', $e->getMessage()), -1, null, $e);
  214. }
  215. }
  216. /**
  217. * Renders the template with the given context and returns it as string.
  218. *
  219. * @param array $context An array of parameters to pass to the template
  220. *
  221. * @return string The rendered template
  222. */
  223. public function render(array $context)
  224. {
  225. $level = ob_get_level();
  226. ob_start();
  227. try {
  228. $this->display($context);
  229. } catch (Exception $e) {
  230. while (ob_get_level() > $level) {
  231. ob_end_clean();
  232. }
  233. throw $e;
  234. }
  235. return ob_get_clean();
  236. }
  237. /**
  238. * Auto-generated method to display the template with the given context.
  239. *
  240. * @param array $context An array of parameters to pass to the template
  241. * @param array $blocks An array of blocks to pass to the template
  242. */
  243. abstract protected function doDisplay(array $context, array $blocks = array());
  244. /**
  245. * Returns a variable from the context.
  246. *
  247. * @param array $context The context
  248. * @param string $item The variable to return from the context
  249. *
  250. * @return The content of the context variable
  251. *
  252. * @throws Twig_Error_Runtime if the variable does not exist and Twig is running in strict mode
  253. */
  254. protected function getContext($context, $item)
  255. {
  256. if (!array_key_exists($item, $context)) {
  257. if (!$this->env->isStrictVariables()) {
  258. return null;
  259. }
  260. throw new Twig_Error_Runtime(sprintf('Variable "%s" does not exist', $item));
  261. }
  262. return $context[$item];
  263. }
  264. /**
  265. * Returns the attribute value for a given array/object.
  266. *
  267. * @param mixed $object The object or array from where to get the item
  268. * @param mixed $item The item to get from the array or object
  269. * @param array $arguments An array of arguments to pass if the item is an object method
  270. * @param string $type The type of attribute (@see Twig_TemplateInterface)
  271. * @param Boolean $isDefinedTest Whether this is only a defined check
  272. */
  273. protected function getAttribute($object, $item, array $arguments = array(), $type = Twig_TemplateInterface::ANY_CALL, $isDefinedTest = false)
  274. {
  275. // array
  276. if (Twig_TemplateInterface::METHOD_CALL !== $type) {
  277. if ((is_array($object) && array_key_exists($item, $object))
  278. || ($object instanceof ArrayAccess && isset($object[$item]))
  279. ) {
  280. if ($isDefinedTest) {
  281. return true;
  282. }
  283. return $object[$item];
  284. }
  285. if (Twig_TemplateInterface::ARRAY_CALL === $type) {
  286. if ($isDefinedTest) {
  287. return false;
  288. }
  289. if (!$this->env->isStrictVariables()) {
  290. return null;
  291. }
  292. if (is_object($object)) {
  293. throw new Twig_Error_Runtime(sprintf('Key "%s" in object (with ArrayAccess) of type "%s" does not exist', $item, get_class($object)));
  294. // array
  295. } else {
  296. throw new Twig_Error_Runtime(sprintf('Key "%s" for array with keys "%s" does not exist', $item, implode(', ', array_keys($object))));
  297. }
  298. }
  299. }
  300. if (!is_object($object)) {
  301. if ($isDefinedTest) {
  302. return false;
  303. }
  304. if (!$this->env->isStrictVariables()) {
  305. return null;
  306. }
  307. throw new Twig_Error_Runtime(sprintf('Item "%s" for "%s" does not exist', $item, $object));
  308. }
  309. // get some information about the object
  310. $class = get_class($object);
  311. if (!isset(self::$cache[$class])) {
  312. $r = new ReflectionClass($class);
  313. self::$cache[$class] = array('methods' => array(), 'properties' => array());
  314. foreach ($r->getMethods(ReflectionMethod::IS_PUBLIC) as $method) {
  315. self::$cache[$class]['methods'][strtolower($method->getName())] = true;
  316. }
  317. foreach ($r->getProperties(ReflectionProperty::IS_PUBLIC) as $property) {
  318. self::$cache[$class]['properties'][$property->getName()] = true;
  319. }
  320. }
  321. // object property
  322. if (Twig_TemplateInterface::METHOD_CALL !== $type) {
  323. if (isset(self::$cache[$class]['properties'][$item])
  324. || isset($object->$item) || array_key_exists($item, $object)
  325. ) {
  326. if ($isDefinedTest) {
  327. return true;
  328. }
  329. if ($this->env->hasExtension('sandbox')) {
  330. $this->env->getExtension('sandbox')->checkPropertyAllowed($object, $item);
  331. }
  332. return $object->$item;
  333. }
  334. }
  335. // object method
  336. $lcItem = strtolower($item);
  337. if (isset(self::$cache[$class]['methods'][$lcItem])) {
  338. $method = $item;
  339. } elseif (isset(self::$cache[$class]['methods']['get'.$lcItem])) {
  340. $method = 'get'.$item;
  341. } elseif (isset(self::$cache[$class]['methods']['is'.$lcItem])) {
  342. $method = 'is'.$item;
  343. } elseif (isset(self::$cache[$class]['methods']['__call'])) {
  344. $method = $item;
  345. } else {
  346. if ($isDefinedTest) {
  347. return false;
  348. }
  349. if (!$this->env->isStrictVariables()) {
  350. return null;
  351. }
  352. throw new Twig_Error_Runtime(sprintf('Method "%s" for object "%s" does not exist', $item, get_class($object)));
  353. }
  354. if ($isDefinedTest) {
  355. return true;
  356. }
  357. if ($this->env->hasExtension('sandbox')) {
  358. $this->env->getExtension('sandbox')->checkMethodAllowed($object, $method);
  359. }
  360. $ret = call_user_func_array(array($object, $method), $arguments);
  361. if ($object instanceof Twig_TemplateInterface) {
  362. return new Twig_Markup($ret);
  363. }
  364. return $ret;
  365. }
  366. }