Module.php 9.5KB

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