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'); + } +}