diff --git a/src/Symfony/Bundle/FrameworkBundle/Resources/config/routing.xml b/src/Symfony/Bundle/FrameworkBundle/Resources/config/routing.xml
index 6b2f7c9688699..a2e485f0c0a21 100644
--- a/src/Symfony/Bundle/FrameworkBundle/Resources/config/routing.xml
+++ b/src/Symfony/Bundle/FrameworkBundle/Resources/config/routing.xml
@@ -12,6 +12,7 @@
Symfony\Component\Routing\Loader\XmlFileLoader
Symfony\Component\Routing\Loader\YamlFileLoader
Symfony\Component\Routing\Loader\PhpFileLoader
+ Symfony\Component\Routing\Loader\DirectoryLoader
Symfony\Component\Routing\Generator\UrlGenerator
Symfony\Component\Routing\Generator\UrlGenerator
Symfony\Component\Routing\Generator\Dumper\PhpGeneratorDumper
@@ -45,6 +46,11 @@
+
+
+
+
+
diff --git a/src/Symfony/Component/Config/Loader/FileLoader.php b/src/Symfony/Component/Config/Loader/FileLoader.php
index 8e8cb0be6ccd2..4513b75661602 100644
--- a/src/Symfony/Component/Config/Loader/FileLoader.php
+++ b/src/Symfony/Component/Config/Loader/FileLoader.php
@@ -54,6 +54,14 @@ public function setCurrentDir($dir)
$this->currentDir = $dir;
}
+ /**
+ * @return string
+ */
+ public function getCurrentDir()
+ {
+ return $this->currentDir;
+ }
+
/**
* Returns the file locator used by this loader.
*
diff --git a/src/Symfony/Component/DependencyInjection/Loader/DirectoryLoader.php b/src/Symfony/Component/DependencyInjection/Loader/DirectoryLoader.php
new file mode 100644
index 0000000000000..1e3cf9d696333
--- /dev/null
+++ b/src/Symfony/Component/DependencyInjection/Loader/DirectoryLoader.php
@@ -0,0 +1,58 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\DependencyInjection\Loader;
+
+use Symfony\Component\Config\Resource\DirectoryResource;
+
+/**
+ * DirectoryLoader is a recursive loader to go through directories
+ *
+ * @author Sebastien Lavoie
+ */
+class DirectoryLoader extends FileLoader
+{
+ /**
+ * @param mixed $file The resource
+ * @param string $type The resource type
+ */
+ public function load($file, $type = null)
+ {
+ $file = rtrim($file, '/');
+ $path = $this->locator->locate($file);
+ $this->container->addResource(new DirectoryResource($path));
+
+ foreach (scandir($path) as $dir) {
+ if ($dir[0] !== '.') {
+ if (is_dir("$path/$dir")) {
+ $dir .= '/'; // append / to allow recursion
+ }
+
+ $this->setCurrentDir($path);
+
+ $this->import($dir, null, false, $path);
+ }
+ }
+ }
+
+ /**
+ * Returns true if this class supports the given resource.
+ *
+ * @param mixed $resource A resource
+ * @param string $type The resource type
+ *
+ * @return bool true if this class supports the given resource, false otherwise
+ */
+ public function supports($resource, $type = null)
+ {
+ return 'directory' === $type || (!$type && preg_match('/\/$/', $resource) === 1); // ends with a slash
+ }
+}
diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/directory/recurse/simple.ini b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/directory/recurse/simple.ini
new file mode 100644
index 0000000000000..0984cdac770a4
--- /dev/null
+++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/directory/recurse/simple.ini
@@ -0,0 +1,2 @@
+[parameters]
+ ini = ini
diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/directory/recurse/simple.yml b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/directory/recurse/simple.yml
new file mode 100644
index 0000000000000..f98ef12ea3c65
--- /dev/null
+++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/directory/recurse/simple.yml
@@ -0,0 +1,2 @@
+parameters:
+ yaml: yaml
diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/directory/simple.php b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/directory/simple.php
new file mode 100644
index 0000000000000..4750324ad1de3
--- /dev/null
+++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/directory/simple.php
@@ -0,0 +1,3 @@
+setParameter('php', 'php');
diff --git a/src/Symfony/Component/DependencyInjection/Tests/Loader/DirectoryLoaderTest.php b/src/Symfony/Component/DependencyInjection/Tests/Loader/DirectoryLoaderTest.php
new file mode 100644
index 0000000000000..0fdb1d716e580
--- /dev/null
+++ b/src/Symfony/Component/DependencyInjection/Tests/Loader/DirectoryLoaderTest.php
@@ -0,0 +1,82 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\DependencyInjection\Tests\Loader;
+
+use Symfony\Component\DependencyInjection\ContainerBuilder;
+use Symfony\Component\DependencyInjection\Loader\PhpFileLoader;
+use Symfony\Component\DependencyInjection\Loader\IniFileLoader;
+use Symfony\Component\DependencyInjection\Loader\YamlFileLoader;
+use Symfony\Component\DependencyInjection\Loader\DirectoryLoader;
+use Symfony\Component\Config\Loader\LoaderResolver;
+use Symfony\Component\Config\FileLocator;
+
+class DirectoryLoaderTest extends \PHPUnit_Framework_TestCase
+{
+ protected static $fixturesPath;
+
+ protected $container;
+ protected $loader;
+
+ public static function setUpBeforeClass()
+ {
+ self::$fixturesPath = realpath(__DIR__.'/../Fixtures/');
+ }
+
+ protected function setUp()
+ {
+ $locator = new FileLocator(self::$fixturesPath);
+ $this->container = new ContainerBuilder();
+ $this->loader = new DirectoryLoader($this->container, $locator);
+ $resolver = new LoaderResolver(array(
+ new PhpFileLoader($this->container, $locator),
+ new IniFileLoader($this->container, $locator),
+ new YamlFileLoader($this->container, $locator),
+ $this->loader,
+ ));
+ $this->loader->setResolver($resolver);
+ }
+
+ /**
+ * @covers Symfony\Component\DependencyInjection\Loader\DirectoryLoader::__construct
+ * @covers Symfony\Component\DependencyInjection\Loader\DirectoryLoader::load
+ */
+ public function testDirectoryCanBeLoadedRecursively()
+ {
+ $this->loader->load('directory/');
+ $this->assertEquals(array('ini' => 'ini', 'yaml' => 'yaml', 'php' => 'php'), $this->container->getParameterBag()->all(), '->load() takes a single file name as its first argument');
+ }
+
+ /**
+ * @covers Symfony\Component\DependencyInjection\Loader\DirectoryLoader::__construct
+ * @covers Symfony\Component\DependencyInjection\Loader\DirectoryLoader::load
+ *
+ * @expectedException \InvalidArgumentException
+ * @expectedExceptionMessage The file "foo" does not exist (in:
+ */
+ public function testExceptionIsRaisedWhenDirectoryDoesNotExist()
+ {
+ $this->loader->load('foo/');
+ }
+
+ /**
+ * @covers Symfony\Component\DependencyInjection\Loader\DirectoryLoader::supports
+ */
+ public function testSupports()
+ {
+ $loader = new DirectoryLoader(new ContainerBuilder(), new FileLocator());
+
+ $this->assertTrue($loader->supports('foo/'), '->supports() returns true if the resource is loadable');
+ $this->assertTrue($loader->supports('foo/', 'directory'), '->supports() returns true if the resource is loadable');
+ $this->assertTrue($loader->supports('foo', 'directory'), '->supports() returns true if the resource is loadable');
+ $this->assertFalse($loader->supports('foo'), '->supports() returns true if the resource is loadable');
+ }
+}
diff --git a/src/Symfony/Component/HttpKernel/Kernel.php b/src/Symfony/Component/HttpKernel/Kernel.php
index 5f533f9587c15..c760a8d2e0ba3 100644
--- a/src/Symfony/Component/HttpKernel/Kernel.php
+++ b/src/Symfony/Component/HttpKernel/Kernel.php
@@ -21,6 +21,7 @@
use Symfony\Component\DependencyInjection\Loader\YamlFileLoader;
use Symfony\Component\DependencyInjection\Loader\IniFileLoader;
use Symfony\Component\DependencyInjection\Loader\PhpFileLoader;
+use Symfony\Component\DependencyInjection\Loader\DirectoryLoader;
use Symfony\Component\DependencyInjection\Loader\ClosureLoader;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
@@ -741,6 +742,7 @@ protected function getContainerLoader(ContainerInterface $container)
new YamlFileLoader($container, $locator),
new IniFileLoader($container, $locator),
new PhpFileLoader($container, $locator),
+ new DirectoryLoader($container, $locator),
new ClosureLoader($container),
));
diff --git a/src/Symfony/Component/Routing/Loader/DirectoryLoader.php b/src/Symfony/Component/Routing/Loader/DirectoryLoader.php
new file mode 100644
index 0000000000000..12cba622ea9d2
--- /dev/null
+++ b/src/Symfony/Component/Routing/Loader/DirectoryLoader.php
@@ -0,0 +1,65 @@
+locator->locate($file);
+
+ $collection = new RouteCollection();
+ $collection->addResource(new DirectoryResource($path));
+
+ foreach (scandir($path) as $dir) {
+ if ($dir[0] !== '.') {
+ $this->setCurrentDir($path);
+
+ $subType = is_dir("$path/$dir") ? 'directory' : null;
+ $subCollection = $this->import("$path/$dir", $subType, false, $path);
+ $collection->addCollection($subCollection);
+ }
+ }
+
+ return $collection;
+ }
+
+ /**
+ * Store here as well because FileLoader::currentDir is private
+ */
+ public function setCurrentDir($currentDir)
+ {
+ $this->currentDir = $currentDir;
+
+ parent::setCurrentDir($currentDir);
+ }
+
+ /**
+ * Returns true if this class supports the given resource.
+ *
+ * @param mixed $resource A resource
+ * @param string $type The resource type
+ *
+ * @return bool true if this class supports the given resource, false otherwise
+ */
+ public function supports($resource, $type = null)
+ {
+ try {
+ $path = $this->locator->locate($resource, $this->currentDir);
+ } catch (\Exception $e) {
+ return false;
+ }
+
+ return is_string($resource) && 'directory' === $type;
+ }
+}
diff --git a/src/Symfony/Component/Routing/Tests/Fixtures/directory/recurse/routes1.yml b/src/Symfony/Component/Routing/Tests/Fixtures/directory/recurse/routes1.yml
new file mode 100644
index 0000000000000..d078836625c6b
--- /dev/null
+++ b/src/Symfony/Component/Routing/Tests/Fixtures/directory/recurse/routes1.yml
@@ -0,0 +1,2 @@
+route1:
+ path: /route/1
diff --git a/src/Symfony/Component/Routing/Tests/Fixtures/directory/recurse/routes2.yml b/src/Symfony/Component/Routing/Tests/Fixtures/directory/recurse/routes2.yml
new file mode 100644
index 0000000000000..938fb2457e9a3
--- /dev/null
+++ b/src/Symfony/Component/Routing/Tests/Fixtures/directory/recurse/routes2.yml
@@ -0,0 +1,2 @@
+route2:
+ path: /route/2
diff --git a/src/Symfony/Component/Routing/Tests/Fixtures/directory/routes3.yml b/src/Symfony/Component/Routing/Tests/Fixtures/directory/routes3.yml
new file mode 100644
index 0000000000000..088cfb4d4315e
--- /dev/null
+++ b/src/Symfony/Component/Routing/Tests/Fixtures/directory/routes3.yml
@@ -0,0 +1,2 @@
+route3:
+ path: /route/3
diff --git a/src/Symfony/Component/Routing/Tests/Loader/DirectoryLoaderTest.php b/src/Symfony/Component/Routing/Tests/Loader/DirectoryLoaderTest.php
new file mode 100644
index 0000000000000..bdc13ef1a374a
--- /dev/null
+++ b/src/Symfony/Component/Routing/Tests/Loader/DirectoryLoaderTest.php
@@ -0,0 +1,63 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Routing\Tests\Loader;
+
+use Symfony\Component\Routing\Loader\DirectoryLoader;
+use Symfony\Component\Routing\Loader\YamlFileLoader;
+use Symfony\Component\Routing\Loader\AnnotationFileLoader;
+use Symfony\Component\Config\Loader\LoaderResolver;
+use Symfony\Component\Config\FileLocator;
+
+class DirectoryLoaderTest extends AbstractAnnotationLoaderTest
+{
+ protected $loader;
+ protected $reader;
+
+ protected function setUp()
+ {
+ parent::setUp();
+
+ $locator = new FileLocator();
+ $this->reader = $this->getReader();
+ $this->loader = new DirectoryLoader($locator);
+ $resolver = new LoaderResolver(array(
+ new YamlFileLoader($locator),
+ new AnnotationFileLoader($locator, $this->getClassLoader($this->reader)),
+ $this->loader,
+ ));
+ $this->loader->setResolver($resolver);
+ }
+
+ public function testLoadDirectory()
+ {
+ $collection = $this->loader->load(__DIR__.'/../Fixtures/directory', 'directory');
+ $routes = $collection->all();
+
+ $this->assertCount(3, $routes, 'Three routes are loaded');
+ $this->assertContainsOnly('Symfony\Component\Routing\Route', $routes);
+
+ for ($i = 1; $i <= 3; $i++) {
+ $this->assertSame('/route/'.$i, $routes["route".$i]->getPath());
+ }
+ }
+
+ public function testSupports()
+ {
+ $fixturesDir = __DIR__.'/../Fixtures';
+
+ $this->assertFalse($this->loader->supports($fixturesDir), '->supports() returns true if the resource is loadable');
+ $this->assertFalse($this->loader->supports('foo.foo'), '->supports() returns true if the resource is loadable');
+
+ $this->assertTrue($this->loader->supports($fixturesDir, 'directory'), '->supports() checks the resource type if specified');
+ $this->assertFalse($this->loader->supports($fixturesDir, 'foo'), '->supports() checks the resource type if specified');
+ }
+}