Verzeichnisstruktur phpBB-3.3.15


Veröffentlicht
28.08.2024

So funktioniert es


Auf das letzte Element klicken. Dies geht jeweils ein Schritt zurück

Auf das Icon klicken, dies öffnet das Verzeichnis. Nochmal klicken schließt das Verzeichnis.
Auf den Verzeichnisnamen klicken, dies zeigt nur das Verzeichnis mit Inhalt an

(Beispiel Datei-Icons)

Auf das Icon klicken um den Quellcode anzuzeigen

Finder.php

Zuletzt modifiziert: 02.04.2025, 15:02 - Dateigröße: 19.62 KiB


001  <?php
002   
003  /*
004   * This file is part of the Symfony package.
005   *
006   * (c) Fabien Potencier <fabien@symfony.com>
007   *
008   * For the full copyright and license information, please view the LICENSE
009   * file that was distributed with this source code.
010   */
011   
012  namespace Symfony\Component\Finder;
013   
014  use Symfony\Component\Finder\Comparator\DateComparator;
015  use Symfony\Component\Finder\Comparator\NumberComparator;
016  use Symfony\Component\Finder\Iterator\CustomFilterIterator;
017  use Symfony\Component\Finder\Iterator\DateRangeFilterIterator;
018  use Symfony\Component\Finder\Iterator\DepthRangeFilterIterator;
019  use Symfony\Component\Finder\Iterator\ExcludeDirectoryFilterIterator;
020  use Symfony\Component\Finder\Iterator\FilecontentFilterIterator;
021  use Symfony\Component\Finder\Iterator\FilenameFilterIterator;
022  use Symfony\Component\Finder\Iterator\SizeRangeFilterIterator;
023  use Symfony\Component\Finder\Iterator\SortableIterator;
024   
025  /**
026   * Finder allows to build rules to find files and directories.
027   *
028   * It is a thin wrapper around several specialized iterator classes.
029   *
030   * All rules may be invoked several times.
031   *
032   * All methods return the current Finder object to allow chaining:
033   *
034   *     $finder = Finder::create()->files()->name('*.php')->in(__DIR__);
035   *
036   * @author Fabien Potencier <fabien@symfony.com>
037   */
038  class Finder implements \IteratorAggregate, \Countable
039  {
040      const IGNORE_VCS_FILES = 1;
041      const IGNORE_DOT_FILES = 2;
042   
043      private $mode = 0;
044      private $names = [];
045      private $notNames = [];
046      private $exclude = [];
047      private $filters = [];
048      private $depths = [];
049      private $sizes = [];
050      private $followLinks = false;
051      private $sort = false;
052      private $ignore = 0;
053      private $dirs = [];
054      private $dates = [];
055      private $iterators = [];
056      private $contains = [];
057      private $notContains = [];
058      private $paths = [];
059      private $notPaths = [];
060      private $ignoreUnreadableDirs = false;
061   
062      private static $vcsPatterns = ['.svn', '_svn', 'CVS', '_darcs', '.arch-params', '.monotone', '.bzr', '.git', '.hg'];
063   
064      public function __construct()
065      {
066          $this->ignore = static::IGNORE_VCS_FILES | static::IGNORE_DOT_FILES;
067      }
068   
069      /**
070       * Creates a new Finder.
071       *
072       * @return static
073       */
074      public static function create()
075      {
076          return new static();
077      }
078   
079      /**
080       * Restricts the matching to directories only.
081       *
082       * @return $this
083       */
084      public function directories()
085      {
086          $this->mode = Iterator\FileTypeFilterIterator::ONLY_DIRECTORIES;
087   
088          return $this;
089      }
090   
091      /**
092       * Restricts the matching to files only.
093       *
094       * @return $this
095       */
096      public function files()
097      {
098          $this->mode = Iterator\FileTypeFilterIterator::ONLY_FILES;
099   
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       * Directories passed as argument must be relative to the ones defined with the `in()` method. For example:
301       *
302       *     $finder->in(__DIR__)->exclude('ruby');
303       *
304       * @param string|array $dirs A directory path or an array of directories
305       *
306       * @return $this
307       *
308       * @see ExcludeDirectoryFilterIterator
309       */
310      public function exclude($dirs)
311      {
312          $this->exclude = array_merge($this->exclude, (array) $dirs);
313   
314          return $this;
315      }
316   
317      /**
318       * Excludes "hidden" directories and files (starting with a dot).
319       *
320       * This option is enabled by default.
321       *
322       * @param bool $ignoreDotFiles Whether to exclude "hidden" files or not
323       *
324       * @return $this
325       *
326       * @see ExcludeDirectoryFilterIterator
327       */
328      public function ignoreDotFiles($ignoreDotFiles)
329      {
330          if ($ignoreDotFiles) {
331              $this->ignore |= static::IGNORE_DOT_FILES;
332          } else {
333              $this->ignore &= ~static::IGNORE_DOT_FILES;
334          }
335   
336          return $this;
337      }
338   
339      /**
340       * Forces the finder to ignore version control directories.
341       *
342       * This option is enabled by default.
343       *
344       * @param bool $ignoreVCS Whether to exclude VCS files or not
345       *
346       * @return $this
347       *
348       * @see ExcludeDirectoryFilterIterator
349       */
350      public function ignoreVCS($ignoreVCS)
351      {
352          if ($ignoreVCS) {
353              $this->ignore |= static::IGNORE_VCS_FILES;
354          } else {
355              $this->ignore &= ~static::IGNORE_VCS_FILES;
356          }
357   
358          return $this;
359      }
360   
361      /**
362       * Adds VCS patterns.
363       *
364       * @see ignoreVCS()
365       *
366       * @param string|string[] $pattern VCS patterns to ignore
367       */
368      public static function addVCSPattern($pattern)
369      {
370          foreach ((array) $pattern as $p) {
371              self::$vcsPatterns[] = $p;
372          }
373   
374          self::$vcsPatterns = array_unique(self::$vcsPatterns);
375      }
376   
377      /**
378       * Sorts files and directories by an anonymous function.
379       *
380       * The anonymous function receives two \SplFileInfo instances to compare.
381       *
382       * This can be slow as all the matching files and directories must be retrieved for comparison.
383       *
384       * @return $this
385       *
386       * @see SortableIterator
387       */
388      public function sort(\Closure $closure)
389      {
390          $this->sort = $closure;
391   
392          return $this;
393      }
394   
395      /**
396       * Sorts files and directories by name.
397       *
398       * This can be slow as all the matching files and directories must be retrieved for comparison.
399       *
400       * @return $this
401       *
402       * @see SortableIterator
403       */
404      public function sortByName()
405      {
406          $this->sort = Iterator\SortableIterator::SORT_BY_NAME;
407   
408          return $this;
409      }
410   
411      /**
412       * Sorts files and directories by type (directories before files), then by name.
413       *
414       * This can be slow as all the matching files and directories must be retrieved for comparison.
415       *
416       * @return $this
417       *
418       * @see SortableIterator
419       */
420      public function sortByType()
421      {
422          $this->sort = Iterator\SortableIterator::SORT_BY_TYPE;
423   
424          return $this;
425      }
426   
427      /**
428       * Sorts files and directories by the last accessed time.
429       *
430       * This is the time that the file was last accessed, read or written to.
431       *
432       * This can be slow as all the matching files and directories must be retrieved for comparison.
433       *
434       * @return $this
435       *
436       * @see SortableIterator
437       */
438      public function sortByAccessedTime()
439      {
440          $this->sort = Iterator\SortableIterator::SORT_BY_ACCESSED_TIME;
441   
442          return $this;
443      }
444   
445      /**
446       * Sorts files and directories by the last inode changed time.
447       *
448       * This is the time that the inode information was last modified (permissions, owner, group or other metadata).
449       *
450       * On Windows, since inode is not available, changed time is actually the file creation time.
451       *
452       * This can be slow as all the matching files and directories must be retrieved for comparison.
453       *
454       * @return $this
455       *
456       * @see SortableIterator
457       */
458      public function sortByChangedTime()
459      {
460          $this->sort = Iterator\SortableIterator::SORT_BY_CHANGED_TIME;
461   
462          return $this;
463      }
464   
465      /**
466       * Sorts files and directories by the last modified time.
467       *
468       * This is the last time the actual contents of the file were last modified.
469       *
470       * This can be slow as all the matching files and directories must be retrieved for comparison.
471       *
472       * @return $this
473       *
474       * @see SortableIterator
475       */
476      public function sortByModifiedTime()
477      {
478          $this->sort = Iterator\SortableIterator::SORT_BY_MODIFIED_TIME;
479   
480          return $this;
481      }
482   
483      /**
484       * Filters the iterator with an anonymous function.
485       *
486       * The anonymous function receives a \SplFileInfo and must return false
487       * to remove files.
488       *
489       * @return $this
490       *
491       * @see CustomFilterIterator
492       */
493      public function filter(\Closure $closure)
494      {
495          $this->filters[] = $closure;
496   
497          return $this;
498      }
499   
500      /**
501       * Forces the following of symlinks.
502       *
503       * @return $this
504       */
505      public function followLinks()
506      {
507          $this->followLinks = true;
508   
509          return $this;
510      }
511   
512      /**
513       * Tells finder to ignore unreadable directories.
514       *
515       * By default, scanning unreadable directories content throws an AccessDeniedException.
516       *
517       * @param bool $ignore
518       *
519       * @return $this
520       */
521      public function ignoreUnreadableDirs($ignore = true)
522      {
523          $this->ignoreUnreadableDirs = (bool) $ignore;
524   
525          return $this;
526      }
527   
528      /**
529       * Searches files and directories which match defined rules.
530       *
531       * @param string|string[] $dirs A directory path or an array of directories
532       *
533       * @return $this
534       *
535       * @throws \InvalidArgumentException if one of the directories does not exist
536       */
537      public function in($dirs)
538      {
539          $resolvedDirs = [];
540   
541          foreach ((array) $dirs as $dir) {
542              if (is_dir($dir)) {
543                  $resolvedDirs[] = $this->normalizeDir($dir);
544              } elseif ($glob = glob($dir, (\defined('GLOB_BRACE') ? \GLOB_BRACE : 0) | \GLOB_ONLYDIR | \GLOB_NOSORT)) {
545                  sort($glob);
546                  $resolvedDirs = array_merge($resolvedDirs, array_map([$this, 'normalizeDir'], $glob));
547              } else {
548                  throw new \InvalidArgumentException(sprintf('The "%s" directory does not exist.', $dir));
549              }
550          }
551   
552          $this->dirs = array_merge($this->dirs, $resolvedDirs);
553   
554          return $this;
555      }
556   
557      /**
558       * Returns an Iterator for the current Finder configuration.
559       *
560       * This method implements the IteratorAggregate interface.
561       *
562       * @return \Iterator|SplFileInfo[] An iterator
563       *
564       * @throws \LogicException if the in() method has not been called
565       */
566      public function getIterator()
567      {
568          if (0 === \count($this->dirs) && 0 === \count($this->iterators)) {
569              throw new \LogicException('You must call one of in() or append() methods before iterating over a Finder.');
570          }
571   
572          if (1 === \count($this->dirs) && 0 === \count($this->iterators)) {
573              return $this->searchInDirectory($this->dirs[0]);
574          }
575   
576          $iterator = new \AppendIterator();
577          foreach ($this->dirs as $dir) {
578              $iterator->append($this->searchInDirectory($dir));
579          }
580   
581          foreach ($this->iterators as $it) {
582              $iterator->append($it);
583          }
584   
585          return $iterator;
586      }
587   
588      /**
589       * Appends an existing set of files/directories to the finder.
590       *
591       * The set can be another Finder, an Iterator, an IteratorAggregate, or even a plain array.
592       *
593       * @param iterable $iterator
594       *
595       * @return $this
596       *
597       * @throws \InvalidArgumentException when the given argument is not iterable
598       */
599      public function append($iterator)
600      {
601          if ($iterator instanceof \IteratorAggregate) {
602              $this->iterators[] = $iterator->getIterator();
603          } elseif ($iterator instanceof \Iterator) {
604              $this->iterators[] = $iterator;
605          } elseif ($iterator instanceof \Traversable || \is_array($iterator)) {
606              $it = new \ArrayIterator();
607              foreach ($iterator as $file) {
608                  $it->append($file instanceof \SplFileInfo ? $file : new \SplFileInfo($file));
609              }
610              $this->iterators[] = $it;
611          } else {
612              throw new \InvalidArgumentException('Finder::append() method wrong argument type.');
613          }
614   
615          return $this;
616      }
617   
618      /**
619       * Check if any results were found.
620       *
621       * @return bool
622       */
623      public function hasResults()
624      {
625          foreach ($this->getIterator() as $_) {
626              return true;
627          }
628   
629          return false;
630      }
631   
632      /**
633       * Counts all the results collected by the iterators.
634       *
635       * @return int
636       */
637      public function count()
638      {
639          return iterator_count($this->getIterator());
640      }
641   
642      /**
643       * @param string $dir
644       *
645       * @return \Iterator
646       */
647      private function searchInDirectory($dir)
648      {
649          $exclude = $this->exclude;
650          $notPaths = $this->notPaths;
651   
652          if (static::IGNORE_VCS_FILES === (static::IGNORE_VCS_FILES & $this->ignore)) {
653              $exclude = array_merge($exclude, self::$vcsPatterns);
654          }
655   
656          if (static::IGNORE_DOT_FILES === (static::IGNORE_DOT_FILES & $this->ignore)) {
657              $notPaths[] = '#(^|/)\..+(/|$)#';
658          }
659   
660          $minDepth = 0;
661          $maxDepth = \PHP_INT_MAX;
662   
663          foreach ($this->depths as $comparator) {
664              switch ($comparator->getOperator()) {
665                  case '>':
666                      $minDepth = $comparator->getTarget() + 1;
667                      break;
668                  case '>=':
669                      $minDepth = $comparator->getTarget();
670                      break;
671                  case '<':
672                      $maxDepth = $comparator->getTarget() - 1;
673                      break;
674                  case '<=':
675                      $maxDepth = $comparator->getTarget();
676                      break;
677                  default:
678                      $minDepth = $maxDepth = $comparator->getTarget();
679              }
680          }
681   
682          $flags = \RecursiveDirectoryIterator::SKIP_DOTS;
683   
684          if ($this->followLinks) {
685              $flags |= \RecursiveDirectoryIterator::FOLLOW_SYMLINKS;
686          }
687   
688          $iterator = new Iterator\RecursiveDirectoryIterator($dir, $flags, $this->ignoreUnreadableDirs);
689   
690          if ($exclude) {
691              $iterator = new Iterator\ExcludeDirectoryFilterIterator($iterator, $exclude);
692          }
693   
694          $iterator = new \RecursiveIteratorIterator($iterator, \RecursiveIteratorIterator::SELF_FIRST);
695   
696          if ($minDepth > 0 || $maxDepth < \PHP_INT_MAX) {
697              $iterator = new Iterator\DepthRangeFilterIterator($iterator, $minDepth, $maxDepth);
698          }
699   
700          if ($this->mode) {
701              $iterator = new Iterator\FileTypeFilterIterator($iterator, $this->mode);
702          }
703   
704          if ($this->names || $this->notNames) {
705              $iterator = new Iterator\FilenameFilterIterator($iterator, $this->names, $this->notNames);
706          }
707   
708          if ($this->contains || $this->notContains) {
709              $iterator = new Iterator\FilecontentFilterIterator($iterator, $this->contains, $this->notContains);
710          }
711   
712          if ($this->sizes) {
713              $iterator = new Iterator\SizeRangeFilterIterator($iterator, $this->sizes);
714          }
715   
716          if ($this->dates) {
717              $iterator = new Iterator\DateRangeFilterIterator($iterator, $this->dates);
718          }
719   
720          if ($this->filters) {
721              $iterator = new Iterator\CustomFilterIterator($iterator, $this->filters);
722          }
723   
724          if ($this->paths || $notPaths) {
725              $iterator = new Iterator\PathFilterIterator($iterator, $this->paths, $notPaths);
726          }
727   
728          if ($this->sort) {
729              $iteratorAggregate = new Iterator\SortableIterator($iterator, $this->sort);
730              $iterator = $iteratorAggregate->getIterator();
731          }
732   
733          return $iterator;
734      }
735   
736      /**
737       * Normalizes given directory names by removing trailing slashes.
738       *
739       * Excluding: (s)ftp:// or ssh2.(s)ftp:// wrapper
740       *
741       * @param string $dir
742       *
743       * @return string
744       */
745      private function normalizeDir($dir)
746      {
747          if ('/' === $dir) {
748              return $dir;
749          }
750   
751          $dir = rtrim($dir, '/'.\DIRECTORY_SEPARATOR);
752   
753          if (preg_match('#^(ssh2\.)?s?ftp://#', $dir)) {
754              $dir .= '/';
755          }
756   
757          return $dir;
758      }
759  }
760