@@ -38,6 +38,9 @@ class Finder implements \IteratorAggregate
3838 /** @var \Closure[] */
3939 private array $ descentFilters = [];
4040 private bool $ childFirst = false ;
41+
42+ /** @var ?callable */
43+ private $ sort ;
4144 private int $ maxDepth = -1 ;
4245 private bool $ ignoreUnreadableDirs = true ;
4346
@@ -163,6 +166,27 @@ public function ignoreUnreadableDirs(bool $state = true): static
163166 }
164167
165168
169+ /**
170+ * Set a compare function for sorting directory entries. The function will be called to sort entries from the same directory.
171+ * @param callable(FileInfo, FileInfo): int $callback
172+ */
173+ public function sortBy (callable $ callback ): static
174+ {
175+ $ this ->sort = $ callback ;
176+ return $ this ;
177+ }
178+
179+
180+ /**
181+ * Sorts files in each directory naturally by name.
182+ */
183+ public function sortByName (): static
184+ {
185+ $ this ->sort = fn (FileInfo $ a , FileInfo $ b ): int => strnatcmp ($ a ->getBasename (), $ b ->getBasename ());
186+ return $ this ;
187+ }
188+
189+
166190 /********************* filtering ****************d*g**/
167191
168192
@@ -307,16 +331,16 @@ private function traverseDir(string $dir, array $searches, array $subdirs = []):
307331 throw new Nette \InvalidStateException ($ e ->getMessage ());
308332 }
309333 }
310- $ absolute = FileSystem::isAbsolute ($ dir );
311334
312- $ relativePath = implode (DIRECTORY_SEPARATOR , $ subdirs );
335+ $ files = $ this -> convertToFiles ( $ pathNames , implode (' / ' , $ subdirs), FileSystem:: isAbsolute ( $ dir ) );
313336
314- foreach ($ pathNames as $ pathName ) {
315- if (!$ absolute ) {
316- $ pathName = preg_replace ('~\.?/~A ' , '' , $ pathName );
317- }
318- $ pathName = FileSystem::platformSlashes ($ pathName );
319- $ file = new FileInfo ($ pathName , $ relativePath );
337+ if ($ this ->sort ) {
338+ $ files = iterator_to_array ($ files );
339+ usort ($ files , $ this ->sort );
340+ }
341+
342+ foreach ($ files as $ file ) {
343+ $ pathName = $ file ->getPathname ();
320344 $ cache = $ subSearch = [];
321345
322346 if ($ file ->isDir ()) {
@@ -350,6 +374,18 @@ private function traverseDir(string $dir, array $searches, array $subdirs = []):
350374 }
351375
352376
377+ private function convertToFiles (iterable $ pathNames , string $ relativePath , bool $ absolute ): \Generator
378+ {
379+ foreach ($ pathNames as $ pathName ) {
380+ if (!$ absolute ) {
381+ $ pathName = preg_replace ('~\.?/~A ' , '' , $ pathName );
382+ }
383+ $ pathName = FileSystem::platformSlashes ($ pathName );
384+ yield new FileInfo ($ pathName , $ relativePath );
385+ }
386+ }
387+
388+
353389 private function proveFilters (array $ filters , FileInfo $ file , array &$ cache ): bool
354390 {
355391 foreach ($ filters as $ filter ) {
0 commit comments