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\Error\LoaderError; 14use Twig\Loader\FilesystemLoader; 15 16class Twig_Tests_Loader_FilesystemTest extends \PHPUnit\Framework\TestCase 17{ 18 public function testGetSourceContext() 19 { 20 $path = __DIR__.'/../Fixtures'; 21 $loader = new FilesystemLoader([$path]); 22 $this->assertEquals('errors/index.html', $loader->getSourceContext('errors/index.html')->getName()); 23 $this->assertEquals(realpath($path.'/errors/index.html'), realpath($loader->getSourceContext('errors/index.html')->getPath())); 24 } 25 26 /** 27 * @dataProvider getSecurityTests 28 */ 29 public function testSecurity($template) 30 { 31 $loader = new FilesystemLoader([__DIR__.'/../Fixtures']); 32 33 try { 34 $loader->getCacheKey($template); 35 $this->fail(); 36 } catch (LoaderError $e) { 37 $this->assertNotContains('Unable to find template', $e->getMessage()); 38 } 39 } 40 41 public function getSecurityTests() 42 { 43 return [ 44 ["AutoloaderTest\0.php"], 45 ['..\\AutoloaderTest.php'], 46 ['..\\\\\\AutoloaderTest.php'], 47 ['../AutoloaderTest.php'], 48 ['..////AutoloaderTest.php'], 49 ['./../AutoloaderTest.php'], 50 ['.\\..\\AutoloaderTest.php'], 51 ['././././././../AutoloaderTest.php'], 52 ['.\\./.\\./.\\./../AutoloaderTest.php'], 53 ['foo/../../AutoloaderTest.php'], 54 ['foo\\..\\..\\AutoloaderTest.php'], 55 ['foo/../bar/../../AutoloaderTest.php'], 56 ['foo/bar/../../../AutoloaderTest.php'], 57 ['filters/../../AutoloaderTest.php'], 58 ['filters//..//..//AutoloaderTest.php'], 59 ['filters\\..\\..\\AutoloaderTest.php'], 60 ['filters\\\\..\\\\..\\\\AutoloaderTest.php'], 61 ['filters\\//../\\/\\..\\AutoloaderTest.php'], 62 ['/../AutoloaderTest.php'], 63 ]; 64 } 65 66 /** 67 * @dataProvider getBasePaths 68 */ 69 public function testPaths($basePath, $cacheKey, $rootPath) 70 { 71 $loader = new FilesystemLoader([$basePath.'/normal', $basePath.'/normal_bis'], $rootPath); 72 $loader->setPaths([$basePath.'/named', $basePath.'/named_bis'], 'named'); 73 $loader->addPath($basePath.'/named_ter', 'named'); 74 $loader->addPath($basePath.'/normal_ter'); 75 $loader->prependPath($basePath.'/normal_final'); 76 $loader->prependPath($basePath.'/named/../named_quater', 'named'); 77 $loader->prependPath($basePath.'/named_final', 'named'); 78 79 $this->assertEquals([ 80 $basePath.'/normal_final', 81 $basePath.'/normal', 82 $basePath.'/normal_bis', 83 $basePath.'/normal_ter', 84 ], $loader->getPaths()); 85 $this->assertEquals([ 86 $basePath.'/named_final', 87 $basePath.'/named/../named_quater', 88 $basePath.'/named', 89 $basePath.'/named_bis', 90 $basePath.'/named_ter', 91 ], $loader->getPaths('named')); 92 93 // do not use realpath here as it would make the test unuseful 94 $this->assertEquals($cacheKey, str_replace('\\', '/', $loader->getCacheKey('@named/named_absolute.html'))); 95 $this->assertEquals("path (final)\n", $loader->getSourceContext('index.html')->getCode()); 96 $this->assertEquals("path (final)\n", $loader->getSourceContext('@__main__/index.html')->getCode()); 97 $this->assertEquals("named path (final)\n", $loader->getSourceContext('@named/index.html')->getCode()); 98 } 99 100 public function getBasePaths() 101 { 102 return [ 103 [ 104 __DIR__.'/Fixtures', 105 'test/Twig/Tests/Loader/Fixtures/named_quater/named_absolute.html', 106 null, 107 ], 108 [ 109 __DIR__.'/Fixtures/../Fixtures', 110 'test/Twig/Tests/Loader/Fixtures/named_quater/named_absolute.html', 111 null, 112 ], 113 [ 114 'test/Twig/Tests/Loader/Fixtures', 115 'test/Twig/Tests/Loader/Fixtures/named_quater/named_absolute.html', 116 getcwd(), 117 ], 118 [ 119 'Fixtures', 120 'Fixtures/named_quater/named_absolute.html', 121 getcwd().'/test/Twig/Tests/Loader', 122 ], 123 [ 124 'Fixtures', 125 'Fixtures/named_quater/named_absolute.html', 126 getcwd().'/test/../test/Twig/Tests/Loader', 127 ], 128 ]; 129 } 130 131 public function testEmptyConstructor() 132 { 133 $loader = new FilesystemLoader(); 134 $this->assertEquals([], $loader->getPaths()); 135 } 136 137 public function testGetNamespaces() 138 { 139 $loader = new FilesystemLoader(sys_get_temp_dir()); 140 $this->assertEquals([FilesystemLoader::MAIN_NAMESPACE], $loader->getNamespaces()); 141 142 $loader->addPath(sys_get_temp_dir(), 'named'); 143 $this->assertEquals([FilesystemLoader::MAIN_NAMESPACE, 'named'], $loader->getNamespaces()); 144 } 145 146 public function testFindTemplateExceptionNamespace() 147 { 148 $basePath = __DIR__.'/Fixtures'; 149 150 $loader = new FilesystemLoader([$basePath.'/normal']); 151 $loader->addPath($basePath.'/named', 'named'); 152 153 try { 154 $loader->getSourceContext('@named/nowhere.html'); 155 } catch (\Exception $e) { 156 $this->assertInstanceOf('\Twig\Error\LoaderError', $e); 157 $this->assertContains('Unable to find template "@named/nowhere.html"', $e->getMessage()); 158 } 159 } 160 161 public function testFindTemplateWithCache() 162 { 163 $basePath = __DIR__.'/Fixtures'; 164 165 $loader = new FilesystemLoader([$basePath.'/normal']); 166 $loader->addPath($basePath.'/named', 'named'); 167 168 // prime the cache for index.html in the named namespace 169 $namedSource = $loader->getSourceContext('@named/index.html')->getCode(); 170 $this->assertEquals("named path\n", $namedSource); 171 172 // get index.html from the main namespace 173 $this->assertEquals("path\n", $loader->getSourceContext('index.html')->getCode()); 174 } 175 176 public function testLoadTemplateAndRenderBlockWithCache() 177 { 178 $loader = new FilesystemLoader([]); 179 $loader->addPath(__DIR__.'/Fixtures/themes/theme2'); 180 $loader->addPath(__DIR__.'/Fixtures/themes/theme1'); 181 $loader->addPath(__DIR__.'/Fixtures/themes/theme1', 'default_theme'); 182 183 $twig = new Environment($loader); 184 185 $template = $twig->load('blocks.html.twig'); 186 $this->assertSame('block from theme 1', $template->renderBlock('b1', [])); 187 188 $template = $twig->load('blocks.html.twig'); 189 $this->assertSame('block from theme 2', $template->renderBlock('b2', [])); 190 } 191 192 public function getArrayInheritanceTests() 193 { 194 return [ 195 'valid array inheritance' => ['array_inheritance_valid_parent.html.twig'], 196 'array inheritance with null first template' => ['array_inheritance_null_parent.html.twig'], 197 'array inheritance with empty first template' => ['array_inheritance_empty_parent.html.twig'], 198 'array inheritance with non-existent first template' => ['array_inheritance_nonexistent_parent.html.twig'], 199 ]; 200 } 201 202 /** 203 * @dataProvider getArrayInheritanceTests 204 * 205 * @param $templateName string Template name with array inheritance 206 */ 207 public function testArrayInheritance($templateName) 208 { 209 $loader = new FilesystemLoader([]); 210 $loader->addPath(__DIR__.'/Fixtures/inheritance'); 211 212 $twig = new Environment($loader); 213 214 $template = $twig->load($templateName); 215 $this->assertSame('VALID Child', $template->renderBlock('body', [])); 216 } 217 218 /** 219 * @requires PHP 5.3 220 */ 221 public function testLoadTemplateFromPhar() 222 { 223 $loader = new FilesystemLoader([]); 224 // phar-sample.phar was created with the following script: 225 // $f = new Phar('phar-test.phar'); 226 // $f->addFromString('hello.twig', 'hello from phar'); 227 $loader->addPath('phar://'.__DIR__.'/Fixtures/phar/phar-sample.phar'); 228 $this->assertSame('hello from phar', $loader->getSourceContext('hello.twig')->getCode()); 229 } 230 231 public function testTemplateExistsAlwaysReturnsBool() 232 { 233 $loader = new FilesystemLoader([]); 234 $this->assertFalse($loader->exists("foo\0.twig")); 235 $this->assertFalse($loader->exists('../foo.twig')); 236 $this->assertFalse($loader->exists('@foo')); 237 $this->assertFalse($loader->exists('foo')); 238 $this->assertFalse($loader->exists('@foo/bar.twig')); 239 240 $loader->addPath(__DIR__.'/Fixtures/normal'); 241 $this->assertTrue($loader->exists('index.html')); 242 $loader->addPath(__DIR__.'/Fixtures/normal', 'foo'); 243 $this->assertTrue($loader->exists('@foo/index.html')); 244 } 245} 246