Skip to content

Commit 52c3e1e

Browse files
committed
added Finder::sortBy() & sortByName()
1 parent 40b5696 commit 52c3e1e

2 files changed

Lines changed: 113 additions & 8 deletions

File tree

src/Utils/Finder.php

Lines changed: 44 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -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) {

tests/Utils/Finder.sort.phpt

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
<?php
2+
3+
/**
4+
* Test: Nette\Utils\Finder sorting.
5+
*/
6+
7+
declare(strict_types=1);
8+
9+
use Nette\Utils\FileInfo;
10+
use Nette\Utils\Finder;
11+
use Tester\Assert;
12+
13+
14+
require __DIR__ . '/../bootstrap.php';
15+
16+
17+
function export($iterator)
18+
{
19+
$arr = [];
20+
foreach ($iterator as $key => $value) {
21+
$arr[] = strtr($key, '\\', '/');
22+
}
23+
24+
return $arr;
25+
}
26+
27+
28+
test('byName', function () {
29+
$finder = Finder::find('*')
30+
->from('fixtures.finder')
31+
->sortByName();
32+
33+
Assert::same([
34+
'fixtures.finder/file.txt',
35+
'fixtures.finder/images',
36+
'fixtures.finder/images/logo.gif',
37+
'fixtures.finder/subdir',
38+
'fixtures.finder/subdir/file.txt',
39+
'fixtures.finder/subdir/readme',
40+
'fixtures.finder/subdir/subdir2',
41+
'fixtures.finder/subdir/subdir2/file.txt',
42+
], export($finder));
43+
44+
$finder->childFirst();
45+
Assert::same([
46+
'fixtures.finder/file.txt',
47+
'fixtures.finder/images/logo.gif',
48+
'fixtures.finder/images',
49+
'fixtures.finder/subdir/file.txt',
50+
'fixtures.finder/subdir/readme',
51+
'fixtures.finder/subdir/subdir2/file.txt',
52+
'fixtures.finder/subdir/subdir2',
53+
'fixtures.finder/subdir',
54+
], export($finder));
55+
});
56+
57+
test('user func', function () {
58+
$finder = Finder::findFiles('*')
59+
->from('fixtures.finder')
60+
->sortBy(fn(FileInfo $a, FileInfo $b) => substr((string) $a, -1) <=> substr((string) $b, -1));
61+
62+
Assert::same([
63+
'fixtures.finder/subdir/subdir2/file.txt',
64+
'fixtures.finder/subdir/readme',
65+
'fixtures.finder/subdir/file.txt',
66+
'fixtures.finder/images/logo.gif',
67+
'fixtures.finder/file.txt',
68+
], export($finder));
69+
});

0 commit comments

Comments
 (0)