@@ -21,10 +21,15 @@ final class VcsIgnoredFilterIterator extends \FilterIterator
21
21
private $ baseDir ;
22
22
23
23
/**
24
- * @var array<string, string|null>
24
+ * @var array<string, array{0: string, 1: string} |null>
25
25
*/
26
26
private $ gitignoreFilesCache = [];
27
27
28
+ /**
29
+ * @var array<string, bool>
30
+ */
31
+ private $ ignoredPathsCache = [];
32
+
28
33
public function __construct (\Iterator $ iterator , string $ baseDir )
29
34
{
30
35
$ this ->baseDir = $ this ->normalizePath ($ baseDir );
@@ -37,25 +42,50 @@ public function accept(): bool
37
42
$ file = $ this ->current ();
38
43
39
44
$ fileRealPath = $ this ->normalizePath ($ file ->getRealPath ());
40
- if ($ file ->isDir () && !str_ends_with ($ fileRealPath , '/ ' )) {
45
+
46
+ return !$ this ->isIgnored ($ fileRealPath );
47
+ }
48
+
49
+ private function isIgnored (string $ fileRealPath ): bool
50
+ {
51
+ if (is_dir ($ fileRealPath ) && !str_ends_with ($ fileRealPath , '/ ' )) {
41
52
$ fileRealPath .= '/ ' ;
42
53
}
43
54
55
+ if (isset ($ this ->ignoredPathsCache [$ fileRealPath ])) {
56
+ return $ this ->ignoredPathsCache [$ fileRealPath ];
57
+ }
58
+
59
+ $ ignored = false ;
60
+
44
61
foreach ($ this ->parentsDirectoryDownward ($ fileRealPath ) as $ parentDirectory ) {
62
+ if ($ this ->isIgnored ($ parentDirectory )) {
63
+ $ ignored = true ;
64
+
65
+ // rules in ignored directories are ignored, no need to check further.
66
+ break ;
67
+ }
68
+
45
69
$ fileRelativePath = substr ($ fileRealPath , \strlen ($ parentDirectory ) + 1 );
46
70
47
- $ regex = $ this ->readGitignoreFile ("{$ parentDirectory }/.gitignore " );
71
+ if (null === $ regexps = $ this ->readGitignoreFile ("{$ parentDirectory }/.gitignore " )) {
72
+ continue ;
73
+ }
74
+
75
+ [$ exclusionRegex , $ inclusionRegex ] = $ regexps ;
76
+
77
+ if (preg_match ($ exclusionRegex , $ fileRelativePath )) {
78
+ $ ignored = true ;
48
79
49
- if (null !== $ regex && preg_match ($ regex , $ fileRelativePath )) {
50
- return false ;
80
+ continue ;
51
81
}
52
82
53
- if (0 !== strpos ( $ parentDirectory , $ this -> baseDir )) {
54
- break ;
83
+ if (preg_match ( $ inclusionRegex , $ fileRelativePath )) {
84
+ $ ignored = false ;
55
85
}
56
86
}
57
87
58
- return true ;
88
+ return $ this -> ignoredPathsCache [ $ fileRealPath ] = $ ignored ;
59
89
}
60
90
61
91
/**
@@ -87,7 +117,10 @@ private function parentsDirectoryDownward(string $fileRealPath): array
87
117
return array_reverse ($ parentDirectories );
88
118
}
89
119
90
- private function readGitignoreFile (string $ path ): ?string
120
+ /**
121
+ * @return array{0: string, 1: string}|null
122
+ */
123
+ private function readGitignoreFile (string $ path ): ?array
91
124
{
92
125
if (\array_key_exists ($ path , $ this ->gitignoreFilesCache )) {
93
126
return $ this ->gitignoreFilesCache [$ path ];
@@ -101,7 +134,12 @@ private function readGitignoreFile(string $path): ?string
101
134
throw new \RuntimeException ("The \"ignoreVCSIgnored \" option cannot be used by the Finder as the \"{$ path }\" file is not readable. " );
102
135
}
103
136
104
- return $ this ->gitignoreFilesCache [$ path ] = Gitignore::toRegex (file_get_contents ($ path ));
137
+ $ gitignoreFileContent = file_get_contents ($ path );
138
+
139
+ return $ this ->gitignoreFilesCache [$ path ] = [
140
+ Gitignore::toRegex ($ gitignoreFileContent ),
141
+ Gitignore::toRegexMatchingNegatedPatterns ($ gitignoreFileContent ),
142
+ ];
105
143
}
106
144
107
145
private function normalizePath (string $ path ): string
0 commit comments