1 <?php
2
3 4 5 6 7 8 9 10
11 class rex_finder implements IteratorAggregate, Countable
12 {
13 use rex_factory_trait;
14
15 const ALL = '__ALL__';
16
17 private $dir;
18 private $recursive = false;
19 private $recursiveMode = RecursiveIteratorIterator::SELF_FIRST;
20 private $dirsOnly = false;
21 private $ignoreFiles = [];
22 private $ignoreFilesRecursive = [];
23 private $ignoreDirs = [];
24 private $ignoreDirsRecursive = [];
25 private $ignoreSystemStuff = true;
26 private $sort = false;
27
28 29 30 31 32
33 private function __construct($dir)
34 {
35 $this->dir = $dir;
36 }
37
38 39 40 41 42 43 44 45 46
47 public static function factory($dir)
48 {
49 if (!is_dir($dir)) {
50 throw new InvalidArgumentException('Folder "' . $dir . '" not found!');
51 }
52
53 $class = static::getFactoryClass();
54 return new $class($dir);
55 }
56
57 58 59 60 61 62 63
64 public function recursive($recursive = true)
65 {
66 $this->recursive = $recursive;
67
68 return $this;
69 }
70
71 72 73 74 75
76 public function selfFirst()
77 {
78 $this->recursiveMode = RecursiveIteratorIterator::SELF_FIRST;
79
80 return $this;
81 }
82
83 84 85 86 87
88 public function childFirst()
89 {
90 $this->recursiveMode = RecursiveIteratorIterator::CHILD_FIRST;
91
92 return $this;
93 }
94
95 96 97 98 99
100 public function filesOnly()
101 {
102 $this->recursiveMode = RecursiveIteratorIterator::LEAVES_ONLY;
103
104 return $this;
105 }
106
107 108 109 110 111
112 public function dirsOnly()
113 {
114 $this->dirsOnly = true;
115
116 return $this;
117 }
118
119 120 121 122 123 124 125 126
127 public function ignoreFiles($glob, $recursive = true)
128 {
129 $var = $recursive ? 'ignoreFilesRecursive' : 'ignoreFiles';
130 if (is_array($glob)) {
131 $this->$var += $glob;
132 } else {
133 array_push($this->$var, $glob);
134 }
135
136 return $this;
137 }
138
139 140 141 142 143 144 145 146
147 public function ignoreDirs($glob, $recursive = true)
148 {
149 $var = $recursive ? 'ignoreDirsRecursive' : 'ignoreDirs';
150 if (is_array($glob)) {
151 $this->$var += $glob;
152 } else {
153 array_push($this->$var, $glob);
154 }
155
156 return $this;
157 }
158
159 160 161 162 163 164 165
166 public function ignoreSystemStuff($ignoreSystemStuff = true)
167 {
168 $this->ignoreSystemStuff = $ignoreSystemStuff;
169
170 return $this;
171 }
172
173 174 175 176 177 178 179
180 public function sort($sort = rex_sortable_iterator::KEYS)
181 {
182 $this->sort = $sort;
183
184 return $this;
185 }
186
187 188 189
190 public function getIterator()
191 {
192 $iterator = new RecursiveDirectoryIterator($this->dir, FilesystemIterator::KEY_AS_PATHNAME | FilesystemIterator::CURRENT_AS_FILEINFO | FilesystemIterator::SKIP_DOTS);
193
194 $iterator = new RecursiveCallbackFilterIterator($iterator, function (SplFileInfo $current, $key, $currentIterator) use ($iterator) {
195 $filename = $current->getFilename();
196 $isRoot = $currentIterator === $iterator;
197
198 $match = function ($pattern, $filename) {
199 $regex = '/^'.strtr(preg_quote($pattern, '/'), ['\*' => '.*', '\?' => '.']).'$/i';
200 return preg_match($regex, $filename);
201 };
202
203 if ($current->isFile()) {
204 if ($this->dirsOnly) {
205 return false;
206 }
207 $ignoreFiles = $isRoot ? array_merge($this->ignoreFiles, $this->ignoreFilesRecursive) : $this->ignoreFilesRecursive;
208 foreach ($ignoreFiles as $ignore) {
209 if ($match($ignore, $filename)) {
210 return false;
211 }
212 }
213 }
214
215 if ($current->isDir()) {
216 if (!$this->recursive && $this->recursiveMode === RecursiveIteratorIterator::LEAVES_ONLY) {
217 return false;
218 }
219 $ignoreDirs = $isRoot ? array_merge($this->ignoreDirs, $this->ignoreDirsRecursive) : $this->ignoreDirsRecursive;
220 foreach ($ignoreDirs as $ignore) {
221 if ($match($ignore, $filename)) {
222 return false;
223 }
224 }
225 }
226
227 if ($this->ignoreSystemStuff) {
228 static $systemStuff = ['.DS_Store', 'Thumbs.db', 'desktop.ini', '.svn', '_svn', 'CVS', '_darcs', '.arch-params', '.monotone', '.bzr', '.git', '.hg'];
229 foreach ($systemStuff as $systemStuffFile) {
230 if (stripos($filename, $systemStuffFile) === 0) {
231 return false;
232 }
233 }
234 }
235
236 return true;
237 });
238
239 if ($this->recursive) {
240 $iterator = new RecursiveIteratorIterator($iterator, $this->recursiveMode);
241 }
242
243 if ($this->sort) {
244 $iterator = new rex_sortable_iterator($iterator, $this->sort);
245 }
246
247 return $iterator;
248 }
249
250 251 252
253 public function count()
254 {
255 return iterator_count($this->getIterator());
256 }
257 }
258