PageRenderTime 48ms CodeModel.GetById 17ms RepoModel.GetById 1ms app.codeStats 0ms

/_app/vendor/Symfony/Component/Finder/Finder.php

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