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