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/Bundle/FrameworkBundle/composer.json b/src/Symfony/Bundle/FrameworkBundle/composer.json
index dce63a74b99cb..349253e007fcb 100644
--- a/src/Symfony/Bundle/FrameworkBundle/composer.json
+++ b/src/Symfony/Bundle/FrameworkBundle/composer.json
@@ -17,7 +17,7 @@
],
"require": {
"php": ">=5.3.9",
- "symfony/dependency-injection" : "~2.6,>=2.6.2",
+ "symfony/dependency-injection" : "~2.7,>=2.6.2",
"symfony/config" : "~2.4",
"symfony/event-dispatcher": "~2.5|~3.0.0",
"symfony/http-foundation": "~2.4.9|~2.5,>=2.5.4|~3.0.0",
diff --git a/src/Symfony/Component/DependencyInjection/Loader/DirectoryLoader.php b/src/Symfony/Component/DependencyInjection/Loader/DirectoryLoader.php
new file mode 100644
index 0000000000000..48bf2fb1b913d
--- /dev/null
+++ b/src/Symfony/Component/DependencyInjection/Loader/DirectoryLoader.php
@@ -0,0 +1,60 @@
+
+ *
+ * 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
+{
+ /**
+ * {@inheritdoc}
+ */
+ 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);
+ }
+ }
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function supports($resource, $type = null)
+ {
+ if ('directory' === $type) {
+ return true;
+ }
+
+ if (null === $type) {
+ return preg_match('/\/$/', $resource) === 1;
+ }
+
+ return false;
+ }
+}
diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/directory/import/import.yml b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/directory/import/import.yml
new file mode 100644
index 0000000000000..35ec4a0427136
--- /dev/null
+++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/directory/import/import.yml
@@ -0,0 +1,2 @@
+imports:
+ - { resource: ../recurse/ }
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..c19c5de3a57df
--- /dev/null
+++ b/src/Symfony/Component/DependencyInjection/Tests/Loader/DirectoryLoaderTest.php
@@ -0,0 +1,85 @@
+
+ *
+ * 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
+{
+ private static $fixturesPath;
+
+ private $container;
+ private $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);
+ }
+
+ public function testDirectoryCanBeLoadedRecursively()
+ {
+ $this->loader->load('directory/');
+ $this->assertEquals(array('ini' => 'ini', 'yaml' => 'yaml', 'php' => 'php'), $this->container->getParameterBag()->all(), '->load() takes a single directory');
+ }
+
+ public function testImports()
+ {
+ $this->loader->resolve('directory/import/import.yml')->load('directory/import/import.yml');
+ $this->assertEquals(array('ini' => 'ini', 'yaml' => 'yaml'), $this->container->getParameterBag()->all(), '->load() takes a single file that imports a directory');
+ }
+
+ /**
+ * @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('directory/'), '->supports("directory/") returns true');
+ $this->assertTrue($loader->supports('directory/', 'directory'), '->supports("directory/", "directory") returns true');
+ $this->assertFalse($loader->supports('directory'), '->supports("directory") returns false');
+ $this->assertTrue($loader->supports('directory', 'directory'), '->supports("directory", "directory") returns true');
+ $this->assertFalse($loader->supports('directory', 'foo'), '->supports("directory, "foo") returns false');
+ }
+}
diff --git a/src/Symfony/Component/HttpKernel/Kernel.php b/src/Symfony/Component/HttpKernel/Kernel.php
index b9ecd71abe63c..ae36380bbea34 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;
@@ -251,7 +252,7 @@ public function getBundle($name, $first = true)
}
/**
- * {@inheritDoc}
+ * {@inheritdoc}
*
* @throws \RuntimeException if a custom resource is hidden by a resource in a derived bundle
*/
@@ -716,6 +717,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/HttpKernel/composer.json b/src/Symfony/Component/HttpKernel/composer.json
index de1e9d2277b2e..d2969ba1aefc8 100644
--- a/src/Symfony/Component/HttpKernel/composer.json
+++ b/src/Symfony/Component/HttpKernel/composer.json
@@ -33,7 +33,7 @@
"symfony/expression-language": "~2.4|~3.0.0",
"symfony/finder": "~2.0,>=2.0.5|~3.0.0",
"symfony/process": "~2.0,>=2.0.5|~3.0.0",
- "symfony/routing": "~2.2|~3.0.0",
+ "symfony/routing": "~2.7|~3.0.0",
"symfony/stopwatch": "~2.3|~3.0.0",
"symfony/templating": "~2.2|~3.0.0",
"symfony/translation": "~2.0,>=2.0.5|~3.0.0",
diff --git a/src/Symfony/Component/Routing/Loader/DirectoryLoader.php b/src/Symfony/Component/Routing/Loader/DirectoryLoader.php
new file mode 100644
index 0000000000000..92041090e5543
--- /dev/null
+++ b/src/Symfony/Component/Routing/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\Routing\Loader;
+
+use Symfony\Component\Config\Loader\FileLoader;
+use Symfony\Component\Routing\RouteCollection;
+use Symfony\Component\Config\Resource\DirectoryResource;
+
+class DirectoryLoader extends FileLoader
+{
+ /**
+ * {@inheritdoc}
+ */
+ public function load($file, $type = null)
+ {
+ $path = $this->locator->locate($file);
+
+ $collection = new RouteCollection();
+ $collection->addResource(new DirectoryResource($path));
+
+ foreach (scandir($path) as $dir) {
+ if ($dir[0] !== '.') {
+ $this->setCurrentDir($path);
+ $subPath = $path.'/'.$dir;
+ $subType = null;
+
+ if (is_dir($subPath)) {
+ $subPath .= '/';
+ $subType = 'directory';
+ }
+
+ $subCollection = $this->import($subPath, $subType, false, $path);
+ $collection->addCollection($subCollection);
+ }
+ }
+
+ return $collection;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function supports($resource, $type = null)
+ {
+ // only when type is forced to directory, not to conflict with AnnotationLoader
+
+ return '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/Fixtures/directory_import/import.yml b/src/Symfony/Component/Routing/Tests/Fixtures/directory_import/import.yml
new file mode 100644
index 0000000000000..af829e58c7ffd
--- /dev/null
+++ b/src/Symfony/Component/Routing/Tests/Fixtures/directory_import/import.yml
@@ -0,0 +1,3 @@
+_directory:
+ resource: "../directory"
+ type: directory
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..101b5093a2fff
--- /dev/null
+++ b/src/Symfony/Component/Routing/Tests/Loader/DirectoryLoaderTest.php
@@ -0,0 +1,74 @@
+
+ *
+ * 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;
+use Symfony\Component\Routing\RouteCollection;
+
+class DirectoryLoaderTest extends AbstractAnnotationLoaderTest
+{
+ private $loader;
+ private $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');
+ $this->verifyCollection($collection);
+ }
+
+ public function testImportDirectory()
+ {
+ $collection = $this->loader->load(__DIR__.'/../Fixtures/directory_import', 'directory');
+ $this->verifyCollection($collection);
+ }
+
+ private function verifyCollection(RouteCollection $collection)
+ {
+ $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 false');
+
+ $this->assertTrue($this->loader->supports($fixturesDir, 'directory'), '->supports(*, "directory") returns true');
+ $this->assertFalse($this->loader->supports($fixturesDir, 'foo'), '->supports(*, "foo") returns false');
+ }
+}