diff --git a/src/Symfony/Component/Finder/Iterator/ExcludeDirectoryFilterIterator.php b/src/Symfony/Component/Finder/Iterator/ExcludeDirectoryFilterIterator.php index 39797c82cab28..0b40692263be1 100644 --- a/src/Symfony/Component/Finder/Iterator/ExcludeDirectoryFilterIterator.php +++ b/src/Symfony/Component/Finder/Iterator/ExcludeDirectoryFilterIterator.php @@ -26,6 +26,7 @@ class ExcludeDirectoryFilterIterator extends \FilterIterator implements \Recursi private $isRecursive; private $excludedDirs = []; private $excludedPattern; + private $excludedPatternAbsolute; /** * @param \Iterator $iterator The Iterator to filter @@ -36,9 +37,16 @@ public function __construct(\Iterator $iterator, array $directories) $this->iterator = $iterator; $this->isRecursive = $iterator instanceof \RecursiveIterator; $patterns = []; + $patternsAbsolute = []; foreach ($directories as $directory) { $directory = rtrim($directory, '/'); - if (!$this->isRecursive || str_contains($directory, '/')) { + $slashPos = strpos($directory, '/'); + if (false !== $slashPos && \strlen($directory) - 1 !== $slashPos) { + if (0 === $slashPos) { + $directory = substr($directory, 1); + } + $patternsAbsolute[] = preg_quote($directory, '#'); + } elseif (!$this->isRecursive || str_contains($directory, '/')) { $patterns[] = preg_quote($directory, '#'); } else { $this->excludedDirs[$directory] = true; @@ -48,6 +56,10 @@ public function __construct(\Iterator $iterator, array $directories) $this->excludedPattern = '#(?:^|/)(?:'.implode('|', $patterns).')(?:/|$)#'; } + if ($patternsAbsolute) { + $this->excludedPatternAbsolute = '#^('.implode('|', $patternsAbsolute).')$#'; + } + parent::__construct($iterator); } @@ -63,11 +75,17 @@ public function accept() return false; } - if ($this->excludedPattern) { + if ($this->excludedPattern || $this->excludedPatternAbsolute) { $path = $this->isDir() ? $this->current()->getRelativePathname() : $this->current()->getRelativePath(); $path = str_replace('\\', '/', $path); + } - return !preg_match($this->excludedPattern, $path); + if ($this->excludedPattern && preg_match($this->excludedPattern, $path)) { + return false; + } + + if ($this->excludedPatternAbsolute && preg_match($this->excludedPatternAbsolute, $path)) { + return false; } return true; diff --git a/src/Symfony/Component/Finder/Tests/FinderTest.php b/src/Symfony/Component/Finder/Tests/FinderTest.php index 183a09cff190a..f81f796480ad1 100644 --- a/src/Symfony/Component/Finder/Tests/FinderTest.php +++ b/src/Symfony/Component/Finder/Tests/FinderTest.php @@ -25,13 +25,13 @@ public function testDirectories() { $finder = $this->buildFinder(); $this->assertSame($finder, $finder->directories()); - $this->assertIterator($this->toAbsolute(['foo', 'qux', 'toto']), $finder->in(self::$tmpDir)->getIterator()); + $this->assertIterator($this->toAbsolute(['foo', 'qux', 'toto', 'toto/foo']), $finder->in(self::$tmpDir)->getIterator()); $finder = $this->buildFinder(); $finder->directories(); $finder->files(); $finder->directories(); - $this->assertIterator($this->toAbsolute(['foo', 'qux', 'toto']), $finder->in(self::$tmpDir)->getIterator()); + $this->assertIterator($this->toAbsolute(['foo', 'qux', 'toto', 'toto/foo']), $finder->in(self::$tmpDir)->getIterator()); } public function testFiles() @@ -162,7 +162,8 @@ public function testDepth() $this->assertIterator($this->toAbsolute([ 'foo/bar.tmp', 'qux/baz_100_1.py', - 'qux/baz_1_2.py', + 'qux/baz_1_2.py', + 'toto/foo', ]), $finder->in(self::$tmpDir)->getIterator()); $finder = $this->buildFinder(); @@ -178,6 +179,7 @@ public function testDepthWithArrayParam() 'foo/bar.tmp', 'qux/baz_100_1.py', 'qux/baz_1_2.py', + 'toto/foo', ]), $finder->in(self::$tmpDir)->getIterator()); } @@ -237,6 +239,7 @@ public function testNotName() 'foo/bar.tmp', 'test.py', 'toto', + 'toto/foo', 'foo bar', 'qux', 'qux/baz_100_1.py', @@ -250,6 +253,7 @@ public function testNotName() 'foo', 'foo/bar.tmp', 'toto', + 'toto/foo', 'foo bar', 'qux', ]), $finder->in(self::$tmpDir)->getIterator()); @@ -276,6 +280,7 @@ public function testNotNameWithArrayParam() 'foo', 'foo/bar.tmp', 'toto', + 'toto/foo', 'foo bar', 'qux', ]), $finder->in(self::$tmpDir)->getIterator()); @@ -340,7 +345,47 @@ public function testExclude() 'qux_10_2.php', 'qux_12_0.php', 'qux_2_0.php', + ]), $finder->in(self::$tmpDir)->getIterator()); + + $finder = $this->buildFinder(); + $this->assertSame($finder, $finder->exclude('/foo')); + $this->assertIterator($this->toAbsolute([ + 'test.php', + 'test.py', + 'toto', + 'toto/foo', + 'foo bar', + 'qux', + 'qux/baz_100_1.py', + 'qux/baz_1_2.py', + 'qux_0_1.php', + 'qux_1000_1.php', + 'qux_1002_0.php', + 'qux_10_2.php', + 'qux_12_0.php', + 'qux_2_0.php', ]), $finder->in(self::$tmpDir)->getIterator()); + + /* absolute folders lower than the "root" are not currently supported */ + //$finder = $this->buildFinder(); + //$this->assertSame($finder, $finder->exclude('/toto/foo')); + //$this->assertIterator($this->toAbsolute([ + // 'foo', + // 'foo/bar.tmp', + // 'test.php', + // 'test.py', + // 'toto', + // 'foo bar', + // 'qux', + // 'qux/baz_100_1.py', + // 'qux/baz_1_2.py', + // 'qux_0_1.php', + // 'qux_1000_1.php', + // 'qux_1002_0.php', + // 'qux_10_2.php', + // 'qux_12_0.php', + // 'qux_2_0.php', + //]), $finder->in(self::$tmpDir)->getIterator()); } public function testIgnoreVCS() @@ -355,6 +400,7 @@ public function testIgnoreVCS() 'test.py', 'toto', 'toto/.git', + 'toto/foo', '.bar', '.foo', '.foo/.bar', @@ -380,6 +426,7 @@ public function testIgnoreVCS() 'test.php', 'test.py', 'toto', + 'toto/foo', 'toto/.git', '.bar', '.foo', @@ -405,6 +452,7 @@ public function testIgnoreVCS() 'test.php', 'test.py', 'toto', + 'toto/foo', '.bar', '.foo', '.foo/.bar', @@ -468,6 +516,7 @@ public function testIgnoreVCSCanBeDisabledAfterFirstIteration() 'test.php', 'test.py', 'toto', + 'toto/foo', '.bar', '.foo', '.foo/.bar', @@ -492,6 +541,7 @@ public function testIgnoreVCSCanBeDisabledAfterFirstIteration() 'test.php', 'test.py', 'toto', + 'toto/foo', 'toto/.git', '.bar', '.foo', @@ -517,6 +567,7 @@ public function testIgnoreDotFiles() 'test.py', 'toto', 'toto/.git', + 'toto/foo', 'foo bar', 'qux', 'qux/baz_100_1.py', @@ -542,6 +593,7 @@ public function testIgnoreDotFiles() 'test.php', 'test.py', 'toto', + 'toto/foo', 'toto/.git', 'foo bar', 'qux', @@ -563,6 +615,7 @@ public function testIgnoreDotFiles() 'test.php', 'test.py', 'toto', + 'toto/foo', 'foo bar', 'qux', 'qux/baz_100_1.py', @@ -596,6 +649,7 @@ public function testIgnoreDotFilesCanBeDisabledAfterFirstIteration() 'test.php', 'test.py', 'toto', + 'toto/foo', 'foo bar', ]), $finder->getIterator()); @@ -615,6 +669,7 @@ public function testIgnoreDotFilesCanBeDisabledAfterFirstIteration() 'test.php', 'test.py', 'toto', + 'toto/foo', '.bar', '.foo', '.foo/.bar', @@ -643,6 +698,7 @@ public function testSortByName() 'test.php', 'test.py', 'toto', + 'toto/foo', ]), $finder->in(self::$tmpDir)->getIterator()); } @@ -654,6 +710,7 @@ public function testSortByType() 'foo', 'qux', 'toto', + 'toto/foo', 'foo bar', 'foo/bar.tmp', 'qux/baz_100_1.py', @@ -677,6 +734,7 @@ public function testSortByAccessedTime() 'foo/bar.tmp', 'test.php', 'toto', + 'toto/foo', 'test.py', 'foo', 'foo bar', @@ -698,6 +756,7 @@ public function testSortByChangedTime() $this->assertSame($finder, $finder->sortByChangedTime()); $this->assertIterator($this->toAbsolute([ 'toto', + 'toto/foo', 'test.py', 'test.php', 'foo/bar.tmp', @@ -723,6 +782,7 @@ public function testSortByModifiedTime() 'foo/bar.tmp', 'test.php', 'toto', + 'toto/foo', 'test.py', 'foo', 'foo bar', @@ -744,6 +804,7 @@ public function testReverseSorting() $this->assertSame($finder, $finder->sortByName()); $this->assertSame($finder, $finder->reverseSorting()); $this->assertOrderedIteratorInForeach($this->toAbsolute([ + 'toto/foo', 'toto', 'test.py', 'test.php', @@ -782,6 +843,7 @@ public function testSortByNameNatural() 'test.php', 'test.py', 'toto', + 'toto/foo', ]), $finder->in(self::$tmpDir)->getIterator()); $finder = $this->buildFinder(); @@ -802,6 +864,7 @@ public function testSortByNameNatural() 'test.php', 'test.py', 'toto', + 'toto/foo', ]), $finder->in(self::$tmpDir)->getIterator()); } @@ -825,6 +888,7 @@ public function testSort() 'test.php', 'test.py', 'toto', + 'toto/foo', ]), $finder->in(self::$tmpDir)->getIterator()); } @@ -882,6 +946,7 @@ public function testFollowLinks() 'test.php', 'test.py', 'toto', + 'toto/foo', 'foo bar', 'qux', 'qux/baz_100_1.py', @@ -972,7 +1037,7 @@ public function testGetIterator() $dirs[] = (string) $dir; } - $expected = $this->toAbsolute(['foo', 'qux', 'toto']); + $expected = $this->toAbsolute(['foo', 'qux', 'toto', 'toto/foo']); sort($dirs); sort($expected); @@ -980,7 +1045,7 @@ public function testGetIterator() $this->assertEquals($expected, $dirs, 'implements the \IteratorAggregate interface'); $finder = $this->buildFinder(); - $this->assertEquals(3, iterator_count($finder->directories()->in(self::$tmpDir)), 'implements the \IteratorAggregate interface'); + $this->assertEquals(4, iterator_count($finder->directories()->in(self::$tmpDir)), 'implements the \IteratorAggregate interface'); $finder = $this->buildFinder(); $a = iterator_to_array($finder->directories()->in(self::$tmpDir)); @@ -999,7 +1064,7 @@ public function testRelativePath() $paths[] = $file->getRelativePath(); } - $ref = ['', '', '', '', '', '', '', '', '', '', '', 'foo', 'qux', 'qux', '']; + $ref = ['', '', '', '', '', '', '', '', '', '', '', 'foo', 'qux', 'qux', '', 'toto']; sort($ref); sort($paths); @@ -1020,6 +1085,7 @@ public function testRelativePathname() $ref = [ 'test.php', 'toto', + 'toto/foo', 'test.py', 'foo', 'foo'.\DIRECTORY_SEPARATOR.'bar.tmp', @@ -1056,6 +1122,7 @@ public function testGetFilenameWithoutExtension() 'toto', 'test', 'foo', + 'foo', 'bar', 'foo bar', 'qux', @@ -1085,7 +1152,7 @@ public function testAppendWithAFinder() $finder = $finder->append($finder1); - $this->assertIterator($this->toAbsolute(['foo', 'foo/bar.tmp', 'qux', 'toto']), $finder->getIterator()); + $this->assertIterator($this->toAbsolute(['foo', 'foo/bar.tmp', 'qux', 'toto', 'toto/foo']), $finder->getIterator()); } public function testAppendWithAnArray() diff --git a/src/Symfony/Component/Finder/Tests/Iterator/ExcludeDirectoryFilterIteratorTest.php b/src/Symfony/Component/Finder/Tests/Iterator/ExcludeDirectoryFilterIteratorTest.php index 9b5ed98ad5a72..6edfc6c6b9f00 100644 --- a/src/Symfony/Component/Finder/Tests/Iterator/ExcludeDirectoryFilterIteratorTest.php +++ b/src/Symfony/Component/Finder/Tests/Iterator/ExcludeDirectoryFilterIteratorTest.php @@ -63,6 +63,7 @@ public static function getAcceptData() 'foo/bar.tmp', 'test.php', 'toto', + 'toto/foo', 'toto/.git', 'foo bar', 'qux', @@ -98,10 +99,59 @@ public static function getAcceptData() 'qux_2_0.php', ]; + $foo_absolute_root = [ + '.bar', + '.foo', + '.foo/.bar', + '.foo/bar', + '.git', + 'test.py', + 'test.php', + 'toto', + 'toto/foo', + 'toto/.git', + 'foo bar', + 'qux', + 'qux/baz_100_1.py', + 'qux/baz_1_2.py', + 'qux_0_1.php', + 'qux_1000_1.php', + 'qux_1002_0.php', + 'qux_10_2.php', + 'qux_12_0.php', + 'qux_2_0.php', + ]; + + $foo_toto_subdir_absolute_root = [ + '.bar', + '.foo', + '.foo/.bar', + '.foo/bar', + '.git', + 'test.py', + 'test.php', + 'foo', + 'foo/bar.tmp', + 'toto', + 'toto/.git', + 'foo bar', + 'qux', + 'qux/baz_100_1.py', + 'qux/baz_1_2.py', + 'qux_0_1.php', + 'qux_1000_1.php', + 'qux_1002_0.php', + 'qux_10_2.php', + 'qux_12_0.php', + 'qux_2_0.php', + ]; + return [ [['foo'], self::toAbsolute($foo)], [['fo'], self::toAbsolute($fo)], [['toto/'], self::toAbsolute($toto)], + [['/foo'], self::toAbsolute($foo_absolute_root)], + [['/toto/foo'], self::toAbsolute($foo_toto_subdir_absolute_root)], ]; } } diff --git a/src/Symfony/Component/Finder/Tests/Iterator/RealIteratorTestCase.php b/src/Symfony/Component/Finder/Tests/Iterator/RealIteratorTestCase.php index 8b727f4cee411..cdc80467be840 100644 --- a/src/Symfony/Component/Finder/Tests/Iterator/RealIteratorTestCase.php +++ b/src/Symfony/Component/Finder/Tests/Iterator/RealIteratorTestCase.php @@ -31,6 +31,7 @@ public static function setUpBeforeClass(): void 'foo/bar.tmp', 'test.php', 'toto/', + 'toto/foo/', 'toto/.git/', 'foo bar', 'qux_0_1.php',