1<?php 2 3/* 4 * This file is part of Twig. 5 * 6 * (c) Fabien Potencier 7 * 8 * For the full copyright and license information, please view the LICENSE 9 * file that was distributed with this source code. 10 */ 11 12use Twig\Environment; 13use Twig\Lexer; 14use Twig\Source; 15use Twig\Token; 16 17class Twig_Tests_LexerTest extends \PHPUnit\Framework\TestCase 18{ 19 /** 20 * @group legacy 21 */ 22 public function testLegacyConstructorSignature() 23 { 24 $lexer = new Lexer(new Environment($this->getMockBuilder('\Twig\Loader\LoaderInterface')->getMock())); 25 $stream = $lexer->tokenize('{{ foo }}', 'foo'); 26 $this->assertEquals('foo', $stream->getFilename()); 27 $this->assertEquals('{{ foo }}', $stream->getSource()); 28 } 29 30 public function testNameLabelForTag() 31 { 32 $template = '{% § %}'; 33 34 $lexer = new Lexer(new Environment($this->getMockBuilder('\Twig\Loader\LoaderInterface')->getMock())); 35 $stream = $lexer->tokenize(new Source($template, 'index')); 36 37 $stream->expect(Token::BLOCK_START_TYPE); 38 $this->assertSame('§', $stream->expect(Token::NAME_TYPE)->getValue()); 39 } 40 41 public function testNameLabelForFunction() 42 { 43 $template = '{{ §() }}'; 44 45 $lexer = new Lexer(new Environment($this->getMockBuilder('\Twig\Loader\LoaderInterface')->getMock())); 46 $stream = $lexer->tokenize(new Source($template, 'index')); 47 48 $stream->expect(Token::VAR_START_TYPE); 49 $this->assertSame('§', $stream->expect(Token::NAME_TYPE)->getValue()); 50 } 51 52 public function testBracketsNesting() 53 { 54 $template = '{{ {"a":{"b":"c"}} }}'; 55 56 $this->assertEquals(2, $this->countToken($template, Token::PUNCTUATION_TYPE, '{')); 57 $this->assertEquals(2, $this->countToken($template, Token::PUNCTUATION_TYPE, '}')); 58 } 59 60 protected function countToken($template, $type, $value = null) 61 { 62 $lexer = new Lexer(new Environment($this->getMockBuilder('\Twig\Loader\LoaderInterface')->getMock())); 63 $stream = $lexer->tokenize(new Source($template, 'index')); 64 65 $count = 0; 66 while (!$stream->isEOF()) { 67 $token = $stream->next(); 68 if ($type === $token->getType()) { 69 if (null === $value || $value === $token->getValue()) { 70 ++$count; 71 } 72 } 73 } 74 75 return $count; 76 } 77 78 public function testLineDirective() 79 { 80 $template = "foo\n" 81 ."bar\n" 82 ."{% line 10 %}\n" 83 ."{{\n" 84 ."baz\n" 85 ."}}\n"; 86 87 $lexer = new Lexer(new Environment($this->getMockBuilder('\Twig\Loader\LoaderInterface')->getMock())); 88 $stream = $lexer->tokenize(new Source($template, 'index')); 89 90 // foo\nbar\n 91 $this->assertSame(1, $stream->expect(Token::TEXT_TYPE)->getLine()); 92 // \n (after {% line %}) 93 $this->assertSame(10, $stream->expect(Token::TEXT_TYPE)->getLine()); 94 // {{ 95 $this->assertSame(11, $stream->expect(Token::VAR_START_TYPE)->getLine()); 96 // baz 97 $this->assertSame(12, $stream->expect(Token::NAME_TYPE)->getLine()); 98 } 99 100 public function testLineDirectiveInline() 101 { 102 $template = "foo\n" 103 ."bar{% line 10 %}{{\n" 104 ."baz\n" 105 ."}}\n"; 106 107 $lexer = new Lexer(new Environment($this->getMockBuilder('\Twig\Loader\LoaderInterface')->getMock())); 108 $stream = $lexer->tokenize(new Source($template, 'index')); 109 110 // foo\nbar 111 $this->assertSame(1, $stream->expect(Token::TEXT_TYPE)->getLine()); 112 // {{ 113 $this->assertSame(10, $stream->expect(Token::VAR_START_TYPE)->getLine()); 114 // baz 115 $this->assertSame(11, $stream->expect(Token::NAME_TYPE)->getLine()); 116 } 117 118 public function testLongComments() 119 { 120 $template = '{# '.str_repeat('*', 100000).' #}'; 121 122 $lexer = new Lexer(new Environment($this->getMockBuilder('\Twig\Loader\LoaderInterface')->getMock())); 123 $lexer->tokenize(new Source($template, 'index')); 124 125 // add a dummy assertion here to satisfy PHPUnit, the only thing we want to test is that the code above 126 // can be executed without throwing any exceptions 127 $this->addToAssertionCount(1); 128 } 129 130 public function testLongVerbatim() 131 { 132 $template = '{% verbatim %}'.str_repeat('*', 100000).'{% endverbatim %}'; 133 134 $lexer = new Lexer(new Environment($this->getMockBuilder('\Twig\Loader\LoaderInterface')->getMock())); 135 $lexer->tokenize(new Source($template, 'index')); 136 137 // add a dummy assertion here to satisfy PHPUnit, the only thing we want to test is that the code above 138 // can be executed without throwing any exceptions 139 $this->addToAssertionCount(1); 140 } 141 142 public function testLongVar() 143 { 144 $template = '{{ '.str_repeat('x', 100000).' }}'; 145 146 $lexer = new Lexer(new Environment($this->getMockBuilder('\Twig\Loader\LoaderInterface')->getMock())); 147 $lexer->tokenize(new Source($template, 'index')); 148 149 // add a dummy assertion here to satisfy PHPUnit, the only thing we want to test is that the code above 150 // can be executed without throwing any exceptions 151 $this->addToAssertionCount(1); 152 } 153 154 public function testLongBlock() 155 { 156 $template = '{% '.str_repeat('x', 100000).' %}'; 157 158 $lexer = new Lexer(new Environment($this->getMockBuilder('\Twig\Loader\LoaderInterface')->getMock())); 159 $lexer->tokenize(new Source($template, 'index')); 160 161 // add a dummy assertion here to satisfy PHPUnit, the only thing we want to test is that the code above 162 // can be executed without throwing any exceptions 163 $this->addToAssertionCount(1); 164 } 165 166 public function testBigNumbers() 167 { 168 $template = '{{ 922337203685477580700 }}'; 169 170 $lexer = new Lexer(new Environment($this->getMockBuilder('\Twig\Loader\LoaderInterface')->getMock())); 171 $stream = $lexer->tokenize(new Source($template, 'index')); 172 $stream->next(); 173 $node = $stream->next(); 174 $this->assertEquals('922337203685477580700', $node->getValue()); 175 } 176 177 public function testStringWithEscapedDelimiter() 178 { 179 $tests = [ 180 "{{ 'foo \' bar' }}" => 'foo \' bar', 181 '{{ "foo \" bar" }}' => 'foo " bar', 182 ]; 183 $lexer = new Lexer(new Environment($this->getMockBuilder('\Twig\Loader\LoaderInterface')->getMock())); 184 foreach ($tests as $template => $expected) { 185 $stream = $lexer->tokenize(new Source($template, 'index')); 186 $stream->expect(Token::VAR_START_TYPE); 187 $stream->expect(Token::STRING_TYPE, $expected); 188 189 // add a dummy assertion here to satisfy PHPUnit, the only thing we want to test is that the code above 190 // can be executed without throwing any exceptions 191 $this->addToAssertionCount(1); 192 } 193 } 194 195 public function testStringWithInterpolation() 196 { 197 $template = 'foo {{ "bar #{ baz + 1 }" }}'; 198 199 $lexer = new Lexer(new Environment($this->getMockBuilder('\Twig\Loader\LoaderInterface')->getMock())); 200 $stream = $lexer->tokenize(new Source($template, 'index')); 201 $stream->expect(Token::TEXT_TYPE, 'foo '); 202 $stream->expect(Token::VAR_START_TYPE); 203 $stream->expect(Token::STRING_TYPE, 'bar '); 204 $stream->expect(Token::INTERPOLATION_START_TYPE); 205 $stream->expect(Token::NAME_TYPE, 'baz'); 206 $stream->expect(Token::OPERATOR_TYPE, '+'); 207 $stream->expect(Token::NUMBER_TYPE, '1'); 208 $stream->expect(Token::INTERPOLATION_END_TYPE); 209 $stream->expect(Token::VAR_END_TYPE); 210 211 // add a dummy assertion here to satisfy PHPUnit, the only thing we want to test is that the code above 212 // can be executed without throwing any exceptions 213 $this->addToAssertionCount(1); 214 } 215 216 public function testStringWithEscapedInterpolation() 217 { 218 $template = '{{ "bar \#{baz+1}" }}'; 219 220 $lexer = new Lexer(new Environment($this->getMockBuilder('\Twig\Loader\LoaderInterface')->getMock())); 221 $stream = $lexer->tokenize(new Source($template, 'index')); 222 $stream->expect(Token::VAR_START_TYPE); 223 $stream->expect(Token::STRING_TYPE, 'bar #{baz+1}'); 224 $stream->expect(Token::VAR_END_TYPE); 225 226 // add a dummy assertion here to satisfy PHPUnit, the only thing we want to test is that the code above 227 // can be executed without throwing any exceptions 228 $this->addToAssertionCount(1); 229 } 230 231 public function testStringWithHash() 232 { 233 $template = '{{ "bar # baz" }}'; 234 235 $lexer = new Lexer(new Environment($this->getMockBuilder('\Twig\Loader\LoaderInterface')->getMock())); 236 $stream = $lexer->tokenize(new Source($template, 'index')); 237 $stream->expect(Token::VAR_START_TYPE); 238 $stream->expect(Token::STRING_TYPE, 'bar # baz'); 239 $stream->expect(Token::VAR_END_TYPE); 240 241 // add a dummy assertion here to satisfy PHPUnit, the only thing we want to test is that the code above 242 // can be executed without throwing any exceptions 243 $this->addToAssertionCount(1); 244 } 245 246 /** 247 * @expectedException \Twig\Error\SyntaxError 248 * @expectedExceptionMessage Unclosed """ 249 */ 250 public function testStringWithUnterminatedInterpolation() 251 { 252 $template = '{{ "bar #{x" }}'; 253 254 $lexer = new Lexer(new Environment($this->getMockBuilder('\Twig\Loader\LoaderInterface')->getMock())); 255 $lexer->tokenize(new Source($template, 'index')); 256 } 257 258 public function testStringWithNestedInterpolations() 259 { 260 $template = '{{ "bar #{ "foo#{bar}" }" }}'; 261 262 $lexer = new Lexer(new Environment($this->getMockBuilder('\Twig\Loader\LoaderInterface')->getMock())); 263 $stream = $lexer->tokenize(new Source($template, 'index')); 264 $stream->expect(Token::VAR_START_TYPE); 265 $stream->expect(Token::STRING_TYPE, 'bar '); 266 $stream->expect(Token::INTERPOLATION_START_TYPE); 267 $stream->expect(Token::STRING_TYPE, 'foo'); 268 $stream->expect(Token::INTERPOLATION_START_TYPE); 269 $stream->expect(Token::NAME_TYPE, 'bar'); 270 $stream->expect(Token::INTERPOLATION_END_TYPE); 271 $stream->expect(Token::INTERPOLATION_END_TYPE); 272 $stream->expect(Token::VAR_END_TYPE); 273 274 // add a dummy assertion here to satisfy PHPUnit, the only thing we want to test is that the code above 275 // can be executed without throwing any exceptions 276 $this->addToAssertionCount(1); 277 } 278 279 public function testStringWithNestedInterpolationsInBlock() 280 { 281 $template = '{% foo "bar #{ "foo#{bar}" }" %}'; 282 283 $lexer = new Lexer(new Environment($this->getMockBuilder('\Twig\Loader\LoaderInterface')->getMock())); 284 $stream = $lexer->tokenize(new Source($template, 'index')); 285 $stream->expect(Token::BLOCK_START_TYPE); 286 $stream->expect(Token::NAME_TYPE, 'foo'); 287 $stream->expect(Token::STRING_TYPE, 'bar '); 288 $stream->expect(Token::INTERPOLATION_START_TYPE); 289 $stream->expect(Token::STRING_TYPE, 'foo'); 290 $stream->expect(Token::INTERPOLATION_START_TYPE); 291 $stream->expect(Token::NAME_TYPE, 'bar'); 292 $stream->expect(Token::INTERPOLATION_END_TYPE); 293 $stream->expect(Token::INTERPOLATION_END_TYPE); 294 $stream->expect(Token::BLOCK_END_TYPE); 295 296 // add a dummy assertion here to satisfy PHPUnit, the only thing we want to test is that the code above 297 // can be executed without throwing any exceptions 298 $this->addToAssertionCount(1); 299 } 300 301 public function testOperatorEndingWithALetterAtTheEndOfALine() 302 { 303 $template = "{{ 1 and\n0}}"; 304 305 $lexer = new Lexer(new Environment($this->getMockBuilder('\Twig\Loader\LoaderInterface')->getMock())); 306 $stream = $lexer->tokenize(new Source($template, 'index')); 307 $stream->expect(Token::VAR_START_TYPE); 308 $stream->expect(Token::NUMBER_TYPE, 1); 309 $stream->expect(Token::OPERATOR_TYPE, 'and'); 310 311 // add a dummy assertion here to satisfy PHPUnit, the only thing we want to test is that the code above 312 // can be executed without throwing any exceptions 313 $this->addToAssertionCount(1); 314 } 315 316 /** 317 * @expectedException \Twig\Error\SyntaxError 318 * @expectedExceptionMessage Unclosed "variable" in "index" at line 3 319 */ 320 public function testUnterminatedVariable() 321 { 322 $template = ' 323 324{{ 325 326bar 327 328 329'; 330 331 $lexer = new Lexer(new Environment($this->getMockBuilder('\Twig\Loader\LoaderInterface')->getMock())); 332 $lexer->tokenize(new Source($template, 'index')); 333 } 334 335 /** 336 * @expectedException \Twig\Error\SyntaxError 337 * @expectedExceptionMessage Unclosed "block" in "index" at line 3 338 */ 339 public function testUnterminatedBlock() 340 { 341 $template = ' 342 343{% 344 345bar 346 347 348'; 349 350 $lexer = new Lexer(new Environment($this->getMockBuilder('\Twig\Loader\LoaderInterface')->getMock())); 351 $lexer->tokenize(new Source($template, 'index')); 352 } 353} 354