2525use RecursiveIteratorIterator ;
2626use RuntimeException ;
2727use SplFileInfo ;
28+ use Toolkit \Stdlib \Str ;
2829use Traversable ;
2930use UnexpectedValueException ;
3031use function array_flip ;
3435use function fnmatch ;
3536use function get_object_vars ;
3637use function is_iterable ;
38+ use function is_string ;
3739use function iterator_count ;
3840use function str_contains ;
3941use function stream_get_meta_data ;
@@ -78,16 +80,14 @@ final class FileFinder implements IteratorAggregate, Countable
7880 /** @var bool */
7981 private bool $ ignoreVcsAdded = false ;
8082
81- /**
82- * @var bool
83- */
83+ /** @var bool */
8484 private bool $ skipUnreadableDirs = true ;
8585
8686 /** @var array */
8787 private array $ dirs = [];
8888
8989 /**
90- * File ,dir name match.
90+ * add include file ,dir name match.
9191 *
9292 * eg: '.php' '*.php' 'test.php'
9393 *
@@ -96,26 +96,28 @@ final class FileFinder implements IteratorAggregate, Countable
9696 private array $ names = [];
9797
9898 /**
99- * File ,dir name exclude
99+ * add exclude file ,dir name patterns
100100 *
101101 * eg: '.php' '*.php' 'test.php'
102102 *
103103 * @var array
104104 */
105105 private array $ notNames = [];
106106
107- /** @var array */
107+ /** @var array<string> include paths pattern. */
108108 private array $ paths = [];
109109
110- /** @var array */
110+ /** @var array<string> exclude paths pattern */
111111 private array $ notPaths = [];
112112
113- /**
114- * @var array exclude directories
115- */
113+ /** @var array<string> exclude directory names */
116114 private array $ excludes = [];
117115
118- /** @var array */
116+ /**
117+ * path filters. each filter like: `Closure(SplFileInfo):bool`, return FALSE to exclude.
118+ *
119+ * @var array
120+ */
119121 private array $ filters = [];
120122
121123 /** @var array */
@@ -213,6 +215,8 @@ public function onlyFiles(): self
213215 }
214216
215217 /**
218+ * add include file,dir name match pattern.
219+ *
216220 * $finder->name('*.php')
217221 * $finder->name('test.php')
218222 *
@@ -222,28 +226,29 @@ public function onlyFiles(): self
222226 */
223227 public function name (string $ pattern ): self
224228 {
225- if ($ pattern ) {
226- $ this ->names [] = $ pattern ;
227- }
228-
229- return $ this ;
229+ return $ this ->addNames ($ pattern );
230230 }
231231
232232 /**
233+ * add include file,dir name match pattern.
234+ *
233235 * @param array|string $patterns
234236 *
235237 * @return FileFinder
236238 */
237239 public function addNames (array |string $ patterns ): self
238240 {
239241 if ($ patterns ) {
240- $ this ->names = array_merge ($ this ->names , (array )$ patterns );
242+ $ patterns = is_string ($ patterns ) ? Str::splitTrimmed ($ patterns ) : $ patterns ;
243+ // append
244+ $ this ->names = array_merge ($ this ->names , $ patterns );
241245 }
242-
243246 return $ this ;
244247 }
245248
246249 /**
250+ * add exclude file,dir name patterns
251+ *
247252 * @param string $pattern
248253 *
249254 * @return FileFinder
@@ -255,20 +260,25 @@ public function notName(string $pattern): self
255260 }
256261
257262 /**
263+ * add exclude file,dir name patterns
264+ *
258265 * @param array|string $patterns
259266 *
260267 * @return FileFinder
261268 */
262269 public function notNames (array |string $ patterns ): self
263270 {
264271 if ($ patterns ) {
265- $ this ->notNames = array_merge ($ this ->notNames , (array )$ patterns );
272+ $ patterns = is_string ($ patterns ) ? Str::splitTrimmed ($ patterns ) : $ patterns ;
273+ // append
274+ $ this ->notNames = array_merge ($ this ->notNames , $ patterns );
266275 }
267-
268276 return $ this ;
269277 }
270278
271279 /**
280+ * add exclude file,dir name patterns
281+ *
272282 * @param array|string $patterns
273283 *
274284 * @return FileFinder
@@ -279,6 +289,8 @@ public function addNotNames(array|string $patterns): self
279289 }
280290
281291 /**
292+ * add include paths pattern.
293+ * eg:
282294 * $finder->path('some/special/dir')
283295 *
284296 * @param string $pattern
@@ -292,33 +304,37 @@ public function path(string $pattern): self
292304 }
293305
294306 /**
307+ * add include paths pattern.
308+ *
295309 * @param array|string $patterns
296310 *
297311 * @return FileFinder
298312 */
299313 public function addPaths (array |string $ patterns ): self
300314 {
301315 if ($ patterns ) {
302- $ this ->paths = array_merge ($ this ->paths , (array )$ patterns );
316+ $ patterns = is_string ($ patterns ) ? Str::splitTrimmed ($ patterns ) : $ patterns ;
317+ // append
318+ $ this ->paths = array_merge ($ this ->paths , $ patterns );
303319 }
304-
305320 return $ this ;
306321 }
307322
308323 /**
324+ * add exclude paths pattern
325+ *
309326 * @param string $pattern
310327 *
311328 * @return FileFinder
312329 */
313330 public function notPath (string $ pattern ): self
314331 {
315- if ($ pattern ) {
316- $ this ->notPaths [] = $ pattern ;
317- }
318- return $ this ;
332+ return $ this ->addNotPaths ($ pattern );
319333 }
320334
321335 /**
336+ * add exclude paths pattern. alias of addNotPaths()
337+ *
322338 * @param array|string $patterns
323339 *
324340 * @return FileFinder
@@ -329,30 +345,37 @@ public function notPaths(array|string $patterns): self
329345 }
330346
331347 /**
348+ * add exclude paths pattern.
349+ * eg: $finder->addNotPaths(['vendor', 'node_modules', 'bin/'])
350+ *
332351 * @param array|string $patterns
333352 *
334353 * @return FileFinder
335354 */
336355 public function addNotPaths (array |string $ patterns ): self
337356 {
338357 if ($ patterns ) {
339- $ this ->notPaths = array_merge ($ this ->notPaths , (array )$ patterns );
358+ $ patterns = is_string ($ patterns ) ? Str::splitTrimmed ($ patterns ) : $ patterns ;
359+ // append
360+ $ this ->notPaths = array_merge ($ this ->notPaths , $ patterns );
340361 }
341-
342362 return $ this ;
343363 }
344364
345365 /**
346- * @param $dirs
366+ * exclude directory names
367+ *
368+ * @param array|string $dirNames
347369 *
348370 * @return FileFinder
349371 */
350- public function exclude ($ dirs ): self
372+ public function exclude (array | string $ dirNames ): self
351373 {
352- if ($ dirs ) {
353- $ this ->excludes = array_merge ($ this ->excludes , (array )$ dirs );
374+ if ($ dirNames ) {
375+ $ dirNames = is_string ($ dirNames ) ? Str::splitTrimmed ($ dirNames ) : $ dirNames ;
376+ // append
377+ $ this ->excludes = array_merge ($ this ->excludes , $ dirNames );
354378 }
355-
356379 return $ this ;
357380 }
358381
@@ -429,14 +452,13 @@ public function followLinks(mixed $followLinks = true): self
429452 }
430453
431454 /**
432- * @param Closure $closure
455+ * @param Closure(SplFileInfo): bool $closure
433456 *
434457 * @return FileFinder
435458 */
436459 public function filter (Closure $ closure ): self
437460 {
438461 $ this ->filters [] = $ closure ;
439-
440462 return $ this ;
441463 }
442464
@@ -460,9 +482,10 @@ public function in(array|string $dirs): self
460482 public function inDir (array |string $ dirs ): self
461483 {
462484 if ($ dirs ) {
463- $ this ->dirs = array_merge ($ this ->dirs , (array )$ dirs );
485+ $ dirs = is_string ($ dirs ) ? Str::splitTrimmed ($ dirs ) : $ dirs ;
486+ // append
487+ $ this ->dirs = array_merge ($ this ->dirs , $ dirs );
464488 }
465-
466489 return $ this ;
467490 }
468491
@@ -585,7 +608,7 @@ private function findInDirectory(string $dir): Iterator
585608 $ flags |= RecursiveDirectoryIterator::FOLLOW_SYMLINKS ;
586609 }
587610
588- $ iterator = new class ($ dir , $ flags ) extends RecursiveDirectoryIterator {
611+ $ iterator = new class ($ dir , $ flags, $ this -> skipUnreadableDirs ) extends RecursiveDirectoryIterator {
589612 private string $ rootPath ;
590613 private string $ subPath = '' ;
591614 private bool |null $ rewindable = null ;
@@ -610,8 +633,9 @@ public function __construct(string $path, int $flags, bool $skipUnreadableDirs =
610633
611634 public function current (): SplFileInfo
612635 {
636+ // vdump($this->getPathname(), $this);
613637 if (null === $ subPathname = $ this ->subPath ) {
614- $ subPathname = $ this ->subPath = ( string ) $ this ->getSubPath ();
638+ $ subPathname = $ this ->subPath = $ this ->getSubPath ();
615639 }
616640
617641 if ('' !== $ subPathname ) {
@@ -620,8 +644,9 @@ public function current(): SplFileInfo
620644
621645 $ subPathname .= $ this ->getFilename ();
622646
623- // $fileInfo = new \SplFileInfo($this->getPathname());
624- $ fileInfo = new SplFileInfo ($ this ->rootPath . $ this ->directorySep . $ subPathname );
647+ $ fileInfo = new SplFileInfo ($ this ->getPathname ());
648+ // $fileInfo = new SplFileInfo($this->rootPath . $this->directorySep . $subPathname);
649+ // vdump($this->rootPath, $subPathname, $fileInfo);
625650 // add props
626651 $ fileInfo ->relativePath = $ this ->subPath ;
627652 $ fileInfo ->relativePathname = $ subPathname ;
@@ -634,7 +659,7 @@ public function getChildren(): RecursiveDirectoryIterator
634659 try {
635660 $ children = parent ::getChildren ();
636661 if ($ children instanceof self) {
637- $ children ->rootPath = $ this ->rootPath ;
662+ $ children ->rootPath = $ this ->rootPath ;
638663 $ children ->rewindable = &$ this ->rewindable ;
639664 $ children ->skipUnreadableDirs = $ this ->skipUnreadableDirs ;
640665 }
@@ -680,6 +705,7 @@ public function isRewindable(): ?bool
680705 // exclude directories
681706 if ($ this ->excludes ) {
682707 $ iterator = new class ($ iterator , $ this ->excludes ) extends FilterIterator implements RecursiveIterator {
708+ /** @var array<string, int> */
683709 private array $ excludes ;
684710
685711 private RecursiveIterator $ iterator ;
@@ -734,6 +760,7 @@ public function __construct(Iterator $iterator, int $mode)
734760
735761 public function accept (): bool
736762 {
763+ /** @var SplFileInfo $info */
737764 $ info = $ this ->current ();
738765 if (FileFinder::ONLY_DIR === $ this ->mode && $ info ->isFile ()) {
739766 return false ;
@@ -796,6 +823,7 @@ public function __construct(Iterator $iterator, array $filters)
796823
797824 public function accept (): bool
798825 {
826+ /** @var SplFileInfo $fileInfo */
799827 $ fileInfo = $ this ->current ();
800828 foreach ($ this ->filters as $ filter ) {
801829 if (false === $ filter ($ fileInfo )) {
@@ -810,8 +838,9 @@ public function accept(): bool
810838
811839 if ($ this ->paths || $ this ->notPaths ) {
812840 $ iterator = new class ($ iterator , $ this ->paths , $ this ->notPaths ) extends FilterIterator {
841+ /** @var array<string> */
813842 private array $ paths ;
814-
843+ /** @var array<string> */
815844 private array $ notPaths ;
816845
817846 public function __construct (Iterator $ iterator , array $ paths , array $ notPaths )
@@ -823,7 +852,8 @@ public function __construct(Iterator $iterator, array $paths, array $notPaths)
823852
824853 public function accept (): bool
825854 {
826- $ pathname = $ this ->current ()->relativePathname ;
855+ /** @var string $pathname {@see SplFileInfo::getPathname()} */
856+ $ pathname = $ this ->current ()->getPathname ();
827857 if ('\\' === DIRECTORY_SEPARATOR ) {
828858 $ pathname = str_replace ('\\' , '/ ' , $ pathname );
829859 }
0 commit comments