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