4 * This file is part of Twig.
8 * For the full copyright and license information, please view the LICENSE
9 * file that was distributed with this source code.
12 require_once dirname(__FILE__).'/FilesystemHelper.php';
14 class Twig_Tests_EnvironmentTest extends PHPUnit_Framework_TestCase
16 private $deprecations = array();
21 public function testLegacyTokenizeSignature()
23 $env = new Twig_Environment();
24 $stream = $env->tokenize('{{ foo }}', 'foo');
25 $this->assertEquals('{{ foo }}', $stream->getSource());
26 $this->assertEquals('foo', $stream->getFilename());
32 public function testLegacyCompileSourceSignature()
34 $loader = new Twig_Loader_Array(array('foo' => '{{ foo }}'));
35 $env = new Twig_Environment($loader);
36 $this->assertContains('getTemplateName', $env->compileSource('{{ foo }}', 'foo'));
40 * @expectedException LogicException
41 * @expectedExceptionMessage You must set a loader first.
44 public function testRenderNoLoader()
46 $env = new Twig_Environment();
50 public function testAutoescapeOption()
52 $loader = new Twig_Loader_Array(array(
53 'html' => '{{ foo }} {{ foo }}',
54 'js' => '{{ bar }} {{ bar }}',
57 $twig = new Twig_Environment($loader, array(
60 'autoescape' => array($this, 'escapingStrategyCallback'),
63 $this->assertEquals('foo<br/ > foo<br/ >', $twig->render('html', array('foo' => 'foo<br/ >')));
64 $this->assertEquals('foo\x3Cbr\x2F\x20\x3E foo\x3Cbr\x2F\x20\x3E', $twig->render('js', array('bar' => 'foo<br/ >')));
67 public function escapingStrategyCallback($name)
72 public function testGlobals()
74 // to be removed in 2.0
75 $loader = $this->getMockBuilder('Twig_EnvironmentTestLoaderInterface')->getMock();
76 //$loader = $this->getMockBuilder(array('Twig_LoaderInterface', 'Twig_SourceContextLoaderInterface'))->getMock();
77 $loader->expects($this->any())->method('getSourceContext')->will($this->returnValue(new Twig_Source('', '')));
79 // globals can be added after calling getGlobals
81 $twig = new Twig_Environment($loader);
82 $twig->addGlobal('foo', 'foo');
84 $twig->addGlobal('foo', 'bar');
85 $globals = $twig->getGlobals();
86 $this->assertEquals('bar', $globals['foo']);
88 // globals can be modified after a template has been loaded
89 $twig = new Twig_Environment($loader);
90 $twig->addGlobal('foo', 'foo');
92 $twig->loadTemplate('index');
93 $twig->addGlobal('foo', 'bar');
94 $globals = $twig->getGlobals();
95 $this->assertEquals('bar', $globals['foo']);
97 // globals can be modified after extensions init
98 $twig = new Twig_Environment($loader);
99 $twig->addGlobal('foo', 'foo');
101 $twig->getFunctions();
102 $twig->addGlobal('foo', 'bar');
103 $globals = $twig->getGlobals();
104 $this->assertEquals('bar', $globals['foo']);
106 // globals can be modified after extensions and a template has been loaded
107 $arrayLoader = new Twig_Loader_Array(array('index' => '{{foo}}'));
108 $twig = new Twig_Environment($arrayLoader);
109 $twig->addGlobal('foo', 'foo');
111 $twig->getFunctions();
112 $twig->loadTemplate('index');
113 $twig->addGlobal('foo', 'bar');
114 $globals = $twig->getGlobals();
115 $this->assertEquals('bar', $globals['foo']);
117 $twig = new Twig_Environment($arrayLoader);
119 $twig->addGlobal('foo', 'bar');
120 $template = $twig->loadTemplate('index');
121 $this->assertEquals('bar', $template->render(array()));
123 /* to be uncomment in Twig 2.0
124 // globals cannot be added after a template has been loaded
125 $twig = new Twig_Environment($loader);
126 $twig->addGlobal('foo', 'foo');
128 $twig->loadTemplate('index');
130 $twig->addGlobal('bar', 'bar');
132 } catch (LogicException $e) {
133 $this->assertFalse(array_key_exists('bar', $twig->getGlobals()));
136 // globals cannot be added after extensions init
137 $twig = new Twig_Environment($loader);
138 $twig->addGlobal('foo', 'foo');
140 $twig->getFunctions();
142 $twig->addGlobal('bar', 'bar');
144 } catch (LogicException $e) {
145 $this->assertFalse(array_key_exists('bar', $twig->getGlobals()));
148 // globals cannot be added after extensions and a template has been loaded
149 $twig = new Twig_Environment($loader);
150 $twig->addGlobal('foo', 'foo');
152 $twig->getFunctions();
153 $twig->loadTemplate('index');
155 $twig->addGlobal('bar', 'bar');
157 } catch (LogicException $e) {
158 $this->assertFalse(array_key_exists('bar', $twig->getGlobals()));
161 // test adding globals after a template has been loaded without call to getGlobals
162 $twig = new Twig_Environment($loader);
163 $twig->loadTemplate('index');
165 $twig->addGlobal('bar', 'bar');
167 } catch (LogicException $e) {
168 $this->assertFalse(array_key_exists('bar', $twig->getGlobals()));
173 public function testExtensionsAreNotInitializedWhenRenderingACompiledTemplate()
175 $cache = new Twig_Cache_Filesystem($dir = sys_get_temp_dir().'/twig');
176 $options = array('cache' => $cache, 'auto_reload' => false, 'debug' => false);
179 $twig = new Twig_Environment($loader = new Twig_Loader_Array(array('index' => '{{ foo }}')), $options);
181 $key = $cache->generateKey('index', $twig->getTemplateClass('index'));
182 $cache->write($key, $twig->compileSource(new Twig_Source('{{ foo }}', 'index')));
184 // check that extensions won't be initialized when rendering a template that is already in the cache
186 ->getMockBuilder('Twig_Environment')
187 ->setConstructorArgs(array($loader, $options))
188 ->setMethods(array('initExtensions'))
192 $twig->expects($this->never())->method('initExtensions');
195 $output = $twig->render('index', array('foo' => 'bar'));
196 $this->assertEquals('bar', $output);
198 Twig_Tests_FilesystemHelper::removeDir($dir);
201 public function testAutoReloadCacheMiss()
203 $templateName = __FUNCTION__;
204 $templateContent = __FUNCTION__;
206 $cache = $this->getMockBuilder('Twig_CacheInterface')->getMock();
207 $loader = $this->getMockLoader($templateName, $templateContent);
208 $twig = new Twig_Environment($loader, array('cache' => $cache, 'auto_reload' => true, 'debug' => false));
210 // Cache miss: getTimestamp returns 0 and as a result the load() is
212 $cache->expects($this->once())
213 ->method('generateKey')
214 ->will($this->returnValue('key'));
215 $cache->expects($this->once())
216 ->method('getTimestamp')
217 ->will($this->returnValue(0));
218 $loader->expects($this->never())
220 $cache->expects($this->once())
222 $cache->expects($this->once())
225 $twig->loadTemplate($templateName);
228 public function testAutoReloadCacheHit()
230 $templateName = __FUNCTION__;
231 $templateContent = __FUNCTION__;
233 $cache = $this->getMockBuilder('Twig_CacheInterface')->getMock();
234 $loader = $this->getMockLoader($templateName, $templateContent);
235 $twig = new Twig_Environment($loader, array('cache' => $cache, 'auto_reload' => true, 'debug' => false));
239 // Cache hit: getTimestamp returns something > extension timestamps and
240 // the loader returns true for isFresh().
241 $cache->expects($this->once())
242 ->method('generateKey')
243 ->will($this->returnValue('key'));
244 $cache->expects($this->once())
245 ->method('getTimestamp')
246 ->will($this->returnValue($now));
247 $loader->expects($this->once())
249 ->will($this->returnValue(true));
250 $cache->expects($this->atLeastOnce())
253 $twig->loadTemplate($templateName);
256 public function testAutoReloadOutdatedCacheHit()
258 $templateName = __FUNCTION__;
259 $templateContent = __FUNCTION__;
261 $cache = $this->getMockBuilder('Twig_CacheInterface')->getMock();
262 $loader = $this->getMockLoader($templateName, $templateContent);
263 $twig = new Twig_Environment($loader, array('cache' => $cache, 'auto_reload' => true, 'debug' => false));
267 $cache->expects($this->once())
268 ->method('generateKey')
269 ->will($this->returnValue('key'));
270 $cache->expects($this->once())
271 ->method('getTimestamp')
272 ->will($this->returnValue($now));
273 $loader->expects($this->once())
275 ->will($this->returnValue(false));
276 $cache->expects($this->once())
278 $cache->expects($this->once())
281 $twig->loadTemplate($templateName);
287 public function testHasGetExtensionWithDynamicName()
289 $twig = new Twig_Environment($this->getMockBuilder('Twig_LoaderInterface')->getMock());
291 $ext1 = new Twig_Tests_EnvironmentTest_Extension_DynamicWithDeprecatedName('ext1');
292 $ext2 = new Twig_Tests_EnvironmentTest_Extension_DynamicWithDeprecatedName('ext2');
293 $twig->addExtension($ext1);
294 $twig->addExtension($ext2);
296 $this->assertTrue($twig->hasExtension('ext1'));
297 $this->assertTrue($twig->hasExtension('ext2'));
299 $this->assertTrue($twig->hasExtension('Twig_Tests_EnvironmentTest_Extension_DynamicWithDeprecatedName'));
301 $this->assertSame($ext1, $twig->getExtension('ext1'));
302 $this->assertSame($ext2, $twig->getExtension('ext2'));
305 public function testHasGetExtensionByClassName()
307 $twig = new Twig_Environment($this->getMockBuilder('Twig_LoaderInterface')->getMock());
308 $twig->addExtension($ext = new Twig_Tests_EnvironmentTest_Extension());
309 $this->assertTrue($twig->hasExtension('Twig_Tests_EnvironmentTest_Extension'));
310 $this->assertTrue($twig->hasExtension('\Twig_Tests_EnvironmentTest_Extension'));
312 $this->assertSame($ext, $twig->getExtension('Twig_Tests_EnvironmentTest_Extension'));
313 $this->assertSame($ext, $twig->getExtension('\Twig_Tests_EnvironmentTest_Extension'));
315 $this->assertTrue($twig->hasExtension('Twig\Tests\EnvironmentTest\Extension'));
316 $this->assertSame($ext, $twig->getExtension('Twig\Tests\EnvironmentTest\Extension'));
319 public function testAddExtension()
321 $twig = new Twig_Environment($this->getMockBuilder('Twig_LoaderInterface')->getMock());
322 $twig->addExtension(new Twig_Tests_EnvironmentTest_Extension());
324 $this->assertArrayHasKey('test', $twig->getTags());
325 $this->assertArrayHasKey('foo_filter', $twig->getFilters());
326 $this->assertArrayHasKey('foo_function', $twig->getFunctions());
327 $this->assertArrayHasKey('foo_test', $twig->getTests());
328 $this->assertArrayHasKey('foo_unary', $twig->getUnaryOperators());
329 $this->assertArrayHasKey('foo_binary', $twig->getBinaryOperators());
330 $this->assertArrayHasKey('foo_global', $twig->getGlobals());
331 $visitors = $twig->getNodeVisitors();
333 foreach ($visitors as $visitor) {
334 if ($visitor instanceof Twig_Tests_EnvironmentTest_NodeVisitor) {
338 $this->assertTrue($found);
344 public function testAddExtensionWithDeprecatedGetGlobals()
346 $twig = new Twig_Environment($this->getMockBuilder('Twig_LoaderInterface')->getMock());
347 $twig->addExtension(new Twig_Tests_EnvironmentTest_Extension_WithGlobals());
349 $this->deprecations = array();
350 set_error_handler(array($this, 'handleError'));
352 $this->assertArrayHasKey('foo_global', $twig->getGlobals());
354 $this->assertCount(1, $this->deprecations);
355 $this->assertContains('Defining the getGlobals() method in the "Twig_Tests_EnvironmentTest_Extension_WithGlobals" extension ', $this->deprecations[0]);
357 restore_error_handler();
363 public function testRemoveExtension()
365 $twig = new Twig_Environment($this->getMockBuilder('Twig_LoaderInterface')->getMock());
366 $twig->addExtension(new Twig_Tests_EnvironmentTest_Extension_WithDeprecatedName());
367 $twig->removeExtension('environment_test');
369 $this->assertArrayNotHasKey('test', $twig->getTags());
370 $this->assertArrayNotHasKey('foo_filter', $twig->getFilters());
371 $this->assertArrayNotHasKey('foo_function', $twig->getFunctions());
372 $this->assertArrayNotHasKey('foo_test', $twig->getTests());
373 $this->assertArrayNotHasKey('foo_unary', $twig->getUnaryOperators());
374 $this->assertArrayNotHasKey('foo_binary', $twig->getBinaryOperators());
375 $this->assertArrayNotHasKey('foo_global', $twig->getGlobals());
376 $this->assertCount(2, $twig->getNodeVisitors());
379 public function testAddMockExtension()
381 // should be replaced by the following in 2.0 (this current code is just to avoid a dep notice)
382 // $extension = $this->getMockBuilder('Twig_Extension')->getMock();
383 $extension = eval(<<<EOF
384 class Twig_Tests_EnvironmentTest_ExtensionInEval extends Twig_Extension
389 $extension = new Twig_Tests_EnvironmentTest_ExtensionInEval();
391 $loader = new Twig_Loader_Array(array('page' => 'hey'));
393 $twig = new Twig_Environment($loader);
394 $twig->addExtension($extension);
396 $this->assertInstanceOf('Twig_ExtensionInterface', $twig->getExtension(get_class($extension)));
397 $this->assertTrue($twig->isTemplateFresh('page', time()));
400 public function testInitRuntimeWithAnExtensionUsingInitRuntimeNoDeprecation()
402 $twig = new Twig_Environment($this->getMockBuilder('Twig_LoaderInterface')->getMock());
403 $twig->addExtension(new Twig_Tests_EnvironmentTest_ExtensionWithoutDeprecationInitRuntime());
404 $twig->initRuntime();
406 // add a dummy assertion here to satisfy PHPUnit, the only thing we want to test is that the code above
407 // can be executed without throwing any deprecations
408 $this->addToAssertionCount(1);
414 public function testInitRuntimeWithAnExtensionUsingInitRuntimeDeprecation()
416 $twig = new Twig_Environment($this->getMockBuilder('Twig_LoaderInterface')->getMock());
417 $twig->addExtension(new Twig_Tests_EnvironmentTest_ExtensionWithDeprecationInitRuntime());
419 $this->deprecations = array();
420 set_error_handler(array($this, 'handleError'));
422 $twig->initRuntime();
424 $this->assertCount(1, $this->deprecations);
425 $this->assertContains('Defining the initRuntime() method in the "Twig_Tests_EnvironmentTest_ExtensionWithDeprecationInitRuntime" extension is deprecated since version 1.23.', $this->deprecations[0]);
427 restore_error_handler();
430 public function handleError($type, $msg)
432 if (E_USER_DEPRECATED === $type) {
433 $this->deprecations[] = $msg;
440 public function testOverrideExtension()
442 $twig = new Twig_Environment($this->getMockBuilder('Twig_LoaderInterface')->getMock());
443 $twig->addExtension(new Twig_Tests_EnvironmentTest_ExtensionWithDeprecationInitRuntime());
445 $this->deprecations = array();
446 set_error_handler(array($this, 'handleError'));
448 $twig->addExtension(new Twig_Tests_EnvironmentTest_Extension_WithDeprecatedName());
449 $twig->addExtension(new Twig_Tests_EnvironmentTest_Extension_WithDeprecatedName());
451 $this->assertCount(1, $this->deprecations);
452 $this->assertContains('The possibility to register the same extension twice', $this->deprecations[0]);
454 restore_error_handler();
457 public function testAddRuntimeLoader()
459 $runtimeLoader = $this->getMockBuilder('Twig_RuntimeLoaderInterface')->getMock();
460 $runtimeLoader->expects($this->any())->method('load')->will($this->returnValue(new Twig_Tests_EnvironmentTest_Runtime()));
462 $loader = new Twig_Loader_Array(array(
463 'func_array' => '{{ from_runtime_array("foo") }}',
464 'func_array_default' => '{{ from_runtime_array() }}',
465 'func_array_named_args' => '{{ from_runtime_array(name="foo") }}',
466 'func_string' => '{{ from_runtime_string("foo") }}',
467 'func_string_default' => '{{ from_runtime_string() }}',
468 'func_string_named_args' => '{{ from_runtime_string(name="foo") }}',
471 $twig = new Twig_Environment($loader);
472 $twig->addExtension(new Twig_Tests_EnvironmentTest_ExtensionWithoutRuntime());
473 $twig->addRuntimeLoader($runtimeLoader);
475 $this->assertEquals('foo', $twig->render('func_array'));
476 $this->assertEquals('bar', $twig->render('func_array_default'));
477 $this->assertEquals('foo', $twig->render('func_array_named_args'));
478 $this->assertEquals('foo', $twig->render('func_string'));
479 $this->assertEquals('bar', $twig->render('func_string_default'));
480 $this->assertEquals('foo', $twig->render('func_string_named_args'));
483 protected function getMockLoader($templateName, $templateContent)
485 // to be removed in 2.0
486 $loader = $this->getMockBuilder('Twig_EnvironmentTestLoaderInterface')->getMock();
487 //$loader = $this->getMockBuilder(array('Twig_LoaderInterface', 'Twig_SourceContextLoaderInterface'))->getMock();
488 $loader->expects($this->any())
489 ->method('getSourceContext')
490 ->with($templateName)
491 ->will($this->returnValue(new Twig_Source($templateContent, $templateName)));
492 $loader->expects($this->any())
493 ->method('getCacheKey')
494 ->with($templateName)
495 ->will($this->returnValue($templateName));
501 class Twig_Tests_EnvironmentTest_Extension_WithGlobals extends Twig_Extension
503 public function getGlobals()
506 'foo_global' => 'foo_global',
511 class Twig_Tests_EnvironmentTest_Extension extends Twig_Extension implements Twig_Extension_GlobalsInterface
513 public function getTokenParsers()
516 new Twig_Tests_EnvironmentTest_TokenParser(),
520 public function getNodeVisitors()
523 new Twig_Tests_EnvironmentTest_NodeVisitor(),
527 public function getFilters()
530 new Twig_SimpleFilter('foo_filter', 'foo_filter'),
534 public function getTests()
537 new Twig_SimpleTest('foo_test', 'foo_test'),
541 public function getFunctions()
544 new Twig_SimpleFunction('foo_function', 'foo_function'),
548 public function getOperators()
551 array('foo_unary' => array()),
552 array('foo_binary' => array()),
556 public function getGlobals()
559 'foo_global' => 'foo_global',
563 class_alias('Twig_Tests_EnvironmentTest_Extension', 'Twig\Tests\EnvironmentTest\Extension', false);
565 class Twig_Tests_EnvironmentTest_Extension_WithDeprecatedName extends Twig_Extension
567 public function getName()
569 return 'environment_test';
573 class Twig_Tests_EnvironmentTest_Extension_DynamicWithDeprecatedName extends Twig_Extension
577 public function __construct($name)
582 public function getName()
588 class Twig_Tests_EnvironmentTest_TokenParser extends Twig_TokenParser
590 public function parse(Twig_Token $token)
594 public function getTag()
600 class Twig_Tests_EnvironmentTest_NodeVisitor implements Twig_NodeVisitorInterface
602 public function enterNode(Twig_NodeInterface $node, Twig_Environment $env)
607 public function leaveNode(Twig_NodeInterface $node, Twig_Environment $env)
612 public function getPriority()
618 class Twig_Tests_EnvironmentTest_ExtensionWithDeprecationInitRuntime extends Twig_Extension
620 public function initRuntime(Twig_Environment $env)
625 class Twig_Tests_EnvironmentTest_ExtensionWithoutDeprecationInitRuntime extends Twig_Extension implements Twig_Extension_InitRuntimeInterface
627 public function initRuntime(Twig_Environment $env)
632 class Twig_Tests_EnvironmentTest_ExtensionWithoutRuntime extends Twig_Extension
634 public function getFunctions()
637 new Twig_SimpleFunction('from_runtime_array', array('Twig_Tests_EnvironmentTest_Runtime', 'fromRuntime')),
638 new Twig_SimpleFunction('from_runtime_string', 'Twig_Tests_EnvironmentTest_Runtime::fromRuntime'),
642 public function getName()
644 return 'from_runtime';
648 class Twig_Tests_EnvironmentTest_Runtime
650 public function fromRuntime($name = 'bar')
656 // to be removed in 2.0
657 interface Twig_EnvironmentTestLoaderInterface extends Twig_LoaderInterface, Twig_SourceContextLoaderInterface