Factory.php 4.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154
  1. <?php declare(strict_types=1);
  2. /*
  3. * This file is part of phpunit/php-file-iterator.
  4. *
  5. * (c) Sebastian Bergmann <sebastian@phpunit.de>
  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 SebastianBergmann\FileIterator;
  11. use const GLOB_ONLYDIR;
  12. use function array_filter;
  13. use function array_map;
  14. use function array_merge;
  15. use function array_unique;
  16. use function array_values;
  17. use function glob;
  18. use function is_dir;
  19. use function is_string;
  20. use function realpath;
  21. use function sort;
  22. use function stripos;
  23. use function substr;
  24. use AppendIterator;
  25. use FilesystemIterator;
  26. use RecursiveDirectoryIterator;
  27. use RecursiveIteratorIterator;
  28. /**
  29. * @internal This class is not covered by the backward compatibility promise for phpunit/php-file-iterator
  30. */
  31. final class Factory
  32. {
  33. /**
  34. * @param list<non-empty-string>|non-empty-string $paths
  35. * @param list<non-empty-string>|string $suffixes
  36. * @param list<non-empty-string>|string $prefixes
  37. * @param list<non-empty-string> $exclude
  38. */
  39. public function getFileIterator(array|string $paths, array|string $suffixes = '', array|string $prefixes = '', array $exclude = []): AppendIterator
  40. {
  41. if (is_string($paths)) {
  42. $paths = [$paths];
  43. }
  44. $paths = $this->resolveWildcards($paths);
  45. $exclude = $this->resolveWildcards($exclude);
  46. if (is_string($prefixes)) {
  47. if ($prefixes !== '') {
  48. $prefixes = [$prefixes];
  49. } else {
  50. $prefixes = [];
  51. }
  52. }
  53. if (is_string($suffixes)) {
  54. if ($suffixes !== '') {
  55. $suffixes = [$suffixes];
  56. } else {
  57. $suffixes = [];
  58. }
  59. }
  60. $iterator = new AppendIterator;
  61. foreach ($paths as $path) {
  62. if (is_dir($path)) {
  63. $iterator->append(
  64. new Iterator(
  65. $path,
  66. new RecursiveIteratorIterator(
  67. new ExcludeIterator(
  68. new RecursiveDirectoryIterator($path, FilesystemIterator::FOLLOW_SYMLINKS | FilesystemIterator::SKIP_DOTS),
  69. $exclude,
  70. ),
  71. ),
  72. $suffixes,
  73. $prefixes,
  74. ),
  75. );
  76. }
  77. }
  78. return $iterator;
  79. }
  80. /**
  81. * @param list<non-empty-string> $paths
  82. *
  83. * @return list<non-empty-string>
  84. */
  85. private function resolveWildcards(array $paths): array
  86. {
  87. $_paths = [[]];
  88. foreach ($paths as $path) {
  89. if ($locals = $this->globstar($path)) {
  90. $_paths[] = array_map('\realpath', $locals);
  91. } else {
  92. // @codeCoverageIgnoreStart
  93. $_paths[] = [realpath($path)];
  94. // @codeCoverageIgnoreEnd
  95. }
  96. }
  97. return array_values(array_filter(array_merge(...$_paths)));
  98. }
  99. /**
  100. * @see https://gist.github.com/funkjedi/3feee27d873ae2297b8e2370a7082aad
  101. *
  102. * @return list<string>
  103. */
  104. private function globstar(string $pattern)
  105. {
  106. if (stripos($pattern, '**') === false) {
  107. $files = glob($pattern, GLOB_ONLYDIR);
  108. } else {
  109. $position = stripos($pattern, '**');
  110. $rootPattern = substr($pattern, 0, $position - 1);
  111. $restPattern = substr($pattern, $position + 2);
  112. $patterns = [$rootPattern . $restPattern];
  113. $rootPattern .= '/*';
  114. while ($dirs = glob($rootPattern, GLOB_ONLYDIR)) {
  115. $rootPattern .= '/*';
  116. foreach ($dirs as $dir) {
  117. $patterns[] = $dir . $restPattern;
  118. }
  119. }
  120. $files = [];
  121. foreach ($patterns as $pat) {
  122. $files = array_merge($files, $this->globstar($pat));
  123. }
  124. }
  125. if ($files !== false) {
  126. $files = array_unique($files);
  127. sort($files);
  128. return $files;
  129. }
  130. // @codeCoverageIgnoreStart
  131. return [];
  132. // @codeCoverageIgnoreEnd
  133. }
  134. }