bug #30313 Avoid mutating the Finder when building the iterator (stof)

This PR was merged into the 3.4 branch.

Discussion
----------

Avoid mutating the Finder when building the iterator

| Q             | A
| ------------- | ---
| Branch?       | 3.4
| Bug fix?      | yes
| New feature?  | no
| BC breaks?    | no
| Deprecations? | no
| Tests pass?   | yes
| Fixed tickets | n/a
| License       | MIT
| Doc PR        | n/a

When excluding dot files or vcs files (which is done by default), the Finder object was mutated each time `searchInDirectory` was called to register the extra exclusions. This leads to registering them multiple times when the method is called multiple times (which happens either because you have multiple directories in `->in()` or because you call `getIterator` multiple times, for instance because of using `hasResults` or `count`).

This mutation create bugs if the Finder object is reconfigured between the 2 calls to `getIterator` to disable some of these ignore rules, as they would already be registered in the other config properties. New tests have been added to reproduce these bugs and prevent regressions.

This mutation is now avoided by using a local array for the final configuration, preserving the user configuration.

Commits
-------

94989fe794 Avoid mutating the Finder when building the iterator
This commit is contained in:
Fabien Potencier 2019-02-20 11:26:20 +01:00
commit 47d26f6f70
2 changed files with 32 additions and 6 deletions

View File

@ -645,12 +645,15 @@ class Finder implements \IteratorAggregate, \Countable
*/
private function searchInDirectory($dir)
{
$exclude = $this->exclude;
$notPaths = $this->notPaths;
if (static::IGNORE_VCS_FILES === (static::IGNORE_VCS_FILES & $this->ignore)) {
$this->exclude = array_merge($this->exclude, self::$vcsPatterns);
$exclude = array_merge($exclude, self::$vcsPatterns);
}
if (static::IGNORE_DOT_FILES === (static::IGNORE_DOT_FILES & $this->ignore)) {
$this->notPaths[] = '#(^|/)\..+(/|$)#';
$notPaths[] = '#(^|/)\..+(/|$)#';
}
$minDepth = 0;
@ -683,8 +686,8 @@ class Finder implements \IteratorAggregate, \Countable
$iterator = new Iterator\RecursiveDirectoryIterator($dir, $flags, $this->ignoreUnreadableDirs);
if ($this->exclude) {
$iterator = new Iterator\ExcludeDirectoryFilterIterator($iterator, $this->exclude);
if ($exclude) {
$iterator = new Iterator\ExcludeDirectoryFilterIterator($iterator, $exclude);
}
$iterator = new \RecursiveIteratorIterator($iterator, \RecursiveIteratorIterator::SELF_FIRST);
@ -717,8 +720,8 @@ class Finder implements \IteratorAggregate, \Countable
$iterator = new Iterator\CustomFilterIterator($iterator, $this->filters);
}
if ($this->paths || $this->notPaths) {
$iterator = new Iterator\PathFilterIterator($iterator, $this->paths, $this->notPaths);
if ($this->paths || $notPaths) {
$iterator = new Iterator\PathFilterIterator($iterator, $this->paths, $notPaths);
}
if ($this->sort) {

View File

@ -199,6 +199,18 @@ class FinderTest extends Iterator\RealIteratorTestCase
$this->assertIterator($this->toAbsolute(['foo', 'foo/bar.tmp', 'test.php', 'test.py', 'toto', '.bar', '.foo', '.foo/.bar', '.foo/bar', 'foo bar']), $finder->in(self::$tmpDir)->getIterator());
}
public function testIgnoreVCSCanBeDisabledAfterFirstIteration()
{
$finder = $this->buildFinder();
$finder->in(self::$tmpDir);
$finder->ignoreDotFiles(false);
$this->assertIterator($this->toAbsolute(['foo', 'foo/bar.tmp', 'test.php', 'test.py', 'toto', '.bar', '.foo', '.foo/.bar', '.foo/bar', 'foo bar']), $finder->getIterator());
$finder->ignoreVCS(false);
$this->assertIterator($this->toAbsolute(['.git', 'foo', 'foo/bar.tmp', 'test.php', 'test.py', 'toto', 'toto/.git', '.bar', '.foo', '.foo/.bar', '.foo/bar', 'foo bar']), $finder->getIterator());
}
public function testIgnoreDotFiles()
{
$finder = $this->buildFinder();
@ -214,6 +226,17 @@ class FinderTest extends Iterator\RealIteratorTestCase
$this->assertIterator($this->toAbsolute(['foo', 'foo/bar.tmp', 'test.php', 'test.py', 'toto', 'foo bar']), $finder->in(self::$tmpDir)->getIterator());
}
public function testIgnoreDotFilesCanBeDisabledAfterFirstIteration()
{
$finder = $this->buildFinder();
$finder->in(self::$tmpDir);
$this->assertIterator($this->toAbsolute(['foo', 'foo/bar.tmp', 'test.php', 'test.py', 'toto', 'foo bar']), $finder->getIterator());
$finder->ignoreDotFiles(false);
$this->assertIterator($this->toAbsolute(['foo', 'foo/bar.tmp', 'test.php', 'test.py', 'toto', '.bar', '.foo', '.foo/.bar', '.foo/bar', 'foo bar']), $finder->getIterator());
}
public function testSortByName()
{
$finder = $this->buildFinder();