vendor/symfony/finder/Finder.php line 626

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