Module.php 9.5KB

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