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