-
-
Notifications
You must be signed in to change notification settings - Fork 9.6k
added support for glob loaders in Config #21635
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,36 @@ | ||
<?php | ||
|
||
/* | ||
* This file is part of the Symfony package. | ||
* | ||
* (c) Fabien Potencier <fabien@symfony.com> | ||
* | ||
* 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 <fabien@symfony.com> | ||
*/ | ||
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; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. add a check on $resource, like There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Not needed as we force There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @nicolas-grekas could the check be done at some other place, see #22160 |
||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -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) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. if you call glob outside of the loop, you can compute prefixLen outside of the foreach There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. that does not work |
||
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; | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -61,21 +61,28 @@ public function __construct(LoaderInterface $loader = null) | |
*/ | ||
public function import($resource, $prefix = '/', $type = null) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. in case of a glob, this is missing tracking of the directory prefixing the pattern - see " There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Not about the "leak", as routing resources are tracked in a different file, so we cannot reused the same logic. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. By the way, we have the same issue with the container. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think there is nothing to fix here. Calling |
||
{ | ||
/** @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); | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
drop $ct then:
isset($ret[1]) ? $ret : ($ret ? $ret[0] : null)
or$ret ? (isset($ret[1]) ? $ret : $ret[0]) : null
?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
which would be less readable