Vous êtes connecté en tant que anonymous Se Deconnecter
Browse code

Application modulaire fonctionnelle !

Emmanuel ROY authored on 12/08/2019 15:10:25
Showing 1 changed files
1 1
deleted file mode 100644
... ...
@@ -1,811 +0,0 @@
1
-<?php
2
-
3
-/*
4
- * This file is part of the Symfony package.
5
- *
6
- * (c) Fabien Potencier <fabien@symfony.com>
7
- *
8
- * For the full copyright and license information, please view the LICENSE
9
- * file that was distributed with this source code.
10
- */
11
-
12
-namespace Symfony\Component\Finder;
13
-
14
-use Symfony\Component\Finder\Comparator\DateComparator;
15
-use Symfony\Component\Finder\Comparator\NumberComparator;
16
-use Symfony\Component\Finder\Exception\DirectoryNotFoundException;
17
-use Symfony\Component\Finder\Iterator\CustomFilterIterator;
18
-use Symfony\Component\Finder\Iterator\DateRangeFilterIterator;
19
-use Symfony\Component\Finder\Iterator\DepthRangeFilterIterator;
20
-use Symfony\Component\Finder\Iterator\ExcludeDirectoryFilterIterator;
21
-use Symfony\Component\Finder\Iterator\FilecontentFilterIterator;
22
-use Symfony\Component\Finder\Iterator\FilenameFilterIterator;
23
-use Symfony\Component\Finder\Iterator\SizeRangeFilterIterator;
24
-use Symfony\Component\Finder\Iterator\SortableIterator;
25
-
26
-/**
27
- * Finder allows to build rules to find files and directories.
28
- *
29
- * It is a thin wrapper around several specialized iterator classes.
30
- *
31
- * All rules may be invoked several times.
32
- *
33
- * All methods return the current Finder object to allow chaining:
34
- *
35
- *     $finder = Finder::create()->files()->name('*.php')->in(__DIR__);
36
- *
37
- * @author Fabien Potencier <fabien@symfony.com>
38
- */
39
-class Finder implements \IteratorAggregate, \Countable
40
-{
41
-    const IGNORE_VCS_FILES = 1;
42
-    const IGNORE_DOT_FILES = 2;
43
-    const IGNORE_VCS_IGNORED_FILES = 4;
44
-
45
-    private $mode = 0;
46
-    private $names = [];
47
-    private $notNames = [];
48
-    private $exclude = [];
49
-    private $filters = [];
50
-    private $depths = [];
51
-    private $sizes = [];
52
-    private $followLinks = false;
53
-    private $reverseSorting = false;
54
-    private $sort = false;
55
-    private $ignore = 0;
56
-    private $dirs = [];
57
-    private $dates = [];
58
-    private $iterators = [];
59
-    private $contains = [];
60
-    private $notContains = [];
61
-    private $paths = [];
62
-    private $notPaths = [];
63
-    private $ignoreUnreadableDirs = false;
64
-
65
-    private static $vcsPatterns = ['.svn', '_svn', 'CVS', '_darcs', '.arch-params', '.monotone', '.bzr', '.git', '.hg'];
66
-
67
-    public function __construct()
68
-    {
69
-        $this->ignore = static::IGNORE_VCS_FILES | static::IGNORE_DOT_FILES;
70
-    }
71
-
72
-    /**
73
-     * Creates a new Finder.
74
-     *
75
-     * @return static
76
-     */
77
-    public static function create()
78
-    {
79
-        return new static();
80
-    }
81
-
82
-    /**
83
-     * Restricts the matching to directories only.
84
-     *
85
-     * @return $this
86
-     */
87
-    public function directories()
88
-    {
89
-        $this->mode = Iterator\FileTypeFilterIterator::ONLY_DIRECTORIES;
90
-
91
-        return $this;
92
-    }
93
-
94
-    /**
95
-     * Restricts the matching to files only.
96
-     *
97
-     * @return $this
98
-     */
99
-    public function files()
100
-    {
101
-        $this->mode = Iterator\FileTypeFilterIterator::ONLY_FILES;
102
-
103
-        return $this;
104
-    }
105
-
106
-    /**
107
-     * Adds tests for the directory depth.
108
-     *
109
-     * Usage:
110
-     *
111
-     *     $finder->depth('> 1') // the Finder will start matching at level 1.
112
-     *     $finder->depth('< 3') // the Finder will descend at most 3 levels of directories below the starting point.
113
-     *     $finder->depth(['>= 1', '< 3'])
114
-     *
115
-     * @param string|int|string[]|int[] $levels The depth level expression or an array of depth levels
116
-     *
117
-     * @return $this
118
-     *
119
-     * @see DepthRangeFilterIterator
120
-     * @see NumberComparator
121
-     */
122
-    public function depth($levels)
123
-    {
124
-        foreach ((array) $levels as $level) {
125
-            $this->depths[] = new Comparator\NumberComparator($level);
126
-        }
127
-
128
-        return $this;
129
-    }
130
-
131
-    /**
132
-     * Adds tests for file dates (last modified).
133
-     *
134
-     * The date must be something that strtotime() is able to parse:
135
-     *
136
-     *     $finder->date('since yesterday');
137
-     *     $finder->date('until 2 days ago');
138
-     *     $finder->date('> now - 2 hours');
139
-     *     $finder->date('>= 2005-10-15');
140
-     *     $finder->date(['>= 2005-10-15', '<= 2006-05-27']);
141
-     *
142
-     * @param string|string[] $dates A date range string or an array of date ranges
143
-     *
144
-     * @return $this
145
-     *
146
-     * @see strtotime
147
-     * @see DateRangeFilterIterator
148
-     * @see DateComparator
149
-     */
150
-    public function date($dates)
151
-    {
152
-        foreach ((array) $dates as $date) {
153
-            $this->dates[] = new Comparator\DateComparator($date);
154
-        }
155
-
156
-        return $this;
157
-    }
158
-
159
-    /**
160
-     * Adds rules that files must match.
161
-     *
162
-     * You can use patterns (delimited with / sign), globs or simple strings.
163
-     *
164
-     *     $finder->name('*.php')
165
-     *     $finder->name('/\.php$/') // same as above
166
-     *     $finder->name('test.php')
167
-     *     $finder->name(['test.py', 'test.php'])
168
-     *
169
-     * @param string|string[] $patterns A pattern (a regexp, a glob, or a string) or an array of patterns
170
-     *
171
-     * @return $this
172
-     *
173
-     * @see FilenameFilterIterator
174
-     */
175
-    public function name($patterns)
176
-    {
177
-        $this->names = array_merge($this->names, (array) $patterns);
178
-
179
-        return $this;
180
-    }
181
-
182
-    /**
183
-     * Adds rules that files must not match.
184
-     *
185
-     * @param string|string[] $patterns A pattern (a regexp, a glob, or a string) or an array of patterns
186
-     *
187
-     * @return $this
188
-     *
189
-     * @see FilenameFilterIterator
190
-     */
191
-    public function notName($patterns)
192
-    {
193
-        $this->notNames = array_merge($this->notNames, (array) $patterns);
194
-
195
-        return $this;
196
-    }
197
-
198
-    /**
199
-     * Adds tests that file contents must match.
200
-     *
201
-     * Strings or PCRE patterns can be used:
202
-     *
203
-     *     $finder->contains('Lorem ipsum')
204
-     *     $finder->contains('/Lorem ipsum/i')
205
-     *     $finder->contains(['dolor', '/ipsum/i'])
206
-     *
207
-     * @param string|string[] $patterns A pattern (string or regexp) or an array of patterns
208
-     *
209
-     * @return $this
210
-     *
211
-     * @see FilecontentFilterIterator
212
-     */
213
-    public function contains($patterns)
214
-    {
215
-        $this->contains = array_merge($this->contains, (array) $patterns);
216
-
217
-        return $this;
218
-    }
219
-
220
-    /**
221
-     * Adds tests that file contents must not match.
222
-     *
223
-     * Strings or PCRE patterns can be used:
224
-     *
225
-     *     $finder->notContains('Lorem ipsum')
226
-     *     $finder->notContains('/Lorem ipsum/i')
227
-     *     $finder->notContains(['lorem', '/dolor/i'])
228
-     *
229
-     * @param string|string[] $patterns A pattern (string or regexp) or an array of patterns
230
-     *
231
-     * @return $this
232
-     *
233
-     * @see FilecontentFilterIterator
234
-     */
235
-    public function notContains($patterns)
236
-    {
237
-        $this->notContains = array_merge($this->notContains, (array) $patterns);
238
-
239
-        return $this;
240
-    }
241
-
242
-    /**
243
-     * Adds rules that filenames must match.
244
-     *
245
-     * You can use patterns (delimited with / sign) or simple strings.
246
-     *
247
-     *     $finder->path('some/special/dir')
248
-     *     $finder->path('/some\/special\/dir/') // same as above
249
-     *     $finder->path(['some dir', 'another/dir'])
250
-     *
251
-     * Use only / as dirname separator.
252
-     *
253
-     * @param string|string[] $patterns A pattern (a regexp or a string) or an array of patterns
254
-     *
255
-     * @return $this
256
-     *
257
-     * @see FilenameFilterIterator
258
-     */
259
-    public function path($patterns)
260
-    {
261
-        $this->paths = array_merge($this->paths, (array) $patterns);
262
-
263
-        return $this;
264
-    }
265
-
266
-    /**
267
-     * Adds rules that filenames must not match.
268
-     *
269
-     * You can use patterns (delimited with / sign) or simple strings.
270
-     *
271
-     *     $finder->notPath('some/special/dir')
272
-     *     $finder->notPath('/some\/special\/dir/') // same as above
273
-     *     $finder->notPath(['some/file.txt', 'another/file.log'])
274
-     *
275
-     * Use only / as dirname separator.
276
-     *
277
-     * @param string|string[] $patterns A pattern (a regexp or a string) or an array of patterns
278
-     *
279
-     * @return $this
280
-     *
281
-     * @see FilenameFilterIterator
282
-     */
283
-    public function notPath($patterns)
284
-    {
285
-        $this->notPaths = array_merge($this->notPaths, (array) $patterns);
286
-
287
-        return $this;
288
-    }
289
-
290
-    /**
291
-     * Adds tests for file sizes.
292
-     *
293
-     *     $finder->size('> 10K');
294
-     *     $finder->size('<= 1Ki');
295
-     *     $finder->size(4);
296
-     *     $finder->size(['> 10K', '< 20K'])
297
-     *
298
-     * @param string|int|string[]|int[] $sizes A size range string or an integer or an array of size ranges
299
-     *
300
-     * @return $this
301
-     *
302
-     * @see SizeRangeFilterIterator
303
-     * @see NumberComparator
304
-     */
305
-    public function size($sizes)
306
-    {
307
-        foreach ((array) $sizes as $size) {
308
-            $this->sizes[] = new Comparator\NumberComparator($size);
309
-        }
310
-
311
-        return $this;
312
-    }
313
-
314
-    /**
315
-     * Excludes directories.
316
-     *
317
-     * Directories passed as argument must be relative to the ones defined with the `in()` method. For example:
318
-     *
319
-     *     $finder->in(__DIR__)->exclude('ruby');
320
-     *
321
-     * @param string|array $dirs A directory path or an array of directories
322
-     *
323
-     * @return $this
324
-     *
325
-     * @see ExcludeDirectoryFilterIterator
326
-     */
327
-    public function exclude($dirs)
328
-    {
329
-        $this->exclude = array_merge($this->exclude, (array) $dirs);
330
-
331
-        return $this;
332
-    }
333
-
334
-    /**
335
-     * Excludes "hidden" directories and files (starting with a dot).
336
-     *
337
-     * This option is enabled by default.
338
-     *
339
-     * @param bool $ignoreDotFiles Whether to exclude "hidden" files or not
340
-     *
341
-     * @return $this
342
-     *
343
-     * @see ExcludeDirectoryFilterIterator
344
-     */
345
-    public function ignoreDotFiles($ignoreDotFiles)
346
-    {
347
-        if ($ignoreDotFiles) {
348
-            $this->ignore |= static::IGNORE_DOT_FILES;
349
-        } else {
350
-            $this->ignore &= ~static::IGNORE_DOT_FILES;
351
-        }
352
-
353
-        return $this;
354
-    }
355
-
356
-    /**
357
-     * Forces the finder to ignore version control directories.
358
-     *
359
-     * This option is enabled by default.
360
-     *
361
-     * @param bool $ignoreVCS Whether to exclude VCS files or not
362
-     *
363
-     * @return $this
364
-     *
365
-     * @see ExcludeDirectoryFilterIterator
366
-     */
367
-    public function ignoreVCS($ignoreVCS)
368
-    {
369
-        if ($ignoreVCS) {
370
-            $this->ignore |= static::IGNORE_VCS_FILES;
371
-        } else {
372
-            $this->ignore &= ~static::IGNORE_VCS_FILES;
373
-        }
374
-
375
-        return $this;
376
-    }
377
-
378
-    /**
379
-     * Forces Finder to obey .gitignore and ignore files based on rules listed there.
380
-     *
381
-     * This option is disabled by default.
382
-     *
383
-     * @return $this
384
-     */
385
-    public function ignoreVCSIgnored(bool $ignoreVCSIgnored)
386
-    {
387
-        if ($ignoreVCSIgnored) {
388
-            $this->ignore |= static::IGNORE_VCS_IGNORED_FILES;
389
-        } else {
390
-            $this->ignore &= ~static::IGNORE_VCS_IGNORED_FILES;
391
-        }
392
-
393
-        return $this;
394
-    }
395
-
396
-    /**
397
-     * Adds VCS patterns.
398
-     *
399
-     * @see ignoreVCS()
400
-     *
401
-     * @param string|string[] $pattern VCS patterns to ignore
402
-     */
403
-    public static function addVCSPattern($pattern)
404
-    {
405
-        foreach ((array) $pattern as $p) {
406
-            self::$vcsPatterns[] = $p;
407
-        }
408
-
409
-        self::$vcsPatterns = array_unique(self::$vcsPatterns);
410
-    }
411
-
412
-    /**
413
-     * Sorts files and directories by an anonymous function.
414
-     *
415
-     * The anonymous function receives two \SplFileInfo instances to compare.
416
-     *
417
-     * This can be slow as all the matching files and directories must be retrieved for comparison.
418
-     *
419
-     * @return $this
420
-     *
421
-     * @see SortableIterator
422
-     */
423
-    public function sort(\Closure $closure)
424
-    {
425
-        $this->sort = $closure;
426
-
427
-        return $this;
428
-    }
429
-
430
-    /**
431
-     * Sorts files and directories by name.
432
-     *
433
-     * This can be slow as all the matching files and directories must be retrieved for comparison.
434
-     *
435
-     * @param bool $useNaturalSort Whether to use natural sort or not, disabled by default
436
-     *
437
-     * @return $this
438
-     *
439
-     * @see SortableIterator
440
-     */
441
-    public function sortByName(/* bool $useNaturalSort = false */)
442
-    {
443
-        if (\func_num_args() < 1 && __CLASS__ !== \get_class($this) && __CLASS__ !== (new \ReflectionMethod($this, __FUNCTION__))->getDeclaringClass()->getName() && !$this instanceof \PHPUnit\Framework\MockObject\MockObject && !$this instanceof \Prophecy\Prophecy\ProphecySubjectInterface) {
444
-            @trigger_error(sprintf('The "%s()" method will have a new "bool $useNaturalSort = false" argument in version 5.0, not defining it is deprecated since Symfony 4.2.', __METHOD__), E_USER_DEPRECATED);
445
-        }
446
-        $useNaturalSort = 0 < \func_num_args() && func_get_arg(0);
447
-
448
-        $this->sort = $useNaturalSort ? Iterator\SortableIterator::SORT_BY_NAME_NATURAL : Iterator\SortableIterator::SORT_BY_NAME;
449
-
450
-        return $this;
451
-    }
452
-
453
-    /**
454
-     * Sorts files and directories by type (directories before files), then by name.
455
-     *
456
-     * This can be slow as all the matching files and directories must be retrieved for comparison.
457
-     *
458
-     * @return $this
459
-     *
460
-     * @see SortableIterator
461
-     */
462
-    public function sortByType()
463
-    {
464
-        $this->sort = Iterator\SortableIterator::SORT_BY_TYPE;
465
-
466
-        return $this;
467
-    }
468
-
469
-    /**
470
-     * Sorts files and directories by the last accessed time.
471
-     *
472
-     * This is the time that the file was last accessed, read or written to.
473
-     *
474
-     * This can be slow as all the matching files and directories must be retrieved for comparison.
475
-     *
476
-     * @return $this
477
-     *
478
-     * @see SortableIterator
479
-     */
480
-    public function sortByAccessedTime()
481
-    {
482
-        $this->sort = Iterator\SortableIterator::SORT_BY_ACCESSED_TIME;
483
-
484
-        return $this;
485
-    }
486
-
487
-    /**
488
-     * Reverses the sorting.
489
-     *
490
-     * @return $this
491
-     */
492
-    public function reverseSorting()
493
-    {
494
-        $this->reverseSorting = true;
495
-
496
-        return $this;
497
-    }
498
-
499
-    /**
500
-     * Sorts files and directories by the last inode changed time.
501
-     *
502
-     * This is the time that the inode information was last modified (permissions, owner, group or other metadata).
503
-     *
504
-     * On Windows, since inode is not available, changed time is actually the file creation time.
505
-     *
506
-     * This can be slow as all the matching files and directories must be retrieved for comparison.
507
-     *
508
-     * @return $this
509
-     *
510
-     * @see SortableIterator
511
-     */
512
-    public function sortByChangedTime()
513
-    {
514
-        $this->sort = Iterator\SortableIterator::SORT_BY_CHANGED_TIME;
515
-
516
-        return $this;
517
-    }
518
-
519
-    /**
520
-     * Sorts files and directories by the last modified time.
521
-     *
522
-     * This is the last time the actual contents of the file were last modified.
523
-     *
524
-     * This can be slow as all the matching files and directories must be retrieved for comparison.
525
-     *
526
-     * @return $this
527
-     *
528
-     * @see SortableIterator
529
-     */
530
-    public function sortByModifiedTime()
531
-    {
532
-        $this->sort = Iterator\SortableIterator::SORT_BY_MODIFIED_TIME;
533
-
534
-        return $this;
535
-    }
536
-
537
-    /**
538
-     * Filters the iterator with an anonymous function.
539
-     *
540
-     * The anonymous function receives a \SplFileInfo and must return false
541
-     * to remove files.
542
-     *
543
-     * @return $this
544
-     *
545
-     * @see CustomFilterIterator
546
-     */
547
-    public function filter(\Closure $closure)
548
-    {
549
-        $this->filters[] = $closure;
550
-
551
-        return $this;
552
-    }
553
-
554
-    /**
555
-     * Forces the following of symlinks.
556
-     *
557
-     * @return $this
558
-     */
559
-    public function followLinks()
560
-    {
561
-        $this->followLinks = true;
562
-
563
-        return $this;
564
-    }
565
-
566
-    /**
567
-     * Tells finder to ignore unreadable directories.
568
-     *
569
-     * By default, scanning unreadable directories content throws an AccessDeniedException.
570
-     *
571
-     * @param bool $ignore
572
-     *
573
-     * @return $this
574
-     */
575
-    public function ignoreUnreadableDirs($ignore = true)
576
-    {
577
-        $this->ignoreUnreadableDirs = (bool) $ignore;
578
-
579
-        return $this;
580
-    }
581
-
582
-    /**
583
-     * Searches files and directories which match defined rules.
584
-     *
585
-     * @param string|string[] $dirs A directory path or an array of directories
586
-     *
587
-     * @return $this
588
-     *
589
-     * @throws DirectoryNotFoundException if one of the directories does not exist
590
-     */
591
-    public function in($dirs)
592
-    {
593
-        $resolvedDirs = [];
594
-
595
-        foreach ((array) $dirs as $dir) {
596
-            if (is_dir($dir)) {
597
-                $resolvedDirs[] = $this->normalizeDir($dir);
598
-            } elseif ($glob = glob($dir, (\defined('GLOB_BRACE') ? GLOB_BRACE : 0) | GLOB_ONLYDIR)) {
599
-                $resolvedDirs = array_merge($resolvedDirs, array_map([$this, 'normalizeDir'], $glob));
600
-            } else {
601
-                throw new DirectoryNotFoundException(sprintf('The "%s" directory does not exist.', $dir));
602
-            }
603
-        }
604
-
605
-        $this->dirs = array_merge($this->dirs, $resolvedDirs);
606
-
607
-        return $this;
608
-    }
609
-
610
-    /**
611
-     * Returns an Iterator for the current Finder configuration.
612
-     *
613
-     * This method implements the IteratorAggregate interface.
614
-     *
615
-     * @return \Iterator|SplFileInfo[] An iterator
616
-     *
617
-     * @throws \LogicException if the in() method has not been called
618
-     */
619
-    public function getIterator()
620
-    {
621
-        if (0 === \count($this->dirs) && 0 === \count($this->iterators)) {
622
-            throw new \LogicException('You must call one of in() or append() methods before iterating over a Finder.');
623
-        }
624
-
625
-        if (1 === \count($this->dirs) && 0 === \count($this->iterators)) {
626
-            return $this->searchInDirectory($this->dirs[0]);
627
-        }
628
-
629
-        $iterator = new \AppendIterator();
630
-        foreach ($this->dirs as $dir) {
631
-            $iterator->append($this->searchInDirectory($dir));
632
-        }
633
-
634
-        foreach ($this->iterators as $it) {
635
-            $iterator->append($it);
636
-        }
637
-
638
-        return $iterator;
639
-    }
640
-
641
-    /**
642
-     * Appends an existing set of files/directories to the finder.
643
-     *
644
-     * The set can be another Finder, an Iterator, an IteratorAggregate, or even a plain array.
645
-     *
646
-     * @param iterable $iterator
647
-     *
648
-     * @return $this
649
-     *
650
-     * @throws \InvalidArgumentException when the given argument is not iterable
651
-     */
652
-    public function append($iterator)
653
-    {
654
-        if ($iterator instanceof \IteratorAggregate) {
655
-            $this->iterators[] = $iterator->getIterator();
656
-        } elseif ($iterator instanceof \Iterator) {
657
-            $this->iterators[] = $iterator;
658
-        } elseif ($iterator instanceof \Traversable || \is_array($iterator)) {
659
-            $it = new \ArrayIterator();
660
-            foreach ($iterator as $file) {
661
-                $it->append($file instanceof \SplFileInfo ? $file : new \SplFileInfo($file));
662
-            }
663
-            $this->iterators[] = $it;
664
-        } else {
665
-            throw new \InvalidArgumentException('Finder::append() method wrong argument type.');
666
-        }
667
-
668
-        return $this;
669
-    }
670
-
671
-    /**
672
-     * Check if the any results were found.
673
-     *
674
-     * @return bool
675
-     */
676
-    public function hasResults()
677
-    {
678
-        foreach ($this->getIterator() as $_) {
679
-            return true;
680
-        }
681
-
682
-        return false;
683
-    }
684
-
685
-    /**
686
-     * Counts all the results collected by the iterators.
687
-     *
688
-     * @return int
689
-     */
690
-    public function count()
691
-    {
692
-        return iterator_count($this->getIterator());
693
-    }
694
-
695
-    private function searchInDirectory(string $dir): \Iterator
696
-    {
697
-        $exclude = $this->exclude;
698
-        $notPaths = $this->notPaths;
699
-
700
-        if (static::IGNORE_VCS_FILES === (static::IGNORE_VCS_FILES & $this->ignore)) {
701
-            $exclude = array_merge($exclude, self::$vcsPatterns);
702
-        }
703
-
704
-        if (static::IGNORE_DOT_FILES === (static::IGNORE_DOT_FILES & $this->ignore)) {
705
-            $notPaths[] = '#(^|/)\..+(/|$)#';
706
-        }
707
-
708
-        if (static::IGNORE_VCS_IGNORED_FILES === (static::IGNORE_VCS_IGNORED_FILES & $this->ignore)) {
709
-            $gitignoreFilePath = sprintf('%s/.gitignore', $dir);
710
-            if (!is_readable($gitignoreFilePath)) {
711
-                throw new \RuntimeException(sprintf('The "ignoreVCSIgnored" option cannot be used by the Finder as the "%s" file is not readable.', $gitignoreFilePath));
712
-            }
713
-            $notPaths = array_merge($notPaths, [Gitignore::toRegex(file_get_contents($gitignoreFilePath))]);
714
-        }
715
-
716
-        $minDepth = 0;
717
-        $maxDepth = PHP_INT_MAX;
718
-
719
-        foreach ($this->depths as $comparator) {
720
-            switch ($comparator->getOperator()) {
721
-                case '>':
722
-                    $minDepth = $comparator->getTarget() + 1;
723
-                    break;
724
-                case '>=':
725
-                    $minDepth = $comparator->getTarget();
726
-                    break;
727
-                case '<':
728
-                    $maxDepth = $comparator->getTarget() - 1;
729
-                    break;
730
-                case '<=':
731
-                    $maxDepth = $comparator->getTarget();
732
-                    break;
733
-                default:
734
-                    $minDepth = $maxDepth = $comparator->getTarget();
735
-            }
736
-        }
737
-
738
-        $flags = \RecursiveDirectoryIterator::SKIP_DOTS;
739
-
740
-        if ($this->followLinks) {
741
-            $flags |= \RecursiveDirectoryIterator::FOLLOW_SYMLINKS;
742
-        }
743
-
744
-        $iterator = new Iterator\RecursiveDirectoryIterator($dir, $flags, $this->ignoreUnreadableDirs);
745
-
746
-        if ($exclude) {
747
-            $iterator = new Iterator\ExcludeDirectoryFilterIterator($iterator, $exclude);
748
-        }
749
-
750
-        $iterator = new \RecursiveIteratorIterator($iterator, \RecursiveIteratorIterator::SELF_FIRST);
751
-
752
-        if ($minDepth > 0 || $maxDepth < PHP_INT_MAX) {
753
-            $iterator = new Iterator\DepthRangeFilterIterator($iterator, $minDepth, $maxDepth);
754
-        }
755
-
756
-        if ($this->mode) {
757
-            $iterator = new Iterator\FileTypeFilterIterator($iterator, $this->mode);
758
-        }
759
-
760
-        if ($this->names || $this->notNames) {
761
-            $iterator = new Iterator\FilenameFilterIterator($iterator, $this->names, $this->notNames);
762
-        }
763
-
764
-        if ($this->contains || $this->notContains) {
765
-            $iterator = new Iterator\FilecontentFilterIterator($iterator, $this->contains, $this->notContains);
766
-        }
767
-
768
-        if ($this->sizes) {
769
-            $iterator = new Iterator\SizeRangeFilterIterator($iterator, $this->sizes);
770
-        }
771
-
772
-        if ($this->dates) {
773
-            $iterator = new Iterator\DateRangeFilterIterator($iterator, $this->dates);
774
-        }
775
-
776
-        if ($this->filters) {
777
-            $iterator = new Iterator\CustomFilterIterator($iterator, $this->filters);
778
-        }
779
-
780
-        if ($this->paths || $notPaths) {
781
-            $iterator = new Iterator\PathFilterIterator($iterator, $this->paths, $notPaths);
782
-        }
783
-
784
-        if ($this->sort || $this->reverseSorting) {
785
-            $iteratorAggregate = new Iterator\SortableIterator($iterator, $this->sort, $this->reverseSorting);
786
-            $iterator = $iteratorAggregate->getIterator();
787
-        }
788
-
789
-        return $iterator;
790
-    }
791
-
792
-    /**
793
-     * Normalizes given directory names by removing trailing slashes.
794
-     *
795
-     * Excluding: (s)ftp:// wrapper
796
-     *
797
-     * @param string $dir
798
-     *
799
-     * @return string
800
-     */
801
-    private function normalizeDir($dir)
802
-    {
803
-        $dir = rtrim($dir, '/'.\DIRECTORY_SEPARATOR);
804
-
805
-        if (preg_match('#^s?ftp://#', $dir)) {
806
-            $dir .= '/';
807
-        }
808
-
809
-        return $dir;
810
-    }
811
-}
Browse code

