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