PageRenderTime 46ms CodeModel.GetById 16ms RepoModel.GetById 0ms app.codeStats 0ms

/silex_private/symfony/finder/Symfony/Component/Finder/Finder.php

https://bitbucket.org/ortic/deploy
PHP | 631 lines | 237 code | 74 blank | 320 comment | 25 complexity | fc230f3fe86fc11e658a880b95d46b2a MD5 | raw file
Possible License(s): LGPL-3.0, BSD-3-Clause, BSD-2-Clause, LGPL-2.1
  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. /**
  12. * Finder allows to build rules to find files and directories.
  13. *
  14. * It is a thin wrapper around several specialized iterator classes.
  15. *
  16. * All rules may be invoked several times.
  17. *
  18. * All methods return the current Finder object to allow easy chaining:
  19. *
  20. * $finder = Finder::create()->files()->name('*.php')->in(__DIR__);
  21. *
  22. * @author Fabien Potencier <fabien@symfony.com>
  23. *
  24. * @api
  25. */
  26. class Finder implements \IteratorAggregate, \Countable
  27. {
  28. const IGNORE_VCS_FILES = 1;
  29. const IGNORE_DOT_FILES = 2;
  30. private $mode = 0;
  31. private $names = array();
  32. private $notNames = array();
  33. private $exclude = array();
  34. private $filters = array();
  35. private $depths = array();
  36. private $sizes = array();
  37. private $followLinks = false;
  38. private $sort = false;
  39. private $ignore = 0;
  40. private $dirs = array();
  41. private $dates = array();
  42. private $iterators = array();
  43. private $contains = array();
  44. private $notContains = array();
  45. private static $vcsPatterns = array('.svn', '_svn', 'CVS', '_darcs', '.arch-params', '.monotone', '.bzr', '.git', '.hg');
  46. /**
  47. * Constructor.
  48. */
  49. public function __construct()
  50. {
  51. $this->ignore = static::IGNORE_VCS_FILES | static::IGNORE_DOT_FILES;
  52. }
  53. /**
  54. * Creates a new Finder.
  55. *
  56. * @return Finder A new Finder instance
  57. *
  58. * @api
  59. */
  60. public static function create()
  61. {
  62. return new static();
  63. }
  64. /**
  65. * Restricts the matching to directories only.
  66. *
  67. * @return Finder The current Finder instance
  68. *
  69. * @api
  70. */
  71. public function directories()
  72. {
  73. $this->mode = Iterator\FileTypeFilterIterator::ONLY_DIRECTORIES;
  74. return $this;
  75. }
  76. /**
  77. * Restricts the matching to files only.
  78. *
  79. * @return Finder The current Finder instance
  80. *
  81. * @api
  82. */
  83. public function files()
  84. {
  85. $this->mode = Iterator\FileTypeFilterIterator::ONLY_FILES;
  86. return $this;
  87. }
  88. /**
  89. * Adds tests for the directory depth.
  90. *
  91. * Usage:
  92. *
  93. * $finder->depth('> 1') // the Finder will start matching at level 1.
  94. * $finder->depth('< 3') // the Finder will descend at most 3 levels of directories below the starting point.
  95. *
  96. * @param int $level The depth level expression
  97. *
  98. * @return Finder The current Finder instance
  99. *
  100. * @see Symfony\Component\Finder\Iterator\DepthRangeFilterIterator
  101. * @see Symfony\Component\Finder\Comparator\NumberComparator
  102. *
  103. * @api
  104. */
  105. public function depth($level)
  106. {
  107. $this->depths[] = new Comparator\NumberComparator($level);
  108. return $this;
  109. }
  110. /**
  111. * Adds tests for file dates (last modified).
  112. *
  113. * The date must be something that strtotime() is able to parse:
  114. *
  115. * $finder->date('since yesterday');
  116. * $finder->date('until 2 days ago');
  117. * $finder->date('> now - 2 hours');
  118. * $finder->date('>= 2005-10-15');
  119. *
  120. * @param string $date A date rage string
  121. *
  122. * @return Finder The current Finder instance
  123. *
  124. * @see strtotime
  125. * @see Symfony\Component\Finder\Iterator\DateRangeFilterIterator
  126. * @see Symfony\Component\Finder\Comparator\DateComparator
  127. *
  128. * @api
  129. */
  130. public function date($date)
  131. {
  132. $this->dates[] = new Comparator\DateComparator($date);
  133. return $this;
  134. }
  135. /**
  136. * Adds rules that files must match.
  137. *
  138. * You can use patterns (delimited with / sign), globs or simple strings.
  139. *
  140. * $finder->name('*.php')
  141. * $finder->name('/\.php$/') // same as above
  142. * $finder->name('test.php')
  143. *
  144. * @param string $pattern A pattern (a regexp, a glob, or a string)
  145. *
  146. * @return Finder The current Finder instance
  147. *
  148. * @see Symfony\Component\Finder\Iterator\FilenameFilterIterator
  149. *
  150. * @api
  151. */
  152. public function name($pattern)
  153. {
  154. $this->names[] = $pattern;
  155. return $this;
  156. }
  157. /**
  158. * Adds rules that files must not match.
  159. *
  160. * @param string $pattern A pattern (a regexp, a glob, or a string)
  161. *
  162. * @return Finder The current Finder instance
  163. *
  164. * @see Symfony\Component\Finder\Iterator\FilenameFilterIterator
  165. *
  166. * @api
  167. */
  168. public function notName($pattern)
  169. {
  170. $this->notNames[] = $pattern;
  171. return $this;
  172. }
  173. /**
  174. * Adds tests that file contents must match.
  175. *
  176. * Strings or PCRE patterns can be used:
  177. *
  178. * $finder->contains('Lorem ipsum')
  179. * $finder->contains('/Lorem ipsum/i')
  180. *
  181. * @param string $pattern A pattern (string or regexp)
  182. *
  183. * @return Finder The current Finder instance
  184. *
  185. * @see Symfony\Component\Finder\Iterator\FilecontentFilterIterator
  186. */
  187. public function contains($pattern)
  188. {
  189. $this->contains[] = $pattern;
  190. return $this;
  191. }
  192. /**
  193. * Adds tests that file contents must not match.
  194. *
  195. * Strings or PCRE patterns can be used:
  196. *
  197. * $finder->notContains('Lorem ipsum')
  198. * $finder->notContains('/Lorem ipsum/i')
  199. *
  200. * @param string $pattern A pattern (string or regexp)
  201. *
  202. * @return Finder The current Finder instance
  203. *
  204. * @see Symfony\Component\Finder\Iterator\FilecontentFilterIterator
  205. */
  206. public function notContains($pattern)
  207. {
  208. $this->notContains[] = $pattern;
  209. return $this;
  210. }
  211. /**
  212. * Adds tests for file sizes.
  213. *
  214. * $finder->size('> 10K');
  215. * $finder->size('<= 1Ki');
  216. * $finder->size(4);
  217. *
  218. * @param string $size A size range string
  219. *
  220. * @return Finder The current Finder instance
  221. *
  222. * @see Symfony\Component\Finder\Iterator\SizeRangeFilterIterator
  223. * @see Symfony\Component\Finder\Comparator\NumberComparator
  224. *
  225. * @api
  226. */
  227. public function size($size)
  228. {
  229. $this->sizes[] = new Comparator\NumberComparator($size);
  230. return $this;
  231. }
  232. /**
  233. * Excludes directories.
  234. *
  235. * @param string|array $dirs A directory path or an array of directories
  236. *
  237. * @return Finder The current Finder instance
  238. *
  239. * @see Symfony\Component\Finder\Iterator\ExcludeDirectoryFilterIterator
  240. *
  241. * @api
  242. */
  243. public function exclude($dirs)
  244. {
  245. $this->exclude = array_merge($this->exclude, (array) $dirs);
  246. return $this;
  247. }
  248. /**
  249. * Excludes "hidden" directories and files (starting with a dot).
  250. *
  251. * @param Boolean $ignoreDotFiles Whether to exclude "hidden" files or not
  252. *
  253. * @return Finder The current Finder instance
  254. *
  255. * @see Symfony\Component\Finder\Iterator\ExcludeDirectoryFilterIterator
  256. *
  257. * @api
  258. */
  259. public function ignoreDotFiles($ignoreDotFiles)
  260. {
  261. if ($ignoreDotFiles) {
  262. $this->ignore = $this->ignore | static::IGNORE_DOT_FILES;
  263. } else {
  264. $this->ignore = $this->ignore & ~static::IGNORE_DOT_FILES;
  265. }
  266. return $this;
  267. }
  268. /**
  269. * Forces the finder to ignore version control directories.
  270. *
  271. * @param Boolean $ignoreVCS Whether to exclude VCS files or not
  272. *
  273. * @return Finder The current Finder instance
  274. *
  275. * @see Symfony\Component\Finder\Iterator\ExcludeDirectoryFilterIterator
  276. *
  277. * @api
  278. */
  279. public function ignoreVCS($ignoreVCS)
  280. {
  281. if ($ignoreVCS) {
  282. $this->ignore = $this->ignore | static::IGNORE_VCS_FILES;
  283. } else {
  284. $this->ignore = $this->ignore & ~static::IGNORE_VCS_FILES;
  285. }
  286. return $this;
  287. }
  288. public static function addVCSPattern($pattern)
  289. {
  290. self::$vcsPatterns[] = $pattern;
  291. }
  292. /**
  293. * Sorts files and directories by an anonymous function.
  294. *
  295. * The anonymous function receives two \SplFileInfo instances to compare.
  296. *
  297. * This can be slow as all the matching files and directories must be retrieved for comparison.
  298. *
  299. * @param Closure $closure An anonymous function
  300. *
  301. * @return Finder The current Finder instance
  302. *
  303. * @see Symfony\Component\Finder\Iterator\SortableIterator
  304. *
  305. * @api
  306. */
  307. public function sort(\Closure $closure)
  308. {
  309. $this->sort = $closure;
  310. return $this;
  311. }
  312. /**
  313. * Sorts files and directories by name.
  314. *
  315. * This can be slow as all the matching files and directories must be retrieved for comparison.
  316. *
  317. * @return Finder The current Finder instance
  318. *
  319. * @see Symfony\Component\Finder\Iterator\SortableIterator
  320. *
  321. * @api
  322. */
  323. public function sortByName()
  324. {
  325. $this->sort = Iterator\SortableIterator::SORT_BY_NAME;
  326. return $this;
  327. }
  328. /**
  329. * Sorts files and directories by type (directories before files), then by name.
  330. *
  331. * This can be slow as all the matching files and directories must be retrieved for comparison.
  332. *
  333. * @return Finder The current Finder instance
  334. *
  335. * @see Symfony\Component\Finder\Iterator\SortableIterator
  336. *
  337. * @api
  338. */
  339. public function sortByType()
  340. {
  341. $this->sort = Iterator\SortableIterator::SORT_BY_TYPE;
  342. return $this;
  343. }
  344. /**
  345. * Sorts files and directories by the last accessed time.
  346. *
  347. * This is the time that the file was last accessed, read or written to.
  348. *
  349. * This can be slow as all the matching files and directories must be retrieved for comparison.
  350. *
  351. * @return Finder The current Finder instance
  352. *
  353. * @see Symfony\Component\Finder\Iterator\SortableIterator
  354. *
  355. * @api
  356. */
  357. public function sortByAccessedTime()
  358. {
  359. $this->sort = Iterator\SortableIterator::SORT_BY_ACCESSED_TIME;
  360. return $this;
  361. }
  362. /**
  363. * Sorts files and directories by the last inode changed time.
  364. *
  365. * This is the time that the inode information was last modified (permissions, owner, group or other metadata).
  366. *
  367. * On Windows, since inode is not available, changed time is actually the file creation time.
  368. *
  369. * This can be slow as all the matching files and directories must be retrieved for comparison.
  370. *
  371. * @return Finder The current Finder instance
  372. *
  373. * @see Symfony\Component\Finder\Iterator\SortableIterator
  374. *
  375. * @api
  376. */
  377. public function sortByChangedTime()
  378. {
  379. $this->sort = Iterator\SortableIterator::SORT_BY_CHANGED_TIME;
  380. return $this;
  381. }
  382. /**
  383. * Sorts files and directories by the last modified time.
  384. *
  385. * This is the last time the actual contents of the file were last modified.
  386. *
  387. * This can be slow as all the matching files and directories must be retrieved for comparison.
  388. *
  389. * @return Finder The current Finder instance
  390. *
  391. * @see Symfony\Component\Finder\Iterator\SortableIterator
  392. *
  393. * @api
  394. */
  395. public function sortByModifiedTime()
  396. {
  397. $this->sort = Iterator\SortableIterator::SORT_BY_MODIFIED_TIME;
  398. return $this;
  399. }
  400. /**
  401. * Filters the iterator with an anonymous function.
  402. *
  403. * The anonymous function receives a \SplFileInfo and must return false
  404. * to remove files.
  405. *
  406. * @param Closure $closure An anonymous function
  407. *
  408. * @return Finder The current Finder instance
  409. *
  410. * @see Symfony\Component\Finder\Iterator\CustomFilterIterator
  411. *
  412. * @api
  413. */
  414. public function filter(\Closure $closure)
  415. {
  416. $this->filters[] = $closure;
  417. return $this;
  418. }
  419. /**
  420. * Forces the following of symlinks.
  421. *
  422. * @return Finder The current Finder instance
  423. *
  424. * @api
  425. */
  426. public function followLinks()
  427. {
  428. $this->followLinks = true;
  429. return $this;
  430. }
  431. /**
  432. * Searches files and directories which match defined rules.
  433. *
  434. * @param string|array $dirs A directory path or an array of directories
  435. *
  436. * @return Finder The current Finder instance
  437. *
  438. * @throws \InvalidArgumentException if one of the directory does not exist
  439. *
  440. * @api
  441. */
  442. public function in($dirs)
  443. {
  444. $dirs = (array) $dirs;
  445. foreach ($dirs as $dir) {
  446. if (!is_dir($dir)) {
  447. throw new \InvalidArgumentException(sprintf('The "%s" directory does not exist.', $dir));
  448. }
  449. }
  450. $this->dirs = array_merge($this->dirs, $dirs);
  451. return $this;
  452. }
  453. /**
  454. * Returns an Iterator for the current Finder configuration.
  455. *
  456. * This method implements the IteratorAggregate interface.
  457. *
  458. * @return \Iterator An iterator
  459. *
  460. * @throws \LogicException if the in() method has not been called
  461. */
  462. public function getIterator()
  463. {
  464. if (0 === count($this->dirs)) {
  465. throw new \LogicException('You must call the in() method before iterating over a Finder.');
  466. }
  467. if (1 === count($this->dirs) && 0 === count($this->iterators)) {
  468. return $this->searchInDirectory($this->dirs[0]);
  469. }
  470. $iterator = new \AppendIterator();
  471. foreach ($this->dirs as $dir) {
  472. $iterator->append($this->searchInDirectory($dir));
  473. }
  474. foreach ($this->iterators as $it) {
  475. $iterator->append($it);
  476. }
  477. return $iterator;
  478. }
  479. /**
  480. * Appends an existing set of files/directories to the finder.
  481. *
  482. * The set can be another Finder, an Iterator, an IteratorAggregate, or even a plain array.
  483. *
  484. * @param mixed $iterator
  485. */
  486. public function append($iterator)
  487. {
  488. if ($iterator instanceof \IteratorAggregate) {
  489. $this->iterators[] = $iterator->getIterator();
  490. } elseif ($iterator instanceof \Iterator) {
  491. $this->iterators[] = $iterator;
  492. } elseif ($iterator instanceof \Traversable || is_array($iterator)) {
  493. $it = new \ArrayIterator();
  494. foreach ($iterator as $file) {
  495. $it->append($file instanceof \SplFileInfo ? $file : new \SplFileInfo($file));
  496. }
  497. $this->iterators[] = $it;
  498. } else {
  499. throw new \InvalidArgumentException('Finder::append() method wrong argument type.');
  500. }
  501. }
  502. /**
  503. * Counts all the results collected by the iterators.
  504. *
  505. * @return int
  506. */
  507. public function count()
  508. {
  509. return iterator_count($this->getIterator());
  510. }
  511. private function searchInDirectory($dir)
  512. {
  513. $flags = \RecursiveDirectoryIterator::SKIP_DOTS;
  514. if ($this->followLinks) {
  515. $flags |= \RecursiveDirectoryIterator::FOLLOW_SYMLINKS;
  516. }
  517. $iterator = new \RecursiveIteratorIterator(
  518. new Iterator\RecursiveDirectoryIterator($dir, $flags),
  519. \RecursiveIteratorIterator::SELF_FIRST
  520. );
  521. if ($this->depths) {
  522. $iterator = new Iterator\DepthRangeFilterIterator($iterator, $this->depths);
  523. }
  524. if ($this->mode) {
  525. $iterator = new Iterator\FileTypeFilterIterator($iterator, $this->mode);
  526. }
  527. if (static::IGNORE_VCS_FILES === (static::IGNORE_VCS_FILES & $this->ignore)) {
  528. $this->exclude = array_merge($this->exclude, self::$vcsPatterns);
  529. }
  530. if (static::IGNORE_DOT_FILES === (static::IGNORE_DOT_FILES & $this->ignore)) {
  531. $this->notNames[] = '/^\..+/';
  532. }
  533. if ($this->exclude) {
  534. $iterator = new Iterator\ExcludeDirectoryFilterIterator($iterator, $this->exclude);
  535. }
  536. if ($this->names || $this->notNames) {
  537. $iterator = new Iterator\FilenameFilterIterator($iterator, $this->names, $this->notNames);
  538. }
  539. if ($this->contains || $this->notContains) {
  540. $iterator = new Iterator\FilecontentFilterIterator($iterator, $this->contains, $this->notContains);
  541. }
  542. if ($this->sizes) {
  543. $iterator = new Iterator\SizeRangeFilterIterator($iterator, $this->sizes);
  544. }
  545. if ($this->dates) {
  546. $iterator = new Iterator\DateRangeFilterIterator($iterator, $this->dates);
  547. }
  548. if ($this->filters) {
  549. $iterator = new Iterator\CustomFilterIterator($iterator, $this->filters);
  550. }
  551. if ($this->sort) {
  552. $iteratorAggregate = new Iterator\SortableIterator($iterator, $this->sort);
  553. $iterator = $iteratorAggregate->getIterator();
  554. }
  555. return $iterator;
  556. }
  557. }