Skip to content

Commit

Permalink
Allow the filter to return LocalFile objects to FileList
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
anomiex committed Oct 4, 2024
1 parent 285b441 commit 5f0a289
Show file tree
Hide file tree
Showing 3 changed files with 90 additions and 7 deletions.
18 changes: 16 additions & 2 deletions src/Files/FileList.php
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,12 @@ public function __construct(Config $config, Ruleset $ruleset)
$iterator = new RecursiveIteratorIterator($filter);

foreach ($iterator as $file) {
$this->files[$file->getPathname()] = null;
if ($file instanceof LocalFile) {
$this->files[$file->getFilename()] = $file;
} else {
$this->files[$file->getPathname()] = null;
}

$this->numFiles++;
}
} else {
Expand Down Expand Up @@ -132,7 +137,16 @@ public function addFile($path, $file=null)
$iterator = new RecursiveIteratorIterator($filter);

foreach ($iterator as $path) {
$this->files[$path] = $file;
if ($path instanceof LocalFile) {
if ($file !== null) {
$this->files[$path->getFilename()] = $file;
} else {
$this->files[$path->getFilename()] = $path;
}
} else {
$this->files[$path] = $file;
}

$this->numFiles++;
}

Expand Down
42 changes: 37 additions & 5 deletions src/Filters/Filter.php
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,14 @@
namespace PHP_CodeSniffer\Filters;

use FilesystemIterator;
use PHP_CodeSniffer\Files\LocalFile;
use PHP_CodeSniffer\Config;
use PHP_CodeSniffer\Ruleset;
use PHP_CodeSniffer\Util\Common;
use RecursiveDirectoryIterator;
use RecursiveFilterIterator;
use ReturnTypeWillChange;
use SplFileInfo;

class Filter extends RecursiveFilterIterator
{
Expand Down Expand Up @@ -64,6 +66,15 @@ class Filter extends RecursiveFilterIterator
*/
protected $acceptedPaths = [];

/**
* Whether to generate LocalFile objects instead of string|SplFileInfo.
*
* This is intended to be set in subclasses that want that behavior.
*
* @var boolean
*/
protected $produceLocalFileObjects = false;


/**
* Constructs a filter.
Expand Down Expand Up @@ -96,7 +107,7 @@ public function __construct($iterator, $basedir, Config $config, Ruleset $rulese
#[ReturnTypeWillChange]
public function accept()
{
$filePath = $this->current();
$filePath = $this->getInnerIterator()->current();
$realPath = Common::realpath($filePath);

if ($realPath !== false) {
Expand All @@ -108,7 +119,6 @@ public function accept()
}
}

$filePath = $this->current();
if (is_dir($filePath) === true) {
if ($this->config->local === true) {
return false;
Expand All @@ -127,6 +137,28 @@ public function accept()
}//end accept()


/**
* Map the input string or SplFileInfo into a LocalFile, if desired.
*
* @return string|SplFileInfo|LocalFile
*/
#[ReturnTypeWillChange]
public function current()
{
if ($this->produceLocalFileObjects === false) {
return parent::current();
}

$filePath = (string) parent::current();
if (is_dir($filePath) === true) {
return $filePath;
} else {
return new LocalFile($filePath, $this->ruleset, $this->config);
}

}//end current()


/**
* Returns an iterator for the current entry.
*
Expand All @@ -140,7 +172,7 @@ public function getChildren()
{
$filterClass = get_called_class();
$children = new $filterClass(
new RecursiveDirectoryIterator($this->current(), (RecursiveDirectoryIterator::SKIP_DOTS | FilesystemIterator::FOLLOW_SYMLINKS)),
new RecursiveDirectoryIterator($this->getInnerIterator()->current(), (RecursiveDirectoryIterator::SKIP_DOTS | FilesystemIterator::FOLLOW_SYMLINKS)),
$this->basedir,
$this->config,
$this->ruleset
Expand All @@ -160,7 +192,7 @@ public function getChildren()
*
* Checks both file extension filters and path ignore filters.
*
* @param string $path The path to the file being checked.
* @param string|SplFileInfo $path The path to the file being checked.
*
* @return bool
*/
Expand Down Expand Up @@ -197,7 +229,7 @@ protected function shouldProcessFile($path)
/**
* Checks filtering rules to see if a path should be ignored.
*
* @param string $path The path to the file or directory being checked.
* @param string|SplFileInfo $path The path to the file or directory being checked.
*
* @return bool
*/
Expand Down
37 changes: 37 additions & 0 deletions tests/Core/Filters/Filter/AcceptTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,15 @@

namespace PHP_CodeSniffer\Tests\Core\Filters\Filter;

use PHP_CodeSniffer\Config;
use PHP_CodeSniffer\Files\LocalFile;
use PHP_CodeSniffer\Filters\Filter;
use PHP_CodeSniffer\Ruleset;
use PHP_CodeSniffer\Tests\ConfigDouble;
use PHP_CodeSniffer\Tests\Core\Filters\AbstractFilterTestCase;
use RecursiveArrayIterator;
use RecursiveIteratorIterator;
use ReflectionClass;

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


/**
* Test filtering a file list for excluded paths, when producing LocalFile objects.
*
* @param array $inputPaths List of file paths to be filtered.
* @param array $expectedOutput Expected filtering result.
*
* @dataProvider dataExcludePatterns
*
* @return void
*/
public function testExcludePatternsProducingLocalFileObjects($inputPaths, $expectedOutput)
{
$fakeDI = new RecursiveArrayIterator($inputPaths);
$filter = new Filter($fakeDI, '/', self::$config, self::$ruleset);
$iterator = new RecursiveIteratorIterator($filter);
$files = [];

// Set the filter object to produce LocalFile objects.
$rc = new ReflectionClass($filter);
$rp = $rc->getProperty('produceLocalFileObjects');
$rp->setAccessible(true);
$rp->setValue($filter, true);

foreach ($iterator as $file) {
$this->assertInstanceOf('PHP_CodeSniffer\\Files\\LocalFile', $file);
$files[] = $file->getFilename();
}

$this->assertEquals($expectedOutput, $files);

}//end testExcludePatternsProducingLocalFileObjects()


/**
* Data provider.
*
Expand Down

0 comments on commit 5f0a289

Please sign in to comment.