From 4ada4dca436aed833ae3b743e833bc78a3a3d996 Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Fri, 30 Nov 2018 21:42:46 +0100 Subject: [PATCH] [Config] fix path exclusion during glob discovery --- .../Config/Resource/GlobResource.php | 24 ++++++++++++-- .../Tests/Resource/GlobResourceTest.php | 14 +++++++++ .../Tests/Loader/FileLoaderTest.php | 31 ++++++------------- 3 files changed, 45 insertions(+), 24 deletions(-) diff --git a/src/Symfony/Component/Config/Resource/GlobResource.php b/src/Symfony/Component/Config/Resource/GlobResource.php index 323352058f701..bd110d72f5d94 100644 --- a/src/Symfony/Component/Config/Resource/GlobResource.php +++ b/src/Symfony/Component/Config/Resource/GlobResource.php @@ -96,9 +96,19 @@ public function getIterator() if (!file_exists($this->prefix) || (!$this->recursive && '' === $this->pattern)) { return; } + $prefix = str_replace('\\', '/', $this->prefix); if (0 !== strpos($this->prefix, 'phar://') && false === strpos($this->pattern, '/**/') && (\defined('GLOB_BRACE') || false === strpos($this->pattern, '{'))) { foreach (glob($this->prefix.$this->pattern, \defined('GLOB_BRACE') ? GLOB_BRACE : 0) as $path) { + if ($this->excludedPrefixes) { + $normalizedPath = str_replace('\\', '/', $path); + do { + if (isset($this->excludedPrefixes[$dirPath = $normalizedPath])) { + continue 2; + } + } while ($prefix !== $dirPath && $dirPath !== $normalizedPath = \dirname($dirPath)); + } + if (is_file($path)) { yield $path => new \SplFileInfo($path); } @@ -145,9 +155,19 @@ function (\SplFileInfo $file, $path) { $prefixLen = \strlen($this->prefix); foreach ($finder->followLinks()->sortByName()->in($this->prefix) as $path => $info) { - if (preg_match($regex, substr(str_replace('\\', '/', $path), $prefixLen)) && $info->isFile()) { - yield $path => $info; + $normalizedPath = str_replace('\\', '/', $path); + if (!preg_match($regex, substr($normalizedPath, $prefixLen)) || !$info->isFile()) { + continue; } + if ($this->excludedPrefixes) { + do { + if (isset($this->excludedPrefixes[$dirPath = $normalizedPath])) { + continue 2; + } + } while ($prefix !== $dirPath && $dirPath !== $normalizedPath = \dirname($dirPath)); + } + + yield $path => $info; } } diff --git a/src/Symfony/Component/Config/Tests/Resource/GlobResourceTest.php b/src/Symfony/Component/Config/Tests/Resource/GlobResourceTest.php index ac7ae5bd7507f..188b5572ecb42 100644 --- a/src/Symfony/Component/Config/Tests/Resource/GlobResourceTest.php +++ b/src/Symfony/Component/Config/Tests/Resource/GlobResourceTest.php @@ -73,6 +73,20 @@ public function testIteratorSkipsFoldersForGivenExcludedPrefixes() $this->assertArrayNotHasKey($file, $paths); } + public function testIteratorSkipsSubfoldersForGivenExcludedPrefixes() + { + $dir = \dirname(__DIR__).\DIRECTORY_SEPARATOR.'Fixtures'; + $resource = new GlobResource($dir, '/*Exclude/*', true, false, array($dir.\DIRECTORY_SEPARATOR.'Exclude' => true)); + + $paths = iterator_to_array($resource); + + $file = $dir.\DIRECTORY_SEPARATOR.'Exclude'.\DIRECTORY_SEPARATOR.'AnExcludedFile.txt'; + $this->assertArrayNotHasKey($file, $paths); + + $file = $dir.\DIRECTORY_SEPARATOR.'Exclude'.\DIRECTORY_SEPARATOR.'ExcludeToo'.\DIRECTORY_SEPARATOR.'AnotheExcludedFile.txt'; + $this->assertArrayNotHasKey($file, $paths); + } + public function testIteratorSkipsFoldersWithForwardSlashForGivenExcludedPrefixes() { $dir = \dirname(__DIR__).\DIRECTORY_SEPARATOR.'Fixtures'; diff --git a/src/Symfony/Component/DependencyInjection/Tests/Loader/FileLoaderTest.php b/src/Symfony/Component/DependencyInjection/Tests/Loader/FileLoaderTest.php index 32e404cc5b149..87971eadd8369 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Loader/FileLoaderTest.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Loader/FileLoaderTest.php @@ -18,7 +18,6 @@ use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\DependencyInjection\ContainerInterface; use Symfony\Component\DependencyInjection\Definition; -use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException; use Symfony\Component\DependencyInjection\Loader\FileLoader; use Symfony\Component\DependencyInjection\Loader\IniFileLoader; use Symfony\Component\DependencyInjection\Loader\PhpFileLoader; @@ -217,32 +216,20 @@ public function testRegisterClassesWithBadPrefix() } /** - * @dataProvider getIncompatibleExcludeTests + * @expectedException \Symfony\Component\DependencyInjection\Exception\InvalidArgumentException + * @expectedExceptionMessage Invalid "exclude" pattern when importing classes for "Symfony\Component\DependencyInjection\Tests\Fixtures\Prototype\": make sure your "exclude" pattern (yaml/*) is a subset of the "resource" pattern (Prototype/*) */ - public function testRegisterClassesWithIncompatibleExclude($resourcePattern, $excludePattern) + public function testRegisterClassesWithIncompatibleExclude() { $container = new ContainerBuilder(); $loader = new TestFileLoader($container, new FileLocator(self::$fixturesPath.'/Fixtures')); - try { - $loader->registerClasses( - new Definition(), - 'Symfony\Component\DependencyInjection\Tests\Fixtures\Prototype\\', - $resourcePattern, - $excludePattern - ); - } catch (InvalidArgumentException $e) { - $this->assertEquals( - sprintf('Invalid "exclude" pattern when importing classes for "Symfony\Component\DependencyInjection\Tests\Fixtures\Prototype\": make sure your "exclude" pattern (%s) is a subset of the "resource" pattern (%s)', $excludePattern, $resourcePattern), - $e->getMessage() - ); - } - } - - public function getIncompatibleExcludeTests() - { - yield array('Prototype/*', 'yaml/*', false); - yield array('Prototype/OtherDir/*', 'Prototype/*', false); + $loader->registerClasses( + new Definition(), + 'Symfony\Component\DependencyInjection\Tests\Fixtures\Prototype\\', + 'Prototype/*', + 'yaml/*' + ); } }