From 025585d5e8421840d37cd63b162524e37b6c732e Mon Sep 17 00:00:00 2001 From: Fabien Potencier Date: Wed, 15 Feb 2017 14:40:32 +0100 Subject: [PATCH] added support for glob loaders in Config --- .../Resources/config/routing.xml | 5 ++ .../Component/Config/Loader/FileLoader.php | 86 +++++++++++++++++- .../Config/Loader/GlobFileLoader.php | 36 ++++++++ .../DependencyInjection/Loader/FileLoader.php | 88 ++----------------- src/Symfony/Component/HttpKernel/Kernel.php | 2 + .../Routing/RouteCollectionBuilder.php | 39 +++++--- 6 files changed, 162 insertions(+), 94 deletions(-) create mode 100644 src/Symfony/Component/Config/Loader/GlobFileLoader.php diff --git a/src/Symfony/Bundle/FrameworkBundle/Resources/config/routing.xml b/src/Symfony/Bundle/FrameworkBundle/Resources/config/routing.xml index 6050686d6c3c5..1ce967ad712ee 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Resources/config/routing.xml +++ b/src/Symfony/Bundle/FrameworkBundle/Resources/config/routing.xml @@ -36,6 +36,11 @@ + + + + + diff --git a/src/Symfony/Component/Config/Loader/FileLoader.php b/src/Symfony/Component/Config/Loader/FileLoader.php index f0896a3b7b53e..5259cdb3bcab3 100644 --- a/src/Symfony/Component/Config/Loader/FileLoader.php +++ b/src/Symfony/Component/Config/Loader/FileLoader.php @@ -14,6 +14,9 @@ use Symfony\Component\Config\FileLocatorInterface; use Symfony\Component\Config\Exception\FileLoaderLoadException; use Symfony\Component\Config\Exception\FileLoaderImportCircularReferenceException; +use Symfony\Component\Config\Exception\FileLocatorFileNotFoundException; +use Symfony\Component\Finder\Finder; +use Symfony\Component\Finder\Glob; /** * FileLoader is the abstract class used by all built-in loaders that are file based. @@ -32,7 +35,7 @@ abstract class FileLoader extends Loader */ protected $locator; - protected $currentDir; + private $currentDir; /** * Constructor. @@ -78,6 +81,87 @@ public function getLocator() * @throws FileLoaderImportCircularReferenceException */ public function import($resource, $type = null, $ignoreErrors = false, $sourceResource = null) + { + $ret = array(); + $ct = 0; + foreach ($this->glob($resource, false, $_, $ignoreErrors) as $resource => $info) { + ++$ct; + $ret[] = $this->doImport($resource, $type, $ignoreErrors, $sourceResource); + } + + return $ct > 1 ? $ret : isset($ret[0]) ? $ret[0] : null; + } + + /** + * @internal + */ + protected function glob($resource, $recursive, &$prefix = null, $ignoreErrors = false) + { + if (strlen($resource) === $i = strcspn($resource, '*?{[')) { + if (!$recursive) { + $prefix = null; + + yield $resource => new \SplFileInfo($resource); + + return; + } + $prefix = $resource; + $resource = ''; + } elseif (0 === $i) { + $prefix = '.'; + $resource = '/'.$resource; + } else { + $prefix = dirname(substr($resource, 0, 1 + $i)); + $resource = substr($resource, strlen($prefix)); + } + + try { + $prefix = $this->locator->locate($prefix, $this->currentDir, true); + } catch (FileLocatorFileNotFoundException $e) { + if (!$ignoreErrors) { + throw $e; + } + + return; + } + $prefix = realpath($prefix) ?: $prefix; + + if (false === strpos($resource, '/**/') && (defined('GLOB_BRACE') || false === strpos($resource, '{'))) { + foreach (glob($prefix.$resource, defined('GLOB_BRACE') ? GLOB_BRACE : 0) as $path) { + if ($recursive && is_dir($path)) { + $flags = \FilesystemIterator::SKIP_DOTS | \FilesystemIterator::FOLLOW_SYMLINKS; + foreach (new \RecursiveIteratorIterator(new \RecursiveDirectoryIterator($path, $flags)) as $path => $info) { + if ($info->isFile()) { + yield $path => $info; + } + } + } elseif (is_file($path)) { + yield $path => new \SplFileInfo($path); + } + } + + return; + } + + if (!class_exists(Finder::class)) { + throw new LogicException(sprintf('Extended glob pattern "%s" cannot be used as the Finder component is not installed.', $resource)); + } + + $finder = new Finder(); + $regex = Glob::toRegex($resource); + if ($recursive) { + $regex = substr_replace($regex, '(/|$)', -2, 1); + } + + $prefixLen = strlen($prefix); + foreach ($finder->followLinks()->in($prefix) as $path => $info) { + if (preg_match($regex, substr($path, $prefixLen)) && $info->isFile()) { + yield $path => $info; + } + } + } + + private function doImport($resource, $type = null, $ignoreErrors = false, $sourceResource = null) { try { $loader = $this->resolve($resource, $type); diff --git a/src/Symfony/Component/Config/Loader/GlobFileLoader.php b/src/Symfony/Component/Config/Loader/GlobFileLoader.php new file mode 100644 index 0000000000000..8b0c2401e9ff8 --- /dev/null +++ b/src/Symfony/Component/Config/Loader/GlobFileLoader.php @@ -0,0 +1,36 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Config\Loader; + +/** + * GlobFileLoader loads files from a glob pattern. + * + * @author Fabien Potencier + */ +class GlobFileLoader extends FileLoader +{ + /** + * {@inheritdoc} + */ + public function load($resource, $type = null) + { + return $this->import($resource, null, true); + } + + /** + * {@inheritdoc} + */ + public function supports($resource, $type = null) + { + return 'glob' === $type; + } +} diff --git a/src/Symfony/Component/DependencyInjection/Loader/FileLoader.php b/src/Symfony/Component/DependencyInjection/Loader/FileLoader.php index ba0b77f0f5bf0..bb9dc214e6a50 100644 --- a/src/Symfony/Component/DependencyInjection/Loader/FileLoader.php +++ b/src/Symfony/Component/DependencyInjection/Loader/FileLoader.php @@ -11,16 +11,12 @@ namespace Symfony\Component\DependencyInjection\Loader; -use Symfony\Component\Config\Exception\FileLocatorFileNotFoundException; use Symfony\Component\DependencyInjection\ChildDefinition; use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\DependencyInjection\Definition; use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException; -use Symfony\Component\DependencyInjection\Exception\LogicException; use Symfony\Component\Config\Loader\FileLoader as BaseFileLoader; use Symfony\Component\Config\FileLocatorInterface; -use Symfony\Component\Finder\Finder; -use Symfony\Component\Finder\Glob; /** * FileLoader is the abstract class used by all built-in loaders that are file based. @@ -44,22 +40,6 @@ public function __construct(ContainerBuilder $container, FileLocatorInterface $l parent::__construct($locator); } - /** - * {@inheritdoc} - */ - public function import($resource, $type = null, $ignoreErrors = false, $sourceResource = null) - { - try { - foreach ($this->glob($resource, false) as $path => $info) { - parent::import($path, $type, $ignoreErrors, $sourceResource); - } - } catch (FileLocatorFileNotFoundException $e) { - if (!$ignoreErrors) { - throw $e; - } - } - } - /** * Registers a set of classes as services using PSR-4 for discovery. * @@ -106,8 +86,12 @@ private function findClasses($namespace, $resource) { $classes = array(); $extRegexp = defined('HHVM_VERSION') ? '/\\.(?:php|hh)$/' : '/\\.php$/'; + $prefixLen = null; + foreach ($this->glob($resource, true, $prefix) as $path => $info) { + if (null === $prefixLen) { + $prefixLen = strlen($prefix); + } - foreach ($this->glob($resource, true, $prefixLen) as $path => $info) { if (!preg_match($extRegexp, $path, $m) || !$info->isReadable()) { continue; } @@ -124,65 +108,11 @@ private function findClasses($namespace, $resource) } } - return $classes; - } - - private function glob($resource, $recursive, &$prefixLen = null) - { - if (strlen($resource) === $i = strcspn($resource, '*?{[')) { - if (!$recursive) { - yield $resource => new \SplFileInfo($resource); - - return; - } - $resourcePrefix = $resource; - $resource = ''; - } elseif (0 === $i) { - $resourcePrefix = '.'; - $resource = '/'.$resource; - } else { - $resourcePrefix = dirname(substr($resource, 0, 1 + $i)); - $resource = substr($resource, strlen($resourcePrefix)); - } - - $resourcePrefix = $this->locator->locate($resourcePrefix, $this->currentDir, true); - $resourcePrefix = realpath($resourcePrefix) ?: $resourcePrefix; - $prefixLen = strlen($resourcePrefix); - - // track directories only for new & removed files - $this->container->fileExists($resourcePrefix, '/^$/'); - - if (false === strpos($resource, '/**/') && (defined('GLOB_BRACE') || false === strpos($resource, '{'))) { - foreach (glob($resourcePrefix.$resource, defined('GLOB_BRACE') ? GLOB_BRACE : 0) as $path) { - if ($recursive && is_dir($path)) { - $flags = \FilesystemIterator::SKIP_DOTS | \FilesystemIterator::FOLLOW_SYMLINKS; - foreach (new \RecursiveIteratorIterator(new \RecursiveDirectoryIterator($path, $flags)) as $path => $info) { - if ($info->isFile()) { - yield $path => $info; - } - } - } elseif (is_file($path)) { - yield $path => new \SplFileInfo($path); - } - } - - return; + if (null !== $prefix) { + // track directories only for new & removed files + $this->container->fileExists($prefix, '/^$/'); } - if (!class_exists(Finder::class)) { - throw new LogicException(sprintf('Extended glob pattern "%s" cannot be used as the Finder component is not installed.', $resource)); - } - - $finder = new Finder(); - $regex = Glob::toRegex($resource); - if ($recursive) { - $regex = substr_replace($regex, '(/|$)', -2, 1); - } - - foreach ($finder->followLinks()->in($resourcePrefix) as $path => $info) { - if (preg_match($regex, substr($path, $prefixLen)) && $info->isFile()) { - yield $path => $info; - } - } + return $classes; } } diff --git a/src/Symfony/Component/HttpKernel/Kernel.php b/src/Symfony/Component/HttpKernel/Kernel.php index e83fd248d9c28..9523b7a3ca997 100644 --- a/src/Symfony/Component/HttpKernel/Kernel.php +++ b/src/Symfony/Component/HttpKernel/Kernel.php @@ -29,6 +29,7 @@ use Symfony\Component\HttpKernel\Config\FileLocator; use Symfony\Component\HttpKernel\DependencyInjection\MergeExtensionConfigurationPass; use Symfony\Component\HttpKernel\DependencyInjection\AddClassesToCachePass; +use Symfony\Component\Config\Loader\GlobFileLoader; use Symfony\Component\Config\Loader\LoaderResolver; use Symfony\Component\Config\Loader\DelegatingLoader; use Symfony\Component\Config\ConfigCache; @@ -686,6 +687,7 @@ protected function getContainerLoader(ContainerInterface $container) new YamlFileLoader($container, $locator), new IniFileLoader($container, $locator), new PhpFileLoader($container, $locator), + new GlobFileLoader($locator), new DirectoryLoader($container, $locator), new ClosureLoader($container), )); diff --git a/src/Symfony/Component/Routing/RouteCollectionBuilder.php b/src/Symfony/Component/Routing/RouteCollectionBuilder.php index 726dfa9022cfb..89b426266b84e 100644 --- a/src/Symfony/Component/Routing/RouteCollectionBuilder.php +++ b/src/Symfony/Component/Routing/RouteCollectionBuilder.php @@ -61,21 +61,28 @@ public function __construct(LoaderInterface $loader = null) */ public function import($resource, $prefix = '/', $type = null) { - /** @var RouteCollection $collection */ - $collection = $this->load($resource, $type); + /** @var RouteCollection[] $collection */ + $collections = $this->load($resource, $type); // create a builder from the RouteCollection $builder = $this->createBuilder(); - foreach ($collection->all() as $name => $route) { - $builder->addRoute($route, $name); - } - foreach ($collection->getResources() as $resource) { - $builder->addResource($resource); - } + foreach ($collections as $collection) { + if (null === $collection) { + continue; + } - // mount into this builder - $this->mount($prefix, $builder); + foreach ($collection->all() as $name => $route) { + $builder->addRoute($route, $name); + } + + foreach ($collection->getResources() as $resource) { + $builder->addResource($resource); + } + + // mount into this builder + $this->mount($prefix, $builder); + } return $builder; } @@ -201,7 +208,7 @@ public function setRequirement($key, $regex) } /** - * Sets an opiton that will be added to all embedded routes (unless that + * Sets an option that will be added to all embedded routes (unless that * option is already set). * * @param string $key @@ -345,7 +352,7 @@ private function generateRouteName(Route $route) * @param mixed $resource A resource * @param string|null $type The resource type or null if unknown * - * @return RouteCollection + * @return RouteCollection[] * * @throws FileLoaderLoadException If no loader is found */ @@ -356,7 +363,9 @@ private function load($resource, $type = null) } if ($this->loader->supports($resource, $type)) { - return $this->loader->load($resource, $type); + $collections = $this->loader->load($resource, $type); + + return is_array($collections) ? $collections : array($collections); } if (null === $resolver = $this->loader->getResolver()) { @@ -367,6 +376,8 @@ private function load($resource, $type = null) throw new FileLoaderLoadException($resource); } - return $loader->load($resource, $type); + $collections = $loader->load($resource, $type); + + return is_array($collections) ? $collections : array($collections); } }