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\Error\SyntaxError; 16 17/** 18 * Represents a token stream. 19 * 20 * @final 21 * 22 * @author Fabien Potencier <fabien@symfony.com> 23 */ 24class TokenStream 25{ 26 protected $tokens; 27 protected $current = 0; 28 protected $filename; 29 30 private $source; 31 32 /** 33 * @param array $tokens An array of tokens 34 * @param string|null $name The name of the template which tokens are associated with 35 * @param string|null $source The source code associated with the tokens 36 */ 37 public function __construct(array $tokens, $name = null, $source = null) 38 { 39 if (!$name instanceof Source) { 40 if (null !== $name || null !== $source) { 41 @trigger_error(sprintf('Passing a string as the $name argument of %s() is deprecated since version 1.27. Pass a \Twig\Source instance instead.', __METHOD__), E_USER_DEPRECATED); 42 } 43 $this->source = new Source($source, $name); 44 } else { 45 $this->source = $name; 46 } 47 48 $this->tokens = $tokens; 49 50 // deprecated, not used anymore, to be removed in 2.0 51 $this->filename = $this->source->getName(); 52 } 53 54 public function __toString() 55 { 56 return implode("\n", $this->tokens); 57 } 58 59 public function injectTokens(array $tokens) 60 { 61 $this->tokens = array_merge(\array_slice($this->tokens, 0, $this->current), $tokens, \array_slice($this->tokens, $this->current)); 62 } 63 64 /** 65 * Sets the pointer to the next token and returns the old one. 66 * 67 * @return Token 68 */ 69 public function next() 70 { 71 if (!isset($this->tokens[++$this->current])) { 72 throw new SyntaxError('Unexpected end of template.', $this->tokens[$this->current - 1]->getLine(), $this->source); 73 } 74 75 return $this->tokens[$this->current - 1]; 76 } 77 78 /** 79 * Tests a token, sets the pointer to the next one and returns it or throws a syntax error. 80 * 81 * @return Token|null The next token if the condition is true, null otherwise 82 */ 83 public function nextIf($primary, $secondary = null) 84 { 85 if ($this->tokens[$this->current]->test($primary, $secondary)) { 86 return $this->next(); 87 } 88 } 89 90 /** 91 * Tests a token and returns it or throws a syntax error. 92 * 93 * @return Token 94 */ 95 public function expect($type, $value = null, $message = null) 96 { 97 $token = $this->tokens[$this->current]; 98 if (!$token->test($type, $value)) { 99 $line = $token->getLine(); 100 throw new SyntaxError(sprintf('%sUnexpected token "%s" of value "%s" ("%s" expected%s).', 101 $message ? $message.'. ' : '', 102 Token::typeToEnglish($token->getType()), $token->getValue(), 103 Token::typeToEnglish($type), $value ? sprintf(' with value "%s"', $value) : ''), 104 $line, 105 $this->source 106 ); 107 } 108 $this->next(); 109 110 return $token; 111 } 112 113 /** 114 * Looks at the next token. 115 * 116 * @param int $number 117 * 118 * @return Token 119 */ 120 public function look($number = 1) 121 { 122 if (!isset($this->tokens[$this->current + $number])) { 123 throw new SyntaxError('Unexpected end of template.', $this->tokens[$this->current + $number - 1]->getLine(), $this->source); 124 } 125 126 return $this->tokens[$this->current + $number]; 127 } 128 129 /** 130 * Tests the current token. 131 * 132 * @return bool 133 */ 134 public function test($primary, $secondary = null) 135 { 136 return $this->tokens[$this->current]->test($primary, $secondary); 137 } 138 139 /** 140 * Checks if end of stream was reached. 141 * 142 * @return bool 143 */ 144 public function isEOF() 145 { 146 return Token::EOF_TYPE === $this->tokens[$this->current]->getType(); 147 } 148 149 /** 150 * @return Token 151 */ 152 public function getCurrent() 153 { 154 return $this->tokens[$this->current]; 155 } 156 157 /** 158 * Gets the name associated with this stream (null if not defined). 159 * 160 * @return string|null 161 * 162 * @deprecated since 1.27 (to be removed in 2.0) 163 */ 164 public function getFilename() 165 { 166 @trigger_error(sprintf('The %s() method is deprecated since version 1.27 and will be removed in 2.0. Use getSourceContext() instead.', __METHOD__), E_USER_DEPRECATED); 167 168 return $this->source->getName(); 169 } 170 171 /** 172 * Gets the source code associated with this stream. 173 * 174 * @return string 175 * 176 * @internal Don't use this as it might be empty depending on the environment configuration 177 * 178 * @deprecated since 1.27 (to be removed in 2.0) 179 */ 180 public function getSource() 181 { 182 @trigger_error(sprintf('The %s() method is deprecated since version 1.27 and will be removed in 2.0. Use getSourceContext() instead.', __METHOD__), E_USER_DEPRECATED); 183 184 return $this->source->getCode(); 185 } 186 187 /** 188 * Gets the source associated with this stream. 189 * 190 * @return Source 191 * 192 * @internal 193 */ 194 public function getSourceContext() 195 { 196 return $this->source; 197 } 198} 199 200class_alias('Twig\TokenStream', 'Twig_TokenStream'); 201