Skip to content

Commit 1d6912f

Browse files
Copilotswissspidy
andcommitted
Add RecursiveFilterIterator to skip scanning ignored directories
Co-authored-by: swissspidy <[email protected]>
1 parent a99f12a commit 1d6912f

File tree

2 files changed

+78
-2
lines changed

2 files changed

+78
-2
lines changed

src/Dist_Archive_Command.php

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -495,8 +495,10 @@ private function get_file_list( $source_dir_path, $excluded = false ) {
495495
$included_files = [];
496496
$excluded_files = [];
497497

498-
$iterator = new RecursiveIteratorIterator(
499-
new RecursiveDirectoryIterator( $source_dir_path, RecursiveDirectoryIterator::SKIP_DOTS ),
498+
$directory_iterator = new RecursiveDirectoryIterator( $source_dir_path, RecursiveDirectoryIterator::SKIP_DOTS );
499+
$filter_iterator = new Distignore_Filter_Iterator( $directory_iterator, $this->checker, $source_dir_path );
500+
$iterator = new RecursiveIteratorIterator(
501+
$filter_iterator,
500502
RecursiveIteratorIterator::SELF_FIRST
501503
);
502504

src/Distignore_Filter_Iterator.php

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
<?php
2+
3+
use Inmarelibero\GitIgnoreChecker\GitIgnoreChecker;
4+
5+
/**
6+
* Filter iterator that skips ignored directories to improve performance.
7+
*
8+
* This filter prevents RecursiveIteratorIterator from descending into
9+
* directories that are marked as ignored in .distignore, avoiding unnecessary
10+
* iteration through thousands of files in directories like node_modules.
11+
*/
12+
class Distignore_Filter_Iterator extends RecursiveFilterIterator {
13+
/**
14+
* @var GitIgnoreChecker
15+
*/
16+
private $checker;
17+
18+
/**
19+
* @var string
20+
*/
21+
private $source_dir_path;
22+
23+
/**
24+
* Constructor.
25+
*
26+
* @param RecursiveIterator<string, SplFileInfo> $iterator Iterator to filter.
27+
* @param GitIgnoreChecker $checker GitIgnore checker instance.
28+
* @param string $source_dir_path Base directory path.
29+
*/
30+
public function __construct( RecursiveIterator $iterator, GitIgnoreChecker $checker, $source_dir_path ) {
31+
parent::__construct( $iterator );
32+
$this->checker = $checker;
33+
$this->source_dir_path = $source_dir_path;
34+
}
35+
36+
/**
37+
* Check whether the current element of the iterator is acceptable.
38+
*
39+
* @return bool True if the current element is acceptable, false otherwise.
40+
*/
41+
#[\ReturnTypeWillChange]
42+
public function accept() {
43+
/** @var SplFileInfo $item */
44+
$item = $this->current();
45+
46+
// If it's not a directory, accept it (filtering will happen later in get_file_list).
47+
if ( ! $item->isDir() ) {
48+
return true;
49+
}
50+
51+
// For directories, check if they should be ignored to prevent descending into them.
52+
$relative_filepath = str_replace( $this->source_dir_path, '', $item->getPathname() );
53+
54+
try {
55+
// If the directory is ignored, reject it to prevent descending.
56+
return ! $this->checker->isPathIgnored( $relative_filepath );
57+
} catch ( \Inmarelibero\GitIgnoreChecker\Exception\InvalidArgumentException $exception ) {
58+
// If there's an error checking, allow it through (error will be handled in get_file_list).
59+
return true;
60+
}
61+
}
62+
63+
/**
64+
* Return the inner iterator's children wrapped in this filter.
65+
*
66+
* @return RecursiveFilterIterator
67+
*/
68+
#[\ReturnTypeWillChange]
69+
public function getChildren() {
70+
/** @var RecursiveDirectoryIterator $inner */
71+
$inner = $this->getInnerIterator();
72+
return new self( $inner->getChildren(), $this->checker, $this->source_dir_path );
73+
}
74+
}

0 commit comments

Comments
 (0)