Module.php 11KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373
  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. * Represents a module node.
  13. *
  14. * @package twig
  15. * @author Fabien Potencier <fabien@symfony.com>
  16. */
  17. class Twig_Node_Module extends Twig_Node
  18. {
  19. public function __construct(Twig_NodeInterface $body, Twig_Node_Expression $parent = null, Twig_NodeInterface $blocks, Twig_NodeInterface $macros, Twig_NodeInterface $traits, $embeddedTemplates, $filename)
  20. {
  21. // embedded templates are set as attributes so that they are only visited once by the visitors
  22. parent::__construct(array('parent' => $parent, 'body' => $body, 'blocks' => $blocks, 'macros' => $macros, 'traits' => $traits), array('filename' => $filename, 'index' => null, 'embedded_templates' => $embeddedTemplates), 1);
  23. }
  24. public function setIndex($index)
  25. {
  26. $this->setAttribute('index', $index);
  27. }
  28. /**
  29. * Compiles the node to PHP.
  30. *
  31. * @param Twig_Compiler A Twig_Compiler instance
  32. */
  33. public function compile(Twig_Compiler $compiler)
  34. {
  35. $this->compileTemplate($compiler);
  36. foreach ($this->getAttribute('embedded_templates') as $template) {
  37. $compiler->subcompile($template);
  38. }
  39. }
  40. protected function compileTemplate(Twig_Compiler $compiler)
  41. {
  42. if (!$this->getAttribute('index')) {
  43. $compiler->write('<?php');
  44. }
  45. $this->compileClassHeader($compiler);
  46. if (count($this->getNode('blocks')) || count($this->getNode('traits')) || null === $this->getNode('parent') || $this->getNode('parent') instanceof Twig_Node_Expression_Constant) {
  47. $this->compileConstructor($compiler);
  48. }
  49. $this->compileGetParent($compiler);
  50. $this->compileDisplayHeader($compiler);
  51. $this->compileDisplayBody($compiler);
  52. $this->compileDisplayFooter($compiler);
  53. $compiler->subcompile($this->getNode('blocks'));
  54. $this->compileMacros($compiler);
  55. $this->compileGetTemplateName($compiler);
  56. $this->compileIsTraitable($compiler);
  57. $this->compileDebugInfo($compiler);
  58. $this->compileClassFooter($compiler);
  59. }
  60. protected function compileGetParent(Twig_Compiler $compiler)
  61. {
  62. if (null === $this->getNode('parent')) {
  63. return;
  64. }
  65. $compiler
  66. ->write("protected function doGetParent(array \$context)\n", "{\n")
  67. ->indent()
  68. ->write("return ")
  69. ;
  70. if ($this->getNode('parent') instanceof Twig_Node_Expression_Constant) {
  71. $compiler->subcompile($this->getNode('parent'));
  72. } else {
  73. $compiler
  74. ->raw("\$this->env->resolveTemplate(")
  75. ->subcompile($this->getNode('parent'))
  76. ->raw(")")
  77. ;
  78. }
  79. $compiler
  80. ->raw(";\n")
  81. ->outdent()
  82. ->write("}\n\n")
  83. ;
  84. }
  85. protected function compileDisplayBody(Twig_Compiler $compiler)
  86. {
  87. $compiler->subcompile($this->getNode('body'));
  88. if (null !== $this->getNode('parent')) {
  89. if ($this->getNode('parent') instanceof Twig_Node_Expression_Constant) {
  90. $compiler->write("\$this->parent");
  91. } else {
  92. $compiler->write("\$this->getParent(\$context)");
  93. }
  94. $compiler->raw("->display(\$context, array_merge(\$this->blocks, \$blocks));\n");
  95. }
  96. }
  97. protected function compileClassHeader(Twig_Compiler $compiler)
  98. {
  99. $compiler
  100. ->write("\n\n")
  101. // if the filename contains */, add a blank to avoid a PHP parse error
  102. ->write("/* ".str_replace('*/', '* /', $this->getAttribute('filename'))." */\n")
  103. ->write('class '.$compiler->getEnvironment()->getTemplateClass($this->getAttribute('filename'), $this->getAttribute('index')))
  104. ->raw(sprintf(" extends %s\n", $compiler->getEnvironment()->getBaseTemplateClass()))
  105. ->write("{\n")
  106. ->indent()
  107. ;
  108. }
  109. protected function compileConstructor(Twig_Compiler $compiler)
  110. {
  111. $compiler
  112. ->write("public function __construct(Twig_Environment \$env)\n", "{\n")
  113. ->indent()
  114. ->write("parent::__construct(\$env);\n\n")
  115. ;
  116. // parent
  117. if (null === $this->getNode('parent')) {
  118. $compiler->write("\$this->parent = false;\n\n");
  119. } elseif ($this->getNode('parent') instanceof Twig_Node_Expression_Constant) {
  120. $compiler
  121. ->write("\$this->parent = \$this->env->loadTemplate(")
  122. ->subcompile($this->getNode('parent'))
  123. ->raw(");\n\n")
  124. ;
  125. }
  126. $countTraits = count($this->getNode('traits'));
  127. if ($countTraits) {
  128. // traits
  129. foreach ($this->getNode('traits') as $i => $trait) {
  130. $this->compileLoadTemplate($compiler, $trait->getNode('template'), sprintf('$_trait_%s', $i));
  131. $compiler
  132. ->addDebugInfo($trait->getNode('template'))
  133. ->write(sprintf("if (!\$_trait_%s->isTraitable()) {\n", $i))
  134. ->indent()
  135. ->write("throw new Twig_Error_Runtime('Template \"'.")
  136. ->subcompile($trait->getNode('template'))
  137. ->raw(".'\" cannot be used as a trait.');\n")
  138. ->outdent()
  139. ->write("}\n")
  140. ->write(sprintf("\$_trait_%s_blocks = \$_trait_%s->getBlocks();\n\n", $i, $i))
  141. ;
  142. foreach ($trait->getNode('targets') as $key => $value) {
  143. $compiler
  144. ->write(sprintf("\$_trait_%s_blocks[", $i))
  145. ->subcompile($value)
  146. ->raw(sprintf("] = \$_trait_%s_blocks[", $i))
  147. ->string($key)
  148. ->raw(sprintf("]; unset(\$_trait_%s_blocks[", $i))
  149. ->string($key)
  150. ->raw("]);\n\n")
  151. ;
  152. }
  153. }
  154. if ($countTraits > 1) {
  155. $compiler
  156. ->write("\$this->traits = array_merge(\n")
  157. ->indent()
  158. ;
  159. for ($i = 0; $i < $countTraits; $i++) {
  160. $compiler
  161. ->write(sprintf("\$_trait_%s_blocks".($i == $countTraits - 1 ? '' : ',')."\n", $i))
  162. ;
  163. }
  164. $compiler
  165. ->outdent()
  166. ->write(");\n\n")
  167. ;
  168. } else {
  169. $compiler
  170. ->write("\$this->traits = \$_trait_0_blocks;\n\n")
  171. ;
  172. }
  173. $compiler
  174. ->write("\$this->blocks = array_merge(\n")
  175. ->indent()
  176. ->write("\$this->traits,\n")
  177. ->write("array(\n")
  178. ;
  179. } else {
  180. $compiler
  181. ->write("\$this->blocks = array(\n")
  182. ;
  183. }
  184. // blocks
  185. $compiler
  186. ->indent()
  187. ;
  188. foreach ($this->getNode('blocks') as $name => $node) {
  189. $compiler
  190. ->write(sprintf("'%s' => array(\$this, 'block_%s'),\n", $name, $name))
  191. ;
  192. }
  193. if ($countTraits) {
  194. $compiler
  195. ->outdent()
  196. ->write(")\n")
  197. ;
  198. }
  199. $compiler
  200. ->outdent()
  201. ->write(");\n")
  202. ->outdent()
  203. ->write("}\n\n");
  204. ;
  205. }
  206. protected function compileDisplayHeader(Twig_Compiler $compiler)
  207. {
  208. $compiler
  209. ->write("protected function doDisplay(array \$context, array \$blocks = array())\n", "{\n")
  210. ->indent()
  211. ;
  212. }
  213. protected function compileDisplayFooter(Twig_Compiler $compiler)
  214. {
  215. $compiler
  216. ->outdent()
  217. ->write("}\n\n")
  218. ;
  219. }
  220. protected function compileClassFooter(Twig_Compiler $compiler)
  221. {
  222. $compiler
  223. ->outdent()
  224. ->write("}\n")
  225. ;
  226. }
  227. protected function compileMacros(Twig_Compiler $compiler)
  228. {
  229. $compiler->subcompile($this->getNode('macros'));
  230. }
  231. protected function compileGetTemplateName(Twig_Compiler $compiler)
  232. {
  233. $compiler
  234. ->write("public function getTemplateName()\n", "{\n")
  235. ->indent()
  236. ->write('return ')
  237. ->repr($this->getAttribute('filename'))
  238. ->raw(";\n")
  239. ->outdent()
  240. ->write("}\n\n")
  241. ;
  242. }
  243. protected function compileIsTraitable(Twig_Compiler $compiler)
  244. {
  245. // A template can be used as a trait if:
  246. // * it has no parent
  247. // * it has no macros
  248. // * it has no body
  249. //
  250. // Put another way, a template can be used as a trait if it
  251. // only contains blocks and use statements.
  252. $traitable = null === $this->getNode('parent') && 0 === count($this->getNode('macros'));
  253. if ($traitable) {
  254. if ($this->getNode('body') instanceof Twig_Node_Body) {
  255. $nodes = $this->getNode('body')->getNode(0);
  256. } else {
  257. $nodes = $this->getNode('body');
  258. }
  259. if (!count($nodes)) {
  260. $nodes = new Twig_Node(array($nodes));
  261. }
  262. foreach ($nodes as $node) {
  263. if (!count($node)) {
  264. continue;
  265. }
  266. if ($node instanceof Twig_Node_Text && ctype_space($node->getAttribute('data'))) {
  267. continue;
  268. }
  269. if ($node instanceof Twig_Node_BlockReference) {
  270. continue;
  271. }
  272. $traitable = false;
  273. break;
  274. }
  275. }
  276. if ($traitable) {
  277. return;
  278. }
  279. $compiler
  280. ->write("public function isTraitable()\n", "{\n")
  281. ->indent()
  282. ->write(sprintf("return %s;\n", $traitable ? 'true' : 'false'))
  283. ->outdent()
  284. ->write("}\n\n")
  285. ;
  286. }
  287. protected function compileDebugInfo(Twig_Compiler $compiler)
  288. {
  289. $compiler
  290. ->write("public function getDebugInfo()\n", "{\n")
  291. ->indent()
  292. ->write(sprintf("return %s;\n", str_replace("\n", '', var_export(array_reverse($compiler->getDebugInfo(), true), true))))
  293. ->outdent()
  294. ->write("}\n")
  295. ;
  296. }
  297. protected function compileLoadTemplate(Twig_Compiler $compiler, $node, $var)
  298. {
  299. if ($node instanceof Twig_Node_Expression_Constant) {
  300. $compiler
  301. ->write(sprintf("%s = \$this->env->loadTemplate(", $var))
  302. ->subcompile($node)
  303. ->raw(");\n")
  304. ;
  305. } else {
  306. $compiler
  307. ->write(sprintf("%s = ", $var))
  308. ->subcompile($node)
  309. ->raw(";\n")
  310. ->write(sprintf("if (!%s", $var))
  311. ->raw(" instanceof Twig_Template) {\n")
  312. ->indent()
  313. ->write(sprintf("%s = \$this->env->loadTemplate(%s);\n", $var, $var))
  314. ->outdent()
  315. ->write("}\n")
  316. ;
  317. }
  318. }
  319. }