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\Node; 16 17/** 18 * Compiles a node to PHP code. 19 * 20 * @author Fabien Potencier <fabien@symfony.com> 21 */ 22class Compiler 23{ 24 private $lastLine; 25 private $source; 26 private $indentation; 27 private $env; 28 private $debugInfo = []; 29 private $sourceOffset; 30 private $sourceLine; 31 private $varNameSalt = 0; 32 33 public function __construct(Environment $env) 34 { 35 $this->env = $env; 36 } 37 38 /** 39 * Returns the environment instance related to this compiler. 40 * 41 * @return Environment 42 */ 43 public function getEnvironment() 44 { 45 return $this->env; 46 } 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 /** 59 * Compiles a node. 60 * 61 * @param int $indentation The current indentation 62 * 63 * @return $this 64 */ 65 public function compile(Node $node, $indentation = 0) 66 { 67 $this->lastLine = null; 68 $this->source = ''; 69 $this->debugInfo = []; 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 $this->varNameSalt = 0; 75 76 $node->compile($this); 77 78 return $this; 79 } 80 81 public function subcompile(Node $node, $raw = true) 82 { 83 if (false === $raw) { 84 $this->source .= str_repeat(' ', $this->indentation * 4); 85 } 86 87 $node->compile($this); 88 89 return $this; 90 } 91 92 /** 93 * Adds a raw string to the compiled code. 94 * 95 * @param string $string The string 96 * 97 * @return $this 98 */ 99 public function raw($string) 100 { 101 $this->source .= $string; 102 103 return $this; 104 } 105 106 /** 107 * Writes a string to the compiled code by adding indentation. 108 * 109 * @return $this 110 */ 111 public function write(...$strings) 112 { 113 foreach ($strings as $string) { 114 $this->source .= str_repeat(' ', $this->indentation * 4).$string; 115 } 116 117 return $this; 118 } 119 120 /** 121 * Adds a quoted string to the compiled code. 122 * 123 * @param string $value The string 124 * 125 * @return $this 126 */ 127 public function string($value) 128 { 129 $this->source .= sprintf('"%s"', addcslashes($value, "\0\t\"\$\\")); 130 131 return $this; 132 } 133 134 /** 135 * Returns a PHP representation of a given value. 136 * 137 * @param mixed $value The value to convert 138 * 139 * @return $this 140 */ 141 public function repr($value) 142 { 143 if (\is_int($value) || \is_float($value)) { 144 if (false !== $locale = setlocale(\LC_NUMERIC, '0')) { 145 setlocale(\LC_NUMERIC, 'C'); 146 } 147 148 $this->raw(var_export($value, true)); 149 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 174 return $this; 175 } 176 177 /** 178 * Adds debugging information. 179 * 180 * @return $this 181 */ 182 public function addDebugInfo(Node $node) 183 { 184 if ($node->getTemplateLine() != $this->lastLine) { 185 $this->write(sprintf("// line %d\n", $node->getTemplateLine())); 186 187 $this->sourceLine += substr_count($this->source, "\n", $this->sourceOffset); 188 $this->sourceOffset = \strlen($this->source); 189 $this->debugInfo[$this->sourceLine] = $node->getTemplateLine(); 190 191 $this->lastLine = $node->getTemplateLine(); 192 } 193 194 return $this; 195 } 196 197 public function getDebugInfo() 198 { 199 ksort($this->debugInfo); 200 201 return $this->debugInfo; 202 } 203 204 /** 205 * Indents the generated code. 206 * 207 * @param int $step The number of indentation to add 208 * 209 * @return $this 210 */ 211 public function indent($step = 1) 212 { 213 $this->indentation += $step; 214 215 return $this; 216 } 217 218 /** 219 * Outdents the generated code. 220 * 221 * @param int $step The number of indentation to remove 222 * 223 * @return $this 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 234 $this->indentation -= $step; 235 236 return $this; 237 } 238 239 public function getVarName() 240 { 241 return sprintf('__internal_compile_%d', $this->varNameSalt++); 242 } 243} 244 245class_alias('Twig\Compiler', 'Twig_Compiler'); 246