initial commit

Emmanuel ROY authored on 09/08/2019 08:39:02
Showing 1 changed files
1 1
new file mode 100644
... ...
@@ -0,0 +1,811 @@
1
+<?php
2
+
3
+/*
4
+ * This file is part of the Symfony package.
5
+ *
6
+ * (c) Fabien Potencier <fabien@symfony.com>
7
+ *
8
+ * For the full copyright and license information, please view the LICENSE
9
+ * file that was distributed with this source code.
10
+ */
11
+
12
+namespace Symfony\Component\Finder;
13
+
14
+use Symfony\Component\Finder\Comparator\DateComparator;
15
+use Symfony\Component\Finder\Comparator\NumberComparator;
16
+use Symfony\Component\Finder\Exception\DirectoryNotFoundException;
17
+use Symfony\Component\Finder\Iterator\CustomFilterIterator;
18
+use Symfony\Component\Finder\Iterator\DateRangeFilterIterator;
19
+use Symfony\Component\Finder\Iterator\DepthRangeFilterIterator;
20
+use Symfony\Component\Finder\Iterator\ExcludeDirectoryFilterIterator;
21
+use Symfony\Component\Finder\Iterator\FilecontentFilterIterator;
22
+use Symfony\Component\Finder\Iterator\FilenameFilterIterator;
23
+use Symfony\Component\Finder\Iterator\SizeRangeFilterIterator;
24
+use Symfony\Component\Finder\Iterator\SortableIterator;
25
+
26
+/**
27
+ * Finder allows to build rules to find files and directories.
28
+ *
29
+ * It is a thin wrapper around several specialized iterator classes.
30
+ *
31
+ * All rules may be invoked several times.
32
+ *
33
+ * All methods return the current Finder object to allow chaining:
34
+ *
35
+ *     $finder = Finder::create()->files()->name('*.php')->in(__DIR__);
36
+ *
37
+ * @author Fabien Potencier <fabien@symfony.com>
38
+ */
39
+class Finder implements \IteratorAggregate, \Countable
40
+{
41
+    const IGNORE_VCS_FILES = 1;
42
+    const IGNORE_DOT_FILES = 2;
43
+    const IGNORE_VCS_IGNORED_FILES = 4;
44
+
45
+    private $mode = 0;
46
+    private $names = [];
47
+    private $notNames = [];
48
+    private $exclude = [];
49
+    private $filters = [];
50
+    private $depths = [];
51
+    private $sizes = [];
52
+    private $followLinks = false;
53
+    private $reverseSorting = false;
54
+    private $sort = false;
55
+    private $ignore = 0;
56
+    private $dirs = [];
57
+    private $dates = [];
58
+    private $iterators = [];
59
+    private $contains = [];
60
+    private $notContains = [];
61
+    private $paths = [];
62
+    private $notPaths = [];
63
+    private $ignoreUnreadableDirs = false;
64
+
65
+    private static $vcsPatterns = ['.svn', '_svn', 'CVS', '_darcs', '.arch-params', '.monotone', '.bzr', '.git', '.hg'];
66
+
67
+    public function __construct()
68
+    {
69
+        $this->ignore = static::IGNORE_VCS_FILES | static::IGNORE_DOT_FILES;
70
+    }
71
+
72
+    /**
73
+     * Creates a new Finder.
74
+     *
75
+     * @return static
76
+     */
77
+    public static function create()
78
+    {
79
+        return new static();
80
+    }
81
+
82
+    /**
83
+     * Restricts the matching to directories only.
84
+     *
85
+     * @return $this
86
+     */
87
+    public function directories()
88
+    {
89
+        $this->mode = Iterator\FileTypeFilterIterator::ONLY_DIRECTORIES;
90
+
91
+        return $this;
92
+    }
93
+
94
+    /**
95
+     * Restricts the matching to files only.
96
+     *
97
+     * @return $this
98
+     */
99
+    public function files()
100
+    {
101
+        $this->mode = Iterator\FileTypeFilterIterator::ONLY_FILES;
102
+
103
+        return $this;
104
+    }
105
+
106
+    /**
107
+     * Adds tests for the directory depth.
108
+     *
109
+     * Usage:
110
+     *
111
+     *     $finder->depth('> 1') // the Finder will start matching at level 1.
112
+     *     $finder->depth('< 3') // the Finder will descend at most 3 levels of directories below the starting point.
113
+     *     $finder->depth(['>= 1', '< 3'])
114
+     *
115
+     * @param string|int|string[]|int[] $levels The depth level expression or an array of depth levels
116
+     *
117
+     * @return $this
118
+     *
119
+     * @see DepthRangeFilterIterator
120
+     * @see NumberComparator
121
+     */
122
+    public function depth($levels)
123
+    {
124
+        foreach ((array) $levels as $level) {
125
+            $this->depths[] = new Comparator\NumberComparator($level);
126
+        }
127
+
128
+        return $this;
129
+    }
130
+
131
+    /**
132
+     * Adds tests for file dates (last modified).
133
+     *
134
+     * The date must be something that strtotime() is able to parse:
135
+     *
136
+     *     $finder->date('since yesterday');
137
+     *     $finder->date('until 2 days ago');
138
+     *     $finder->date('> now - 2 hours');
139
+     *     $finder->date('>= 2005-10-15');
140
+     *     $finder->date(['>= 2005-10-15', '<= 2006-05-27']);
141
+     *
142
+     * @param string|string[] $dates A date range string or an array of date ranges
143
+     *
144
+     * @return $this
145
+     *
146
+     * @see strtotime
147
+     * @see DateRangeFilterIterator
148
+     * @see DateComparator
149
+     */
150
+    public function date($dates)
151
+    {
152
+        foreach ((array) $dates as $date) {
153
+            $this->dates[] = new Comparator\DateComparator($date);
154
+        }
155
+
156
+        return $this;
157
+    }
158
+
159
+    /**
160
+     * Adds rules that files must match.
161
+     *
162
+     * You can use patterns (delimited with / sign), globs or simple strings.
163
+     *
164
+     *     $finder->name('*.php')
165
+     *     $finder->name('/\.php$/') // same as above
166
+     *     $finder->name('test.php')
167
+     *     $finder->name(['test.py', 'test.php'])
168
+     *
169
+     * @param string|string[] $patterns A pattern (a regexp, a glob, or a string) or an array of patterns
170
+     *
171
+     * @return $this
172
+     *
173
+     * @see FilenameFilterIterator
174
+     */
175
+    public function name($patterns)
176
+    {
177
+        $this->names = array_merge($this->names, (array) $patterns);
178
+
179
+        return $this;
180
+    }
181
+
182
+    /**
183
+     * Adds rules that files must not match.
184
+     *
185
+     * @param string|string[] $patterns A pattern (a regexp, a glob, or a string) or an array of patterns
186
+     *
187
+     * @return $this
188
+     *
189
+     * @see FilenameFilterIterator
190
+     */
191
+    public function notName($patterns)
192
+    {
193
+        $this->notNames = array_merge($this->notNames, (array) $patterns);
194
+
195
+        return $this;
196
+    }
197
+
198
+    /**
199
+     * Adds tests that file contents must match.
200
+     *
201
+     * Strings or PCRE patterns can be used:
202
+     *
203
+     *     $finder->contains('Lorem ipsum')
204
+     *     $finder->contains('/Lorem ipsum/i')
205
+     *     $finder->contains(['dolor', '/ipsum/i'])
206
+     *
207
+     * @param string|string[] $patterns A pattern (string or regexp) or an array of patterns
208
+     *
209
+     * @return $this
210
+     *
211
+     * @see FilecontentFilterIterator
212
+     */
213
+    public function contains($patterns)
214
+    {
215
+        $this->contains = array_merge($this->contains, (array) $patterns);
216
+
217
+        return $this;
218
+    }
219
+
220
+    /**
221
+     * Adds tests that file contents must not match.
222
+     *
223
+     * Strings or PCRE patterns can be used:
224
+     *
225
+     *     $finder->notContains('Lorem ipsum')
226
+     *     $finder->notContains('/Lorem ipsum/i')
227
+     *     $finder->notContains(['lorem', '/dolor/i'])
228
+     *
229
+     * @param string|string[] $patterns A pattern (string or regexp) or an array of patterns
230
+     *
231
+     * @return $this
232
+     *
233
+     * @see FilecontentFilterIterator
234
+     */
235
+    public function notContains($patterns)
236
+    {
237
+        $this->notContains = array_merge($this->notContains, (array) $patterns);
238
+
239
+        return $this;
240
+    }
241
+
242
+    /**
243
+     * Adds rules that filenames must match.
244
+     *
245
+     * You can use patterns (delimited with / sign) or simple strings.
246
+     *
247
+     *     $finder->path('some/special/dir')
248
+     *     $finder->path('/some\/special\/dir/') // same as above
249
+     *     $finder->path(['some dir', 'another/dir'])
250
+     *
251
+     * Use only / as dirname separator.
252
+     *
253
+     * @param string|string[] $patterns A pattern (a regexp or a string) or an array of patterns
254
+     *
255
+     * @return $this
256
+     *
257
+     * @see FilenameFilterIterator
258
+     */
259
+    public function path($patterns)
260
+    {
261
+        $this->paths = array_merge($this->paths, (array) $patterns);
262
+
263
+        return $this;
264
+    }
265
+
266
+    /**
267
+     * Adds rules that filenames must not match.
268
+     *
269
+     * You can use patterns (delimited with / sign) or simple strings.
270
+     *
271
+     *     $finder->notPath('some/special/dir')
272
+     *     $finder->notPath('/some\/special\/dir/') // same as above
273
+     *     $finder->notPath(['some/file.txt', 'another/file.log'])
274
+     *
275
+     * Use only / as dirname separator.
276
+     *
277
+     * @param string|string[] $patterns A pattern (a regexp or a string) or an array of patterns
278
+     *
279
+     * @return $this
280
+     *
281
+     * @see FilenameFilterIterator
282
+     */
283
+    public function notPath($patterns)
284
+    {
285
+        $this->notPaths = array_merge($this->notPaths, (array) $patterns);
286
+
287
+        return $this;
288
+    }
289
+
290
+    /**
291
+     * Adds tests for file sizes.
292
+     *
293
+     *     $finder->size('> 10K');
294
+     *     $finder->size('<= 1Ki');
295
+     *     $finder->size(4);
296
+     *     $finder->size(['> 10K', '< 20K'])
297
+     *
298
+     * @param string|int|string[]|int[] $sizes A size range string or an integer or an array of size ranges
299
+     *
300
+     * @return $this
301
+     *
302
+     * @see SizeRangeFilterIterator
303
+     * @see NumberComparator
304
+     */
305
+    public function size($sizes)
306
+    {
307
+        foreach ((array) $sizes as $size) {
308
+            $this->sizes[] = new Comparator\NumberComparator($size);
309
+        }
310
+
311
+        return $this;
312
+    }
313
+
314
+    /**
315
+     * Excludes directories.
316
+     *
317
+     * Directories passed as argument must be relative to the ones defined with the `in()` method. For example:
318
+     *
319
+     *     $finder->in(__DIR__)->exclude('ruby');
320
+     *
321
+     * @param string|array $dirs A directory path or an array of directories
322
+     *
323
+     * @return $this
324
+     *
325
+     * @see ExcludeDirectoryFilterIterator
326
+     */
327
+    public function exclude($dirs)
328
+    {
329
+        $this->exclude = array_merge($this->exclude, (array) $dirs);
330
+
331
+        return $this;
332
+    }
333
+
334
+    /**
335
+     * Excludes "hidden" directories and files (starting with a dot).
336
+     *
337
+     * This option is enabled by default.
338
+     *
339
+     * @param bool $ignoreDotFiles Whether to exclude "hidden" files or not
340
+     *
341
+     * @return $this
342
+     *
343
+     * @see ExcludeDirectoryFilterIterator
344
+     */
345
+    public function ignoreDotFiles($ignoreDotFiles)
346
+    {
347
+        if ($ignoreDotFiles) {
348
+            $this->ignore |= static::IGNORE_DOT_FILES;
349
+        } else {
350
+            $this->ignore &= ~static::IGNORE_DOT_FILES;
351
+        }
352
+
353
+        return $this;
354
+    }
355
+
356
+    /**
357
+     * Forces the finder to ignore version control directories.
358
+     *
359
+     * This option is enabled by default.
360
+     *
361
+     * @param bool $ignoreVCS Whether to exclude VCS files or not
362
+     *
363
+     * @return $this
364
+     *
365
+     * @see ExcludeDirectoryFilterIterator
366
+     */
367
+    public function ignoreVCS($ignoreVCS)
368
+    {
369
+        if ($ignoreVCS) {
370
+            $this->ignore |= static::IGNORE_VCS_FILES;
371
+        } else {
372
+            $this->ignore &= ~static::IGNORE_VCS_FILES;
373
+        }
374
+
375
+        return $this;
376
+    }
377
+
378
+    /**
379
+     * Forces Finder to obey .gitignore and ignore files based on rules listed there.
380
+     *
381
+     * This option is disabled by default.
382
+     *
383
+     * @return $this
384
+     */
385
+    public function ignoreVCSIgnored(bool $ignoreVCSIgnored)
386
+    {
387
+        if ($ignoreVCSIgnored) {
388
+            $this->ignore |= static::IGNORE_VCS_IGNORED_FILES;
389
+        } else {
390
+            $this->ignore &= ~static::IGNORE_VCS_IGNORED_FILES;
391
+        }
392
+
393
+        return $this;
394
+    }
395
+
396
+    /**
397
+     * Adds VCS patterns.
398
+     *
399
+     * @see ignoreVCS()
400
+     *
401
+     * @param string|string[] $pattern VCS patterns to ignore
402
+     */
403
+    public static function addVCSPattern($pattern)
404
+    {
405
+        foreach ((array) $pattern as $p) {
406
+            self::$vcsPatterns[] = $p;
407
+        }
408
+
409
+        self::$vcsPatterns = array_unique(self::$vcsPatterns);
410
+    }
411
+
412
+    /**
413
+     * Sorts files and directories by an anonymous function.
414
+     *
415
+     * The anonymous function receives two \SplFileInfo instances to compare.
416
+     *
417
+     * This can be slow as all the matching files and directories must be retrieved for comparison.
418
+     *
419
+     * @return $this
420
+     *
421
+     * @see SortableIterator
422
+     */
423
+    public function sort(\Closure $closure)
424
+    {
425
+        $this->sort = $closure;
426
+
427
+        return $this;
428
+    }
429
+
430
+    /**
431
+     * Sorts files and directories by name.
432
+     *
433
+     * This can be slow as all the matching files and directories must be retrieved for comparison.
434
+     *
435
+     * @param bool $useNaturalSort Whether to use natural sort or not, disabled by default
436
+     *
437
+     * @return $this
438
+     *
439
+     * @see SortableIterator
440
+     */
441
+    public function sortByName(/* bool $useNaturalSort = false */)
442
+    {
443
+        if (\func_num_args() < 1 && __CLASS__ !== \get_class($this) && __CLASS__ !== (new \ReflectionMethod($this, __FUNCTION__))->getDeclaringClass()->getName() && !$this instanceof \PHPUnit\Framework\MockObject\MockObject && !$this instanceof \Prophecy\Prophecy\ProphecySubjectInterface) {
444
+            @trigger_error(sprintf('The "%s()" method will have a new "bool $useNaturalSort = false" argument in version 5.0, not defining it is deprecated since Symfony 4.2.', __METHOD__), E_USER_DEPRECATED);
445
+        }
446
+        $useNaturalSort = 0 < \func_num_args() && func_get_arg(0);
447
+
448
+        $this->sort = $useNaturalSort ? Iterator\SortableIterator::SORT_BY_NAME_NATURAL : Iterator\SortableIterator::SORT_BY_NAME;
449
+
450
+        return $this;
451
+    }
452
+
453
+    /**
454
+     * Sorts files and directories by type (directories before files), then by name.
455
+     *
456
+     * This can be slow as all the matching files and directories must be retrieved for comparison.
457
+     *
458
+     * @return $this
459
+     *
460
+     * @see SortableIterator
461
+     */
462
+    public function sortByType()
463
+    {
464
+        $this->sort = Iterator\SortableIterator::SORT_BY_TYPE;
465
+
466
+        return $this;
467
+    }
468
+
469
+    /**
470
+     * Sorts files and directories by the last accessed time.
471
+     *
472
+     * This is the time that the file was last accessed, read or written to.
473
+     *
474
+     * This can be slow as all the matching files and directories must be retrieved for comparison.
475
+     *
476
+     * @return $this
477
+     *
478
+     * @see SortableIterator
479
+     */
480
+    public function sortByAccessedTime()
481
+    {
482
+        $this->sort = Iterator\SortableIterator::SORT_BY_ACCESSED_TIME;
483
+
484
+        return $this;
485
+    }
486
+
487
+    /**
488
+     * Reverses the sorting.
489
+     *
490
+     * @return $this
491
+     */
492
+    public function reverseSorting()
493
+    {
494
+        $this->reverseSorting = true;
495
+
496
+        return $this;
497
+    }
498
+
499
+    /**
500
+     * Sorts files and directories by the last inode changed time.
501
+     *
502
+     * This is the time that the inode information was last modified (permissions, owner, group or other metadata).
503
+     *
504
+     * On Windows, since inode is not available, changed time is actually the file creation time.
505
+     *
506
+     * This can be slow as all the matching files and directories must be retrieved for comparison.
507
+     *
508
+     * @return $this
509
+     *
510
+     * @see SortableIterator
511
+     */
512
+    public function sortByChangedTime()
513
+    {
514
+        $this->sort = Iterator\SortableIterator::SORT_BY_CHANGED_TIME;
515
+
516
+        return $this;
517
+    }
518
+
519
+    /**
520
+     * Sorts files and directories by the last modified time.
521
+     *
522
+     * This is the last time the actual contents of the file were last modified.
523
+     *
524
+     * This can be slow as all the matching files and directories must be retrieved for comparison.
525
+     *
526
+     * @return $this
527
+     *
528
+     * @see SortableIterator
529
+     */
530
+    public function sortByModifiedTime()
531
+    {
532
+        $this->sort = Iterator\SortableIterator::SORT_BY_MODIFIED_TIME;
533
+
534
+        return $this;
535
+    }
536
+
537
+    /**
538
+     * Filters the iterator with an anonymous function.
539
+     *
540
+     * The anonymous function receives a \SplFileInfo and must return false
541
+     * to remove files.
542
+     *
543
+     * @return $this
544
+     *
545
+     * @see CustomFilterIterator
546
+     */
547
+    public function filter(\Closure $closure)
548
+    {
549
+        $this->filters[] = $closure;
550
+
551
+        return $this;
552
+    }
553
+
554
+    /**
555
+     * Forces the following of symlinks.
556
+     *
557
+     * @return $this
558
+     */
559
+    public function followLinks()
560
+    {
561
+        $this->followLinks = true;
562
+
563
+        return $this;
564
+    }
565
+
566
+    /**
567
+     * Tells finder to ignore unreadable directories.
568
+     *
569
+     * By default, scanning unreadable directories content throws an AccessDeniedException.
570
+     *
571
+     * @param bool $ignore
572
+     *
573
+     * @return $this
574
+     */
575
+    public function ignoreUnreadableDirs($ignore = true)
576
+    {
577
+        $this->ignoreUnreadableDirs = (bool) $ignore;
578
+
579
+        return $this;
580
+    }
581
+
582
+    /**
583
+     * Searches files and directories which match defined rules.
584
+     *
585
+     * @param string|string[] $dirs A directory path or an array of directories
586
+     *
587
+     * @return $this
588
+     *
589
+     * @throws DirectoryNotFoundException if one of the directories does not exist
590
+     */
591
+    public function in($dirs)
592
+    {
593
+        $resolvedDirs = [];
594
+
595
+        foreach ((array) $dirs as $dir) {
596
+            if (is_dir($dir)) {
597
+                $resolvedDirs[] = $this->normalizeDir($dir);
598
+            } elseif ($glob = glob($dir, (\defined('GLOB_BRACE') ? GLOB_BRACE : 0) | GLOB_ONLYDIR)) {
599
+                $resolvedDirs = array_merge($resolvedDirs, array_map([$this, 'normalizeDir'], $glob));
600
+            } else {
601
+                throw new DirectoryNotFoundException(sprintf('The "%s" directory does not exist.', $dir));
602
+            }
603
+        }
604
+
605
+        $this->dirs = array_merge($this->dirs, $resolvedDirs);
606
+
607
+        return $this;
608
+    }
609
+
610
+    /**
611
+     * Returns an Iterator for the current Finder configuration.
612
+     *
613
+     * This method implements the IteratorAggregate interface.
614
+     *
615
+     * @return \Iterator|SplFileInfo[] An iterator
616
+     *
617
+     * @throws \LogicException if the in() method has not been called
618
+     */
619
+    public function getIterator()
620
+    {
621
+        if (0 === \count($this->dirs) && 0 === \count($this->iterators)) {
622
+            throw new \LogicException('You must call one of in() or append() methods before iterating over a Finder.');
623
+        }
624
+
625
+        if (1 === \count($this->dirs) && 0 === \count($this->iterators)) {
626
+            return $this->searchInDirectory($this->dirs[0]);
627
+        }
628
+
629
+        $iterator = new \AppendIterator();
630
+        foreach ($this->dirs as $dir) {
631
+            $iterator->append($this->searchInDirectory($dir));
632
+        }
633
+
634
+        foreach ($this->iterators as $it) {
635
+            $iterator->append($it);
636
+        }
637
+
638
+        return $iterator;
639
+    }
640
+
641
+    /**
642
+     * Appends an existing set of files/directories to the finder.
643
+     *
644
+     * The set can be another Finder, an Iterator, an IteratorAggregate, or even a plain array.
645
+     *
646
+     * @param iterable $iterator
647
+     *
648
+     * @return $this
649
+     *
650
+     * @throws \InvalidArgumentException when the given argument is not iterable
651
+     */
652
+    public function append($iterator)
653
+    {
654
+        if ($iterator instanceof \IteratorAggregate) {
655
+            $this->iterators[] = $iterator->getIterator();
656
+        } elseif ($iterator instanceof \Iterator) {
657
+            $this->iterators[] = $iterator;
658
+        } elseif ($iterator instanceof \Traversable || \is_array($iterator)) {
659
+            $it = new \ArrayIterator();
660
+            foreach ($iterator as $file) {
661
+                $it->append($file instanceof \SplFileInfo ? $file : new \SplFileInfo($file));
662
+            }
663
+            $this->iterators[] = $it;
664
+        } else {
665
+            throw new \InvalidArgumentException('Finder::append() method wrong argument type.');
666
+        }
667
+
668
+        return $this;
669
+    }
670
+
671
+    /**
672
+     * Check if the any results were found.
673
+     *
674
+     * @return bool
675
+     */
676
+    public function hasResults()
677
+    {
678
+        foreach ($this->getIterator() as $_) {
679
+            return true;
680
+        }
681
+
682
+        return false;
683
+    }
684
+
685
+    /**
686
+     * Counts all the results collected by the iterators.
687
+     *
688
+     * @return int
689
+     */
690
+    public function count()
691
+    {
692
+        return iterator_count($this->getIterator());
693
+    }
694
+
695
+    private function searchInDirectory(string $dir): \Iterator
696
+    {
697
+        $exclude = $this->exclude;
698
+        $notPaths = $this->notPaths;
699
+
700
+        if (static::IGNORE_VCS_FILES === (static::IGNORE_VCS_FILES & $this->ignore)) {
701
+            $exclude = array_merge($exclude, self::$vcsPatterns);
702
+        }
703
+
704
+        if (static::IGNORE_DOT_FILES === (static::IGNORE_DOT_FILES & $this->ignore)) {
705
+            $notPaths[] = '#(^|/)\..+(/|$)#';
706
+        }
707
+
708
+        if (static::IGNORE_VCS_IGNORED_FILES === (static::IGNORE_VCS_IGNORED_FILES & $this->ignore)) {
709
+            $gitignoreFilePath = sprintf('%s/.gitignore', $dir);
710
+            if (!is_readable($gitignoreFilePath)) {
711
+                throw new \RuntimeException(sprintf('The "ignoreVCSIgnored" option cannot be used by the Finder as the "%s" file is not readable.', $gitignoreFilePath));
712
+            }
713
+            $notPaths = array_merge($notPaths, [Gitignore::toRegex(file_get_contents($gitignoreFilePath))]);
714
+        }
715
+
716
+        $minDepth = 0;
717
+        $maxDepth = PHP_INT_MAX;
718
+
719
+        foreach ($this->depths as $comparator) {
720
+            switch ($comparator->getOperator()) {
721
+                case '>':
722
+                    $minDepth = $comparator->getTarget() + 1;
723
+                    break;
724
+                case '>=':
725
+                    $minDepth = $comparator->getTarget();
726
+                    break;
727
+                case '<':
728
+                    $maxDepth = $comparator->getTarget() - 1;
729
+                    break;
730
+                case '<=':
731
+                    $maxDepth = $comparator->getTarget();
732
+                    break;
733
+                default:
734
+                    $minDepth = $maxDepth = $comparator->getTarget();
735
+            }
736
+        }
737
+
738
+        $flags = \RecursiveDirectoryIterator::SKIP_DOTS;
739
+
740
+        if ($this->followLinks) {
741
+            $flags |= \RecursiveDirectoryIterator::FOLLOW_SYMLINKS;
742
+        }
743
+
744
+        $iterator = new Iterator\RecursiveDirectoryIterator($dir, $flags, $this->ignoreUnreadableDirs);
745
+
746
+        if ($exclude) {
747
+            $iterator = new Iterator\ExcludeDirectoryFilterIterator($iterator, $exclude);
748
+        }
749
+
750
+        $iterator = new \RecursiveIteratorIterator($iterator, \RecursiveIteratorIterator::SELF_FIRST);
751
+
752
+        if ($minDepth > 0 || $maxDepth < PHP_INT_MAX) {
753
+            $iterator = new Iterator\DepthRangeFilterIterator($iterator, $minDepth, $maxDepth);
754
+        }
755
+
756
+        if ($this->mode) {
757
+            $iterator = new Iterator\FileTypeFilterIterator($iterator, $this->mode);
758
+        }
759
+
760
+        if ($this->names || $this->notNames) {
761
+            $iterator = new Iterator\FilenameFilterIterator($iterator, $this->names, $this->notNames);
762
+        }
763
+
764
+        if ($this->contains || $this->notContains) {
765
+            $iterator = new Iterator\FilecontentFilterIterator($iterator, $this->contains, $this->notContains);
766
+        }
767
+
768
+        if ($this->sizes) {
769
+            $iterator = new Iterator\SizeRangeFilterIterator($iterator, $this->sizes);
770
+        }
771
+
772
+        if ($this->dates) {
773
+            $iterator = new Iterator\DateRangeFilterIterator($iterator, $this->dates);
774
+        }
775
+
776
+        if ($this->filters) {
777
+            $iterator = new Iterator\CustomFilterIterator($iterator, $this->filters);
778
+        }
779
+
780
+        if ($this->paths || $notPaths) {
781
+            $iterator = new Iterator\PathFilterIterator($iterator, $this->paths, $notPaths);
782
+        }
783
+
784
+        if ($this->sort || $this->reverseSorting) {
785
+            $iteratorAggregate = new Iterator\SortableIterator($iterator, $this->sort, $this->reverseSorting);
786
+            $iterator = $iteratorAggregate->getIterator();
787
+        }
788
+
789
+        return $iterator;
790
+    }
791
+
792
+    /**
793
+     * Normalizes given directory names by removing trailing slashes.
794
+     *
795
+     * Excluding: (s)ftp:// wrapper
796
+     *
797
+     * @param string $dir
798
+     *
799
+     * @return string
800
+     */
801
+    private function normalizeDir($dir)
802
+    {
803
+        $dir = rtrim($dir, '/'.\DIRECTORY_SEPARATOR);
804
+
805
+        if (preg_match('#^s?ftp://#', $dir)) {
806
+            $dir .= '/';
807
+        }
808
+
809
+        return $dir;
810
+    }
811
+}