This commit is contained in:
2025-02-03 18:49:47 +03:00
parent f1a79d66ec
commit dd62ad0ca4
1739 changed files with 154102 additions and 0 deletions

View File

@@ -0,0 +1,80 @@
<?php declare(strict_types=1);
/*
* This file is part of phpunit/php-file-iterator.
*
* (c) Sebastian Bergmann <sebastian@phpunit.de>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace SebastianBergmann\FileIterator;
use function assert;
use function str_starts_with;
use RecursiveDirectoryIterator;
use RecursiveFilterIterator;
use SplFileInfo;
/**
* @internal This class is not covered by the backward compatibility promise for phpunit/php-file-iterator
*/
final class ExcludeIterator extends RecursiveFilterIterator
{
/**
* @var list<string>
*/
private array $exclude;
/**
* @param list<string> $exclude
*/
public function __construct(RecursiveDirectoryIterator $iterator, array $exclude)
{
parent::__construct($iterator);
$this->exclude = $exclude;
}
public function accept(): bool
{
$current = $this->current();
assert($current instanceof SplFileInfo);
$path = $current->getRealPath();
if ($path === false) {
return false;
}
foreach ($this->exclude as $exclude) {
if (str_starts_with($path, $exclude)) {
return false;
}
}
return true;
}
public function hasChildren(): bool
{
return $this->getInnerIterator()->hasChildren();
}
public function getChildren(): self
{
return new self(
$this->getInnerIterator()->getChildren(),
$this->exclude,
);
}
public function getInnerIterator(): RecursiveDirectoryIterator
{
$innerIterator = parent::getInnerIterator();
assert($innerIterator instanceof RecursiveDirectoryIterator);
return $innerIterator;
}
}

View File

@@ -0,0 +1,52 @@
<?php declare(strict_types=1);
/*
* This file is part of phpunit/php-file-iterator.
*
* (c) Sebastian Bergmann <sebastian@phpunit.de>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace SebastianBergmann\FileIterator;
use function array_unique;
use function assert;
use function sort;
use SplFileInfo;
/**
* @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit
*/
final class Facade
{
/**
* @param list<non-empty-string>|non-empty-string $paths
* @param list<non-empty-string>|string $suffixes
* @param list<non-empty-string>|string $prefixes
* @param list<non-empty-string> $exclude
*
* @return list<non-empty-string>
*/
public function getFilesAsArray(array|string $paths, array|string $suffixes = '', array|string $prefixes = '', array $exclude = []): array
{
$iterator = (new Factory)->getFileIterator($paths, $suffixes, $prefixes, $exclude);
$files = [];
foreach ($iterator as $file) {
assert($file instanceof SplFileInfo);
$file = $file->getRealPath();
if ($file) {
$files[] = $file;
}
}
$files = array_unique($files);
sort($files);
return $files;
}
}

View File

