ILIAS  release_9 Revision v9.13-25-g2c18ec4c24f
Finder.php
Go to the documentation of this file.
1 <?php
2 
19 declare(strict_types=1);
20 
21 namespace ILIAS\Filesystem\Finder;
22 
23 use AppendIterator;
24 use ArrayIterator;
25 use Closure;
26 use Countable;
31 use Iterator as PhpIterator;
33 use LogicException;
36 
44 final class Finder implements IteratorAggregate, Countable
45 {
46  private const IGNORE_VCS_FILES = 1;
47  private const IGNORE_DOT_FILES = 2;
49  private array $vcsPatterns = ['.svn', '_svn', 'CVS', '_darcs', '.arch-params', '.monotone', '.bzr', '.git', '.hg'];
51  private array $iterators = [];
53  protected array $dirs = [];
55  private array $exclude = [];
56  private int $ignore = 0;
58  private bool $reverseSorting = false;
60  private array $dates = [];
62  private array $sizes = [];
64  private array $depths = [];
67 
68  public function __construct(private Filesystem $filesystem)
69  {
70  $this->ignore = self::IGNORE_VCS_FILES|self::IGNORE_DOT_FILES;
71  }
72 
73  public function files(): self
74  {
75  $clone = clone $this;
77 
78  return $clone;
79  }
80 
81  public function directories(): self
82  {
83  $clone = clone $this;
85 
86  return $clone;
87  }
88 
89  public function allTypes(): self
90  {
91  $clone = clone $this;
93 
94  return $clone;
95  }
96 
101  public function exclude(array $directories): self
102  {
103  array_walk($directories, static function ($directory): void {
104  if (!is_string($directory)) {
105  throw new InvalidArgumentException(sprintf('Invalid directory given: %s', $directory::class));
106  }
107  });
108 
109  $clone = clone $this;
110  $clone->exclude = array_merge($clone->exclude, $directories);
111 
112  return $clone;
113  }
114 
119  public function in(array $directories): self
120  {
121  array_walk($directories, static function ($directory): void {
122  if (!is_string($directory)) {
123  throw new InvalidArgumentException(sprintf('Invalid directory given: %s', $directory::class));
124  }
125  });
126 
127  $clone = clone $this;
128  $clone->dirs = array_unique(array_merge($clone->dirs, $directories));
129 
130  return $clone;
131  }
132 
144  public function depth(string|int $level): self
145  {
146  $clone = clone $this;
147  $clone->depths[] = new Comparator\NumberComparator((string) $level);
148 
149  return $clone;
150  }
151 
167  public function date(string $date): self
168  {
169  $clone = clone $this;
170  $clone->dates[] = new Comparator\DateComparator($date);
171 
172  return $clone;
173  }
174 
188  public function size(string|int|array $sizes): self
189  {
190  $sizes = is_array($sizes) ? $sizes : [$sizes];
191 
192  $clone = clone $this;
193 
194  foreach ($sizes as $size) {
195  $clone->sizes[] = new Comparator\NumberComparator((string) $size);
196  }
197 
198  return $clone;
199  }
200 
201  public function reverseSorting(): self
202  {
203  $clone = clone $this;
204  $clone->reverseSorting = true;
205 
206  return $clone;
207  }
208 
209  public function ignoreVCS(bool $ignoreVCS): self
210  {
211  $clone = clone $this;
212  if ($ignoreVCS) {
213  $clone->ignore |= self::IGNORE_VCS_FILES;
214  } else {
215  $clone->ignore &= ~self::IGNORE_VCS_FILES;
216  }
217 
218  return $clone;
219  }
220 
225  public function addVCSPattern(array $pattern): self
226  {
227  array_walk($pattern, static function ($p): void {
228  if (!is_string($p)) {
229  throw new InvalidArgumentException(sprintf('Invalid pattern given: %s', $p::class));
230  }
231  });
232 
233  $clone = clone $this;
234  foreach ($pattern as $p) {
235  $clone->vcsPatterns[] = $p;
236  }
237 
238  $clone->vcsPatterns = array_unique($clone->vcsPatterns);
239 
240  return $clone;
241  }
242 
248  public function sort(Closure $closure): self
249  {
250  $clone = clone $this;
251  $clone->sort = $closure;
252 
253  return $clone;
254  }
255 
256  public function sortByName(bool $useNaturalSort = false): self
257  {
258  $clone = clone $this;
260  if ($useNaturalSort) {
262  }
263 
264  return $clone;
265  }
266 
267  public function sortByType(): self
268  {
269  $clone = clone $this;
271 
272  return $clone;
273  }
274 
275  public function sortByTime(): self
276  {
277  $clone = clone $this;
279 
280  return $clone;
281  }
282 
288  public function append(iterable $iterator): self
289  {
290  $clone = clone $this;
291 
292  if ($iterator instanceof IteratorAggregate) {
293  $clone->iterators[] = $iterator->getIterator();
294  } elseif ($iterator instanceof PhpIterator) {
295  $clone->iterators[] = $iterator;
296  } elseif (is_iterable($iterator)) {
297  $it = new ArrayIterator();
298  foreach ($iterator as $file) {
299  if ($file instanceof MetadataType) {
300  $it->append($file);
301  } else {
302  throw new InvalidArgumentException(
303  'Finder::append() method wrong argument type in passed iterator.'
304  );
305  }
306  }
307  $clone->iterators[] = $it;
308  } else {
309  throw new InvalidArgumentException('Finder::append() method wrong argument type.');
310  }
311 
312  return $clone;
313  }
314 
315  private function searchInDirectory(string $dir): \Traversable
316  {
317  if (self::IGNORE_VCS_FILES === (self::IGNORE_VCS_FILES&$this->ignore)) {
318  $this->exclude = array_merge($this->exclude, $this->vcsPatterns);
319  }
320 
321  $iterator = new Iterator\RecursiveDirectoryIterator($this->filesystem, $dir);
322 
323  if ($this->exclude) {
324  $iterator = new Iterator\ExcludeDirectoryFilterIterator($iterator, $this->exclude);
325  }
326 
327  $iterator = new RecursiveIteratorIterator($iterator, \RecursiveIteratorIterator::SELF_FIRST);
328 
329  if ($this->depths) {
330  $iterator = new Iterator\DepthRangeFilterIterator($iterator, $this->depths);
331  }
332 
333  if ($this->mode !== 0) {
334  $iterator = new Iterator\FileTypeFilterIterator($iterator, $this->mode);
335  }
336 
337  if ($this->dates) {
338  $iterator = new Iterator\DateRangeFilterIterator($this->filesystem, $iterator, $this->dates);
339  }
340 
341  if ($this->sizes) {
342  $iterator = new Iterator\SizeRangeFilterIterator($this->filesystem, $iterator, $this->sizes);
343  }
344 
345  if ($this->sort || $this->reverseSorting) {
346  $iteratorAggregate = new Iterator\SortableIterator(
347  $this->filesystem,
348  $iterator,
349  $this->sort,
350  $this->reverseSorting
351  );
352  $iterator = $iteratorAggregate->getIterator();
353  }
354 
355  return $iterator;
356  }
357 
363  #[\ReturnTypeWillChange]
364  public function getIterator(): \Iterator
365  {
366  if ([] === $this->dirs && [] === $this->iterators) {
367  throw new LogicException('You must call one of in() or append() methods before iterating over a Finder.');
368  }
369 
370  if (1 === count($this->dirs) && [] === $this->iterators) {
371  return $this->searchInDirectory($this->dirs[0]);
372  }
373 
374  $iterator = new AppendIterator();
375  foreach ($this->dirs as $dir) {
376  $iterator->append($this->searchInDirectory($dir));
377  }
378 
379  foreach ($this->iterators as $it) {
380  $iterator->append($it);
381  }
382 
383  return $iterator;
384  }
385 
389  public function count(): int
390  {
391  return iterator_count($this->getIterator());
392  }
393 }
size(string|int|array $sizes)
Adds tests for file sizes.
Definition: Finder.php:188
__construct(private Filesystem $filesystem)
Definition: Finder.php:68
ignoreVCS(bool $ignoreVCS)
Definition: Finder.php:209
sort(Closure $closure)
Sorts files and directories by an anonymous function.
Definition: Finder.php:248
addVCSPattern(array $pattern)
Definition: Finder.php:225
searchInDirectory(string $dir)
Definition: Finder.php:315
depth(string|int $level)
Adds tests for the directory depth.
Definition: Finder.php:144
exclude(array $directories)
Definition: Finder.php:101
The possible metadata types of the filesystem metadata.
date(string $date)
Adds tests for file dates.
Definition: Finder.php:167
append(iterable $iterator)
Appends an existing set of files/directories to the finder.
Definition: Finder.php:288
in(array $directories)
Definition: Finder.php:119
sortByName(bool $useNaturalSort=false)
Definition: Finder.php:256