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\Cache\FilesystemCache; 13use Twig\Environment; 14use Twig\Extension\AbstractExtension; 15use Twig\Extension\GlobalsInterface; 16use Twig\Extension\InitRuntimeInterface; 17use Twig\Loader\ArrayLoader; 18use Twig\Loader\LoaderInterface; 19use Twig\Loader\SourceContextLoaderInterface; 20use Twig\NodeVisitor\NodeVisitorInterface; 21use Twig\Source; 22use Twig\Token; 23use Twig\TokenParser\AbstractTokenParser; 24use Twig\TwigFilter; 25use Twig\TwigFunction; 26use Twig\TwigTest; 27 28require_once __DIR__.'/FilesystemHelper.php'; 29 30class Twig_Tests_EnvironmentTest extends \PHPUnit\Framework\TestCase 31{ 32 private $deprecations = []; 33 34 /** 35 * @group legacy 36 */ 37 public function testLegacyTokenizeSignature() 38 { 39 $env = new Environment(); 40 $stream = $env->tokenize('{{ foo }}', 'foo'); 41 $this->assertEquals('{{ foo }}', $stream->getSource()); 42 $this->assertEquals('foo', $stream->getFilename()); 43 } 44 45 /** 46 * @group legacy 47 */ 48 public function testLegacyCompileSourceSignature() 49 { 50 $loader = new ArrayLoader(['foo' => '{{ foo }}']); 51 $env = new Environment($loader); 52 $this->assertContains('getTemplateName', $env->compileSource('{{ foo }}', 'foo')); 53 } 54 55 /** 56 * @expectedException \LogicException 57 * @expectedExceptionMessage You must set a loader first. 58 * @group legacy 59 */ 60 public function testRenderNoLoader() 61 { 62 $env = new Environment(); 63 $env->render('test'); 64 } 65 66 public function testAutoescapeOption() 67 { 68 $loader = new ArrayLoader([ 69 'html' => '{{ foo }} {{ foo }}', 70 'js' => '{{ bar }} {{ bar }}', 71 ]); 72 73 $twig = new Environment($loader, [ 74 'debug' => true, 75 'cache' => false, 76 'autoescape' => [$this, 'escapingStrategyCallback'], 77 ]); 78 79 $this->assertEquals('foo<br/ > foo<br/ >', $twig->render('html', ['foo' => 'foo<br/ >'])); 80 $this->assertEquals('foo\u003Cbr\/\u0020\u003E foo\u003Cbr\/\u0020\u003E', $twig->render('js', ['bar' => 'foo<br/ >'])); 81 } 82 83 public function escapingStrategyCallback($name) 84 { 85 return $name; 86 } 87 88 public function testGlobals() 89 { 90 // to be removed in 2.0 91 $loader = $this->getMockBuilder('Twig_EnvironmentTestLoaderInterface')->getMock(); 92 //$loader = $this->getMockBuilder(['\Twig\Loader\LoaderInterface', '\Twig\Loader\SourceContextLoaderInterface'])->getMock(); 93 $loader->expects($this->any())->method('getSourceContext')->will($this->returnValue(new Source('', ''))); 94 95 // globals can be added after calling getGlobals 96 97 $twig = new Environment($loader); 98 $twig->addGlobal('foo', 'foo'); 99 $twig->getGlobals(); 100 $twig->addGlobal('foo', 'bar'); 101 $globals = $twig->getGlobals(); 102 $this->assertEquals('bar', $globals['foo']); 103 104 // globals can be modified after a template has been loaded 105 $twig = new Environment($loader); 106 $twig->addGlobal('foo', 'foo'); 107 $twig->getGlobals(); 108 $twig->load('index'); 109 $twig->addGlobal('foo', 'bar'); 110 $globals = $twig->getGlobals(); 111 $this->assertEquals('bar', $globals['foo']); 112 113 // globals can be modified after extensions init 114 $twig = new Environment($loader); 115 $twig->addGlobal('foo', 'foo'); 116 $twig->getGlobals(); 117 $twig->getFunctions(); 118 $twig->addGlobal('foo', 'bar'); 119 $globals = $twig->getGlobals(); 120 $this->assertEquals('bar', $globals['foo']); 121 122 // globals can be modified after extensions and a template has been loaded 123 $arrayLoader = new ArrayLoader(['index' => '{{foo}}']); 124 $twig = new Environment($arrayLoader); 125 $twig->addGlobal('foo', 'foo'); 126 $twig->getGlobals(); 127 $twig->getFunctions(); 128 $twig->load('index'); 129 $twig->addGlobal('foo', 'bar'); 130 $globals = $twig->getGlobals(); 131 $this->assertEquals('bar', $globals['foo']); 132 133 $twig = new Environment($arrayLoader); 134 $twig->getGlobals(); 135 $twig->addGlobal('foo', 'bar'); 136 $template = $twig->load('index'); 137 $this->assertEquals('bar', $template->render([])); 138 139 /* to be uncomment in Twig 2.0 140 // globals cannot be added after a template has been loaded 141 $twig = new Environment($loader); 142 $twig->addGlobal('foo', 'foo'); 143 $twig->getGlobals(); 144 $twig->load('index'); 145 try { 146 $twig->addGlobal('bar', 'bar'); 147 $this->fail(); 148 } catch (\LogicException $e) { 149 $this->assertFalse(array_key_exists('bar', $twig->getGlobals())); 150 } 151 152 // globals cannot be added after extensions init 153 $twig = new Environment($loader); 154 $twig->addGlobal('foo', 'foo'); 155 $twig->getGlobals(); 156 $twig->getFunctions(); 157 try { 158 $twig->addGlobal('bar', 'bar'); 159 $this->fail(); 160 } catch (\LogicException $e) { 161 $this->assertFalse(array_key_exists('bar', $twig->getGlobals())); 162 } 163 164 // globals cannot be added after extensions and a template has been loaded 165 $twig = new Environment($loader); 166 $twig->addGlobal('foo', 'foo'); 167 $twig->getGlobals(); 168 $twig->getFunctions(); 169 $twig->load('index'); 170 try { 171 $twig->addGlobal('bar', 'bar'); 172 $this->fail(); 173 } catch (\LogicException $e) { 174 $this->assertFalse(array_key_exists('bar', $twig->getGlobals())); 175 } 176 177 // test adding globals after a template has been loaded without call to getGlobals 178 $twig = new Environment($loader); 179 $twig->load('index'); 180 try { 181 $twig->addGlobal('bar', 'bar'); 182 $this->fail(); 183 } catch (\LogicException $e) { 184 $this->assertFalse(array_key_exists('bar', $twig->getGlobals())); 185 } 186 */ 187 } 188 189 public function testExtensionsAreNotInitializedWhenRenderingACompiledTemplate() 190 { 191 $cache = new FilesystemCache($dir = sys_get_temp_dir().'/twig'); 192 $options = ['cache' => $cache, 'auto_reload' => false, 'debug' => false]; 193 194 // force compilation 195 $twig = new Environment($loader = new ArrayLoader(['index' => '{{ foo }}']), $options); 196 197 $key = $cache->generateKey('index', $twig->getTemplateClass('index')); 198 $cache->write($key, $twig->compileSource(new Source('{{ foo }}', 'index'))); 199 200 // check that extensions won't be initialized when rendering a template that is already in the cache 201 $twig = $this 202 ->getMockBuilder('\Twig\Environment') 203 ->setConstructorArgs([$loader, $options]) 204 ->setMethods(['initExtensions']) 205 ->getMock() 206 ; 207 208 $twig->expects($this->never())->method('initExtensions'); 209 210 // render template 211 $output = $twig->render('index', ['foo' => 'bar']); 212 $this->assertEquals('bar', $output); 213 214 Twig_Tests_FilesystemHelper::removeDir($dir); 215 } 216 217 public function testAutoReloadCacheMiss() 218 { 219 $templateName = __FUNCTION__; 220 $templateContent = __FUNCTION__; 221 222 $cache = $this->getMockBuilder('\Twig\Cache\CacheInterface')->getMock(); 223 $loader = $this->getMockLoader($templateName, $templateContent); 224 $twig = new Environment($loader, ['cache' => $cache, 'auto_reload' => true, 'debug' => false]); 225 226 // Cache miss: getTimestamp returns 0 and as a result the load() is 227 // skipped. 228 $cache->expects($this->once()) 229 ->method('generateKey') 230 ->will($this->returnValue('key')); 231 $cache->expects($this->once()) 232 ->method('getTimestamp') 233 ->will($this->returnValue(0)); 234 $loader->expects($this->never()) 235 ->method('isFresh'); 236 $cache->expects($this->once()) 237 ->method('write'); 238 $cache->expects($this->once()) 239 ->method('load'); 240 241 $twig->load($templateName); 242 } 243 244 public function testAutoReloadCacheHit() 245 { 246 $templateName = __FUNCTION__; 247 $templateContent = __FUNCTION__; 248 249 $cache = $this->getMockBuilder('\Twig\Cache\CacheInterface')->getMock(); 250 $loader = $this->getMockLoader($templateName, $templateContent); 251 $twig = new Environment($loader, ['cache' => $cache, 'auto_reload' => true, 'debug' => false]); 252 253 $now = time(); 254 255 // Cache hit: getTimestamp returns something > extension timestamps and 256 // the loader returns true for isFresh(). 257 $cache->expects($this->once()) 258 ->method('generateKey') 259 ->will($this->returnValue('key')); 260 $cache->expects($this->once()) 261 ->method('getTimestamp') 262 ->will($this->returnValue($now)); 263 $loader->expects($this->once()) 264 ->method('isFresh') 265 ->will($this->returnValue(true)); 266 $cache->expects($this->atLeastOnce()) 267 ->method('load'); 268 269 $twig->load($templateName); 270 } 271 272 public function testAutoReloadOutdatedCacheHit() 273 { 274 $templateName = __FUNCTION__; 275 $templateContent = __FUNCTION__; 276 277 $cache = $this->getMockBuilder('\Twig\Cache\CacheInterface')->getMock(); 278 $loader = $this->getMockLoader($templateName, $templateContent); 279 $twig = new Environment($loader, ['cache' => $cache, 'auto_reload' => true, 'debug' => false]); 280 281 $now = time(); 282 283 $cache->expects($this->once()) 284 ->method('generateKey') 285 ->will($this->returnValue('key')); 286 $cache->expects($this->once()) 287 ->method('getTimestamp') 288 ->will($this->returnValue($now)); 289 $loader->expects($this->once()) 290 ->method('isFresh') 291 ->will($this->returnValue(false)); 292 $cache->expects($this->once()) 293 ->method('write'); 294 $cache->expects($this->once()) 295 ->method('load'); 296 297 $twig->load($templateName); 298 } 299 300 /** 301 * @group legacy 302 */ 303 public function testHasGetExtensionWithDynamicName() 304 { 305 $twig = new Environment($this->getMockBuilder('\Twig\Loader\LoaderInterface')->getMock()); 306 307 $ext1 = new Twig_Tests_EnvironmentTest_Extension_DynamicWithDeprecatedName('ext1'); 308 $ext2 = new Twig_Tests_EnvironmentTest_Extension_DynamicWithDeprecatedName('ext2'); 309 $twig->addExtension($ext1); 310 $twig->addExtension($ext2); 311 312 $this->assertTrue($twig->hasExtension('ext1')); 313 $this->assertTrue($twig->hasExtension('ext2')); 314 315 $this->assertTrue($twig->hasExtension('Twig_Tests_EnvironmentTest_Extension_DynamicWithDeprecatedName')); 316 317 $this->assertSame($ext1, $twig->getExtension('ext1')); 318 $this->assertSame($ext2, $twig->getExtension('ext2')); 319 } 320 321 public function testHasGetExtensionByClassName() 322 { 323 $twig = new Environment($this->getMockBuilder('\Twig\Loader\LoaderInterface')->getMock()); 324 $twig->addExtension($ext = new Twig_Tests_EnvironmentTest_Extension()); 325 $this->assertTrue($twig->hasExtension('Twig_Tests_EnvironmentTest_Extension')); 326 $this->assertTrue($twig->hasExtension('\Twig_Tests_EnvironmentTest_Extension')); 327 328 $this->assertSame($ext, $twig->getExtension('Twig_Tests_EnvironmentTest_Extension')); 329 $this->assertSame($ext, $twig->getExtension('\Twig_Tests_EnvironmentTest_Extension')); 330 331 $this->assertTrue($twig->hasExtension('Twig\Tests\EnvironmentTest\Extension')); 332 $this->assertSame($ext, $twig->getExtension('Twig\Tests\EnvironmentTest\Extension')); 333 } 334 335 public function testAddExtension() 336 { 337 $twig = new Environment($this->getMockBuilder('\Twig\Loader\LoaderInterface')->getMock()); 338 $twig->addExtension(new Twig_Tests_EnvironmentTest_Extension()); 339 340 $this->assertArrayHasKey('test', $twig->getTags()); 341 $this->assertArrayHasKey('foo_filter', $twig->getFilters()); 342 $this->assertArrayHasKey('foo_function', $twig->getFunctions()); 343 $this->assertArrayHasKey('foo_test', $twig->getTests()); 344 $this->assertArrayHasKey('foo_unary', $twig->getUnaryOperators()); 345 $this->assertArrayHasKey('foo_binary', $twig->getBinaryOperators()); 346 $this->assertArrayHasKey('foo_global', $twig->getGlobals()); 347 $visitors = $twig->getNodeVisitors(); 348 $found = false; 349 foreach ($visitors as $visitor) { 350 if ($visitor instanceof Twig_Tests_EnvironmentTest_NodeVisitor) { 351 $found = true; 352 } 353 } 354 $this->assertTrue($found); 355 } 356 357 /** 358 * @requires PHP 5.3 359 */ 360 public function testAddExtensionWithDeprecatedGetGlobals() 361 { 362 $twig = new Environment($this->getMockBuilder('\Twig\Loader\LoaderInterface')->getMock()); 363 $twig->addExtension(new Twig_Tests_EnvironmentTest_Extension_WithGlobals()); 364 365 $this->deprecations = []; 366 set_error_handler([$this, 'handleError']); 367 368 $this->assertArrayHasKey('foo_global', $twig->getGlobals()); 369 370 $this->assertCount(1, $this->deprecations); 371 $this->assertContains('Defining the getGlobals() method in the "Twig_Tests_EnvironmentTest_Extension_WithGlobals" extension ', $this->deprecations[0]); 372 373 restore_error_handler(); 374 } 375 376 /** 377 * @group legacy 378 */ 379 public function testRemoveExtension() 380 { 381 $twig = new Environment($this->getMockBuilder('\Twig\Loader\LoaderInterface')->getMock()); 382 $twig->addExtension(new Twig_Tests_EnvironmentTest_Extension_WithDeprecatedName()); 383 $twig->removeExtension('environment_test'); 384 385 $this->assertArrayNotHasKey('test', $twig->getTags()); 386 $this->assertArrayNotHasKey('foo_filter', $twig->getFilters()); 387 $this->assertArrayNotHasKey('foo_function', $twig->getFunctions()); 388 $this->assertArrayNotHasKey('foo_test', $twig->getTests()); 389 $this->assertArrayNotHasKey('foo_unary', $twig->getUnaryOperators()); 390 $this->assertArrayNotHasKey('foo_binary', $twig->getBinaryOperators()); 391 $this->assertArrayNotHasKey('foo_global', $twig->getGlobals()); 392 $this->assertCount(2, $twig->getNodeVisitors()); 393 } 394 395 public function testAddMockExtension() 396 { 397 // should be replaced by the following in 2.0 (this current code is just to avoid a dep notice) 398 // $extension = $this->getMockBuilder('\Twig\Extension\AbstractExtension')->getMock(); 399 $extension = eval(<<<EOF 400use Twig\Extension\AbstractExtension; 401 402class Twig_Tests_EnvironmentTest_ExtensionInEval extends AbstractExtension 403{ 404} 405EOF 406 ); 407 $extension = new Twig_Tests_EnvironmentTest_ExtensionInEval(); 408 409 $loader = new ArrayLoader(['page' => 'hey']); 410 411 $twig = new Environment($loader); 412 $twig->addExtension($extension); 413 414 $this->assertInstanceOf('\Twig\Extension\ExtensionInterface', $twig->getExtension(\get_class($extension))); 415 $this->assertTrue($twig->isTemplateFresh('page', time())); 416 } 417 418 public function testInitRuntimeWithAnExtensionUsingInitRuntimeNoDeprecation() 419 { 420 $twig = new Environment($this->getMockBuilder('\Twig\Loader\LoaderInterface')->getMock()); 421 $twig->addExtension(new Twig_Tests_EnvironmentTest_ExtensionWithoutDeprecationInitRuntime()); 422 $twig->initRuntime(); 423 424 // add a dummy assertion here to satisfy PHPUnit, the only thing we want to test is that the code above 425 // can be executed without throwing any deprecations 426 $this->addToAssertionCount(1); 427 } 428 429 /** 430 * @requires PHP 5.3 431 */ 432 public function testInitRuntimeWithAnExtensionUsingInitRuntimeDeprecation() 433 { 434 $twig = new Environment($this->getMockBuilder('\Twig\Loader\LoaderInterface')->getMock()); 435 $twig->addExtension(new Twig_Tests_EnvironmentTest_ExtensionWithDeprecationInitRuntime()); 436 437 $this->deprecations = []; 438 set_error_handler([$this, 'handleError']); 439 440 $twig->initRuntime(); 441 442 $this->assertCount(1, $this->deprecations); 443 $this->assertContains('Defining the initRuntime() method in the "Twig_Tests_EnvironmentTest_ExtensionWithDeprecationInitRuntime" extension is deprecated since version 1.23.', $this->deprecations[0]); 444 445 restore_error_handler(); 446 } 447 448 public function handleError($type, $msg) 449 { 450 if (E_USER_DEPRECATED === $type) { 451 $this->deprecations[] = $msg; 452 } 453 } 454 455 /** 456 * @requires PHP 5.3 457 */ 458 public function testOverrideExtension() 459 { 460 $twig = new Environment($this->getMockBuilder('\Twig\Loader\LoaderInterface')->getMock()); 461 $twig->addExtension(new Twig_Tests_EnvironmentTest_ExtensionWithDeprecationInitRuntime()); 462 463 $this->deprecations = []; 464 set_error_handler([$this, 'handleError']); 465 466 $twig->addExtension(new Twig_Tests_EnvironmentTest_Extension_WithDeprecatedName()); 467 $twig->addExtension(new Twig_Tests_EnvironmentTest_Extension_WithDeprecatedName()); 468 469 $this->assertCount(1, $this->deprecations); 470 $this->assertContains('The possibility to register the same extension twice', $this->deprecations[0]); 471 472 restore_error_handler(); 473 } 474 475 public function testAddRuntimeLoader() 476 { 477 $runtimeLoader = $this->getMockBuilder('\Twig\RuntimeLoader\RuntimeLoaderInterface')->getMock(); 478 $runtimeLoader->expects($this->any())->method('load')->will($this->returnValue(new Twig_Tests_EnvironmentTest_Runtime())); 479 480 $loader = new ArrayLoader([ 481 'func_array' => '{{ from_runtime_array("foo") }}', 482 'func_array_default' => '{{ from_runtime_array() }}', 483 'func_array_named_args' => '{{ from_runtime_array(name="foo") }}', 484 'func_string' => '{{ from_runtime_string("foo") }}', 485 'func_string_default' => '{{ from_runtime_string() }}', 486 'func_string_named_args' => '{{ from_runtime_string(name="foo") }}', 487 ]); 488 489 $twig = new Environment($loader); 490 $twig->addExtension(new Twig_Tests_EnvironmentTest_ExtensionWithoutRuntime()); 491 $twig->addRuntimeLoader($runtimeLoader); 492 493 $this->assertEquals('foo', $twig->render('func_array')); 494 $this->assertEquals('bar', $twig->render('func_array_default')); 495 $this->assertEquals('foo', $twig->render('func_array_named_args')); 496 $this->assertEquals('foo', $twig->render('func_string')); 497 $this->assertEquals('bar', $twig->render('func_string_default')); 498 $this->assertEquals('foo', $twig->render('func_string_named_args')); 499 } 500 501 /** 502 * @expectedException \Twig\Error\RuntimeError 503 * @expectedExceptionMessage Circular reference detected for Twig template "base.html.twig", path: base.html.twig -> base.html.twig in "base.html.twig" at line 1 504 */ 505 public function testFailLoadTemplateOnCircularReference() 506 { 507 $twig = new Environment(new ArrayLoader([ 508 'base.html.twig' => '{% extends "base.html.twig" %}', 509 ])); 510 511 $twig->load('base.html.twig'); 512 } 513 514 /** 515 * @expectedException \Twig\Error\RuntimeError 516 * @expectedExceptionMessage Circular reference detected for Twig template "base1.html.twig", path: base1.html.twig -> base2.html.twig -> base1.html.twig in "base1.html.twig" at line 1 517 */ 518 public function testFailLoadTemplateOnComplexCircularReference() 519 { 520 $twig = new Environment(new ArrayLoader([ 521 'base1.html.twig' => '{% extends "base2.html.twig" %}', 522 'base2.html.twig' => '{% extends "base1.html.twig" %}', 523 ])); 524 525 $twig->load('base1.html.twig'); 526 } 527 528 protected function getMockLoader($templateName, $templateContent) 529 { 530 // to be removed in 2.0 531 $loader = $this->getMockBuilder('Twig_EnvironmentTestLoaderInterface')->getMock(); 532 //$loader = $this->getMockBuilder(['\Twig\Loader\LoaderInterface', '\Twig\Loader\SourceContextLoaderInterface'])->getMock(); 533 $loader->expects($this->any()) 534 ->method('getSourceContext') 535 ->with($templateName) 536 ->will($this->returnValue(new Source($templateContent, $templateName))); 537 $loader->expects($this->any()) 538 ->method('getCacheKey') 539 ->with($templateName) 540 ->will($this->returnValue($templateName)); 541 542 return $loader; 543 } 544} 545 546class Twig_Tests_EnvironmentTest_Extension_WithGlobals extends AbstractExtension 547{ 548 public function getGlobals() 549 { 550 return [ 551 'foo_global' => 'foo_global', 552 ]; 553 } 554} 555 556class Twig_Tests_EnvironmentTest_Extension extends AbstractExtension implements GlobalsInterface 557{ 558 public function getTokenParsers() 559 { 560 return [ 561 new Twig_Tests_EnvironmentTest_TokenParser(), 562 ]; 563 } 564 565 public function getNodeVisitors() 566 { 567 return [ 568 new Twig_Tests_EnvironmentTest_NodeVisitor(), 569 ]; 570 } 571 572 public function getFilters() 573 { 574 return [ 575 new TwigFilter('foo_filter', 'foo_filter'), 576 ]; 577 } 578 579 public function getTests() 580 { 581 return [ 582 new TwigTest('foo_test', 'foo_test'), 583 ]; 584 } 585 586 public function getFunctions() 587 { 588 return [ 589 new TwigFunction('foo_function', 'foo_function'), 590 ]; 591 } 592 593 public function getOperators() 594 { 595 return [ 596 ['foo_unary' => []], 597 ['foo_binary' => []], 598 ]; 599 } 600 601 public function getGlobals() 602 { 603 return [ 604 'foo_global' => 'foo_global', 605 ]; 606 } 607} 608class_alias('Twig_Tests_EnvironmentTest_Extension', 'Twig\Tests\EnvironmentTest\Extension', false); 609 610class Twig_Tests_EnvironmentTest_Extension_WithDeprecatedName extends AbstractExtension 611{ 612 public function getName() 613 { 614 return 'environment_test'; 615 } 616} 617 618class Twig_Tests_EnvironmentTest_Extension_DynamicWithDeprecatedName extends AbstractExtension 619{ 620 private $name; 621 622 public function __construct($name) 623 { 624 $this->name = $name; 625 } 626 627 public function getName() 628 { 629 return $this->name; 630 } 631} 632 633class Twig_Tests_EnvironmentTest_TokenParser extends AbstractTokenParser 634{ 635 public function parse(Token $token) 636 { 637 } 638 639 public function getTag() 640 { 641 return 'test'; 642 } 643} 644 645class Twig_Tests_EnvironmentTest_NodeVisitor implements NodeVisitorInterface 646{ 647 public function enterNode(Twig_NodeInterface $node, Environment $env) 648 { 649 return $node; 650 } 651 652 public function leaveNode(Twig_NodeInterface $node, Environment $env) 653 { 654 return $node; 655 } 656 657 public function getPriority() 658 { 659 return 0; 660 } 661} 662 663class Twig_Tests_EnvironmentTest_ExtensionWithDeprecationInitRuntime extends AbstractExtension 664{ 665 public function initRuntime(Environment $env) 666 { 667 } 668} 669 670class Twig_Tests_EnvironmentTest_ExtensionWithoutDeprecationInitRuntime extends AbstractExtension implements InitRuntimeInterface 671{ 672 public function initRuntime(Environment $env) 673 { 674 } 675} 676 677class Twig_Tests_EnvironmentTest_ExtensionWithoutRuntime extends AbstractExtension 678{ 679 public function getFunctions() 680 { 681 return [ 682 new TwigFunction('from_runtime_array', ['Twig_Tests_EnvironmentTest_Runtime', 'fromRuntime']), 683 new TwigFunction('from_runtime_string', 'Twig_Tests_EnvironmentTest_Runtime::fromRuntime'), 684 ]; 685 } 686 687 public function getName() 688 { 689 return 'from_runtime'; 690 } 691} 692 693class Twig_Tests_EnvironmentTest_Runtime 694{ 695 public function fromRuntime($name = 'bar') 696 { 697 return $name; 698 } 699} 700 701// to be removed in 2.0 702interface Twig_EnvironmentTestLoaderInterface extends LoaderInterface, SourceContextLoaderInterface 703{ 704} 705