Upgrade framework

This commit is contained in:
2023-11-14 16:54:35 +01:00
parent 1648a5cd42
commit 4fcf6fffcc
10548 changed files with 693138 additions and 466698 deletions

View File

@@ -18,23 +18,23 @@ namespace Symfony\Component\Finder\Iterator;
* to remove files.
*
* @author Fabien Potencier <fabien@symfony.com>
*
* @extends \FilterIterator<string, \SplFileInfo>
*/
class CustomFilterIterator extends FilterIterator
class CustomFilterIterator extends \FilterIterator
{
private $filters = array();
private array $filters = [];
/**
* Constructor.
*
* @param \Iterator $iterator The Iterator to filter
* @param callable[] $filters An array of PHP callbacks
* @param \Iterator<string, \SplFileInfo> $iterator The Iterator to filter
* @param callable[] $filters An array of PHP callbacks
*
* @throws \InvalidArgumentException
*/
public function __construct(\Iterator $iterator, array $filters)
{
foreach ($filters as $filter) {
if (!is_callable($filter)) {
if (!\is_callable($filter)) {
throw new \InvalidArgumentException('Invalid PHP callback.');
}
}
@@ -45,15 +45,13 @@ class CustomFilterIterator extends FilterIterator
/**
* Filters the iterator values.
*
* @return bool true if the value should be kept, false otherwise
*/
public function accept()
public function accept(): bool
{
$fileinfo = $this->current();
foreach ($this->filters as $filter) {
if (false === call_user_func($filter, $fileinfo)) {
if (false === $filter($fileinfo)) {
return false;
}
}

View File

@@ -17,16 +17,16 @@ use Symfony\Component\Finder\Comparator\DateComparator;
* DateRangeFilterIterator filters out files that are not in the given date range (last modified dates).
*
* @author Fabien Potencier <fabien@symfony.com>
*
* @extends \FilterIterator<string, \SplFileInfo>
*/
class DateRangeFilterIterator extends FilterIterator
class DateRangeFilterIterator extends \FilterIterator
{
private $comparators = array();
private array $comparators = [];
/**
* Constructor.
*
* @param \Iterator $iterator The Iterator to filter
* @param DateComparator[] $comparators An array of DateComparator instances
* @param \Iterator<string, \SplFileInfo> $iterator
* @param DateComparator[] $comparators
*/
public function __construct(\Iterator $iterator, array $comparators)
{
@@ -37,10 +37,8 @@ class DateRangeFilterIterator extends FilterIterator
/**
* Filters the iterator values.
*
* @return bool true if the value should be kept, false otherwise
*/
public function accept()
public function accept(): bool
{
$fileinfo = $this->current();

View File

@@ -15,32 +15,33 @@ namespace Symfony\Component\Finder\Iterator;
* DepthRangeFilterIterator limits the directory depth.
*
* @author Fabien Potencier <fabien@symfony.com>
*
* @template-covariant TKey
* @template-covariant TValue
*
* @extends \FilterIterator<TKey, TValue>
*/
class DepthRangeFilterIterator extends FilterIterator
class DepthRangeFilterIterator extends \FilterIterator
{
private $minDepth = 0;
private int $minDepth = 0;
/**
* Constructor.
*
* @param \RecursiveIteratorIterator $iterator The Iterator to filter
* @param int $minDepth The min depth
* @param int $maxDepth The max depth
* @param \RecursiveIteratorIterator<\RecursiveIterator<TKey, TValue>> $iterator The Iterator to filter
* @param int $minDepth The min depth
* @param int $maxDepth The max depth
*/
public function __construct(\RecursiveIteratorIterator $iterator, $minDepth = 0, $maxDepth = PHP_INT_MAX)
public function __construct(\RecursiveIteratorIterator $iterator, int $minDepth = 0, int $maxDepth = \PHP_INT_MAX)
{
$this->minDepth = $minDepth;
$iterator->setMaxDepth(PHP_INT_MAX === $maxDepth ? -1 : $maxDepth);
$iterator->setMaxDepth(\PHP_INT_MAX === $maxDepth ? -1 : $maxDepth);
parent::__construct($iterator);
}
/**
* Filters the iterator values.
*
* @return bool true if the value should be kept, false otherwise
*/
public function accept()
public function accept(): bool
{
return $this->getInnerIterator()->getDepth() >= $this->minDepth;
}

View File

@@ -15,28 +15,29 @@ namespace Symfony\Component\Finder\Iterator;
* ExcludeDirectoryFilterIterator filters out directories.
*
* @author Fabien Potencier <fabien@symfony.com>
*
* @extends \FilterIterator<string, \SplFileInfo>
* @implements \RecursiveIterator<string, \SplFileInfo>
*/
class ExcludeDirectoryFilterIterator extends FilterIterator implements \RecursiveIterator
class ExcludeDirectoryFilterIterator extends \FilterIterator implements \RecursiveIterator
{
private $iterator;
private $isRecursive;
private $excludedDirs = array();
private $excludedPattern;
private \Iterator $iterator;
private bool $isRecursive;
private array $excludedDirs = [];
private ?string $excludedPattern = null;
/**
* Constructor.
*
* @param \Iterator $iterator The Iterator to filter
* @param array $directories An array of directories to exclude
* @param string[] $directories An array of directories to exclude
*/
public function __construct(\Iterator $iterator, array $directories)
{
$this->iterator = $iterator;
$this->isRecursive = $iterator instanceof \RecursiveIterator;
$patterns = array();
$patterns = [];
foreach ($directories as $directory) {
$directory = rtrim($directory, '/');
if (!$this->isRecursive || false !== strpos($directory, '/')) {
if (!$this->isRecursive || str_contains($directory, '/')) {
$patterns[] = preg_quote($directory, '#');
} else {
$this->excludedDirs[$directory] = true;
@@ -51,10 +52,8 @@ class ExcludeDirectoryFilterIterator extends FilterIterator implements \Recursiv
/**
* Filters the iterator values.
*
* @return bool True if the value should be kept, false otherwise
*/
public function accept()
public function accept(): bool
{
if ($this->isRecursive && isset($this->excludedDirs[$this->getFilename()]) && $this->isDir()) {
return false;
@@ -70,14 +69,14 @@ class ExcludeDirectoryFilterIterator extends FilterIterator implements \Recursiv
return true;
}
public function hasChildren()
public function hasChildren(): bool
{
return $this->isRecursive && $this->iterator->hasChildren();
}
public function getChildren()
public function getChildren(): self
{
$children = new self($this->iterator->getChildren(), array());
$children = new self($this->iterator->getChildren(), []);
$children->excludedDirs = $this->excludedDirs;
$children->excludedPattern = $this->excludedPattern;

View File

@@ -15,21 +15,21 @@ namespace Symfony\Component\Finder\Iterator;
* FileTypeFilterIterator only keeps files, directories, or both.
*
* @author Fabien Potencier <fabien@symfony.com>
*
* @extends \FilterIterator<string, \SplFileInfo>
*/
class FileTypeFilterIterator extends FilterIterator
class FileTypeFilterIterator extends \FilterIterator
{
const ONLY_FILES = 1;
const ONLY_DIRECTORIES = 2;
public const ONLY_FILES = 1;
public const ONLY_DIRECTORIES = 2;
private $mode;
private int $mode;
/**
* Constructor.
*
* @param \Iterator $iterator The Iterator to filter
* @param int $mode The mode (self::ONLY_FILES or self::ONLY_DIRECTORIES)
*/
public function __construct(\Iterator $iterator, $mode)
public function __construct(\Iterator $iterator, int $mode)
{
$this->mode = $mode;
@@ -38,10 +38,8 @@ class FileTypeFilterIterator extends FilterIterator
/**
* Filters the iterator values.
*
* @return bool true if the value should be kept, false otherwise
*/
public function accept()
public function accept(): bool
{
$fileinfo = $this->current();
if (self::ONLY_DIRECTORIES === (self::ONLY_DIRECTORIES & $this->mode) && $fileinfo->isFile()) {

View File

@@ -16,15 +16,15 @@ namespace Symfony\Component\Finder\Iterator;
*
* @author Fabien Potencier <fabien@symfony.com>
* @author Włodzimierz Gajda <gajdaw@gajdaw.pl>
*
* @extends MultiplePcreFilterIterator<string, \SplFileInfo>
*/
class FilecontentFilterIterator extends MultiplePcreFilterIterator
{
/**
* Filters the iterator values.
*
* @return bool true if the value should be kept, false otherwise
*/
public function accept()
public function accept(): bool
{
if (!$this->matchRegexps && !$this->noMatchRegexps) {
return true;
@@ -48,10 +48,8 @@ class FilecontentFilterIterator extends MultiplePcreFilterIterator
* Converts string to regexp if necessary.
*
* @param string $str Pattern: string or regexp
*
* @return string regexp corresponding to a given string or regexp
*/
protected function toRegex($str)
protected function toRegex(string $str): string
{
return $this->isRegex($str) ? $str : '/'.preg_quote($str, '/').'/';
}

View File

@@ -17,15 +17,15 @@ use Symfony\Component\Finder\Glob;
* FilenameFilterIterator filters files by patterns (a regexp, a glob, or a string).
*
* @author Fabien Potencier <fabien@symfony.com>
*
* @extends MultiplePcreFilterIterator<string, \SplFileInfo>
*/
class FilenameFilterIterator extends MultiplePcreFilterIterator
{
/**
* Filters the iterator values.
*
* @return bool true if the value should be kept, false otherwise
*/
public function accept()
public function accept(): bool
{
return $this->isAccepted($this->current()->getFilename());
}
@@ -37,10 +37,8 @@ class FilenameFilterIterator extends MultiplePcreFilterIterator
* Glob strings are transformed with Glob::toRegex().
*
* @param string $str Pattern: glob or regexp
*
* @return string regexp corresponding to a given glob or regexp
*/
protected function toRegex($str)
protected function toRegex(string $str): string
{
return $this->isRegex($str) ? $str : Glob::toRegex($str);
}

View File

@@ -1,58 +0,0 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\Finder\Iterator;
/**
* This iterator just overrides the rewind method in order to correct a PHP bug,
* which existed before version 5.5.23/5.6.7.
*
* @see https://bugs.php.net/68557
*
* @author Alex Bogomazov
*/
abstract class FilterIterator extends \FilterIterator
{
/**
* This is a workaround for the problem with \FilterIterator leaving inner \FilesystemIterator in wrong state after
* rewind in some cases.
*
* @see FilterIterator::rewind()
*/
public function rewind()
{
if (\PHP_VERSION_ID > 50607 || (\PHP_VERSION_ID > 50523 && \PHP_VERSION_ID < 50600)) {
parent::rewind();
return;
}
$iterator = $this;
while ($iterator instanceof \OuterIterator) {
$innerIterator = $iterator->getInnerIterator();
if ($innerIterator instanceof RecursiveDirectoryIterator) {
// this condition is necessary for iterators to work properly with non-local filesystems like ftp
if ($innerIterator->isRewindable()) {
$innerIterator->next();
$innerIterator->rewind();
}
} elseif ($innerIterator instanceof \FilesystemIterator) {
$innerIterator->next();
$innerIterator->rewind();
}
$iterator = $innerIterator;
}
parent::rewind();
}
}

View File

@@ -0,0 +1,32 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\Finder\Iterator;
/**
* @author Jérémy Derussé <jeremy@derusse.com>
*
* @internal
*/
class LazyIterator implements \IteratorAggregate
{
private \Closure $iteratorFactory;
public function __construct(callable $iteratorFactory)
{
$this->iteratorFactory = $iteratorFactory instanceof \Closure ? $iteratorFactory : \Closure::fromCallable($iteratorFactory);
}
public function getIterator(): \Traversable
{
yield from ($this->iteratorFactory)();
}
}

View File

@@ -15,18 +15,21 @@ namespace Symfony\Component\Finder\Iterator;
* MultiplePcreFilterIterator filters files using patterns (regexps, globs or strings).
*
* @author Fabien Potencier <fabien@symfony.com>
*
* @template-covariant TKey
* @template-covariant TValue
*
* @extends \FilterIterator<TKey, TValue>
*/
abstract class MultiplePcreFilterIterator extends FilterIterator
abstract class MultiplePcreFilterIterator extends \FilterIterator
{
protected $matchRegexps = array();
protected $noMatchRegexps = array();
protected $matchRegexps = [];
protected $noMatchRegexps = [];
/**
* Constructor.
*
* @param \Iterator $iterator The Iterator to filter
* @param array $matchPatterns An array of patterns that need to match
* @param array $noMatchPatterns An array of patterns that need to not match
* @param string[] $matchPatterns An array of patterns that need to match
* @param string[] $noMatchPatterns An array of patterns that need to not match
*/
public function __construct(\Iterator $iterator, array $matchPatterns, array $noMatchPatterns)
{
@@ -47,12 +50,8 @@ abstract class MultiplePcreFilterIterator extends FilterIterator
* If there is no regexps defined in the class, this method will accept the string.
* Such case can be handled by child classes before calling the method if they want to
* apply a different behavior.
*
* @param string $string The string to be matched against filters
*
* @return bool
*/
protected function isAccepted($string)
protected function isAccepted(string $string): bool
{
// should at least not match one rule to exclude
foreach ($this->noMatchRegexps as $regex) {
@@ -78,14 +77,16 @@ abstract class MultiplePcreFilterIterator extends FilterIterator
/**
* Checks whether the string is a regex.
*
* @param string $str
*
* @return bool Whether the given string is a regex
*/
protected function isRegex($str)
protected function isRegex(string $str): bool
{
if (preg_match('/^(.{3,}?)[imsxuADU]*$/', $str, $m)) {
$availableModifiers = 'imsxuADU';
if (\PHP_VERSION_ID >= 80200) {
$availableModifiers .= 'n';
}
if (preg_match('/^(.{3,}?)['.$availableModifiers.']*$/', $str, $m)) {
$start = substr($m[1], 0, 1);
$end = substr($m[1], -1);
@@ -93,7 +94,7 @@ abstract class MultiplePcreFilterIterator extends FilterIterator
return !preg_match('/[*?[:alnum:] \\\\]/', $start);
}
foreach (array(array('{', '}'), array('(', ')'), array('[', ']'), array('<', '>')) as $delimiters) {
foreach ([['{', '}'], ['(', ')'], ['[', ']'], ['<', '>']] as $delimiters) {
if ($start === $delimiters[0] && $end === $delimiters[1]) {
return true;
}
@@ -105,10 +106,6 @@ abstract class MultiplePcreFilterIterator extends FilterIterator
/**
* Converts string into regexp.
*
* @param string $str Pattern
*
* @return string regexp corresponding to a given string
*/
abstract protected function toRegex($str);
abstract protected function toRegex(string $str): string;
}

View File

@@ -16,19 +16,19 @@ namespace Symfony\Component\Finder\Iterator;
*
* @author Fabien Potencier <fabien@symfony.com>
* @author Włodzimierz Gajda <gajdaw@gajdaw.pl>
*
* @extends MultiplePcreFilterIterator<string, \SplFileInfo>
*/
class PathFilterIterator extends MultiplePcreFilterIterator
{
/**
* Filters the iterator values.
*
* @return bool true if the value should be kept, false otherwise
*/
public function accept()
public function accept(): bool
{
$filename = $this->current()->getRelativePathname();
if ('\\' === DIRECTORY_SEPARATOR) {
if ('\\' === \DIRECTORY_SEPARATOR) {
$filename = str_replace('\\', '/', $filename);
}
@@ -46,10 +46,8 @@ class PathFilterIterator extends MultiplePcreFilterIterator
* Use only / as directory separator (on Windows also).
*
* @param string $str Pattern: regexp or dirname
*
* @return string regexp corresponding to a given string or regexp
*/
protected function toRegex($str)
protected function toRegex(string $str): string
{
return $this->isRegex($str) ? $str : '/'.preg_quote($str, '/').'/';
}

View File

@@ -21,31 +21,18 @@ use Symfony\Component\Finder\SplFileInfo;
*/
class RecursiveDirectoryIterator extends \RecursiveDirectoryIterator
{
/**
* @var bool
*/
private $ignoreUnreadableDirs;
/**
* @var bool
*/
private $rewindable;
private bool $ignoreUnreadableDirs;
private ?bool $rewindable = null;
// these 3 properties take part of the performance optimization to avoid redoing the same work in all iterations
private $rootPath;
private $subPath;
private $directorySeparator = '/';
private string $rootPath;
private string $subPath;
private string $directorySeparator = '/';
/**
* Constructor.
*
* @param string $path
* @param int $flags
* @param bool $ignoreUnreadableDirs
*
* @throws \RuntimeException
*/
public function __construct($path, $flags, $ignoreUnreadableDirs = false)
public function __construct(string $path, int $flags, bool $ignoreUnreadableDirs = false)
{
if ($flags & (self::CURRENT_AS_PATHNAME | self::CURRENT_AS_SELF)) {
throw new \RuntimeException('This iterator only support returning current as fileinfo.');
@@ -54,37 +41,56 @@ class RecursiveDirectoryIterator extends \RecursiveDirectoryIterator
parent::__construct($path, $flags);
$this->ignoreUnreadableDirs = $ignoreUnreadableDirs;
$this->rootPath = $path;
if ('/' !== DIRECTORY_SEPARATOR && !($flags & self::UNIX_PATHS)) {
$this->directorySeparator = DIRECTORY_SEPARATOR;
if ('/' !== \DIRECTORY_SEPARATOR && !($flags & self::UNIX_PATHS)) {
$this->directorySeparator = \DIRECTORY_SEPARATOR;
}
}
/**
* Return an instance of SplFileInfo with support for relative paths.
*
* @return SplFileInfo File information
*/
public function current()
public function current(): SplFileInfo
{
// the logic here avoids redoing the same work in all iterations
if (null === $subPathname = $this->subPath) {
$subPathname = $this->subPath = (string) $this->getSubPath();
if (!isset($this->subPath)) {
$this->subPath = $this->getSubPath();
}
$subPathname = $this->subPath;
if ('' !== $subPathname) {
$subPathname .= $this->directorySeparator;
}
$subPathname .= $this->getFilename();
return new SplFileInfo($this->rootPath.$this->directorySeparator.$subPathname, $this->subPath, $subPathname);
if ('/' !== $basePath = $this->rootPath) {
$basePath .= $this->directorySeparator;
}
return new SplFileInfo($basePath.$subPathname, $this->subPath, $subPathname);
}
public function hasChildren(bool $allowLinks = false): bool
{
$hasChildren = parent::hasChildren($allowLinks);
if (!$hasChildren || !$this->ignoreUnreadableDirs) {
return $hasChildren;
}
try {
parent::getChildren();
return true;
} catch (\UnexpectedValueException $e) {
// If directory is unreadable and finder is set to ignore it, skip children
return false;
}
}
/**
* @return \RecursiveIterator
*
* @throws AccessDeniedException
*/
public function getChildren()
public function getChildren(): \RecursiveDirectoryIterator
{
try {
$children = parent::getChildren();
@@ -100,48 +106,31 @@ class RecursiveDirectoryIterator extends \RecursiveDirectoryIterator
return $children;
} catch (\UnexpectedValueException $e) {
if ($this->ignoreUnreadableDirs) {
// If directory is unreadable and finder is set to ignore it, a fake empty content is returned.
return new \RecursiveArrayIterator(array());
} else {
throw new AccessDeniedException($e->getMessage(), $e->getCode(), $e);
}
throw new AccessDeniedException($e->getMessage(), $e->getCode(), $e);
}
}
/**
* Do nothing for non rewindable stream.
*/
public function rewind()
public function rewind(): void
{
if (false === $this->isRewindable()) {
return;
}
// @see https://bugs.php.net/68557
if (\PHP_VERSION_ID < 50523 || \PHP_VERSION_ID >= 50600 && \PHP_VERSION_ID < 50607) {
parent::next();
}
parent::rewind();
}
/**
* Checks if the stream is rewindable.
*
* @return bool true when the stream is rewindable, false otherwise
*/
public function isRewindable()
public function isRewindable(): bool
{
if (null !== $this->rewindable) {
return $this->rewindable;
}
// workaround for an HHVM bug, should be removed when https://github.com/facebook/hhvm/issues/7281 is fixed
if ('' === $this->getPath()) {
return $this->rewindable = false;
}
if (false !== $stream = @opendir($this->getPath())) {
$infos = stream_get_meta_data($stream);
closedir($stream);

View File

@@ -17,16 +17,16 @@ use Symfony\Component\Finder\Comparator\NumberComparator;
* SizeRangeFilterIterator filters out files that are not in the given size range.
*
* @author Fabien Potencier <fabien@symfony.com>
*
* @extends \FilterIterator<string, \SplFileInfo>
*/
class SizeRangeFilterIterator extends FilterIterator
class SizeRangeFilterIterator extends \FilterIterator
{
private $comparators = array();
private array $comparators = [];
/**
* Constructor.
*
* @param \Iterator $iterator The Iterator to filter
* @param NumberComparator[] $comparators An array of NumberComparator instances
* @param \Iterator<string, \SplFileInfo> $iterator
* @param NumberComparator[] $comparators
*/
public function __construct(\Iterator $iterator, array $comparators)
{
@@ -37,10 +37,8 @@ class SizeRangeFilterIterator extends FilterIterator
/**
* Filters the iterator values.
*
* @return bool true if the value should be kept, false otherwise
*/
public function accept()
public function accept(): bool
{
$fileinfo = $this->current();
if (!$fileinfo->isFile()) {

View File

@@ -15,67 +15,85 @@ namespace Symfony\Component\Finder\Iterator;
* SortableIterator applies a sort on a given Iterator.
*
* @author Fabien Potencier <fabien@symfony.com>
*
* @implements \IteratorAggregate<string, \SplFileInfo>
*/
class SortableIterator implements \IteratorAggregate
{
const SORT_BY_NAME = 1;
const SORT_BY_TYPE = 2;
const SORT_BY_ACCESSED_TIME = 3;
const SORT_BY_CHANGED_TIME = 4;
const SORT_BY_MODIFIED_TIME = 5;
public const SORT_BY_NONE = 0;
public const SORT_BY_NAME = 1;
public const SORT_BY_TYPE = 2;
public const SORT_BY_ACCESSED_TIME = 3;
public const SORT_BY_CHANGED_TIME = 4;
public const SORT_BY_MODIFIED_TIME = 5;
public const SORT_BY_NAME_NATURAL = 6;
private $iterator;
private $sort;
private \Traversable $iterator;
private \Closure|int $sort;
/**
* Constructor.
*
* @param \Traversable $iterator The Iterator to filter
* @param int|callable $sort The sort type (SORT_BY_NAME, SORT_BY_TYPE, or a PHP callback)
* @param \Traversable<string, \SplFileInfo> $iterator
* @param int|callable $sort The sort type (SORT_BY_NAME, SORT_BY_TYPE, or a PHP callback)
*
* @throws \InvalidArgumentException
*/
public function __construct(\Traversable $iterator, $sort)
public function __construct(\Traversable $iterator, int|callable $sort, bool $reverseOrder = false)
{
$this->iterator = $iterator;
$order = $reverseOrder ? -1 : 1;
if (self::SORT_BY_NAME === $sort) {
$this->sort = function ($a, $b) {
return strcmp($a->getRealpath() ?: $a->getPathname(), $b->getRealpath() ?: $b->getPathname());
$this->sort = static function (\SplFileInfo $a, \SplFileInfo $b) use ($order) {
return $order * strcmp($a->getRealPath() ?: $a->getPathname(), $b->getRealPath() ?: $b->getPathname());
};
} elseif (self::SORT_BY_NAME_NATURAL === $sort) {
$this->sort = static function (\SplFileInfo $a, \SplFileInfo $b) use ($order) {
return $order * strnatcmp($a->getRealPath() ?: $a->getPathname(), $b->getRealPath() ?: $b->getPathname());
};
} elseif (self::SORT_BY_TYPE === $sort) {
$this->sort = function ($a, $b) {
$this->sort = static function (\SplFileInfo $a, \SplFileInfo $b) use ($order) {
if ($a->isDir() && $b->isFile()) {
return -1;
return -$order;
} elseif ($a->isFile() && $b->isDir()) {
return 1;
return $order;
}
return strcmp($a->getRealpath() ?: $a->getPathname(), $b->getRealpath() ?: $b->getPathname());
return $order * strcmp($a->getRealPath() ?: $a->getPathname(), $b->getRealPath() ?: $b->getPathname());
};
} elseif (self::SORT_BY_ACCESSED_TIME === $sort) {
$this->sort = function ($a, $b) {
return $a->getATime() - $b->getATime();
$this->sort = static function (\SplFileInfo $a, \SplFileInfo $b) use ($order) {
return $order * ($a->getATime() - $b->getATime());
};
} elseif (self::SORT_BY_CHANGED_TIME === $sort) {
$this->sort = function ($a, $b) {
return $a->getCTime() - $b->getCTime();
$this->sort = static function (\SplFileInfo $a, \SplFileInfo $b) use ($order) {
return $order * ($a->getCTime() - $b->getCTime());
};
} elseif (self::SORT_BY_MODIFIED_TIME === $sort) {
$this->sort = function ($a, $b) {
return $a->getMTime() - $b->getMTime();
$this->sort = static function (\SplFileInfo $a, \SplFileInfo $b) use ($order) {
return $order * ($a->getMTime() - $b->getMTime());
};
} elseif (is_callable($sort)) {
$this->sort = $sort;
} elseif (self::SORT_BY_NONE === $sort) {
$this->sort = $order;
} elseif (\is_callable($sort)) {
$this->sort = $reverseOrder ? static function (\SplFileInfo $a, \SplFileInfo $b) use ($sort) { return -$sort($a, $b); } : \Closure::fromCallable($sort);
} else {
throw new \InvalidArgumentException('The SortableIterator takes a PHP callable or a valid built-in sort algorithm as an argument.');
}
}
public function getIterator()
public function getIterator(): \Traversable
{
if (1 === $this->sort) {
return $this->iterator;
}
$array = iterator_to_array($this->iterator, true);
uasort($array, $this->sort);
if (-1 === $this->sort) {
$array = array_reverse($array);
} else {
uasort($array, $this->sort);
}
return new \ArrayIterator($array);
}

View File

@@ -0,0 +1,151 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\Finder\Iterator;
use Symfony\Component\Finder\Gitignore;
final class VcsIgnoredFilterIterator extends \FilterIterator
{
/**
* @var string
*/
private $baseDir;
/**
* @var array<string, array{0: string, 1: string}|null>
*/
private $gitignoreFilesCache = [];
/**
* @var array<string, bool>
*/
private $ignoredPathsCache = [];
public function __construct(\Iterator $iterator, string $baseDir)
{
$this->baseDir = $this->normalizePath($baseDir);
parent::__construct($iterator);
}
public function accept(): bool
{
$file = $this->current();
$fileRealPath = $this->normalizePath($file->getRealPath());
return !$this->isIgnored($fileRealPath);
}
private function isIgnored(string $fileRealPath): bool
{
if (is_dir($fileRealPath) && !str_ends_with($fileRealPath, '/')) {
$fileRealPath .= '/';
}
if (isset($this->ignoredPathsCache[$fileRealPath])) {
return $this->ignoredPathsCache[$fileRealPath];
}
$ignored = false;
foreach ($this->parentsDirectoryDownward($fileRealPath) as $parentDirectory) {
if ($this->isIgnored($parentDirectory)) {
// rules in ignored directories are ignored, no need to check further.
break;
}
$fileRelativePath = substr($fileRealPath, \strlen($parentDirectory) + 1);
if (null === $regexps = $this->readGitignoreFile("{$parentDirectory}/.gitignore")) {
continue;
}
[$exclusionRegex, $inclusionRegex] = $regexps;
if (preg_match($exclusionRegex, $fileRelativePath)) {
$ignored = true;
continue;
}
if (preg_match($inclusionRegex, $fileRelativePath)) {
$ignored = false;
}
}
return $this->ignoredPathsCache[$fileRealPath] = $ignored;
}
/**
* @return list<string>
*/
private function parentsDirectoryDownward(string $fileRealPath): array
{
$parentDirectories = [];
$parentDirectory = $fileRealPath;
while (true) {
$newParentDirectory = \dirname($parentDirectory);
// dirname('/') = '/'
if ($newParentDirectory === $parentDirectory) {
break;
}
$parentDirectory = $newParentDirectory;
if (0 !== strpos($parentDirectory, $this->baseDir)) {
break;
}
$parentDirectories[] = $parentDirectory;
}
return array_reverse($parentDirectories);
}
/**
* @return array{0: string, 1: string}|null
*/
private function readGitignoreFile(string $path): ?array
{
if (\array_key_exists($path, $this->gitignoreFilesCache)) {
return $this->gitignoreFilesCache[$path];
}
if (!file_exists($path)) {
return $this->gitignoreFilesCache[$path] = null;
}
if (!is_file($path) || !is_readable($path)) {
throw new \RuntimeException("The \"ignoreVCSIgnored\" option cannot be used by the Finder as the \"{$path}\" file is not readable.");
}
$gitignoreFileContent = file_get_contents($path);
return $this->gitignoreFilesCache[$path] = [
Gitignore::toRegex($gitignoreFileContent),
Gitignore::toRegexMatchingNegatedPatterns($gitignoreFileContent),
];
}
private function normalizePath(string $path): string
{
if ('\\' === \DIRECTORY_SEPARATOR) {
return str_replace('\\', '/', $path);
}
return $path;
}
}