Skip to content

Commit ad322da

Browse files
committed
Allow the filter to return LocalFile objects to FileList
This gets us most of the way to fixing #2551. Now it's just a matter of writing a filter that updates the Config and/or Ruleset as appropriate while processing the directory tree. Since this change to `Filter::current()` may break expectations of existing filters, a filter must opt in to this change by overriding the protected `$this->produceLocalFileObjects` to true.
1 parent 811963c commit ad322da

File tree

3 files changed

+90
-7
lines changed

3 files changed

+90
-7
lines changed

src/Files/FileList.php

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -91,7 +91,12 @@ public function __construct(Config $config, Ruleset $ruleset)
9191
$iterator = new RecursiveIteratorIterator($filter);
9292

9393
foreach ($iterator as $file) {
94-
$this->files[$file->getPathname()] = null;
94+
if ($file instanceof LocalFile) {
95+
$this->files[$file->getFilename()] = $file;
96+
} else {
97+
$this->files[$file->getPathname()] = null;
98+
}
99+
95100
$this->numFiles++;
96101
}
97102
} else {
@@ -132,7 +137,16 @@ public function addFile($path, $file=null)
132137
$iterator = new RecursiveIteratorIterator($filter);
133138

134139
foreach ($iterator as $path) {
135-
$this->files[$path] = $file;
140+
if ($path instanceof LocalFile) {
141+
if ($file !== null) {
142+
$this->files[$path->getFilename()] = $file;
143+
} else {
144+
$this->files[$path->getFilename()] = $path;
145+
}
146+
} else {
147+
$this->files[$path] = $file;
148+
}
149+
136150
$this->numFiles++;
137151
}
138152

src/Filters/Filter.php

Lines changed: 37 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -10,12 +10,14 @@
1010
namespace PHP_CodeSniffer\Filters;
1111

1212
use FilesystemIterator;
13+
use PHP_CodeSniffer\Files\LocalFile;
1314
use PHP_CodeSniffer\Config;
1415
use PHP_CodeSniffer\Ruleset;
1516
use PHP_CodeSniffer\Util\Common;
1617
use RecursiveDirectoryIterator;
1718
use RecursiveFilterIterator;
1819
use ReturnTypeWillChange;
20+
use SplFileInfo;
1921

2022
class Filter extends RecursiveFilterIterator
2123
{
@@ -64,6 +66,15 @@ class Filter extends RecursiveFilterIterator
6466
*/
6567
protected $acceptedPaths = [];
6668

69+
/**
70+
* Whether to generate LocalFile objects instead of string|SplFileInfo.
71+
*
72+
* This is intended to be set in subclasses that want that behavior.
73+
*
74+
* @var boolean
75+
*/
76+
protected $produceLocalFileObjects = false;
77+
6778

6879
/**
6980
* Constructs a filter.
@@ -96,7 +107,7 @@ public function __construct($iterator, $basedir, Config $config, Ruleset $rulese
96107
#[ReturnTypeWillChange]
97108
public function accept()
98109
{
99-
$filePath = $this->current();
110+
$filePath = $this->getInnerIterator()->current();
100111
$realPath = Common::realpath($filePath);
101112

102113
if ($realPath !== false) {
@@ -108,7 +119,6 @@ public function accept()
108119
}
109120
}
110121

111-
$filePath = $this->current();
112122
if (is_dir($filePath) === true) {
113123
if ($this->config->local === true) {
114124
return false;
@@ -127,6 +137,28 @@ public function accept()
127137
}//end accept()
128138

129139

140+
/**
141+
* Map the input string or SplFileInfo into a LocalFile, if desired.
142+
*
143+
* @return string|SplFileInfo|LocalFile
144+
*/
145+
#[ReturnTypeWillChange]
146+
public function current()
147+
{
148+
if ($this->produceLocalFileObjects === false) {
149+
return parent::current();
150+
}
151+
152+
$filePath = (string) parent::current();
153+
if (is_dir($filePath) === true) {
154+
return $filePath;
155+
} else {
156+
return new LocalFile($filePath, $this->ruleset, $this->config);
157+
}
158+
159+
}//end current()
160+
161+
130162
/**
131163
* Returns an iterator for the current entry.
132164
*
@@ -140,7 +172,7 @@ public function getChildren()
140172
{
141173
$filterClass = get_called_class();
142174
$children = new $filterClass(
143-
new RecursiveDirectoryIterator($this->current(), (RecursiveDirectoryIterator::SKIP_DOTS | FilesystemIterator::FOLLOW_SYMLINKS)),
175+
new RecursiveDirectoryIterator($this->getInnerIterator()->current(), (RecursiveDirectoryIterator::SKIP_DOTS | FilesystemIterator::FOLLOW_SYMLINKS)),
144176
$this->basedir,
145177
$this->config,
146178
$this->ruleset
@@ -160,7 +192,7 @@ public function getChildren()
160192
*
161193
* Checks both file extension filters and path ignore filters.
162194
*
163-
* @param string $path The path to the file being checked.
195+
* @param string|SplFileInfo $path The path to the file being checked.
164196
*
165197
* @return bool
166198
*/
@@ -197,7 +229,7 @@ protected function shouldProcessFile($path)
197229
/**
198230
* Checks filtering rules to see if a path should be ignored.
199231
*
200-
* @param string $path The path to the file or directory being checked.
232+
* @param string|SplFileInfo $path The path to the file or directory being checked.
201233
*
202234
* @return bool
203235
*/

tests/Core/Filters/Filter/AcceptTest.php

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,11 +10,15 @@
1010

1111
namespace PHP_CodeSniffer\Tests\Core\Filters\Filter;
1212

13+
use PHP_CodeSniffer\Config;
14+
use PHP_CodeSniffer\Files\LocalFile;
1315
use PHP_CodeSniffer\Filters\Filter;
1416
use PHP_CodeSniffer\Ruleset;
1517
use PHP_CodeSniffer\Tests\ConfigDouble;
1618
use PHP_CodeSniffer\Tests\Core\Filters\AbstractFilterTestCase;
1719
use RecursiveArrayIterator;
20+
use RecursiveIteratorIterator;
21+
use ReflectionClass;
1822

1923
/**
2024
* Tests for the \PHP_CodeSniffer\Filters\Filter::accept method.
@@ -61,6 +65,39 @@ public function testExcludePatterns($inputPaths, $expectedOutput)
6165
}//end testExcludePatterns()
6266

6367

68+
/**
69+
* Test filtering a file list for excluded paths, when producing LocalFile objects.
70+
*
71+
* @param array $inputPaths List of file paths to be filtered.
72+
* @param array $expectedOutput Expected filtering result.
73+
*
74+
* @dataProvider dataExcludePatterns
75+
*
76+
* @return void
77+
*/
78+
public function testExcludePatternsProducingLocalFileObjects($inputPaths, $expectedOutput)
79+
{
80+
$fakeDI = new RecursiveArrayIterator($inputPaths);
81+
$filter = new Filter($fakeDI, '/', self::$config, self::$ruleset);
82+
$iterator = new RecursiveIteratorIterator($filter);
83+
$files = [];
84+
85+
// Set the filter object to produce LocalFile objects.
86+
$rc = new ReflectionClass($filter);
87+
$rp = $rc->getProperty('produceLocalFileObjects');
88+
$rp->setAccessible(true);
89+
$rp->setValue($filter, true);
90+
91+
foreach ($iterator as $file) {
92+
$this->assertInstanceOf('PHP_CodeSniffer\\Files\\LocalFile', $file);
93+
$files[] = $file->getFilename();
94+
}
95+
96+
$this->assertEquals($expectedOutput, $files);
97+
98+
}//end testExcludePatternsProducingLocalFileObjects()
99+
100+
64101
/**
65102
* Data provider.
66103
*

0 commit comments

Comments
 (0)