diff --git a/CHANGELOG.md b/CHANGELOG.md
index 59f86af8..3ddc5d80 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,6 +1,12 @@
CHANGELOG
=========
+2.2.0
+-----
+
+ * added automatic registration of namespaced paths for registered bundles
+ * added support for namespaced paths
+
2.1.0
-----
diff --git a/DependencyInjection/Configuration.php b/DependencyInjection/Configuration.php
index 7936d219..3b3c8ed6 100644
--- a/DependencyInjection/Configuration.php
+++ b/DependencyInjection/Configuration.php
@@ -126,6 +126,29 @@ private function addTwigOptions(ArrayNodeDefinition $rootNode)
->scalarNode('auto_reload')->end()
->scalarNode('optimizations')->end()
->arrayNode('paths')
+ ->beforeNormalization()
+ ->always()
+ ->then(function ($paths) {
+ $normalized = array();
+ foreach ($paths as $path => $namespace) {
+ if (is_array($namespace)) {
+ // xml
+ $path = $namespace['value'];
+ $namespace = $namespace['namespace'];
+ }
+
+ // path within the default namespace
+ if (ctype_digit((string) $path)) {
+ $path = $namespace;
+ $namespace = null;
+ }
+
+ $normalized[$path] = $namespace;
+ }
+
+ return $normalized;
+ })
+ ->end()
->prototype('variable')->end()
->end()
->end()
diff --git a/DependencyInjection/TwigExtension.php b/DependencyInjection/TwigExtension.php
index d65dc9fb..e13fe086 100644
--- a/DependencyInjection/TwigExtension.php
+++ b/DependencyInjection/TwigExtension.php
@@ -60,12 +60,33 @@ public function load(array $configs, ContainerBuilder $container)
$reflClass = new \ReflectionClass('Symfony\Bridge\Twig\Extension\FormExtension');
$container->getDefinition('twig.loader')->addMethodCall('addPath', array(dirname(dirname($reflClass->getFileName())).'/Resources/views/Form'));
- if (!empty($config['paths'])) {
- foreach ($config['paths'] as $path) {
- $container->getDefinition('twig.loader')->addMethodCall('addPath', array($path));
+ $twigLoaderDefinition = $container->getDefinition('twig.loader');
+
+ // register user-configured paths
+ foreach ($config['paths'] as $path => $namespace) {
+ if (!$namespace) {
+ $twigLoaderDefinition->addMethodCall('addPath', array($path));
+ } else {
+ $twigLoaderDefinition->addMethodCall('addPath', array($path, $namespace));
}
}
+ // register bundles as Twig namespaces
+ foreach ($container->getParameter('kernel.bundles') as $bundle => $class) {
+ if (is_dir($dir = $container->getParameter('kernel.root_dir').'/Resources/'.$bundle.'/views')) {
+ $this->addTwigPath($twigLoaderDefinition, $dir, $bundle);
+ }
+
+ $reflection = new \ReflectionClass($class);
+ if (is_dir($dir = dirname($reflection->getFilename()).'/Resources/views')) {
+ $this->addTwigPath($twigLoaderDefinition, $dir, $bundle);
+ }
+ }
+
+ if (is_dir($dir = $container->getParameter('kernel.root_dir').'/Resources/views')) {
+ $twigLoaderDefinition->addMethodCall('addPath', array($dir));
+ }
+
if (!empty($config['globals'])) {
$def = $container->getDefinition('twig');
foreach ($config['globals'] as $key => $global) {
@@ -108,6 +129,15 @@ public function load(array $configs, ContainerBuilder $container)
));
}
+ private function addTwigPath($twigLoaderDefinition, $dir, $bundle)
+ {
+ $name = $bundle;
+ if ('Bundle' === substr($name, -6)) {
+ $name = substr($name, 0, -6);
+ }
+ $twigLoaderDefinition->addMethodCall('addPath', array($dir, $name));
+ }
+
/**
* Returns the base path for the XSD files.
*
diff --git a/Loader/FilesystemLoader.php b/Loader/FilesystemLoader.php
index a79d35c8..64d5616c 100644
--- a/Loader/FilesystemLoader.php
+++ b/Loader/FilesystemLoader.php
@@ -64,16 +64,19 @@ protected function findTemplate($template)
$file = null;
$previous = null;
try {
- $template = $this->parser->parse($template);
- try {
- $file = $this->locator->locate($template);
- } catch (\InvalidArgumentException $e) {
- $previous = $e;
- }
- } catch (\Exception $e) {
+ $file = parent::findTemplate($template);
+ } catch (\Twig_Error_Loader $e) {
+ $previous = $e;
+
+ // for BC
try {
- $file = parent::findTemplate($template);
- } catch (\Twig_Error_Loader $e) {
+ $template = $this->parser->parse($template);
+ try {
+ $file = $this->locator->locate($template);
+ } catch (\InvalidArgumentException $e) {
+ $previous = $e;
+ }
+ } catch (\Exception $e) {
$previous = $e;
}
}
diff --git a/Resources/config/schema/twig-1.0.xsd b/Resources/config/schema/twig-1.0.xsd
index 2a72ef6f..3706f148 100644
--- a/Resources/config/schema/twig-1.0.xsd
+++ b/Resources/config/schema/twig-1.0.xsd
@@ -11,7 +11,7 @@
-
+
@@ -30,6 +30,10 @@
+
+
+
+
diff --git a/Tests/DependencyInjection/Fixtures/Resources/TwigBundle/views/layout.html.twig b/Tests/DependencyInjection/Fixtures/Resources/TwigBundle/views/layout.html.twig
new file mode 100644
index 00000000..bb07ecfe
--- /dev/null
+++ b/Tests/DependencyInjection/Fixtures/Resources/TwigBundle/views/layout.html.twig
@@ -0,0 +1 @@
+This is a layout
diff --git a/Tests/DependencyInjection/Fixtures/Resources/views/layout.html.twig b/Tests/DependencyInjection/Fixtures/Resources/views/layout.html.twig
new file mode 100644
index 00000000..bb07ecfe
--- /dev/null
+++ b/Tests/DependencyInjection/Fixtures/Resources/views/layout.html.twig
@@ -0,0 +1 @@
+This is a layout
diff --git a/Tests/DependencyInjection/Fixtures/php/full.php b/Tests/DependencyInjection/Fixtures/php/full.php
index ba3150ea..bad71a38 100644
--- a/Tests/DependencyInjection/Fixtures/php/full.php
+++ b/Tests/DependencyInjection/Fixtures/php/full.php
@@ -18,5 +18,10 @@
'charset' => 'ISO-8859-1',
'debug' => true,
'strict_variables' => true,
- 'paths' => array('path1', 'path2'),
+ 'paths' => array(
+ 'path1',
+ 'path2',
+ 'namespaced_path1' => 'namespace',
+ 'namespaced_path2' => 'namespace',
+ ),
));
diff --git a/Tests/DependencyInjection/Fixtures/xml/full.xml b/Tests/DependencyInjection/Fixtures/xml/full.xml
index 63cbe3e6..0d3c053c 100644
--- a/Tests/DependencyInjection/Fixtures/xml/full.xml
+++ b/Tests/DependencyInjection/Fixtures/xml/full.xml
@@ -14,5 +14,7 @@
3.14
path1
path2
+ namespaced_path1
+ namespaced_path2
diff --git a/Tests/DependencyInjection/Fixtures/yml/full.yml b/Tests/DependencyInjection/Fixtures/yml/full.yml
index 8378e331..afc14615 100644
--- a/Tests/DependencyInjection/Fixtures/yml/full.yml
+++ b/Tests/DependencyInjection/Fixtures/yml/full.yml
@@ -13,4 +13,8 @@ twig:
charset: ISO-8859-1
debug: true
strict_variables: true
- paths: [path1, path2]
+ paths:
+ path1: ''
+ path2: ''
+ namespaced_path1: namespace
+ namespaced_path2: namespace
diff --git a/Tests/DependencyInjection/TwigExtensionTest.php b/Tests/DependencyInjection/TwigExtensionTest.php
index eccc1f2e..01a5e54a 100644
--- a/Tests/DependencyInjection/TwigExtensionTest.php
+++ b/Tests/DependencyInjection/TwigExtensionTest.php
@@ -108,6 +108,37 @@ public function testGlobalsWithDifferentTypesAndValues()
}
}
+ /**
+ * @dataProvider getFormats
+ */
+ public function testTwigLoaderPaths($format)
+ {
+ $container = $this->createContainer();
+ $container->registerExtension(new TwigExtension());
+ $this->loadFromFile($container, 'full', $format);
+ $this->compileContainer($container);
+
+ $def = $container->getDefinition('twig.loader');
+ $paths = array();
+ foreach ($def->getMethodCalls() as $call) {
+ if ('addPath' === $call[0]) {
+ if (false === strpos($call[1][0], 'Form')) {
+ $paths[] = $call[1];
+ }
+ }
+ }
+
+ $this->assertEquals(array(
+ array('path1'),
+ array('path2'),
+ array('namespaced_path1', 'namespace'),
+ array('namespaced_path2', 'namespace'),
+ array(__DIR__.'/Fixtures/Resources/TwigBundle/views', 'Twig'),
+ array(realpath(__DIR__.'/../../Resources/views'), 'Twig'),
+ array(__DIR__.'/Fixtures/Resources/views'),
+ ), $paths);
+ }
+
public function getFormats()
{
return array(
@@ -121,8 +152,10 @@ private function createContainer()
{
$container = new ContainerBuilder(new ParameterBag(array(
'kernel.cache_dir' => __DIR__,
+ 'kernel.root_dir' => __DIR__.'/Fixtures',
'kernel.charset' => 'UTF-8',
'kernel.debug' => false,
+ 'kernel.bundles' => array('TwigBundle' => 'Symfony\\Bundle\\TwigBundle\\TwigBundle'),
)));
return $container;
diff --git a/Tests/Loader/FilesystemLoaderTest.php b/Tests/Loader/FilesystemLoaderTest.php
index 0d29d30b..8bb1c169 100644
--- a/Tests/Loader/FilesystemLoaderTest.php
+++ b/Tests/Loader/FilesystemLoaderTest.php
@@ -16,59 +16,73 @@
use Symfony\Component\Config\FileLocatorInterface;
use Symfony\Bundle\FrameworkBundle\Templating\TemplateReference;
use Symfony\Component\Templating\TemplateNameParserInterface;
-use InvalidArgumentException;
class FilesystemLoaderTest extends TestCase
{
- /** @var FileLocatorInterface */
- private $locator;
- /** @var TemplateNameParserInterface */
- private $parser;
- /** @var FilesystemLoader */
- private $loader;
-
- protected function setUp()
+ public function testGetSource()
{
- parent::setUp();
-
- $this->locator = $this->getMock('Symfony\Component\Config\FileLocatorInterface');
- $this->parser = $this->getMock('Symfony\Component\Templating\TemplateNameParserInterface');
- $this->loader = new FilesystemLoader($this->locator, $this->parser);
-
- $this->parser->expects($this->once())
- ->method('parse')
- ->with('name.format.engine')
- ->will($this->returnValue(new TemplateReference('', '', 'name', 'format', 'engine')))
+ $parser = $this->getMock('Symfony\Component\Templating\TemplateNameParserInterface');
+ $locator = $this->getMock('Symfony\Component\Config\FileLocatorInterface');
+ $locator
+ ->expects($this->once())
+ ->method('locate')
+ ->will($this->returnValue(__DIR__.'/../DependencyInjection/Fixtures/Resources/views/layout.html.twig'))
;
- }
+ $loader = new FilesystemLoader($locator, $parser);
+ $loader->addPath(__DIR__.'/../DependencyInjection/Fixtures/Resources/views', 'namespace');
- protected function tearDown()
- {
- parent::tearDown();
+ // Twig-style
+ $this->assertEquals("This is a layout\n", $loader->getSource('@namespace/layout.html.twig'));
- $this->locator = null;
- $this->parser = null;
- $this->loader = null;
+ // Symfony-style
+ $this->assertEquals("This is a layout\n", $loader->getSource('TwigBundle::layout.html.twig'));
}
+ /**
+ * @expectedException Twig_Error_Loader
+ */
public function testTwigErrorIfLocatorThrowsInvalid()
{
- $this->setExpectedException('Twig_Error_Loader');
- $invalidException = new InvalidArgumentException('Unable to find template "NonExistent".');
- $this->locator->expects($this->once())
- ->method('locate')
- ->will($this->throwException($invalidException));
+ $parser = $this->getMock('Symfony\Component\Templating\TemplateNameParserInterface');
+ $parser
+ ->expects($this->once())
+ ->method('parse')
+ ->with('name.format.engine')
+ ->will($this->returnValue(new TemplateReference('', '', 'name', 'format', 'engine')))
+ ;
- $this->loader->getCacheKey('name.format.engine');
+ $locator = $this->getMock('Symfony\Component\Config\FileLocatorInterface');
+ $locator
+ ->expects($this->once())
+ ->method('locate')
+ ->will($this->throwException(new \InvalidArgumentException('Unable to find template "NonExistent".')))
+ ;
+
+ $loader = new FilesystemLoader($locator, $parser);
+ $loader->getCacheKey('name.format.engine');
}
+ /**
+ * @expectedException Twig_Error_Loader
+ */
public function testTwigErrorIfLocatorReturnsFalse()
{
- $this->setExpectedException('Twig_Error_Loader');
- $this->locator->expects($this->once())
- ->method('locate')
- ->will($this->returnValue(false));
+ $parser = $this->getMock('Symfony\Component\Templating\TemplateNameParserInterface');
+ $parser
+ ->expects($this->once())
+ ->method('parse')
+ ->with('name.format.engine')
+ ->will($this->returnValue(new TemplateReference('', '', 'name', 'format', 'engine')))
+ ;
+
+ $locator = $this->getMock('Symfony\Component\Config\FileLocatorInterface');
+ $locator
+ ->expects($this->once())
+ ->method('locate')
+ ->will($this->returnValue(false))
+ ;
- $this->loader->getCacheKey('name.format.engine');
+ $loader = new FilesystemLoader($locator, $parser);
+ $loader->getCacheKey('name.format.engine');
}
}