Module.php 9.4KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308
  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("public function getParent(array \$context)\n", "{\n")
  55. ->indent()
  56. ->write("\$parent = ")
  57. ->subcompile($this->getNode('parent'))
  58. ->raw(";\n")
  59. ->write("if (\$parent instanceof Twig_Template) {\n")
  60. ->indent()
  61. ->write("\$name = \$parent->getTemplateName();\n")
  62. ->write("\$this->parent[\$name] = \$parent;\n")
  63. ->write("\$parent = \$name;\n")
  64. ->outdent()
  65. ->write("} elseif (!isset(\$this->parent[\$parent])) {\n")
  66. ->indent()
  67. ->write("\$this->parent[\$parent] = \$this->env->loadTemplate(\$parent);\n")
  68. ->outdent()
  69. ->write("}\n\n")
  70. ->write("return \$this->parent[\$parent];\n")
  71. ->outdent()
  72. ->write("}\n\n")
  73. ;
  74. }
  75. protected function compileDisplayBody(Twig_Compiler $compiler)
  76. {
  77. $compiler->write("\$context = array_merge(\$this->env->getGlobals(), \$context);\n\n");
  78. $compiler->subcompile($this->getNode('body'));
  79. if (null !== $this->getNode('parent')) {
  80. $compiler->write("\$this->getParent(\$context)->display(\$context, array_merge(\$this->blocks, \$blocks));\n");
  81. }
  82. }
  83. protected function compileClassHeader(Twig_Compiler $compiler)
  84. {
  85. $compiler
  86. ->write("<?php\n\n")
  87. // if the filename contains */, add a blank to avoid a PHP parse error
  88. ->write("/* ".str_replace('*/', '* /', $this->getAttribute('filename'))." */\n")
  89. ->write('class '.$compiler->getEnvironment()->getTemplateClass($this->getAttribute('filename')))
  90. ->raw(sprintf(" extends %s\n", $compiler->getEnvironment()->getBaseTemplateClass()))
  91. ->write("{\n")
  92. ->indent()
  93. ;
  94. if (null !== $this->getNode('parent')) {
  95. $compiler->write("protected \$parent;\n\n");
  96. }
  97. }
  98. protected function compileConstructor(Twig_Compiler $compiler)
  99. {
  100. $compiler
  101. ->write("public function __construct(Twig_Environment \$env)\n", "{\n")
  102. ->indent()
  103. ->write("parent::__construct(\$env);\n\n")
  104. ->write("\$this->parent = array();\n")
  105. ;
  106. $countTraits = count($this->getNode('traits'));
  107. if ($countTraits) {
  108. // traits
  109. foreach ($this->getNode('traits') as $i => $trait) {
  110. $this->compileLoadTemplate($compiler, $trait->getNode('template'), sprintf('$_trait_%s', $i));
  111. $compiler
  112. ->addDebugInfo($trait->getNode('template'))
  113. ->write(sprintf("if (!\$_trait_%s->isTraitable()) {\n", $i))
  114. ->indent()
  115. ->write("throw new Twig_Error_Runtime('Template \"'.")
  116. ->subcompile($trait->getNode('template'))
  117. ->raw(".'\" cannot be used as a trait.');\n")
  118. ->outdent()
  119. ->write("}\n")
  120. ->write(sprintf("\$_trait_%s_blocks = \$_trait_%s->getBlocks();\n\n", $i, $i))
  121. ;
  122. foreach ($trait->getNode('targets') as $key => $value) {
  123. $compiler
  124. ->write(sprintf("\$_trait_%s_blocks[", $i))
  125. ->subcompile($value)
  126. ->raw(sprintf("] = \$_trait_%s_blocks[", $i))
  127. ->string($key)
  128. ->raw(sprintf("]; unset(\$_trait_%s_blocks[", $i))
  129. ->string($key)
  130. ->raw("]);\n\n")
  131. ;
  132. }
  133. }
  134. $compiler
  135. ->write("\$this->blocks = array_replace(\n")
  136. ->indent()
  137. ;
  138. for ($i = 0; $i < $countTraits; $i++) {
  139. $compiler
  140. ->write(sprintf("\$_trait_%s_blocks,\n", $i))
  141. ;
  142. }
  143. $compiler
  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 (!count($nodes = $this->getNode('body'))) {
  222. $nodes = new Twig_Node(array($this->getNode('body')));
  223. }
  224. foreach ($nodes as $node) {
  225. if ($node instanceof Twig_Node_Text && ctype_space($node->getAttribute('data'))) {
  226. continue;
  227. }
  228. if ($node instanceof Twig_Node_BlockReference) {
  229. continue;
  230. }
  231. $traitable = false;
  232. break;
  233. }
  234. }
  235. $compiler
  236. ->write("public function isTraitable()\n", "{\n")
  237. ->indent()
  238. ->write(sprintf("return %s;\n", $traitable ? 'true' : 'false'))
  239. ->outdent()
  240. ->write("}\n")
  241. ;
  242. }
  243. public function compileLoadTemplate(Twig_Compiler $compiler, $node, $var)
  244. {
  245. if ($node instanceof Twig_Node_Expression_Constant) {
  246. $compiler
  247. ->write(sprintf("%s = \$this->env->loadTemplate(", $var))
  248. ->subcompile($node)
  249. ->raw(");\n")
  250. ;
  251. } else {
  252. $compiler
  253. ->write(sprintf("%s = ", $var))
  254. ->subcompile($node)
  255. ->raw(";\n")
  256. ->write(sprintf("if (!%s", $var))
  257. ->raw(" instanceof Twig_Template) {\n")
  258. ->indent()
  259. ->write(sprintf("%s = \$this->env->loadTemplate(%s);\n", $var, $var))
  260. ->outdent()
  261. ->write("}\n")
  262. ;
  263. }
  264. }
  265. }