annotate vendor/symfony/finder/Finder.php @ 19:fa3358dc1485 tip

Add ndrum files
author Chris Cannam
date Wed, 28 Aug 2019 13:14:47 +0100
parents af1871eacc83
children
rev   line source
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@18 32 * All methods return the current Finder object to allow chaining:
Chris@0 33 *
Chris@17 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@17 44 private $names = [];
Chris@17 45 private $notNames = [];
Chris@17 46 private $exclude = [];
Chris@17 47 private $filters = [];
Chris@17 48 private $depths = [];
Chris@17 49 private $sizes = [];
Chris@0 50 private $followLinks = false;
Chris@0 51 private $sort = false;
Chris@0 52 private $ignore = 0;
Chris@17 53 private $dirs = [];
Chris@17 54 private $dates = [];
Chris@17 55 private $iterators = [];
Chris@17 56 private $contains = [];
Chris@17 57 private $notContains = [];
Chris@17 58 private $paths = [];
Chris@17 59 private $notPaths = [];
Chris@0 60 private $ignoreUnreadableDirs = false;
Chris@0 61
Chris@17 62 private static $vcsPatterns = ['.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@17 108 * $finder->depth('> 1') // the Finder will start matching at level 1.
Chris@17 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@17 130 * $finder->date('since yesterday');
Chris@17 131 * $finder->date('until 2 days ago');
Chris@17 132 * $finder->date('> now - 2 hours');
Chris@17 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@17 155 * $finder->name('*.php')
Chris@17 156 * $finder->name('/\.php$/') // same as above
Chris@17 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@17 193 * $finder->contains('Lorem ipsum')
Chris@17 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@17 214 * $finder->notContains('Lorem ipsum')
Chris@17 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@17 235 * $finder->path('some/special/dir')
Chris@17 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@17 258 * $finder->notPath('some/special/dir')
Chris@17 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@17 279 * $finder->size('> 10K');
Chris@17 280 * $finder->size('<= 1Ki');
Chris@17 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@13 300 * Directories passed as argument must be relative to the ones defined with the `in()` method. For example:
Chris@13 301 *
Chris@13 302 * $finder->in(__DIR__)->exclude('ruby');
Chris@13 303 *
Chris@0 304 * @param string|array $dirs A directory path or an array of directories
Chris@0 305 *
Chris@0 306 * @return $this
Chris@0 307 *
Chris@0 308 * @see ExcludeDirectoryFilterIterator
Chris@0 309 */
Chris@0 310 public function exclude($dirs)
Chris@0 311 {
Chris@0 312 $this->exclude = array_merge($this->exclude, (array) $dirs);
Chris@0 313
Chris@0 314 return $this;
Chris@0 315 }
Chris@0 316
Chris@0 317 /**
Chris@0 318 * Excludes "hidden" directories and files (starting with a dot).
Chris@0 319 *
Chris@13 320 * This option is enabled by default.
Chris@13 321 *
Chris@0 322 * @param bool $ignoreDotFiles Whether to exclude "hidden" files or not
Chris@0 323 *
Chris@0 324 * @return $this
Chris@0 325 *
Chris@0 326 * @see ExcludeDirectoryFilterIterator
Chris@0 327 */
Chris@0 328 public function ignoreDotFiles($ignoreDotFiles)
Chris@0 329 {
Chris@0 330 if ($ignoreDotFiles) {
Chris@0 331 $this->ignore |= static::IGNORE_DOT_FILES;
Chris@0 332 } else {
Chris@0 333 $this->ignore &= ~static::IGNORE_DOT_FILES;
Chris@0 334 }
Chris@0 335
Chris@0 336 return $this;
Chris@0 337 }
Chris@0 338
Chris@0 339 /**
Chris@0 340 * Forces the finder to ignore version control directories.
Chris@0 341 *
Chris@13 342 * This option is enabled by default.
Chris@13 343 *
Chris@0 344 * @param bool $ignoreVCS Whether to exclude VCS files or not
Chris@0 345 *
Chris@0 346 * @return $this
Chris@0 347 *
Chris@0 348 * @see ExcludeDirectoryFilterIterator
Chris@0 349 */
Chris@0 350 public function ignoreVCS($ignoreVCS)
Chris@0 351 {
Chris@0 352 if ($ignoreVCS) {
Chris@0 353 $this->ignore |= static::IGNORE_VCS_FILES;
Chris@0 354 } else {
Chris@0 355 $this->ignore &= ~static::IGNORE_VCS_FILES;
Chris@0 356 }
Chris@0 357
Chris@0 358 return $this;
Chris@0 359 }
Chris@0 360
Chris@0 361 /**
Chris@0 362 * Adds VCS patterns.
Chris@0 363 *
Chris@0 364 * @see ignoreVCS()
Chris@0 365 *
Chris@0 366 * @param string|string[] $pattern VCS patterns to ignore
Chris@0 367 */
Chris@0 368 public static function addVCSPattern($pattern)
Chris@0 369 {
Chris@0 370 foreach ((array) $pattern as $p) {
Chris@0 371 self::$vcsPatterns[] = $p;
Chris@0 372 }
Chris@0 373
Chris@0 374 self::$vcsPatterns = array_unique(self::$vcsPatterns);
Chris@0 375 }
Chris@0 376
Chris@0 377 /**
Chris@0 378 * Sorts files and directories by an anonymous function.
Chris@0 379 *
Chris@0 380 * The anonymous function receives two \SplFileInfo instances to compare.
Chris@0 381 *
Chris@0 382 * This can be slow as all the matching files and directories must be retrieved for comparison.
Chris@0 383 *
Chris@0 384 * @return $this
Chris@0 385 *
Chris@0 386 * @see SortableIterator
Chris@0 387 */
Chris@0 388 public function sort(\Closure $closure)
Chris@0 389 {
Chris@0 390 $this->sort = $closure;
Chris@0 391
Chris@0 392 return $this;
Chris@0 393 }
Chris@0 394
Chris@0 395 /**
Chris@0 396 * Sorts files and directories by name.
Chris@0 397 *
Chris@0 398 * This can be slow as all the matching files and directories must be retrieved for comparison.
Chris@0 399 *
Chris@0 400 * @return $this
Chris@0 401 *
Chris@0 402 * @see SortableIterator
Chris@0 403 */
Chris@0 404 public function sortByName()
Chris@0 405 {
Chris@0 406 $this->sort = Iterator\SortableIterator::SORT_BY_NAME;
Chris@0 407
Chris@0 408 return $this;
Chris@0 409 }
Chris@0 410
Chris@0 411 /**
Chris@0 412 * Sorts files and directories by type (directories before files), then by name.
Chris@0 413 *
Chris@0 414 * This can be slow as all the matching files and directories must be retrieved for comparison.
Chris@0 415 *
Chris@0 416 * @return $this
Chris@0 417 *
Chris@0 418 * @see SortableIterator
Chris@0 419 */
Chris@0 420 public function sortByType()
Chris@0 421 {
Chris@0 422 $this->sort = Iterator\SortableIterator::SORT_BY_TYPE;
Chris@0 423
Chris@0 424 return $this;
Chris@0 425 }
Chris@0 426
Chris@0 427 /**
Chris@0 428 * Sorts files and directories by the last accessed time.
Chris@0 429 *
Chris@0 430 * This is the time that the file was last accessed, read or written to.
Chris@0 431 *
Chris@0 432 * This can be slow as all the matching files and directories must be retrieved for comparison.
Chris@0 433 *
Chris@0 434 * @return $this
Chris@0 435 *
Chris@0 436 * @see SortableIterator
Chris@0 437 */
Chris@0 438 public function sortByAccessedTime()
Chris@0 439 {
Chris@0 440 $this->sort = Iterator\SortableIterator::SORT_BY_ACCESSED_TIME;
Chris@0 441
Chris@0 442 return $this;
Chris@0 443 }
Chris@0 444
Chris@0 445 /**
Chris@0 446 * Sorts files and directories by the last inode changed time.
Chris@0 447 *
Chris@0 448 * This is the time that the inode information was last modified (permissions, owner, group or other metadata).
Chris@0 449 *
Chris@0 450 * On Windows, since inode is not available, changed time is actually the file creation time.
Chris@0 451 *
Chris@0 452 * This can be slow as all the matching files and directories must be retrieved for comparison.
Chris@0 453 *
Chris@0 454 * @return $this
Chris@0 455 *
Chris@0 456 * @see SortableIterator
Chris@0 457 */
Chris@0 458 public function sortByChangedTime()
Chris@0 459 {
Chris@0 460 $this->sort = Iterator\SortableIterator::SORT_BY_CHANGED_TIME;
Chris@0 461
Chris@0 462 return $this;
Chris@0 463 }
Chris@0 464
Chris@0 465 /**
Chris@0 466 * Sorts files and directories by the last modified time.
Chris@0 467 *
Chris@0 468 * This is the last time the actual contents of the file were last modified.
Chris@0 469 *
Chris@0 470 * This can be slow as all the matching files and directories must be retrieved for comparison.
Chris@0 471 *
Chris@0 472 * @return $this
Chris@0 473 *
Chris@0 474 * @see SortableIterator
Chris@0 475 */
Chris@0 476 public function sortByModifiedTime()
Chris@0 477 {
Chris@0 478 $this->sort = Iterator\SortableIterator::SORT_BY_MODIFIED_TIME;
Chris@0 479
Chris@0 480 return $this;
Chris@0 481 }
Chris@0 482
Chris@0 483 /**
Chris@0 484 * Filters the iterator with an anonymous function.
Chris@0 485 *
Chris@0 486 * The anonymous function receives a \SplFileInfo and must return false
Chris@0 487 * to remove files.
Chris@0 488 *
Chris@0 489 * @return $this
Chris@0 490 *
Chris@0 491 * @see CustomFilterIterator
Chris@0 492 */
Chris@0 493 public function filter(\Closure $closure)
Chris@0 494 {
Chris@0 495 $this->filters[] = $closure;
Chris@0 496
Chris@0 497 return $this;
Chris@0 498 }
Chris@0 499
Chris@0 500 /**
Chris@0 501 * Forces the following of symlinks.
Chris@0 502 *
Chris@0 503 * @return $this
Chris@0 504 */
Chris@0 505 public function followLinks()
Chris@0 506 {
Chris@0 507 $this->followLinks = true;
Chris@0 508
Chris@0 509 return $this;
Chris@0 510 }
Chris@0 511
Chris@0 512 /**
Chris@0 513 * Tells finder to ignore unreadable directories.
Chris@0 514 *
Chris@0 515 * By default, scanning unreadable directories content throws an AccessDeniedException.
Chris@0 516 *
Chris@0 517 * @param bool $ignore
Chris@0 518 *
Chris@0 519 * @return $this
Chris@0 520 */
Chris@0 521 public function ignoreUnreadableDirs($ignore = true)
Chris@0 522 {
Chris@0 523 $this->ignoreUnreadableDirs = (bool) $ignore;
Chris@0 524
Chris@0 525 return $this;
Chris@0 526 }
Chris@0 527
Chris@0 528 /**
Chris@0 529 * Searches files and directories which match defined rules.
Chris@0 530 *
Chris@0 531 * @param string|array $dirs A directory path or an array of directories
Chris@0 532 *
Chris@0 533 * @return $this
Chris@0 534 *
Chris@0 535 * @throws \InvalidArgumentException if one of the directories does not exist
Chris@0 536 */
Chris@0 537 public function in($dirs)
Chris@0 538 {
Chris@17 539 $resolvedDirs = [];
Chris@0 540
Chris@0 541 foreach ((array) $dirs as $dir) {
Chris@0 542 if (is_dir($dir)) {
Chris@13 543 $resolvedDirs[] = $this->normalizeDir($dir);
Chris@17 544 } elseif ($glob = glob($dir, (\defined('GLOB_BRACE') ? GLOB_BRACE : 0) | GLOB_ONLYDIR)) {
Chris@17 545 $resolvedDirs = array_merge($resolvedDirs, array_map([$this, 'normalizeDir'], $glob));
Chris@0 546 } else {
Chris@0 547 throw new \InvalidArgumentException(sprintf('The "%s" directory does not exist.', $dir));
Chris@0 548 }
Chris@0 549 }
Chris@0 550
Chris@0 551 $this->dirs = array_merge($this->dirs, $resolvedDirs);
Chris@0 552
Chris@0 553 return $this;
Chris@0 554 }
Chris@0 555
Chris@0 556 /**
Chris@0 557 * Returns an Iterator for the current Finder configuration.
Chris@0 558 *
Chris@0 559 * This method implements the IteratorAggregate interface.
Chris@0 560 *
Chris@0 561 * @return \Iterator|SplFileInfo[] An iterator
Chris@0 562 *
Chris@0 563 * @throws \LogicException if the in() method has not been called
Chris@0 564 */
Chris@0 565 public function getIterator()
Chris@0 566 {
Chris@17 567 if (0 === \count($this->dirs) && 0 === \count($this->iterators)) {
Chris@0 568 throw new \LogicException('You must call one of in() or append() methods before iterating over a Finder.');
Chris@0 569 }
Chris@0 570
Chris@17 571 if (1 === \count($this->dirs) && 0 === \count($this->iterators)) {
Chris@0 572 return $this->searchInDirectory($this->dirs[0]);
Chris@0 573 }
Chris@0 574
Chris@0 575 $iterator = new \AppendIterator();
Chris@0 576 foreach ($this->dirs as $dir) {
Chris@0 577 $iterator->append($this->searchInDirectory($dir));
Chris@0 578 }
Chris@0 579
Chris@0 580 foreach ($this->iterators as $it) {
Chris@0 581 $iterator->append($it);
Chris@0 582 }
Chris@0 583
Chris@0 584 return $iterator;
Chris@0 585 }
Chris@0 586
Chris@0 587 /**
Chris@0 588 * Appends an existing set of files/directories to the finder.
Chris@0 589 *
Chris@0 590 * The set can be another Finder, an Iterator, an IteratorAggregate, or even a plain array.
Chris@0 591 *
Chris@17 592 * @param iterable $iterator
Chris@0 593 *
Chris@0 594 * @return $this
Chris@0 595 *
Chris@0 596 * @throws \InvalidArgumentException when the given argument is not iterable
Chris@0 597 */
Chris@0 598 public function append($iterator)
Chris@0 599 {
Chris@0 600 if ($iterator instanceof \IteratorAggregate) {
Chris@0 601 $this->iterators[] = $iterator->getIterator();
Chris@0 602 } elseif ($iterator instanceof \Iterator) {
Chris@0 603 $this->iterators[] = $iterator;
Chris@17 604 } elseif ($iterator instanceof \Traversable || \is_array($iterator)) {
Chris@0 605 $it = new \ArrayIterator();
Chris@0 606 foreach ($iterator as $file) {
Chris@0 607 $it->append($file instanceof \SplFileInfo ? $file : new \SplFileInfo($file));
Chris@0 608 }
Chris@0 609 $this->iterators[] = $it;
Chris@0 610 } else {
Chris@0 611 throw new \InvalidArgumentException('Finder::append() method wrong argument type.');
Chris@0 612 }
Chris@0 613
Chris@0 614 return $this;
Chris@0 615 }
Chris@0 616
Chris@0 617 /**
Chris@12 618 * Check if the any results were found.
Chris@12 619 *
Chris@12 620 * @return bool
Chris@12 621 */
Chris@12 622 public function hasResults()
Chris@12 623 {
Chris@12 624 foreach ($this->getIterator() as $_) {
Chris@12 625 return true;
Chris@12 626 }
Chris@12 627
Chris@12 628 return false;
Chris@12 629 }
Chris@12 630
Chris@12 631 /**
Chris@0 632 * Counts all the results collected by the iterators.
Chris@0 633 *
Chris@0 634 * @return int
Chris@0 635 */
Chris@0 636 public function count()
Chris@0 637 {
Chris@0 638 return iterator_count($this->getIterator());
Chris@0 639 }
Chris@0 640
Chris@0 641 /**
Chris@17 642 * @param string $dir
Chris@0 643 *
Chris@0 644 * @return \Iterator
Chris@0 645 */
Chris@0 646 private function searchInDirectory($dir)
Chris@0 647 {
Chris@18 648 $exclude = $this->exclude;
Chris@18 649 $notPaths = $this->notPaths;
Chris@18 650
Chris@0 651 if (static::IGNORE_VCS_FILES === (static::IGNORE_VCS_FILES & $this->ignore)) {
Chris@18 652 $exclude = array_merge($exclude, self::$vcsPatterns);
Chris@0 653 }
Chris@0 654
Chris@0 655 if (static::IGNORE_DOT_FILES === (static::IGNORE_DOT_FILES & $this->ignore)) {
Chris@18 656 $notPaths[] = '#(^|/)\..+(/|$)#';
Chris@0 657 }
Chris@0 658
Chris@0 659 $minDepth = 0;
Chris@0 660 $maxDepth = PHP_INT_MAX;
Chris@0 661
Chris@0 662 foreach ($this->depths as $comparator) {
Chris@0 663 switch ($comparator->getOperator()) {
Chris@0 664 case '>':
Chris@0 665 $minDepth = $comparator->getTarget() + 1;
Chris@0 666 break;
Chris@0 667 case '>=':
Chris@0 668 $minDepth = $comparator->getTarget();
Chris@0 669 break;
Chris@0 670 case '<':
Chris@0 671 $maxDepth = $comparator->getTarget() - 1;
Chris@0 672 break;
Chris@0 673 case '<=':
Chris@0 674 $maxDepth = $comparator->getTarget();
Chris@0 675 break;
Chris@0 676 default:
Chris@0 677 $minDepth = $maxDepth = $comparator->getTarget();
Chris@0 678 }
Chris@0 679 }
Chris@0 680
Chris@0 681 $flags = \RecursiveDirectoryIterator::SKIP_DOTS;
Chris@0 682
Chris@0 683 if ($this->followLinks) {
Chris@0 684 $flags |= \RecursiveDirectoryIterator::FOLLOW_SYMLINKS;
Chris@0 685 }
Chris@0 686
Chris@0 687 $iterator = new Iterator\RecursiveDirectoryIterator($dir, $flags, $this->ignoreUnreadableDirs);
Chris@0 688
Chris@18 689 if ($exclude) {
Chris@18 690 $iterator = new Iterator\ExcludeDirectoryFilterIterator($iterator, $exclude);
Chris@0 691 }
Chris@0 692
Chris@0 693 $iterator = new \RecursiveIteratorIterator($iterator, \RecursiveIteratorIterator::SELF_FIRST);
Chris@0 694
Chris@0 695 if ($minDepth > 0 || $maxDepth < PHP_INT_MAX) {
Chris@0 696 $iterator = new Iterator\DepthRangeFilterIterator($iterator, $minDepth, $maxDepth);
Chris@0 697 }
Chris@0 698
Chris@0 699 if ($this->mode) {
Chris@0 700 $iterator = new Iterator\FileTypeFilterIterator($iterator, $this->mode);
Chris@0 701 }
Chris@0 702
Chris@0 703 if ($this->names || $this->notNames) {
Chris@0 704 $iterator = new Iterator\FilenameFilterIterator($iterator, $this->names, $this->notNames);
Chris@0 705 }
Chris@0 706
Chris@0 707 if ($this->contains || $this->notContains) {
Chris@0 708 $iterator = new Iterator\FilecontentFilterIterator($iterator, $this->contains, $this->notContains);
Chris@0 709 }
Chris@0 710
Chris@0 711 if ($this->sizes) {
Chris@0 712 $iterator = new Iterator\SizeRangeFilterIterator($iterator, $this->sizes);
Chris@0 713 }
Chris@0 714
Chris@0 715 if ($this->dates) {
Chris@0 716 $iterator = new Iterator\DateRangeFilterIterator($iterator, $this->dates);
Chris@0 717 }
Chris@0 718
Chris@0 719 if ($this->filters) {
Chris@0 720 $iterator = new Iterator\CustomFilterIterator($iterator, $this->filters);
Chris@0 721 }
Chris@0 722
Chris@18 723 if ($this->paths || $notPaths) {
Chris@18 724 $iterator = new Iterator\PathFilterIterator($iterator, $this->paths, $notPaths);
Chris@0 725 }
Chris@0 726
Chris@0 727 if ($this->sort) {
Chris@0 728 $iteratorAggregate = new Iterator\SortableIterator($iterator, $this->sort);
Chris@0 729 $iterator = $iteratorAggregate->getIterator();
Chris@0 730 }
Chris@0 731
Chris@0 732 return $iterator;
Chris@0 733 }
Chris@13 734
Chris@13 735 /**
Chris@13 736 * Normalizes given directory names by removing trailing slashes.
Chris@13 737 *
Chris@17 738 * Excluding: (s)ftp:// wrapper
Chris@17 739 *
Chris@13 740 * @param string $dir
Chris@13 741 *
Chris@13 742 * @return string
Chris@13 743 */
Chris@13 744 private function normalizeDir($dir)
Chris@13 745 {
Chris@17 746 $dir = rtrim($dir, '/'.\DIRECTORY_SEPARATOR);
Chris@17 747
Chris@17 748 if (preg_match('#^s?ftp://#', $dir)) {
Chris@17 749 $dir .= '/';
Chris@17 750 }
Chris@17 751
Chris@17 752 return $dir;
Chris@13 753 }
Chris@0 754 }