Compiler.php 6.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278
  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. * Compiles a node to PHP code.
  13. *
  14. * @author Fabien Potencier <fabien@symfony.com>
  15. */
  16. class Twig_Compiler implements Twig_CompilerInterface
  17. {
  18. protected $lastLine;
  19. protected $source;
  20. protected $indentation;
  21. protected $env;
  22. protected $debugInfo = array();
  23. protected $sourceOffset;
  24. protected $sourceLine;
  25. protected $filename;
  26. /**
  27. * Constructor.
  28. *
  29. * @param Twig_Environment $env The twig environment instance
  30. */
  31. public function __construct(Twig_Environment $env)
  32. {
  33. $this->env = $env;
  34. }
  35. public function getFilename()
  36. {
  37. return $this->filename;
  38. }
  39. /**
  40. * Returns the environment instance related to this compiler.
  41. *
  42. * @return Twig_Environment The environment instance
  43. */
  44. public function getEnvironment()
  45. {
  46. return $this->env;
  47. }
  48. /**
  49. * Gets the current PHP code after compilation.
  50. *
  51. * @return string The PHP code
  52. */
  53. public function getSource()
  54. {
  55. return $this->source;
  56. }
  57. /**
  58. * Compiles a node.
  59. *
  60. * @param Twig_NodeInterface $node The node to compile
  61. * @param int $indentation The current indentation
  62. *
  63. * @return Twig_Compiler The current compiler instance
  64. */
  65. public function compile(Twig_NodeInterface $node, $indentation = 0)
  66. {
  67. $this->lastLine = null;
  68. $this->source = '';
  69. $this->debugInfo = array();
  70. $this->sourceOffset = 0;
  71. // source code starts at 1 (as we then increment it when we encounter new lines)
  72. $this->sourceLine = 1;
  73. $this->indentation = $indentation;
  74. if ($node instanceof Twig_Node_Module) {
  75. $this->filename = $node->getAttribute('filename');
  76. }
  77. $node->compile($this);
  78. return $this;
  79. }
  80. public function subcompile(Twig_NodeInterface $node, $raw = true)
  81. {
  82. if (false === $raw) {
  83. $this->addIndentation();
  84. }
  85. $node->compile($this);
  86. return $this;
  87. }
  88. /**
  89. * Adds a raw string to the compiled code.
  90. *
  91. * @param string $string The string
  92. *
  93. * @return Twig_Compiler The current compiler instance
  94. */
  95. public function raw($string)
  96. {
  97. $this->source .= $string;
  98. return $this;
  99. }
  100. /**
  101. * Writes a string to the compiled code by adding indentation.
  102. *
  103. * @return Twig_Compiler The current compiler instance
  104. */
  105. public function write()
  106. {
  107. $strings = func_get_args();
  108. foreach ($strings as $string) {
  109. $this->addIndentation();
  110. $this->source .= $string;
  111. }
  112. return $this;
  113. }
  114. /**
  115. * Appends an indentation to the current PHP code after compilation.
  116. *
  117. * @return Twig_Compiler The current compiler instance
  118. */
  119. public function addIndentation()
  120. {
  121. $this->source .= str_repeat(' ', $this->indentation * 4);
  122. return $this;
  123. }
  124. /**
  125. * Adds a quoted string to the compiled code.
  126. *
  127. * @param string $value The string
  128. *
  129. * @return Twig_Compiler The current compiler instance
  130. */
  131. public function string($value)
  132. {
  133. $this->source .= sprintf('"%s"', addcslashes($value, "\0\t\"\$\\"));
  134. return $this;
  135. }
  136. /**
  137. * Returns a PHP representation of a given value.
  138. *
  139. * @param mixed $value The value to convert
  140. *
  141. * @return Twig_Compiler The current compiler instance
  142. */
  143. public function repr($value)
  144. {
  145. if (is_int($value) || is_float($value)) {
  146. if (false !== $locale = setlocale(LC_NUMERIC, 0)) {
  147. setlocale(LC_NUMERIC, 'C');
  148. }
  149. $this->raw($value);
  150. if (false !== $locale) {
  151. setlocale(LC_NUMERIC, $locale);
  152. }
  153. } elseif (null === $value) {
  154. $this->raw('null');
  155. } elseif (is_bool($value)) {
  156. $this->raw($value ? 'true' : 'false');
  157. } elseif (is_array($value)) {
  158. $this->raw('array(');
  159. $first = true;
  160. foreach ($value as $key => $v) {
  161. if (!$first) {
  162. $this->raw(', ');
  163. }
  164. $first = false;
  165. $this->repr($key);
  166. $this->raw(' => ');
  167. $this->repr($v);
  168. }
  169. $this->raw(')');
  170. } else {
  171. $this->string($value);
  172. }
  173. return $this;
  174. }
  175. /**
  176. * Adds debugging information.
  177. *
  178. * @param Twig_NodeInterface $node The related twig node
  179. *
  180. * @return Twig_Compiler The current compiler instance
  181. */
  182. public function addDebugInfo(Twig_NodeInterface $node)
  183. {
  184. if ($node->getLine() != $this->lastLine) {
  185. $this->write(sprintf("// line %d\n", $node->getLine()));
  186. // when mbstring.func_overload is set to 2
  187. // mb_substr_count() replaces substr_count()
  188. // but they have different signatures!
  189. if (((int) ini_get('mbstring.func_overload')) & 2) {
  190. // this is much slower than the "right" version
  191. $this->sourceLine += mb_substr_count(mb_substr($this->source, $this->sourceOffset), "\n");
  192. } else {
  193. $this->sourceLine += substr_count($this->source, "\n", $this->sourceOffset);
  194. }
  195. $this->sourceOffset = strlen($this->source);
  196. $this->debugInfo[$this->sourceLine] = $node->getLine();
  197. $this->lastLine = $node->getLine();
  198. }
  199. return $this;
  200. }
  201. public function getDebugInfo()
  202. {
  203. ksort($this->debugInfo);
  204. return $this->debugInfo;
  205. }
  206. /**
  207. * Indents the generated code.
  208. *
  209. * @param int $step The number of indentation to add
  210. *
  211. * @return Twig_Compiler The current compiler instance
  212. */
  213. public function indent($step = 1)
  214. {
  215. $this->indentation += $step;
  216. return $this;
  217. }
  218. /**
  219. * Outdents the generated code.
  220. *
  221. * @param int $step The number of indentation to remove
  222. *
  223. * @return Twig_Compiler The current compiler instance
  224. *
  225. * @throws LogicException When trying to outdent too much so the indentation would become negative
  226. */
  227. public function outdent($step = 1)
  228. {
  229. // can't outdent by more steps than the current indentation level
  230. if ($this->indentation < $step) {
  231. throw new LogicException('Unable to call outdent() as the indentation would become negative');
  232. }
  233. $this->indentation -= $step;
  234. return $this;
  235. }
  236. public function getVarName()
  237. {
  238. return sprintf('__internal_%s', hash('sha256', uniqid(mt_rand(), true), false));
  239. }
  240. }