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