@@ -0,0 +1,157 @@
<?php declare(strict_types=1);
/*
* This file is part of phpunit/php-file-iterator.
*
* (c) Sebastian Bergmann <sebastian@phpunit.de>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace SebastianBergmann\FileIterator;
use const GLOB_ONLYDIR;
use function array_filter;
use function array_map;
use function array_merge;
use function array_unique;
use function array_values;
use function glob;
use function is_dir;
use function is_string;
use function realpath;
use function sort;
use function stripos;
use function substr;
use AppendIterator;
use FilesystemIterator;
use RecursiveDirectoryIterator;
use RecursiveIteratorIterator;
/**
* @internal This class is not covered by the backward compatibility promise for phpunit/php-file-iterator
*/
final class Factory
{
/**
* @param list<non-empty-string>|non-empty-string $paths
* @param list<non-empty-string>|string $suffixes
* @param list<non-empty-string>|string $prefixes
* @param list<non-empty-string> $exclude
*
* @phpstan-ignore missingType.generics
*/
public function getFileIterator(array|string $paths, array|string $suffixes = '', array|string $prefixes = '', array $exclude = []): AppendIterator
{
if (is_string($paths)) {
$paths = [$paths];
}
$paths = $this->resolveWildcards($paths);
$exclude = $this->resolveWildcards($exclude);
if (is_string($prefixes)) {
if ($prefixes !== '') {
$prefixes = [$prefixes];
} else {
$prefixes = [];
}
}
if (is_string($suffixes)) {
if ($suffixes !== '') {
$suffixes = [$suffixes];
} else {
$suffixes = [];
}
}
$iterator = new AppendIterator;
foreach ($paths as $path) {
if (is_dir($path)) {
$iterator->append(
new Iterator(
$path,
new RecursiveIteratorIterator(
new ExcludeIterator(
new RecursiveDirectoryIterator($path, FilesystemIterator::FOLLOW_SYMLINKS | FilesystemIterator::SKIP_DOTS),
$exclude,
),
),
$suffixes,
$prefixes,
),
);
}
}
return $iterator;
}
/**
* @param list<non-empty-string> $paths
*
* @return list<non-empty-string>
*/
private function resolveWildcards(array $paths): array
{
$_paths = [[]];
foreach ($paths as $path) {
if ($locals = $this->globstar($path)) {
$_paths[] = array_map('\realpath', $locals);
} else {
// @codeCoverageIgnoreStart
$_paths[] = [realpath($path)];
// @codeCoverageIgnoreEnd
}
}
return array_values(array_filter(array_merge(...$_paths)));
}
/**
* @see https://gist.github.com/funkjedi/3feee27d873ae2297b8e2370a7082aad
*
* @return list<string>
*/
private function globstar(string $pattern): array
{
if (stripos($pattern, '**') === false) {
$files = glob($pattern, GLOB_ONLYDIR);
} else {
$position = stripos($pattern, '**');
$rootPattern = substr($pattern, 0, $position - 1);
$restPattern = substr($pattern, $position + 2);
$patterns = [$rootPattern . $restPattern];
$rootPattern .= '/*';
while ($directories = glob($rootPattern, GLOB_ONLYDIR)) {
$rootPattern .= '/*';
foreach ($directories as $directory) {
$patterns[] = $directory . $restPattern;
}
}
$files = [];
foreach ($patterns as $_pattern) {
$files = array_merge($files, $this->globstar($_pattern));
}
}
if ($files !== false) {
$files = array_unique($files);
sort($files);
return $files;
}
// @codeCoverageIgnoreStart
return [];
// @codeCoverageIgnoreEnd
}
}

View File

@@ -0,0 +1,110 @@
<?php declare(strict_types=1);
/*
* This file is part of phpunit/php-file-iterator.
*
* (c) Sebastian Bergmann <sebastian@phpunit.de>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace SebastianBergmann\FileIterator;
use function preg_match;
use function realpath;
use function str_ends_with;
use function str_replace;
use function str_starts_with;
use FilterIterator;
use SplFileInfo;
/**
* @template-extends FilterIterator<int, SplFileInfo, \Iterator>
*
* @internal This class is not covered by the backward compatibility promise for phpunit/php-file-iterator
*/
final class Iterator extends FilterIterator
{
public const int PREFIX = 0;
public const int SUFFIX = 1;
private false|string $basePath;
/**
* @var list<string>
*/
private array $suffixes;
/**
* @var list<string>
*/
private array $prefixes;
/**
* @param list<string> $suffixes
* @param list<string> $prefixes
*/
public function __construct(string $basePath, \Iterator $iterator, array $suffixes = [], array $prefixes = [])
{
$this->basePath = realpath($basePath);
$this->prefixes = $prefixes;
$this->suffixes = $suffixes;
parent::__construct($iterator);
}
public function accept(): bool
{
$current = $this->getInnerIterator()->current();
$filename = $current->getFilename();
$realPath = $current->getRealPath();
if ($realPath === false) {
// @codeCoverageIgnoreStart
return false;
// @codeCoverageIgnoreEnd
}
return $this->acceptPath($realPath) &&
$this->acceptPrefix($filename) &&
$this->acceptSuffix($filename);
}
private function acceptPath(string $path): bool
{
// Filter files in hidden directories by checking path that is relative to the base path.
if (preg_match('=/\.[^/]*/=', str_replace((string) $this->basePath, '', $path))) {
return false;
}
return true;
}
private function acceptPrefix(string $filename): bool
{
return $this->acceptSubString($filename, $this->prefixes, self::PREFIX);
}
private function acceptSuffix(string $filename): bool
{
return $this->acceptSubString($filename, $this->suffixes, self::SUFFIX);
}
/**
* @param list<string> $subStrings
*/
private function acceptSubString(string $filename, array $subStrings, int $type): bool
{
if (empty($subStrings)) {
return true;
}
foreach ($subStrings as $string) {
if (($type === self::PREFIX && str_starts_with($filename, $string)) ||
($type === self::SUFFIX && str_ends_with($filename, $string))) {
return true;
}
}
return false;
}
}