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

@@ -1,3 +0,0 @@
vendor/
composer.lock
phpunit.xml

View File

@@ -1,6 +1,49 @@
CHANGELOG
=========
6.0
---
* Remove `Comparator::setTarget()` and `Comparator::setOperator()`
5.4.0
-----
* Deprecate `Comparator::setTarget()` and `Comparator::setOperator()`
* Add a constructor to `Comparator` that allows setting target and operator
* Finder's iterator has now `Symfony\Component\Finder\SplFileInfo` inner type specified
* Add recursive .gitignore files support
5.0.0
-----
* added `$useNaturalSort` argument to `Finder::sortByName()`
4.3.0
-----
* added Finder::ignoreVCSIgnored() to ignore files based on rules listed in .gitignore
4.2.0
-----
* added $useNaturalSort option to Finder::sortByName() method
* the `Finder::sortByName()` method will have a new `$useNaturalSort`
argument in version 5.0, not defining it is deprecated
* added `Finder::reverseSorting()` to reverse the sorting
4.0.0
-----
* removed `ExceptionInterface`
* removed `Symfony\Component\Finder\Iterator\FilterIterator`
3.4.0
-----
* deprecated `Symfony\Component\Finder\Iterator\FilterIterator`
* added Finder::hasResults() method to check if any results were found
3.3.0
-----

View File

@@ -12,73 +12,43 @@
namespace Symfony\Component\Finder\Comparator;
/**
* Comparator.
*
* @author Fabien Potencier <fabien@symfony.com>
*/
class Comparator
{
private $target;
private $operator = '==';
private string $target;
private string $operator;
public function __construct(string $target, string $operator = '==')
{
if (!\in_array($operator, ['>', '<', '>=', '<=', '==', '!='])) {
throw new \InvalidArgumentException(sprintf('Invalid operator "%s".', $operator));
}
$this->target = $target;
$this->operator = $operator;
}
/**
* Gets the target value.
*
* @return string The target value
*/
public function getTarget()
public function getTarget(): string
{
return $this->target;
}
/**
* Sets the target value.
*
* @param string $target The target value
*/
public function setTarget($target)
{
$this->target = $target;
}
/**
* Gets the comparison operator.
*
* @return string The operator
*/
public function getOperator()
public function getOperator(): string
{
return $this->operator;
}
/**
* Sets the comparison operator.
*
* @param string $operator A valid operator
*
* @throws \InvalidArgumentException
*/
public function setOperator($operator)
{
if (!$operator) {
$operator = '==';
}
if (!in_array($operator, array('>', '<', '>=', '<=', '==', '!='))) {
throw new \InvalidArgumentException(sprintf('Invalid operator "%s".', $operator));
}
$this->operator = $operator;
}
/**
* Tests against the target.
*
* @param mixed $test A test value
*
* @return bool
*/
public function test($test)
public function test(mixed $test): bool
{
switch ($this->operator) {
case '>':

View File

@@ -19,13 +19,11 @@ namespace Symfony\Component\Finder\Comparator;
class DateComparator extends Comparator
{
/**
* Constructor.
*
* @param string $test A comparison string
*
* @throws \InvalidArgumentException If the test is not understood
*/
public function __construct($test)
public function __construct(string $test)
{
if (!preg_match('#^\s*(==|!=|[<>]=?|after|since|before|until)?\s*(.+?)\s*$#i', $test, $matches)) {
throw new \InvalidArgumentException(sprintf('Don\'t understand "%s" as a date test.', $test));
@@ -38,7 +36,7 @@ class DateComparator extends Comparator
throw new \InvalidArgumentException(sprintf('"%s" is not a valid date.', $matches[2]));
}
$operator = isset($matches[1]) ? $matches[1] : '==';
$operator = $matches[1] ?? '==';
if ('since' === $operator || 'after' === $operator) {
$operator = '>';
}
@@ -47,7 +45,6 @@ class DateComparator extends Comparator
$operator = '<';
}
$this->setOperator($operator);
$this->setTarget($target);
parent::__construct($target, $operator);
}
}

View File

@@ -35,16 +35,14 @@ namespace Symfony\Component\Finder\Comparator;
class NumberComparator extends Comparator
{
/**
* Constructor.
*
* @param string|int $test A comparison string or an integer
*
* @throws \InvalidArgumentException If the test is not understood
*/
public function __construct($test)
public function __construct(?string $test)
{
if (!preg_match('#^\s*(==|!=|[<>]=?)?\s*([0-9\.]+)\s*([kmg]i?)?\s*$#i', $test, $matches)) {
throw new \InvalidArgumentException(sprintf('Don\'t understand "%s" as a number test.', $test));
if (null === $test || !preg_match('#^\s*(==|!=|[<>]=?)?\s*([0-9\.]+)\s*([kmg]i?)?\s*$#i', $test, $matches)) {
throw new \InvalidArgumentException(sprintf('Don\'t understand "%s" as a number test.', $test ?? 'null'));
}
$target = $matches[2];
@@ -75,7 +73,6 @@ class NumberComparator extends Comparator
}
}
$this->setTarget($target);
$this->setOperator(isset($matches[1]) ? $matches[1] : '==');
parent::__construct($target, $matches[1] ?: '==');
}
}

View File

@@ -12,14 +12,8 @@
namespace Symfony\Component\Finder\Exception;
/**
* @author Jean-François Simon <contact@jfsimon.fr>
*
* @deprecated since 3.3, to be removed in 4.0.
* @author Andreas Erhard <andreas.erhard@i-med.ac.at>
*/
interface ExceptionInterface
class DirectoryNotFoundException extends \InvalidArgumentException
{
/**
* @return \Symfony\Component\Finder\Adapter\AdapterInterface
*/
public function getAdapter();
}

View File

@@ -13,12 +13,14 @@ namespace Symfony\Component\Finder;
use Symfony\Component\Finder\Comparator\DateComparator;
use Symfony\Component\Finder\Comparator\NumberComparator;
use Symfony\Component\Finder\Exception\DirectoryNotFoundException;
use Symfony\Component\Finder\Iterator\CustomFilterIterator;
use Symfony\Component\Finder\Iterator\DateRangeFilterIterator;
use Symfony\Component\Finder\Iterator\DepthRangeFilterIterator;
use Symfony\Component\Finder\Iterator\ExcludeDirectoryFilterIterator;
use Symfony\Component\Finder\Iterator\FilecontentFilterIterator;
use Symfony\Component\Finder\Iterator\FilenameFilterIterator;
use Symfony\Component\Finder\Iterator\LazyIterator;
use Symfony\Component\Finder\Iterator\SizeRangeFilterIterator;
use Symfony\Component\Finder\Iterator\SortableIterator;
@@ -29,41 +31,42 @@ use Symfony\Component\Finder\Iterator\SortableIterator;
*
* All rules may be invoked several times.
*
* All methods return the current Finder object to allow easy chaining:
* All methods return the current Finder object to allow chaining:
*
* $finder = Finder::create()->files()->name('*.php')->in(__DIR__);
* $finder = Finder::create()->files()->name('*.php')->in(__DIR__);
*
* @author Fabien Potencier <fabien@symfony.com>
*
* @implements \IteratorAggregate<string, SplFileInfo>
*/
class Finder implements \IteratorAggregate, \Countable
{
const IGNORE_VCS_FILES = 1;
const IGNORE_DOT_FILES = 2;
public const IGNORE_VCS_FILES = 1;
public const IGNORE_DOT_FILES = 2;
public const IGNORE_VCS_IGNORED_FILES = 4;
private $mode = 0;
private $names = array();
private $notNames = array();
private $exclude = array();
private $filters = array();
private $depths = array();
private $sizes = array();
private $followLinks = false;
private $sort = false;
private $ignore = 0;
private $dirs = array();
private $dates = array();
private $iterators = array();
private $contains = array();
private $notContains = array();
private $paths = array();
private $notPaths = array();
private $ignoreUnreadableDirs = false;
private int $mode = 0;
private array $names = [];
private array $notNames = [];
private array $exclude = [];
private array $filters = [];
private array $depths = [];
private array $sizes = [];
private bool $followLinks = false;
private bool $reverseSorting = false;
private \Closure|int|false $sort = false;
private int $ignore = 0;
private array $dirs = [];
private array $dates = [];
private array $iterators = [];
private array $contains = [];
private array $notContains = [];
private array $paths = [];
private array $notPaths = [];
private bool $ignoreUnreadableDirs = false;
private static $vcsPatterns = array('.svn', '_svn', 'CVS', '_darcs', '.arch-params', '.monotone', '.bzr', '.git', '.hg');
private static array $vcsPatterns = ['.svn', '_svn', 'CVS', '_darcs', '.arch-params', '.monotone', '.bzr', '.git', '.hg'];
/**
* Constructor.
*/
public function __construct()
{
$this->ignore = static::IGNORE_VCS_FILES | static::IGNORE_DOT_FILES;
@@ -71,10 +74,8 @@ class Finder implements \IteratorAggregate, \Countable
/**
* Creates a new Finder.
*
* @return static
*/
public static function create()
public static function create(): static
{
return new static();
}
@@ -84,7 +85,7 @@ class Finder implements \IteratorAggregate, \Countable
*
* @return $this
*/
public function directories()
public function directories(): static
{
$this->mode = Iterator\FileTypeFilterIterator::ONLY_DIRECTORIES;
@@ -96,7 +97,7 @@ class Finder implements \IteratorAggregate, \Countable
*
* @return $this
*/
public function files()
public function files(): static
{
$this->mode = Iterator\FileTypeFilterIterator::ONLY_FILES;
@@ -108,19 +109,22 @@ class Finder implements \IteratorAggregate, \Countable
*
* Usage:
*
* $finder->depth('> 1') // the Finder will start matching at level 1.
* $finder->depth('< 3') // the Finder will descend at most 3 levels of directories below the starting point.
* $finder->depth('> 1') // the Finder will start matching at level 1.
* $finder->depth('< 3') // the Finder will descend at most 3 levels of directories below the starting point.
* $finder->depth(['>= 1', '< 3'])
*
* @param string|int $level The depth level expression
* @param string|int|string[]|int[] $levels The depth level expression or an array of depth levels
*
* @return $this
*
* @see DepthRangeFilterIterator
* @see NumberComparator
*/
public function depth($level)
public function depth(string|int|array $levels): static
{
$this->depths[] = new Comparator\NumberComparator($level);
foreach ((array) $levels as $level) {
$this->depths[] = new Comparator\NumberComparator($level);
}
return $this;
}
@@ -130,12 +134,13 @@ class Finder implements \IteratorAggregate, \Countable
*
* The date must be something that strtotime() is able to parse:
*
* $finder->date('since yesterday');
* $finder->date('until 2 days ago');
* $finder->date('> now - 2 hours');
* $finder->date('>= 2005-10-15');
* $finder->date('since yesterday');
* $finder->date('until 2 days ago');
* $finder->date('> now - 2 hours');
* $finder->date('>= 2005-10-15');
* $finder->date(['>= 2005-10-15', '<= 2006-05-27']);
*
* @param string $date A date range string
* @param string|string[] $dates A date range string or an array of date ranges
*
* @return $this
*
@@ -143,9 +148,11 @@ class Finder implements \IteratorAggregate, \Countable
* @see DateRangeFilterIterator
* @see DateComparator
*/
public function date($date)
public function date(string|array $dates): static
{
$this->dates[] = new Comparator\DateComparator($date);
foreach ((array) $dates as $date) {
$this->dates[] = new Comparator\DateComparator($date);
}
return $this;
}
@@ -155,19 +162,20 @@ class Finder implements \IteratorAggregate, \Countable
*
* You can use patterns (delimited with / sign), globs or simple strings.
*
* $finder->name('*.php')
* $finder->name('/\.php$/') // same as above
* $finder->name('test.php')
* $finder->name('*.php')
* $finder->name('/\.php$/') // same as above
* $finder->name('test.php')
* $finder->name(['test.py', 'test.php'])
*
* @param string $pattern A pattern (a regexp, a glob, or a string)
* @param string|string[] $patterns A pattern (a regexp, a glob, or a string) or an array of patterns
*
* @return $this
*
* @see FilenameFilterIterator
*/
public function name($pattern)
public function name(string|array $patterns): static
{
$this->names[] = $pattern;
$this->names = array_merge($this->names, (array) $patterns);
return $this;
}
@@ -175,15 +183,15 @@ class Finder implements \IteratorAggregate, \Countable
/**
* Adds rules that files must not match.
*
* @param string $pattern A pattern (a regexp, a glob, or a string)
* @param string|string[] $patterns A pattern (a regexp, a glob, or a string) or an array of patterns
*
* @return $this
*
* @see FilenameFilterIterator
*/
public function notName($pattern)
public function notName(string|array $patterns): static
{
$this->notNames[] = $pattern;
$this->notNames = array_merge($this->notNames, (array) $patterns);
return $this;
}
@@ -193,18 +201,19 @@ class Finder implements \IteratorAggregate, \Countable
*
* Strings or PCRE patterns can be used:
*
* $finder->contains('Lorem ipsum')
* $finder->contains('/Lorem ipsum/i')
* $finder->contains('Lorem ipsum')
* $finder->contains('/Lorem ipsum/i')
* $finder->contains(['dolor', '/ipsum/i'])
*
* @param string $pattern A pattern (string or regexp)
* @param string|string[] $patterns A pattern (string or regexp) or an array of patterns
*
* @return $this
*
* @see FilecontentFilterIterator
*/
public function contains($pattern)
public function contains(string|array $patterns): static
{
$this->contains[] = $pattern;
$this->contains = array_merge($this->contains, (array) $patterns);
return $this;
}
@@ -214,18 +223,19 @@ class Finder implements \IteratorAggregate, \Countable
*
* Strings or PCRE patterns can be used:
*
* $finder->notContains('Lorem ipsum')
* $finder->notContains('/Lorem ipsum/i')
* $finder->notContains('Lorem ipsum')
* $finder->notContains('/Lorem ipsum/i')
* $finder->notContains(['lorem', '/dolor/i'])
*
* @param string $pattern A pattern (string or regexp)
* @param string|string[] $patterns A pattern (string or regexp) or an array of patterns
*
* @return $this
*
* @see FilecontentFilterIterator
*/
public function notContains($pattern)
public function notContains(string|array $patterns): static
{
$this->notContains[] = $pattern;
$this->notContains = array_merge($this->notContains, (array) $patterns);
return $this;
}
@@ -235,20 +245,21 @@ class Finder implements \IteratorAggregate, \Countable
*
* You can use patterns (delimited with / sign) or simple strings.
*
* $finder->path('some/special/dir')
* $finder->path('/some\/special\/dir/') // same as above
* $finder->path('some/special/dir')
* $finder->path('/some\/special\/dir/') // same as above
* $finder->path(['some dir', 'another/dir'])
*
* Use only / as dirname separator.
*
* @param string $pattern A pattern (a regexp or a string)
* @param string|string[] $patterns A pattern (a regexp or a string) or an array of patterns
*
* @return $this
*
* @see FilenameFilterIterator
*/
public function path($pattern)
public function path(string|array $patterns): static
{
$this->paths[] = $pattern;
$this->paths = array_merge($this->paths, (array) $patterns);
return $this;
}
@@ -258,20 +269,21 @@ class Finder implements \IteratorAggregate, \Countable
*
* You can use patterns (delimited with / sign) or simple strings.
*
* $finder->notPath('some/special/dir')
* $finder->notPath('/some\/special\/dir/') // same as above
* $finder->notPath('some/special/dir')
* $finder->notPath('/some\/special\/dir/') // same as above
* $finder->notPath(['some/file.txt', 'another/file.log'])
*
* Use only / as dirname separator.
*
* @param string $pattern A pattern (a regexp or a string)
* @param string|string[] $patterns A pattern (a regexp or a string) or an array of patterns
*
* @return $this
*
* @see FilenameFilterIterator
*/
public function notPath($pattern)
public function notPath(string|array $patterns): static
{
$this->notPaths[] = $pattern;
$this->notPaths = array_merge($this->notPaths, (array) $patterns);
return $this;
}
@@ -279,20 +291,23 @@ class Finder implements \IteratorAggregate, \Countable
/**
* Adds tests for file sizes.
*
* $finder->size('> 10K');
* $finder->size('<= 1Ki');
* $finder->size(4);
* $finder->size('> 10K');
* $finder->size('<= 1Ki');
* $finder->size(4);
* $finder->size(['> 10K', '< 20K'])
*
* @param string|int $size A size range string or an integer
* @param string|int|string[]|int[] $sizes A size range string or an integer or an array of size ranges
*
* @return $this
*
* @see SizeRangeFilterIterator
* @see NumberComparator
*/
public function size($size)
public function size(string|int|array $sizes): static
{
$this->sizes[] = new Comparator\NumberComparator($size);
foreach ((array) $sizes as $size) {
$this->sizes[] = new Comparator\NumberComparator($size);
}
return $this;
}
@@ -300,13 +315,17 @@ class Finder implements \IteratorAggregate, \Countable
/**
* Excludes directories.
*
* Directories passed as argument must be relative to the ones defined with the `in()` method. For example:
*
* $finder->in(__DIR__)->exclude('ruby');
*
* @param string|array $dirs A directory path or an array of directories
*
* @return $this
*
* @see ExcludeDirectoryFilterIterator
*/
public function exclude($dirs)
public function exclude(string|array $dirs): static
{
$this->exclude = array_merge($this->exclude, (array) $dirs);
@@ -316,13 +335,13 @@ class Finder implements \IteratorAggregate, \Countable
/**
* Excludes "hidden" directories and files (starting with a dot).
*
* @param bool $ignoreDotFiles Whether to exclude "hidden" files or not
* This option is enabled by default.
*
* @return $this
*
* @see ExcludeDirectoryFilterIterator
*/
public function ignoreDotFiles($ignoreDotFiles)
public function ignoreDotFiles(bool $ignoreDotFiles): static
{
if ($ignoreDotFiles) {
$this->ignore |= static::IGNORE_DOT_FILES;
@@ -336,13 +355,13 @@ class Finder implements \IteratorAggregate, \Countable
/**
* Forces the finder to ignore version control directories.
*
* @param bool $ignoreVCS Whether to exclude VCS files or not
* This option is enabled by default.
*
* @return $this
*
* @see ExcludeDirectoryFilterIterator
*/
public function ignoreVCS($ignoreVCS)
public function ignoreVCS(bool $ignoreVCS): static
{
if ($ignoreVCS) {
$this->ignore |= static::IGNORE_VCS_FILES;
@@ -353,6 +372,24 @@ class Finder implements \IteratorAggregate, \Countable
return $this;
}
/**
* Forces Finder to obey .gitignore and ignore files based on rules listed there.
*
* This option is disabled by default.
*
* @return $this
*/
public function ignoreVCSIgnored(bool $ignoreVCSIgnored): static
{
if ($ignoreVCSIgnored) {
$this->ignore |= static::IGNORE_VCS_IGNORED_FILES;
} else {
$this->ignore &= ~static::IGNORE_VCS_IGNORED_FILES;
}
return $this;
}
/**
* Adds VCS patterns.
*
@@ -360,7 +397,7 @@ class Finder implements \IteratorAggregate, \Countable
*
* @param string|string[] $pattern VCS patterns to ignore
*/
public static function addVCSPattern($pattern)
public static function addVCSPattern(string|array $pattern)
{
foreach ((array) $pattern as $p) {
self::$vcsPatterns[] = $p;
@@ -376,13 +413,11 @@ class Finder implements \IteratorAggregate, \Countable
*
* This can be slow as all the matching files and directories must be retrieved for comparison.
*
* @param \Closure $closure An anonymous function
*
* @return $this
*
* @see SortableIterator
*/
public function sort(\Closure $closure)
public function sort(\Closure $closure): static
{
$this->sort = $closure;
@@ -398,9 +433,9 @@ class Finder implements \IteratorAggregate, \Countable
*
* @see SortableIterator
*/
public function sortByName()
public function sortByName(bool $useNaturalSort = false): static
{
$this->sort = Iterator\SortableIterator::SORT_BY_NAME;
$this->sort = $useNaturalSort ? Iterator\SortableIterator::SORT_BY_NAME_NATURAL : Iterator\SortableIterator::SORT_BY_NAME;
return $this;
}
@@ -414,7 +449,7 @@ class Finder implements \IteratorAggregate, \Countable
*
* @see SortableIterator
*/
public function sortByType()
public function sortByType(): static
{
$this->sort = Iterator\SortableIterator::SORT_BY_TYPE;
@@ -432,13 +467,25 @@ class Finder implements \IteratorAggregate, \Countable
*
* @see SortableIterator
*/
public function sortByAccessedTime()
public function sortByAccessedTime(): static
{
$this->sort = Iterator\SortableIterator::SORT_BY_ACCESSED_TIME;
return $this;
}
/**
* Reverses the sorting.
*
* @return $this
*/
public function reverseSorting(): static
{
$this->reverseSorting = true;
return $this;
}
/**
* Sorts files and directories by the last inode changed time.
*
@@ -452,7 +499,7 @@ class Finder implements \IteratorAggregate, \Countable
*
* @see SortableIterator
*/
public function sortByChangedTime()
public function sortByChangedTime(): static
{
$this->sort = Iterator\SortableIterator::SORT_BY_CHANGED_TIME;
@@ -470,7 +517,7 @@ class Finder implements \IteratorAggregate, \Countable
*
* @see SortableIterator
*/
public function sortByModifiedTime()
public function sortByModifiedTime(): static
{
$this->sort = Iterator\SortableIterator::SORT_BY_MODIFIED_TIME;
@@ -483,13 +530,11 @@ class Finder implements \IteratorAggregate, \Countable
* The anonymous function receives a \SplFileInfo and must return false
* to remove files.
*
* @param \Closure $closure An anonymous function
*
* @return $this
*
* @see CustomFilterIterator
*/
public function filter(\Closure $closure)
public function filter(\Closure $closure): static
{
$this->filters[] = $closure;
@@ -501,7 +546,7 @@ class Finder implements \IteratorAggregate, \Countable
*
* @return $this
*/
public function followLinks()
public function followLinks(): static
{
$this->followLinks = true;
@@ -513,13 +558,11 @@ class Finder implements \IteratorAggregate, \Countable
*
* By default, scanning unreadable directories content throws an AccessDeniedException.
*
* @param bool $ignore
*
* @return $this
*/
public function ignoreUnreadableDirs($ignore = true)
public function ignoreUnreadableDirs(bool $ignore = true): static
{
$this->ignoreUnreadableDirs = (bool) $ignore;
$this->ignoreUnreadableDirs = $ignore;
return $this;
}
@@ -527,27 +570,28 @@ class Finder implements \IteratorAggregate, \Countable
/**
* Searches files and directories which match defined rules.
*
* @param string|array $dirs A directory path or an array of directories
* @param string|string[] $dirs A directory path or an array of directories
*
* @return $this
*
* @throws \InvalidArgumentException if one of the directories does not exist
* @throws DirectoryNotFoundException if one of the directories does not exist
*/
public function in($dirs)
public function in(string|array $dirs): static
{
$resolvedDirs = array();
$resolvedDirs = [];
foreach ((array) $dirs as $dir) {
if (is_dir($dir)) {
$resolvedDirs[] = $dir;
} elseif ($glob = glob($dir, (defined('GLOB_BRACE') ? GLOB_BRACE : 0) | GLOB_ONLYDIR)) {
$resolvedDirs = array_merge($resolvedDirs, $glob);
$resolvedDirs[] = [$this->normalizeDir($dir)];
} elseif ($glob = glob($dir, (\defined('GLOB_BRACE') ? \GLOB_BRACE : 0) | \GLOB_ONLYDIR | \GLOB_NOSORT)) {
sort($glob);
$resolvedDirs[] = array_map([$this, 'normalizeDir'], $glob);
} else {
throw new \InvalidArgumentException(sprintf('The "%s" directory does not exist.', $dir));
throw new DirectoryNotFoundException(sprintf('The "%s" directory does not exist.', $dir));
}
}
$this->dirs = array_merge($this->dirs, $resolvedDirs);
$this->dirs = array_merge($this->dirs, ...$resolvedDirs);
return $this;
}
@@ -557,29 +601,41 @@ class Finder implements \IteratorAggregate, \Countable
*
* This method implements the IteratorAggregate interface.
*
* @return \Iterator|SplFileInfo[] An iterator
* @return \Iterator<string, SplFileInfo>
*
* @throws \LogicException if the in() method has not been called
*/
public function getIterator()
public function getIterator(): \Iterator
{
if (0 === count($this->dirs) && 0 === count($this->iterators)) {
if (0 === \count($this->dirs) && 0 === \count($this->iterators)) {
throw new \LogicException('You must call one of in() or append() methods before iterating over a Finder.');
}
if (1 === count($this->dirs) && 0 === count($this->iterators)) {
return $this->searchInDirectory($this->dirs[0]);
if (1 === \count($this->dirs) && 0 === \count($this->iterators)) {
$iterator = $this->searchInDirectory($this->dirs[0]);
if ($this->sort || $this->reverseSorting) {
$iterator = (new Iterator\SortableIterator($iterator, $this->sort, $this->reverseSorting))->getIterator();
}
return $iterator;
}
$iterator = new \AppendIterator();
foreach ($this->dirs as $dir) {
$iterator->append($this->searchInDirectory($dir));
$iterator->append(new \IteratorIterator(new LazyIterator(function () use ($dir) {
return $this->searchInDirectory($dir);
})));
}
foreach ($this->iterators as $it) {
$iterator->append($it);
}
if ($this->sort || $this->reverseSorting) {
$iterator = (new Iterator\SortableIterator($iterator, $this->sort, $this->reverseSorting))->getIterator();
}
return $iterator;
}
@@ -588,22 +644,21 @@ class Finder implements \IteratorAggregate, \Countable
*
* The set can be another Finder, an Iterator, an IteratorAggregate, or even a plain array.
*
* @param mixed $iterator
*
* @return $this
*
* @throws \InvalidArgumentException When the given argument is not iterable.
* @throws \InvalidArgumentException when the given argument is not iterable
*/
public function append($iterator)
public function append(iterable $iterator): static
{
if ($iterator instanceof \IteratorAggregate) {
$this->iterators[] = $iterator->getIterator();
} elseif ($iterator instanceof \Iterator) {
$this->iterators[] = $iterator;
} elseif ($iterator instanceof \Traversable || is_array($iterator)) {
} elseif (is_iterable($iterator)) {
$it = new \ArrayIterator();
foreach ($iterator as $file) {
$it->append($file instanceof \SplFileInfo ? $file : new \SplFileInfo($file));
$file = $file instanceof \SplFileInfo ? $file : new \SplFileInfo($file);
$it[$file->getPathname()] = $file;
}
$this->iterators[] = $it;
} else {
@@ -614,32 +669,40 @@ class Finder implements \IteratorAggregate, \Countable
}
/**
* Counts all the results collected by the iterators.
*
* @return int
* Check if any results were found.
*/
public function count()
public function hasResults(): bool
{
foreach ($this->getIterator() as $_) {
return true;
}
return false;
}
/**
* Counts all the results collected by the iterators.
*/
public function count(): int
{
return iterator_count($this->getIterator());
}
/**
* @param $dir
*
* @return \Iterator
*/
private function searchInDirectory($dir)
private function searchInDirectory(string $dir): \Iterator
{
$exclude = $this->exclude;
$notPaths = $this->notPaths;
if (static::IGNORE_VCS_FILES === (static::IGNORE_VCS_FILES & $this->ignore)) {
$this->exclude = array_merge($this->exclude, self::$vcsPatterns);
$exclude = array_merge($exclude, self::$vcsPatterns);
}
if (static::IGNORE_DOT_FILES === (static::IGNORE_DOT_FILES & $this->ignore)) {
$this->notPaths[] = '#(^|/)\..+(/|$)#';
$notPaths[] = '#(^|/)\..+(/|$)#';
}
$minDepth = 0;
$maxDepth = PHP_INT_MAX;
$maxDepth = \PHP_INT_MAX;
foreach ($this->depths as $comparator) {
switch ($comparator->getOperator()) {
@@ -668,13 +731,13 @@ class Finder implements \IteratorAggregate, \Countable
$iterator = new Iterator\RecursiveDirectoryIterator($dir, $flags, $this->ignoreUnreadableDirs);
if ($this->exclude) {
$iterator = new Iterator\ExcludeDirectoryFilterIterator($iterator, $this->exclude);
if ($exclude) {
$iterator = new Iterator\ExcludeDirectoryFilterIterator($iterator, $exclude);
}
$iterator = new \RecursiveIteratorIterator($iterator, \RecursiveIteratorIterator::SELF_FIRST);
if ($minDepth > 0 || $maxDepth < PHP_INT_MAX) {
if ($minDepth > 0 || $maxDepth < \PHP_INT_MAX) {
$iterator = new Iterator\DepthRangeFilterIterator($iterator, $minDepth, $maxDepth);
}
@@ -702,15 +765,34 @@ class Finder implements \IteratorAggregate, \Countable
$iterator = new Iterator\CustomFilterIterator($iterator, $this->filters);
}
if ($this->paths || $this->notPaths) {
$iterator = new Iterator\PathFilterIterator($iterator, $this->paths, $this->notPaths);
if ($this->paths || $notPaths) {
$iterator = new Iterator\PathFilterIterator($iterator, $this->paths, $notPaths);
}
if ($this->sort) {
$iteratorAggregate = new Iterator\SortableIterator($iterator, $this->sort);
$iterator = $iteratorAggregate->getIterator();
if (static::IGNORE_VCS_IGNORED_FILES === (static::IGNORE_VCS_IGNORED_FILES & $this->ignore)) {
$iterator = new Iterator\VcsIgnoredFilterIterator($iterator, $dir);
}
return $iterator;
}
/**
* Normalizes given directory names by removing trailing slashes.
*
* Excluding: (s)ftp:// or ssh2.(s)ftp:// wrapper
*/
private function normalizeDir(string $dir): string
{
if ('/' === $dir) {
return $dir;
}
$dir = rtrim($dir, '/'.\DIRECTORY_SEPARATOR);
if (preg_match('#^(ssh2\.)?s?ftp://#', $dir)) {
$dir .= '/';
}
return $dir;
}
}

93
vendor/symfony/finder/Gitignore.php vendored Normal file
View File

@@ -0,0 +1,93 @@
<?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;
/**
* Gitignore matches against text.
*
* @author Michael Voříšek <vorismi3@fel.cvut.cz>
* @author Ahmed Abdou <mail@ahmd.io>
*/
class Gitignore
{
/**
* Returns a regexp which is the equivalent of the gitignore pattern.
*
* Format specification: https://git-scm.com/docs/gitignore#_pattern_format
*/
public static function toRegex(string $gitignoreFileContent): string
{
return self::buildRegex($gitignoreFileContent, false);
}
public static function toRegexMatchingNegatedPatterns(string $gitignoreFileContent): string
{
return self::buildRegex($gitignoreFileContent, true);
}
private static function buildRegex(string $gitignoreFileContent, bool $inverted): string
{
$gitignoreFileContent = preg_replace('~(?<!\\\\)#[^\n\r]*~', '', $gitignoreFileContent);
$gitignoreLines = preg_split('~\r\n?|\n~', $gitignoreFileContent);
$res = self::lineToRegex('');
foreach ($gitignoreLines as $line) {
$line = preg_replace('~(?<!\\\\)[ \t]+$~', '', $line);
if ('!' === substr($line, 0, 1)) {
$line = substr($line, 1);
$isNegative = true;
} else {
$isNegative = false;
}
if ('' !== $line) {
if ($isNegative xor $inverted) {
$res = '(?!'.self::lineToRegex($line).'$)'.$res;
} else {
$res = '(?:'.$res.'|'.self::lineToRegex($line).')';
}
}
}
return '~^(?:'.$res.')~s';
}
private static function lineToRegex(string $gitignoreLine): string
{
if ('' === $gitignoreLine) {
return '$f'; // always false
}
$slashPos = strpos($gitignoreLine, '/');
if (false !== $slashPos && \strlen($gitignoreLine) - 1 !== $slashPos) {
if (0 === $slashPos) {
$gitignoreLine = substr($gitignoreLine, 1);
}
$isAbsolute = true;
} else {
$isAbsolute = false;
}
$regex = preg_quote(str_replace('\\', '', $gitignoreLine), '~');
$regex = preg_replace_callback('~\\\\\[((?:\\\\!)?)([^\[\]]*)\\\\\]~', function (array $matches): string {
return '['.('' !== $matches[1] ? '^' : '').str_replace('\\-', '-', $matches[2]).']';
}, $regex);
$regex = preg_replace('~(?:(?:\\\\\*){2,}(/?))+~', '(?:(?:(?!//).(?<!//))+$1)?', $regex);
$regex = preg_replace('~\\\\\*~', '[^/]*', $regex);
$regex = preg_replace('~\\\\\?~', '[^/]', $regex);
return ($isAbsolute ? '' : '(?:[^/]+/)*')
.$regex
.(!str_ends_with($gitignoreLine, '/') ? '(?:$|/)' : '');
}
}

View File

@@ -14,14 +14,14 @@ namespace Symfony\Component\Finder;
/**
* Glob matches globbing patterns against text.
*
* if match_glob("foo.*", "foo.bar") echo "matched\n";
* if match_glob("foo.*", "foo.bar") echo "matched\n";
*
* // prints foo.bar and foo.baz
* $regex = glob_to_regex("foo.*");
* for (array('foo.bar', 'foo.baz', 'foo', 'bar') as $t)
* {
* if (/$regex/) echo "matched: $car\n";
* }
* // prints foo.bar and foo.baz
* $regex = glob_to_regex("foo.*");
* for (['foo.bar', 'foo.baz', 'foo', 'bar'] as $t)
* {
* if (/$regex/) echo "matched: $car\n";
* }
*
* Glob implements glob(3) style matching that can be used to match
* against text, rather than fetching names from a filesystem.
@@ -37,21 +37,14 @@ class Glob
{
/**
* Returns a regexp which is the equivalent of the glob pattern.
*
* @param string $glob The glob pattern
* @param bool $strictLeadingDot
* @param bool $strictWildcardSlash
* @param string $delimiter Optional delimiter
*
* @return string regex The regexp
*/
public static function toRegex($glob, $strictLeadingDot = true, $strictWildcardSlash = true, $delimiter = '#')
public static function toRegex(string $glob, bool $strictLeadingDot = true, bool $strictWildcardSlash = true, string $delimiter = '#'): string
{
$firstByte = true;
$escaping = false;
$inCurlies = 0;
$regex = '';
$sizeGlob = strlen($glob);
$sizeGlob = \strlen($glob);
for ($i = 0; $i < $sizeGlob; ++$i) {
$car = $glob[$i];
if ($firstByte && $strictLeadingDot && '.' !== $car) {

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;
}
}

View File

@@ -1,4 +1,4 @@
Copyright (c) 2004-2017 Fabien Potencier
Copyright (c) 2004-2022 Fabien Potencier
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal

View File

@@ -7,8 +7,8 @@ interface.
Resources
---------
* [Documentation](https://symfony.com/doc/current/components/finder.html)
* [Contributing](https://symfony.com/doc/current/contributing/index.html)
* [Report issues](https://github.com/symfony/symfony/issues) and
[send Pull Requests](https://github.com/symfony/symfony/pulls)
in the [main Symfony repository](https://github.com/symfony/symfony)
* [Documentation](https://symfony.com/doc/current/components/finder.html)
* [Contributing](https://symfony.com/doc/current/contributing/index.html)
* [Report issues](https://github.com/symfony/symfony/issues) and
[send Pull Requests](https://github.com/symfony/symfony/pulls)
in the [main Symfony repository](https://github.com/symfony/symfony)

View File

@@ -18,17 +18,15 @@ namespace Symfony\Component\Finder;
*/
class SplFileInfo extends \SplFileInfo
{
private $relativePath;
private $relativePathname;
private string $relativePath;
private string $relativePathname;
/**
* Constructor.
*
* @param string $file The file name
* @param string $relativePath The relative path
* @param string $relativePathname The relative path name
*/
public function __construct($file, $relativePath, $relativePathname)
public function __construct(string $file, string $relativePath, string $relativePathname)
{
parent::__construct($file);
$this->relativePath = $relativePath;
@@ -39,10 +37,8 @@ class SplFileInfo extends \SplFileInfo
* Returns the relative path.
*
* This path does not contain the file name.
*
* @return string the relative path
*/
public function getRelativePath()
public function getRelativePath(): string
{
return $this->relativePath;
}
@@ -51,29 +47,34 @@ class SplFileInfo extends \SplFileInfo
* Returns the relative path name.
*
* This path contains the file name.
*
* @return string the relative path name
*/
public function getRelativePathname()
public function getRelativePathname(): string
{
return $this->relativePathname;
}
public function getFilenameWithoutExtension(): string
{
$filename = $this->getFilename();
return pathinfo($filename, \PATHINFO_FILENAME);
}
/**
* Returns the contents of the file.
*
* @return string the contents of the file
*
* @throws \RuntimeException
*/
public function getContents()
public function getContents(): string
{
$level = error_reporting(0);
$content = file_get_contents($this->getPathname());
error_reporting($level);
set_error_handler(function ($type, $msg) use (&$error) { $error = $msg; });
try {
$content = file_get_contents($this->getPathname());
} finally {
restore_error_handler();
}
if (false === $content) {
$error = error_get_last();
throw new \RuntimeException($error['message']);
throw new \RuntimeException($error);
}
return $content;

View File

@@ -1,65 +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\Tests\Comparator;
use PHPUnit\Framework\TestCase;
use Symfony\Component\Finder\Comparator\Comparator;
class ComparatorTest extends TestCase
{
public function testGetSetOperator()
{
$comparator = new Comparator();
try {
$comparator->setOperator('foo');
$this->fail('->setOperator() throws an \InvalidArgumentException if the operator is not valid.');
} catch (\Exception $e) {
$this->assertInstanceOf('InvalidArgumentException', $e, '->setOperator() throws an \InvalidArgumentException if the operator is not valid.');
}
$comparator = new Comparator();
$comparator->setOperator('>');
$this->assertEquals('>', $comparator->getOperator(), '->getOperator() returns the current operator');
}
public function testGetSetTarget()
{
$comparator = new Comparator();
$comparator->setTarget(8);
$this->assertEquals(8, $comparator->getTarget(), '->getTarget() returns the target');
}
/**
* @dataProvider getTestData
*/
public function testTest($operator, $target, $match, $noMatch)
{
$c = new Comparator();
$c->setOperator($operator);
$c->setTarget($target);
foreach ($match as $m) {
$this->assertTrue($c->test($m), '->test() tests a string against the expression');
}
foreach ($noMatch as $m) {
$this->assertFalse($c->test($m), '->test() tests a string against the expression');
}
}
public function getTestData()
{
return array(
array('<', '1000', array('500', '999'), array('1000', '1500')),
);
}
}

View File

@@ -1,64 +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\Tests\Comparator;
use PHPUnit\Framework\TestCase;
use Symfony\Component\Finder\Comparator\DateComparator;
class DateComparatorTest extends TestCase
{
public function testConstructor()
{
try {
new DateComparator('foobar');
$this->fail('__construct() throws an \InvalidArgumentException if the test expression is not valid.');
} catch (\Exception $e) {
$this->assertInstanceOf('InvalidArgumentException', $e, '__construct() throws an \InvalidArgumentException if the test expression is not valid.');
}
try {
new DateComparator('');
$this->fail('__construct() throws an \InvalidArgumentException if the test expression is not valid.');
} catch (\Exception $e) {
$this->assertInstanceOf('InvalidArgumentException', $e, '__construct() throws an \InvalidArgumentException if the test expression is not valid.');
}
}
/**
* @dataProvider getTestData
*/
public function testTest($test, $match, $noMatch)
{
$c = new DateComparator($test);
foreach ($match as $m) {
$this->assertTrue($c->test($m), '->test() tests a string against the expression');
}
foreach ($noMatch as $m) {
$this->assertFalse($c->test($m), '->test() tests a string against the expression');
}
}
public function getTestData()
{
return array(
array('< 2005-10-10', array(strtotime('2005-10-09')), array(strtotime('2005-10-15'))),
array('until 2005-10-10', array(strtotime('2005-10-09')), array(strtotime('2005-10-15'))),
array('before 2005-10-10', array(strtotime('2005-10-09')), array(strtotime('2005-10-15'))),
array('> 2005-10-10', array(strtotime('2005-10-15')), array(strtotime('2005-10-09'))),
array('after 2005-10-10', array(strtotime('2005-10-15')), array(strtotime('2005-10-09'))),
array('since 2005-10-10', array(strtotime('2005-10-15')), array(strtotime('2005-10-09'))),
array('!= 2005-10-10', array(strtotime('2005-10-11')), array(strtotime('2005-10-10'))),
);
}
}

View File

@@ -1,108 +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\Tests\Comparator;
use PHPUnit\Framework\TestCase;
use Symfony\Component\Finder\Comparator\NumberComparator;
class NumberComparatorTest extends TestCase
{
/**
* @dataProvider getConstructorTestData
*/
public function testConstructor($successes, $failures)
{
foreach ($successes as $s) {
new NumberComparator($s);
}
foreach ($failures as $f) {
try {
new NumberComparator($f);
$this->fail('__construct() throws an \InvalidArgumentException if the test expression is not valid.');
} catch (\Exception $e) {
$this->assertInstanceOf('InvalidArgumentException', $e, '__construct() throws an \InvalidArgumentException if the test expression is not valid.');
}
}
}
/**
* @dataProvider getTestData
*/
public function testTest($test, $match, $noMatch)
{
$c = new NumberComparator($test);
foreach ($match as $m) {
$this->assertTrue($c->test($m), '->test() tests a string against the expression');
}
foreach ($noMatch as $m) {
$this->assertFalse($c->test($m), '->test() tests a string against the expression');
}
}
public function getTestData()
{
return array(
array('< 1000', array('500', '999'), array('1000', '1500')),
array('< 1K', array('500', '999'), array('1000', '1500')),
array('<1k', array('500', '999'), array('1000', '1500')),
array(' < 1 K ', array('500', '999'), array('1000', '1500')),
array('<= 1K', array('1000'), array('1001')),
array('> 1K', array('1001'), array('1000')),
array('>= 1K', array('1000'), array('999')),
array('< 1KI', array('500', '1023'), array('1024', '1500')),
array('<= 1KI', array('1024'), array('1025')),
array('> 1KI', array('1025'), array('1024')),
array('>= 1KI', array('1024'), array('1023')),
array('1KI', array('1024'), array('1023', '1025')),
array('==1KI', array('1024'), array('1023', '1025')),
array('==1m', array('1000000'), array('999999', '1000001')),
array('==1mi', array(1024 * 1024), array(1024 * 1024 - 1, 1024 * 1024 + 1)),
array('==1g', array('1000000000'), array('999999999', '1000000001')),
array('==1gi', array(1024 * 1024 * 1024), array(1024 * 1024 * 1024 - 1, 1024 * 1024 * 1024 + 1)),
array('!= 1000', array('500', '999'), array('1000')),
);
}
public function getConstructorTestData()
{
return array(
array(
array(
'1', '0',
'3.5', '33.55', '123.456', '123456.78',
'.1', '.123',
'.0', '0.0',
'1.', '0.', '123.',
'==1', '!=1', '<1', '>1', '<=1', '>=1',
'==1k', '==1ki', '==1m', '==1mi', '==1g', '==1gi',
'1k', '1ki', '1m', '1mi', '1g', '1gi',
),
array(
false, null, '',
' ', 'foobar',
'=1', '===1',
'0 . 1', '123 .45', '234. 567',
'..', '.0.', '0.1.2',
),
),
);
}
}

View File

@@ -1,684 +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\Tests;
use Symfony\Component\Finder\Finder;
class FinderTest extends Iterator\RealIteratorTestCase
{
public function testCreate()
{
$this->assertInstanceOf('Symfony\Component\Finder\Finder', Finder::create());
}
public function testDirectories()
{
$finder = $this->buildFinder();
$this->assertSame($finder, $finder->directories());
$this->assertIterator($this->toAbsolute(array('foo', 'toto')), $finder->in(self::$tmpDir)->getIterator());
$finder = $this->buildFinder();
$finder->directories();
$finder->files();
$finder->directories();
$this->assertIterator($this->toAbsolute(array('foo', 'toto')), $finder->in(self::$tmpDir)->getIterator());
}
public function testFiles()
{
$finder = $this->buildFinder();
$this->assertSame($finder, $finder->files());
$this->assertIterator($this->toAbsolute(array('foo/bar.tmp', 'test.php', 'test.py', 'foo bar')), $finder->in(self::$tmpDir)->getIterator());
$finder = $this->buildFinder();
$finder->files();
$finder->directories();
$finder->files();
$this->assertIterator($this->toAbsolute(array('foo/bar.tmp', 'test.php', 'test.py', 'foo bar')), $finder->in(self::$tmpDir)->getIterator());
}
public function testDepth()
{
$finder = $this->buildFinder();
$this->assertSame($finder, $finder->depth('< 1'));
$this->assertIterator($this->toAbsolute(array('foo', 'test.php', 'test.py', 'toto', 'foo bar')), $finder->in(self::$tmpDir)->getIterator());
$finder = $this->buildFinder();
$this->assertSame($finder, $finder->depth('<= 0'));
$this->assertIterator($this->toAbsolute(array('foo', 'test.php', 'test.py', 'toto', 'foo bar')), $finder->in(self::$tmpDir)->getIterator());
$finder = $this->buildFinder();
$this->assertSame($finder, $finder->depth('>= 1'));
$this->assertIterator($this->toAbsolute(array('foo/bar.tmp')), $finder->in(self::$tmpDir)->getIterator());
$finder = $this->buildFinder();
$finder->depth('< 1')->depth('>= 1');
$this->assertIterator(array(), $finder->in(self::$tmpDir)->getIterator());
}
public function testName()
{
$finder = $this->buildFinder();
$this->assertSame($finder, $finder->name('*.php'));
$this->assertIterator($this->toAbsolute(array('test.php')), $finder->in(self::$tmpDir)->getIterator());
$finder = $this->buildFinder();
$finder->name('test.ph*');
$finder->name('test.py');
$this->assertIterator($this->toAbsolute(array('test.php', 'test.py')), $finder->in(self::$tmpDir)->getIterator());
$finder = $this->buildFinder();
$finder->name('~^test~i');
$this->assertIterator($this->toAbsolute(array('test.php', 'test.py')), $finder->in(self::$tmpDir)->getIterator());
$finder = $this->buildFinder();
$finder->name('~\\.php$~i');
$this->assertIterator($this->toAbsolute(array('test.php')), $finder->in(self::$tmpDir)->getIterator());
$finder = $this->buildFinder();
$finder->name('test.p{hp,y}');
$this->assertIterator($this->toAbsolute(array('test.php', 'test.py')), $finder->in(self::$tmpDir)->getIterator());
}
public function testNotName()
{
$finder = $this->buildFinder();
$this->assertSame($finder, $finder->notName('*.php'));
$this->assertIterator($this->toAbsolute(array('foo', 'foo/bar.tmp', 'test.py', 'toto', 'foo bar')), $finder->in(self::$tmpDir)->getIterator());
$finder = $this->buildFinder();
$finder->notName('*.php');
$finder->notName('*.py');
$this->assertIterator($this->toAbsolute(array('foo', 'foo/bar.tmp', 'toto', 'foo bar')), $finder->in(self::$tmpDir)->getIterator());
$finder = $this->buildFinder();
$finder->name('test.ph*');
$finder->name('test.py');
$finder->notName('*.php');
$finder->notName('*.py');
$this->assertIterator(array(), $finder->in(self::$tmpDir)->getIterator());
$finder = $this->buildFinder();
$finder->name('test.ph*');
$finder->name('test.py');
$finder->notName('*.p{hp,y}');
$this->assertIterator(array(), $finder->in(self::$tmpDir)->getIterator());
}
/**
* @dataProvider getRegexNameTestData
*/
public function testRegexName($regex)
{
$finder = $this->buildFinder();
$finder->name($regex);
$this->assertIterator($this->toAbsolute(array('test.py', 'test.php')), $finder->in(self::$tmpDir)->getIterator());
}
public function testSize()
{
$finder = $this->buildFinder();
$this->assertSame($finder, $finder->files()->size('< 1K')->size('> 500'));
$this->assertIterator($this->toAbsolute(array('test.php')), $finder->in(self::$tmpDir)->getIterator());
}
public function testDate()
{
$finder = $this->buildFinder();
$this->assertSame($finder, $finder->files()->date('until last month'));
$this->assertIterator($this->toAbsolute(array('foo/bar.tmp', 'test.php')), $finder->in(self::$tmpDir)->getIterator());
}
public function testExclude()
{
$finder = $this->buildFinder();
$this->assertSame($finder, $finder->exclude('foo'));
$this->assertIterator($this->toAbsolute(array('test.php', 'test.py', 'toto', 'foo bar')), $finder->in(self::$tmpDir)->getIterator());
}
public function testIgnoreVCS()
{
$finder = $this->buildFinder();
$this->assertSame($finder, $finder->ignoreVCS(false)->ignoreDotFiles(false));
$this->assertIterator($this->toAbsolute(array('.git', 'foo', 'foo/bar.tmp', 'test.php', 'test.py', 'toto', 'toto/.git', '.bar', '.foo', '.foo/.bar', '.foo/bar', 'foo bar')), $finder->in(self::$tmpDir)->getIterator());
$finder = $this->buildFinder();
$finder->ignoreVCS(false)->ignoreVCS(false)->ignoreDotFiles(false);
$this->assertIterator($this->toAbsolute(array('.git', 'foo', 'foo/bar.tmp', 'test.php', 'test.py', 'toto', 'toto/.git', '.bar', '.foo', '.foo/.bar', '.foo/bar', 'foo bar')), $finder->in(self::$tmpDir)->getIterator());
$finder = $this->buildFinder();
$this->assertSame($finder, $finder->ignoreVCS(true)->ignoreDotFiles(false));
$this->assertIterator($this->toAbsolute(array('foo', 'foo/bar.tmp', 'test.php', 'test.py', 'toto', '.bar', '.foo', '.foo/.bar', '.foo/bar', 'foo bar')), $finder->in(self::$tmpDir)->getIterator());
}
public function testIgnoreDotFiles()
{
$finder = $this->buildFinder();
$this->assertSame($finder, $finder->ignoreDotFiles(false)->ignoreVCS(false));
$this->assertIterator($this->toAbsolute(array('.git', '.bar', '.foo', '.foo/.bar', '.foo/bar', 'foo', 'foo/bar.tmp', 'test.php', 'test.py', 'toto', 'toto/.git', 'foo bar')), $finder->in(self::$tmpDir)->getIterator());
$finder = $this->buildFinder();
$finder->ignoreDotFiles(false)->ignoreDotFiles(false)->ignoreVCS(false);
$this->assertIterator($this->toAbsolute(array('.git', '.bar', '.foo', '.foo/.bar', '.foo/bar', 'foo', 'foo/bar.tmp', 'test.php', 'test.py', 'toto', 'toto/.git', 'foo bar')), $finder->in(self::$tmpDir)->getIterator());
$finder = $this->buildFinder();
$this->assertSame($finder, $finder->ignoreDotFiles(true)->ignoreVCS(false));
$this->assertIterator($this->toAbsolute(array('foo', 'foo/bar.tmp', 'test.php', 'test.py', 'toto', 'foo bar')), $finder->in(self::$tmpDir)->getIterator());
}
public function testSortByName()
{
$finder = $this->buildFinder();
$this->assertSame($finder, $finder->sortByName());
$this->assertIterator($this->toAbsolute(array('foo', 'foo bar', 'foo/bar.tmp', 'test.php', 'test.py', 'toto')), $finder->in(self::$tmpDir)->getIterator());
}
public function testSortByType()
{
$finder = $this->buildFinder();
$this->assertSame($finder, $finder->sortByType());
$this->assertIterator($this->toAbsolute(array('foo', 'foo bar', 'toto', 'foo/bar.tmp', 'test.php', 'test.py')), $finder->in(self::$tmpDir)->getIterator());
}
public function testSortByAccessedTime()
{
$finder = $this->buildFinder();
$this->assertSame($finder, $finder->sortByAccessedTime());
$this->assertIterator($this->toAbsolute(array('foo/bar.tmp', 'test.php', 'toto', 'test.py', 'foo', 'foo bar')), $finder->in(self::$tmpDir)->getIterator());
}
public function testSortByChangedTime()
{
$finder = $this->buildFinder();
$this->assertSame($finder, $finder->sortByChangedTime());
$this->assertIterator($this->toAbsolute(array('toto', 'test.py', 'test.php', 'foo/bar.tmp', 'foo', 'foo bar')), $finder->in(self::$tmpDir)->getIterator());
}
public function testSortByModifiedTime()
{
$finder = $this->buildFinder();
$this->assertSame($finder, $finder->sortByModifiedTime());
$this->assertIterator($this->toAbsolute(array('foo/bar.tmp', 'test.php', 'toto', 'test.py', 'foo', 'foo bar')), $finder->in(self::$tmpDir)->getIterator());
}
public function testSort()
{
$finder = $this->buildFinder();
$this->assertSame($finder, $finder->sort(function (\SplFileInfo $a, \SplFileInfo $b) { return strcmp($a->getRealPath(), $b->getRealPath()); }));
$this->assertIterator($this->toAbsolute(array('foo', 'foo bar', 'foo/bar.tmp', 'test.php', 'test.py', 'toto')), $finder->in(self::$tmpDir)->getIterator());
}
public function testFilter()
{
$finder = $this->buildFinder();
$this->assertSame($finder, $finder->filter(function (\SplFileInfo $f) { return false !== strpos($f, 'test'); }));
$this->assertIterator($this->toAbsolute(array('test.php', 'test.py')), $finder->in(self::$tmpDir)->getIterator());
}
public function testFollowLinks()
{
if ('\\' == DIRECTORY_SEPARATOR) {
$this->markTestSkipped('symlinks are not supported on Windows');
}
$finder = $this->buildFinder();
$this->assertSame($finder, $finder->followLinks());
$this->assertIterator($this->toAbsolute(array('foo', 'foo/bar.tmp', 'test.php', 'test.py', 'toto', 'foo bar')), $finder->in(self::$tmpDir)->getIterator());
}
public function testIn()
{
$finder = $this->buildFinder();
$iterator = $finder->files()->name('*.php')->depth('< 1')->in(array(self::$tmpDir, __DIR__))->getIterator();
$expected = array(
self::$tmpDir.DIRECTORY_SEPARATOR.'test.php',
__DIR__.DIRECTORY_SEPARATOR.'FinderTest.php',
__DIR__.DIRECTORY_SEPARATOR.'GlobTest.php',
);
$this->assertIterator($expected, $iterator);
}
/**
* @expectedException \InvalidArgumentException
*/
public function testInWithNonExistentDirectory()
{
$finder = new Finder();
$finder->in('foobar');
}
public function testInWithGlob()
{
$finder = $this->buildFinder();
$finder->in(array(__DIR__.'/Fixtures/*/B/C', __DIR__.'/Fixtures/*/*/B/C'))->getIterator();
$this->assertIterator($this->toAbsoluteFixtures(array('A/B/C/abc.dat', 'copy/A/B/C/abc.dat.copy')), $finder);
}
/**
* @expectedException \InvalidArgumentException
*/
public function testInWithNonDirectoryGlob()
{
$finder = new Finder();
$finder->in(__DIR__.'/Fixtures/A/a*');
}
public function testInWithGlobBrace()
{
$finder = $this->buildFinder();
$finder->in(array(__DIR__.'/Fixtures/{A,copy/A}/B/C'))->getIterator();
$this->assertIterator($this->toAbsoluteFixtures(array('A/B/C/abc.dat', 'copy/A/B/C/abc.dat.copy')), $finder);
}
/**
* @expectedException \LogicException
*/
public function testGetIteratorWithoutIn()
{
$finder = Finder::create();
$finder->getIterator();
}
public function testGetIterator()
{
$finder = $this->buildFinder();
$dirs = array();
foreach ($finder->directories()->in(self::$tmpDir) as $dir) {
$dirs[] = (string) $dir;
}
$expected = $this->toAbsolute(array('foo', 'toto'));
sort($dirs);
sort($expected);
$this->assertEquals($expected, $dirs, 'implements the \IteratorAggregate interface');
$finder = $this->buildFinder();
$this->assertEquals(2, iterator_count($finder->directories()->in(self::$tmpDir)), 'implements the \IteratorAggregate interface');
$finder = $this->buildFinder();
$a = iterator_to_array($finder->directories()->in(self::$tmpDir));
$a = array_values(array_map('strval', $a));
sort($a);
$this->assertEquals($expected, $a, 'implements the \IteratorAggregate interface');
}
public function testRelativePath()
{
$finder = $this->buildFinder()->in(self::$tmpDir);
$paths = array();
foreach ($finder as $file) {
$paths[] = $file->getRelativePath();
}
$ref = array('', '', '', '', 'foo', '');
sort($ref);
sort($paths);
$this->assertEquals($ref, $paths);
}
public function testRelativePathname()
{
$finder = $this->buildFinder()->in(self::$tmpDir)->sortByName();
$paths = array();
foreach ($finder as $file) {
$paths[] = $file->getRelativePathname();
}
$ref = array('test.php', 'toto', 'test.py', 'foo', 'foo'.DIRECTORY_SEPARATOR.'bar.tmp', 'foo bar');
sort($paths);
sort($ref);
$this->assertEquals($ref, $paths);
}
public function testAppendWithAFinder()
{
$finder = $this->buildFinder();
$finder->files()->in(self::$tmpDir.DIRECTORY_SEPARATOR.'foo');
$finder1 = $this->buildFinder();
$finder1->directories()->in(self::$tmpDir);
$finder = $finder->append($finder1);
$this->assertIterator($this->toAbsolute(array('foo', 'foo/bar.tmp', 'toto')), $finder->getIterator());
}
public function testAppendWithAnArray()
{
$finder = $this->buildFinder();
$finder->files()->in(self::$tmpDir.DIRECTORY_SEPARATOR.'foo');
$finder->append($this->toAbsolute(array('foo', 'toto')));
$this->assertIterator($this->toAbsolute(array('foo', 'foo/bar.tmp', 'toto')), $finder->getIterator());
}
public function testAppendReturnsAFinder()
{
$this->assertInstanceOf('Symfony\\Component\\Finder\\Finder', Finder::create()->append(array()));
}
public function testAppendDoesNotRequireIn()
{
$finder = $this->buildFinder();
$finder->in(self::$tmpDir.DIRECTORY_SEPARATOR.'foo');
$finder1 = Finder::create()->append($finder);
$this->assertIterator(iterator_to_array($finder->getIterator()), $finder1->getIterator());
}
public function testCountDirectories()
{
$directory = Finder::create()->directories()->in(self::$tmpDir);
$i = 0;
foreach ($directory as $dir) {
++$i;
}
$this->assertCount($i, $directory);
}
public function testCountFiles()
{
$files = Finder::create()->files()->in(__DIR__.DIRECTORY_SEPARATOR.'Fixtures');
$i = 0;
foreach ($files as $file) {
++$i;
}
$this->assertCount($i, $files);
}
/**
* @expectedException \LogicException
*/
public function testCountWithoutIn()
{
$finder = Finder::create()->files();
count($finder);
}
/**
* @dataProvider getContainsTestData
*/
public function testContains($matchPatterns, $noMatchPatterns, $expected)
{
$finder = $this->buildFinder();
$finder->in(__DIR__.DIRECTORY_SEPARATOR.'Fixtures')
->name('*.txt')->sortByName()
->contains($matchPatterns)
->notContains($noMatchPatterns);
$this->assertIterator($this->toAbsoluteFixtures($expected), $finder);
}
public function testContainsOnDirectory()
{
$finder = $this->buildFinder();
$finder->in(__DIR__)
->directories()
->name('Fixtures')
->contains('abc');
$this->assertIterator(array(), $finder);
}
public function testNotContainsOnDirectory()
{
$finder = $this->buildFinder();
$finder->in(__DIR__)
->directories()
->name('Fixtures')
->notContains('abc');
$this->assertIterator(array(), $finder);
}
/**
* Searching in multiple locations involves AppendIterator which does an unnecessary rewind which leaves FilterIterator
* with inner FilesystemIterator in an invalid state.
*
* @see https://bugs.php.net/68557
*/
public function testMultipleLocations()
{
$locations = array(
self::$tmpDir.'/',
self::$tmpDir.'/toto/',
);
// it is expected that there are test.py test.php in the tmpDir
$finder = new Finder();
$finder->in($locations)
// the default flag IGNORE_DOT_FILES fixes the problem indirectly
// so we set it to false for better isolation
->ignoreDotFiles(false)
->depth('< 1')->name('test.php');
$this->assertCount(1, $finder);
}
/**
* Searching in multiple locations with sub directories involves
* AppendIterator which does an unnecessary rewind which leaves
* FilterIterator with inner FilesystemIterator in an invalid state.
*
* @see https://bugs.php.net/68557
*/
public function testMultipleLocationsWithSubDirectories()
{
$locations = array(
__DIR__.'/Fixtures/one',
self::$tmpDir.DIRECTORY_SEPARATOR.'toto',
);
$finder = $this->buildFinder();
$finder->in($locations)->depth('< 10')->name('*.neon');
$expected = array(
__DIR__.'/Fixtures/one'.DIRECTORY_SEPARATOR.'b'.DIRECTORY_SEPARATOR.'c.neon',
__DIR__.'/Fixtures/one'.DIRECTORY_SEPARATOR.'b'.DIRECTORY_SEPARATOR.'d.neon',
);
$this->assertIterator($expected, $finder);
$this->assertIteratorInForeach($expected, $finder);
}
/**
* Iterator keys must be the file pathname.
*/
public function testIteratorKeys()
{
$finder = $this->buildFinder()->in(self::$tmpDir);
foreach ($finder as $key => $file) {
$this->assertEquals($file->getPathname(), $key);
}
}
public function testRegexSpecialCharsLocationWithPathRestrictionContainingStartFlag()
{
$finder = $this->buildFinder();
$finder->in(__DIR__.DIRECTORY_SEPARATOR.'Fixtures'.DIRECTORY_SEPARATOR.'r+e.gex[c]a(r)s')
->path('/^dir/');
$expected = array('r+e.gex[c]a(r)s'.DIRECTORY_SEPARATOR.'dir', 'r+e.gex[c]a(r)s'.DIRECTORY_SEPARATOR.'dir'.DIRECTORY_SEPARATOR.'bar.dat');
$this->assertIterator($this->toAbsoluteFixtures($expected), $finder);
}
public function getContainsTestData()
{
return array(
array('', '', array()),
array('foo', 'bar', array()),
array('', 'foobar', array('dolor.txt', 'ipsum.txt', 'lorem.txt')),
array('lorem ipsum dolor sit amet', 'foobar', array('lorem.txt')),
array('sit', 'bar', array('dolor.txt', 'ipsum.txt', 'lorem.txt')),
array('dolor sit amet', '@^L@m', array('dolor.txt', 'ipsum.txt')),
array('/^lorem ipsum dolor sit amet$/m', 'foobar', array('lorem.txt')),
array('lorem', 'foobar', array('lorem.txt')),
array('', 'lorem', array('dolor.txt', 'ipsum.txt')),
array('ipsum dolor sit amet', '/^IPSUM/m', array('lorem.txt')),
);
}
public function getRegexNameTestData()
{
return array(
array('~.+\\.p.+~i'),
array('~t.*s~i'),
);
}
/**
* @dataProvider getTestPathData
*/
public function testPath($matchPatterns, $noMatchPatterns, array $expected)
{
$finder = $this->buildFinder();
$finder->in(__DIR__.DIRECTORY_SEPARATOR.'Fixtures')
->path($matchPatterns)
->notPath($noMatchPatterns);
$this->assertIterator($this->toAbsoluteFixtures($expected), $finder);
}
public function getTestPathData()
{
return array(
array('', '', array()),
array('/^A\/B\/C/', '/C$/',
array('A'.DIRECTORY_SEPARATOR.'B'.DIRECTORY_SEPARATOR.'C'.DIRECTORY_SEPARATOR.'abc.dat'),
),
array('/^A\/B/', 'foobar',
array(
'A'.DIRECTORY_SEPARATOR.'B',
'A'.DIRECTORY_SEPARATOR.'B'.DIRECTORY_SEPARATOR.'C',
'A'.DIRECTORY_SEPARATOR.'B'.DIRECTORY_SEPARATOR.'ab.dat',
'A'.DIRECTORY_SEPARATOR.'B'.DIRECTORY_SEPARATOR.'C'.DIRECTORY_SEPARATOR.'abc.dat',
),
),
array('A/B/C', 'foobar',
array(
'A'.DIRECTORY_SEPARATOR.'B'.DIRECTORY_SEPARATOR.'C',
'A'.DIRECTORY_SEPARATOR.'B'.DIRECTORY_SEPARATOR.'C'.DIRECTORY_SEPARATOR.'abc.dat',
'copy'.DIRECTORY_SEPARATOR.'A'.DIRECTORY_SEPARATOR.'B'.DIRECTORY_SEPARATOR.'C',
'copy'.DIRECTORY_SEPARATOR.'A'.DIRECTORY_SEPARATOR.'B'.DIRECTORY_SEPARATOR.'C'.DIRECTORY_SEPARATOR.'abc.dat.copy',
),
),
array('A/B', 'foobar',
array(
//dirs
'A'.DIRECTORY_SEPARATOR.'B',
'A'.DIRECTORY_SEPARATOR.'B'.DIRECTORY_SEPARATOR.'C',
'copy'.DIRECTORY_SEPARATOR.'A'.DIRECTORY_SEPARATOR.'B',
'copy'.DIRECTORY_SEPARATOR.'A'.DIRECTORY_SEPARATOR.'B'.DIRECTORY_SEPARATOR.'C',
//files
'A'.DIRECTORY_SEPARATOR.'B'.DIRECTORY_SEPARATOR.'ab.dat',
'A'.DIRECTORY_SEPARATOR.'B'.DIRECTORY_SEPARATOR.'C'.DIRECTORY_SEPARATOR.'abc.dat',
'copy'.DIRECTORY_SEPARATOR.'A'.DIRECTORY_SEPARATOR.'B'.DIRECTORY_SEPARATOR.'ab.dat.copy',
'copy'.DIRECTORY_SEPARATOR.'A'.DIRECTORY_SEPARATOR.'B'.DIRECTORY_SEPARATOR.'C'.DIRECTORY_SEPARATOR.'abc.dat.copy',
),
),
array('/^with space\//', 'foobar',
array(
'with space'.DIRECTORY_SEPARATOR.'foo.txt',
),
),
);
}
public function testAccessDeniedException()
{
if ('\\' === DIRECTORY_SEPARATOR) {
$this->markTestSkipped('chmod is not supported on Windows');
}
$finder = $this->buildFinder();
$finder->files()->in(self::$tmpDir);
// make 'foo' directory non-readable
$testDir = self::$tmpDir.DIRECTORY_SEPARATOR.'foo';
chmod($testDir, 0333);
if (false === $couldRead = is_readable($testDir)) {
try {
$this->assertIterator($this->toAbsolute(array('foo bar', 'test.php', 'test.py')), $finder->getIterator());
$this->fail('Finder should throw an exception when opening a non-readable directory.');
} catch (\Exception $e) {
$expectedExceptionClass = 'Symfony\\Component\\Finder\\Exception\\AccessDeniedException';
if ($e instanceof \PHPUnit_Framework_ExpectationFailedException) {
$this->fail(sprintf("Expected exception:\n%s\nGot:\n%s\nWith comparison failure:\n%s", $expectedExceptionClass, 'PHPUnit_Framework_ExpectationFailedException', $e->getComparisonFailure()->getExpectedAsString()));
}
if ($e instanceof \PHPUnit\Framework\ExpectationFailedException) {
$this->fail(sprintf("Expected exception:\n%s\nGot:\n%s\nWith comparison failure:\n%s", $expectedExceptionClass, '\PHPUnit\Framework\ExpectationFailedException', $e->getComparisonFailure()->getExpectedAsString()));
}
$this->assertInstanceOf($expectedExceptionClass, $e);
}
}
// restore original permissions
chmod($testDir, 0777);
clearstatcache($testDir);
if ($couldRead) {
$this->markTestSkipped('could read test files while test requires unreadable');
}
}
public function testIgnoredAccessDeniedException()
{
if ('\\' === DIRECTORY_SEPARATOR) {
$this->markTestSkipped('chmod is not supported on Windows');
}
$finder = $this->buildFinder();
$finder->files()->ignoreUnreadableDirs()->in(self::$tmpDir);
// make 'foo' directory non-readable
$testDir = self::$tmpDir.DIRECTORY_SEPARATOR.'foo';
chmod($testDir, 0333);
if (false === ($couldRead = is_readable($testDir))) {
$this->assertIterator($this->toAbsolute(array('foo bar', 'test.php', 'test.py')), $finder->getIterator());
}
// restore original permissions
chmod($testDir, 0777);
clearstatcache($testDir);
if ($couldRead) {
$this->markTestSkipped('could read test files while test requires unreadable');
}
}
protected function buildFinder()
{
return Finder::create();
}
}

View File

@@ -1,2 +0,0 @@
dolor sit amet
DOLOR SIT AMET

View File

@@ -1,2 +0,0 @@
ipsum dolor sit amet
IPSUM DOLOR SIT AMET

View File

@@ -1,2 +0,0 @@
lorem ipsum dolor sit amet
LOREM IPSUM DOLOR SIT AMET

View File

@@ -1 +0,0 @@
.dot

View File

@@ -1,95 +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\Tests;
use PHPUnit\Framework\TestCase;
use Symfony\Component\Finder\Finder;
use Symfony\Component\Finder\Glob;
class GlobTest extends TestCase
{
public function testGlobToRegexDelimiters()
{
$this->assertEquals('#^(?=[^\.])\#$#', Glob::toRegex('#'));
$this->assertEquals('#^\.[^/]*$#', Glob::toRegex('.*'));
$this->assertEquals('^\.[^/]*$', Glob::toRegex('.*', true, true, ''));
$this->assertEquals('/^\.[^/]*$/', Glob::toRegex('.*', true, true, '/'));
}
public function testGlobToRegexDoubleStarStrictDots()
{
$finder = new Finder();
$finder->ignoreDotFiles(false);
$regex = Glob::toRegex('/**/*.neon');
foreach ($finder->in(__DIR__) as $k => $v) {
$k = str_replace(DIRECTORY_SEPARATOR, '/', $k);
if (preg_match($regex, substr($k, strlen(__DIR__)))) {
$match[] = substr($k, 10 + strlen(__DIR__));
}
}
sort($match);
$this->assertSame(array('one/b/c.neon', 'one/b/d.neon'), $match);
}
public function testGlobToRegexDoubleStarNonStrictDots()
{
$finder = new Finder();
$finder->ignoreDotFiles(false);
$regex = Glob::toRegex('/**/*.neon', false);
foreach ($finder->in(__DIR__) as $k => $v) {
$k = str_replace(DIRECTORY_SEPARATOR, '/', $k);
if (preg_match($regex, substr($k, strlen(__DIR__)))) {
$match[] = substr($k, 10 + strlen(__DIR__));
}
}
sort($match);
$this->assertSame(array('.dot/b/c.neon', '.dot/b/d.neon', 'one/b/c.neon', 'one/b/d.neon'), $match);
}
public function testGlobToRegexDoubleStarWithoutLeadingSlash()
{
$finder = new Finder();
$finder->ignoreDotFiles(false);
$regex = Glob::toRegex('/Fixtures/one/**');
foreach ($finder->in(__DIR__) as $k => $v) {
$k = str_replace(DIRECTORY_SEPARATOR, '/', $k);
if (preg_match($regex, substr($k, strlen(__DIR__)))) {
$match[] = substr($k, 10 + strlen(__DIR__));
}
}
sort($match);
$this->assertSame(array('one/a', 'one/b', 'one/b/c.neon', 'one/b/d.neon'), $match);
}
public function testGlobToRegexDoubleStarWithoutLeadingSlashNotStrictLeadingDot()
{
$finder = new Finder();
$finder->ignoreDotFiles(false);
$regex = Glob::toRegex('/Fixtures/one/**', false);
foreach ($finder->in(__DIR__) as $k => $v) {
$k = str_replace(DIRECTORY_SEPARATOR, '/', $k);
if (preg_match($regex, substr($k, strlen(__DIR__)))) {
$match[] = substr($k, 10 + strlen(__DIR__));
}
}
sort($match);
$this->assertSame(array('one/.dot', 'one/a', 'one/b', 'one/b/c.neon', 'one/b/d.neon'), $match);
}
}

View File

@@ -1,46 +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\Tests\Iterator;
use Symfony\Component\Finder\Iterator\CustomFilterIterator;
class CustomFilterIteratorTest extends IteratorTestCase
{
/**
* @expectedException \InvalidArgumentException
*/
public function testWithInvalidFilter()
{
new CustomFilterIterator(new Iterator(), array('foo'));
}
/**
* @dataProvider getAcceptData
*/
public function testAccept($filters, $expected)
{
$inner = new Iterator(array('test.php', 'test.py', 'foo.php'));
$iterator = new CustomFilterIterator($inner, $filters);
$this->assertIterator($expected, $iterator);
}
public function getAcceptData()
{
return array(
array(array(function (\SplFileInfo $fileinfo) { return false; }), array()),
array(array(function (\SplFileInfo $fileinfo) { return 0 === strpos($fileinfo, 'test'); }), array('test.php', 'test.py')),
array(array('is_dir'), array()),
);
}
}

View File

@@ -1,74 +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\Tests\Iterator;
use Symfony\Component\Finder\Iterator\DateRangeFilterIterator;
use Symfony\Component\Finder\Comparator\DateComparator;
class DateRangeFilterIteratorTest extends RealIteratorTestCase
{
/**
* @dataProvider getAcceptData
*/
public function testAccept($size, $expected)
{
$files = self::$files;
$files[] = self::toAbsolute('doesnotexist');
$inner = new Iterator($files);
$iterator = new DateRangeFilterIterator($inner, $size);
$this->assertIterator($expected, $iterator);
}
public function getAcceptData()
{
$since20YearsAgo = array(
'.git',
'test.py',
'foo',
'foo/bar.tmp',
'test.php',
'toto',
'toto/.git',
'.bar',
'.foo',
'.foo/.bar',
'foo bar',
'.foo/bar',
);
$since2MonthsAgo = array(
'.git',
'test.py',
'foo',
'toto',
'toto/.git',
'.bar',
'.foo',
'.foo/.bar',
'foo bar',
'.foo/bar',
);
$untilLastMonth = array(
'foo/bar.tmp',
'test.php',
);
return array(
array(array(new DateComparator('since 20 years ago')), $this->toAbsolute($since20YearsAgo)),
array(array(new DateComparator('since 2 months ago')), $this->toAbsolute($since2MonthsAgo)),
array(array(new DateComparator('until last month')), $this->toAbsolute($untilLastMonth)),
);
}
}

View File

@@ -1,83 +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\Tests\Iterator;
use Symfony\Component\Finder\Iterator\DepthRangeFilterIterator;
class DepthRangeFilterIteratorTest extends RealIteratorTestCase
{
/**
* @dataProvider getAcceptData
*/
public function testAccept($minDepth, $maxDepth, $expected)
{
$inner = new \RecursiveIteratorIterator(new \RecursiveDirectoryIterator($this->toAbsolute(), \FilesystemIterator::SKIP_DOTS), \RecursiveIteratorIterator::SELF_FIRST);
$iterator = new DepthRangeFilterIterator($inner, $minDepth, $maxDepth);
$actual = array_keys(iterator_to_array($iterator));
sort($expected);
sort($actual);
$this->assertEquals($expected, $actual);
}
public function getAcceptData()
{
$lessThan1 = array(
'.git',
'test.py',
'foo',
'test.php',
'toto',
'.foo',
'.bar',
'foo bar',
);
$lessThanOrEqualTo1 = array(
'.git',
'test.py',
'foo',
'foo/bar.tmp',
'test.php',
'toto',
'toto/.git',
'.foo',
'.foo/.bar',
'.bar',
'foo bar',
'.foo/bar',
);
$graterThanOrEqualTo1 = array(
'toto/.git',
'foo/bar.tmp',
'.foo/.bar',
'.foo/bar',
);
$equalTo1 = array(
'toto/.git',
'foo/bar.tmp',
'.foo/.bar',
'.foo/bar',
);
return array(
array(0, 0, $this->toAbsolute($lessThan1)),
array(0, 1, $this->toAbsolute($lessThanOrEqualTo1)),
array(2, PHP_INT_MAX, array()),
array(1, PHP_INT_MAX, $this->toAbsolute($graterThanOrEqualTo1)),
array(1, 1, $this->toAbsolute($equalTo1)),
);
}
}

View File

@@ -1,80 +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\Tests\Iterator;
use Symfony\Component\Finder\Iterator\ExcludeDirectoryFilterIterator;
use Symfony\Component\Finder\Iterator\RecursiveDirectoryIterator;
class ExcludeDirectoryFilterIteratorTest extends RealIteratorTestCase
{
/**
* @dataProvider getAcceptData
*/
public function testAccept($directories, $expected)
{
$inner = new \RecursiveIteratorIterator(new RecursiveDirectoryIterator($this->toAbsolute(), \FilesystemIterator::SKIP_DOTS), \RecursiveIteratorIterator::SELF_FIRST);
$iterator = new ExcludeDirectoryFilterIterator($inner, $directories);
$this->assertIterator($expected, $iterator);
}
public function getAcceptData()
{
$foo = array(
'.bar',
'.foo',
'.foo/.bar',
'.foo/bar',
'.git',
'test.py',
'test.php',
'toto',
'toto/.git',
'foo bar',
);
$fo = array(
'.bar',
'.foo',
'.foo/.bar',
'.foo/bar',
'.git',
'test.py',
'foo',
'foo/bar.tmp',
'test.php',
'toto',
'toto/.git',
'foo bar',
);
$toto = array(
'.bar',
'.foo',
'.foo/.bar',
'.foo/bar',
'.git',
'test.py',
'foo',
'foo/bar.tmp',
'test.php',
'foo bar',
);
return array(
array(array('foo'), $this->toAbsolute($foo)),
array(array('fo'), $this->toAbsolute($fo)),
array(array('toto/'), $this->toAbsolute($toto)),
);
}
}

View File

@@ -1,73 +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\Tests\Iterator;
use Symfony\Component\Finder\Iterator\FileTypeFilterIterator;
class FileTypeFilterIteratorTest extends RealIteratorTestCase
{
/**
* @dataProvider getAcceptData
*/
public function testAccept($mode, $expected)
{
$inner = new InnerTypeIterator(self::$files);
$iterator = new FileTypeFilterIterator($inner, $mode);
$this->assertIterator($expected, $iterator);
}
public function getAcceptData()
{
$onlyFiles = array(
'test.py',
'foo/bar.tmp',
'test.php',
'.bar',
'.foo/.bar',
'.foo/bar',
'foo bar',
);
$onlyDirectories = array(
'.git',
'foo',
'toto',
'toto/.git',
'.foo',
);
return array(
array(FileTypeFilterIterator::ONLY_FILES, $this->toAbsolute($onlyFiles)),
array(FileTypeFilterIterator::ONLY_DIRECTORIES, $this->toAbsolute($onlyDirectories)),
);
}
}
class InnerTypeIterator extends \ArrayIterator
{
public function current()
{
return new \SplFileInfo(parent::current());
}
public function isFile()
{
return $this->current()->isFile();
}
public function isDir()
{
return $this->current()->isDir();
}
}

View File

@@ -1,86 +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\Tests\Iterator;
use Symfony\Component\Finder\Iterator\FilecontentFilterIterator;
class FilecontentFilterIteratorTest extends IteratorTestCase
{
public function testAccept()
{
$inner = new MockFileListIterator(array('test.txt'));
$iterator = new FilecontentFilterIterator($inner, array(), array());
$this->assertIterator(array('test.txt'), $iterator);
}
public function testDirectory()
{
$inner = new MockFileListIterator(array('directory'));
$iterator = new FilecontentFilterIterator($inner, array('directory'), array());
$this->assertIterator(array(), $iterator);
}
public function testUnreadableFile()
{
$inner = new MockFileListIterator(array('file r-'));
$iterator = new FilecontentFilterIterator($inner, array('file r-'), array());
$this->assertIterator(array(), $iterator);
}
/**
* @dataProvider getTestFilterData
*/
public function testFilter(\Iterator $inner, array $matchPatterns, array $noMatchPatterns, array $resultArray)
{
$iterator = new FilecontentFilterIterator($inner, $matchPatterns, $noMatchPatterns);
$this->assertIterator($resultArray, $iterator);
}
public function getTestFilterData()
{
$inner = new MockFileListIterator();
$inner[] = new MockSplFileInfo(array(
'name' => 'a.txt',
'contents' => 'Lorem ipsum...',
'type' => 'file',
'mode' => 'r+', )
);
$inner[] = new MockSplFileInfo(array(
'name' => 'b.yml',
'contents' => 'dolor sit...',
'type' => 'file',
'mode' => 'r+', )
);
$inner[] = new MockSplFileInfo(array(
'name' => 'some/other/dir/third.php',
'contents' => 'amet...',
'type' => 'file',
'mode' => 'r+', )
);
$inner[] = new MockSplFileInfo(array(
'name' => 'unreadable-file.txt',
'contents' => false,
'type' => 'file',
'mode' => 'r+', )
);
return array(
array($inner, array('.'), array(), array('a.txt', 'b.yml', 'some/other/dir/third.php')),
array($inner, array('ipsum'), array(), array('a.txt')),
array($inner, array('i', 'amet'), array('Lorem', 'amet'), array('b.yml')),
);
}
}

View File

@@ -1,54 +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\Tests\Iterator;
use Symfony\Component\Finder\Iterator\FilenameFilterIterator;
class FilenameFilterIteratorTest extends IteratorTestCase
{
/**
* @dataProvider getAcceptData
*/
public function testAccept($matchPatterns, $noMatchPatterns, $expected)
{
$inner = new InnerNameIterator(array('test.php', 'test.py', 'foo.php'));
$iterator = new FilenameFilterIterator($inner, $matchPatterns, $noMatchPatterns);
$this->assertIterator($expected, $iterator);
}
public function getAcceptData()
{
return array(
array(array('test.*'), array(), array('test.php', 'test.py')),
array(array(), array('test.*'), array('foo.php')),
array(array('*.php'), array('test.*'), array('foo.php')),
array(array('*.php', '*.py'), array('foo.*'), array('test.php', 'test.py')),
array(array('/\.php$/'), array(), array('test.php', 'foo.php')),
array(array(), array('/\.php$/'), array('test.py')),
);
}
}
class InnerNameIterator extends \ArrayIterator
{
public function current()
{
return new \SplFileInfo(parent::current());
}
public function getFilename()
{
return parent::current();
}
}

View File

@@ -1,51 +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\Tests\Iterator;
/**
* @author Alex Bogomazov
*/
class FilterIteratorTest extends RealIteratorTestCase
{
public function testFilterFilesystemIterators()
{
$i = new \FilesystemIterator($this->toAbsolute());
// it is expected that there are test.py test.php in the tmpDir
$i = $this->getMockForAbstractClass('Symfony\Component\Finder\Iterator\FilterIterator', array($i));
$i->expects($this->any())
->method('accept')
->will($this->returnCallback(function () use ($i) {
return (bool) preg_match('/\.php/', (string) $i->current());
})
);
$c = 0;
foreach ($i as $item) {
++$c;
}
$this->assertEquals(1, $c);
$i->rewind();
$c = 0;
foreach ($i as $item) {
++$c;
}
// This would fail in php older than 5.5.23/5.6.7 with \FilterIterator
// but works with Symfony\Component\Finder\Iterator\FilterIterator
// see https://bugs.php.net/68557
$this->assertEquals(1, $c);
}
}

View File

@@ -1,55 +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\Tests\Iterator;
class Iterator implements \Iterator
{
protected $values = array();
public function __construct(array $values = array())
{
foreach ($values as $value) {
$this->attach(new \SplFileInfo($value));
}
$this->rewind();
}
public function attach(\SplFileInfo $fileinfo)
{
$this->values[] = $fileinfo;
}
public function rewind()
{
reset($this->values);
}
public function valid()
{
return false !== $this->current();
}
public function next()
{
next($this->values);
}
public function current()
{
return current($this->values);
}
public function key()
{
return key($this->values);
}
}

View File

@@ -1,100 +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\Tests\Iterator;
use PHPUnit\Framework\TestCase;
abstract class IteratorTestCase extends TestCase
{
protected function assertIterator($expected, \Traversable $iterator)
{
// set iterator_to_array $use_key to false to avoid values merge
// this made FinderTest::testAppendWithAnArray() fail with GnuFinderAdapter
$values = array_map(function (\SplFileInfo $fileinfo) { return str_replace('/', DIRECTORY_SEPARATOR, $fileinfo->getPathname()); }, iterator_to_array($iterator, false));
$expected = array_map(function ($path) { return str_replace('/', DIRECTORY_SEPARATOR, $path); }, $expected);
sort($values);
sort($expected);
$this->assertEquals($expected, array_values($values));
}
protected function assertOrderedIterator($expected, \Traversable $iterator)
{
$values = array_map(function (\SplFileInfo $fileinfo) { return $fileinfo->getPathname(); }, iterator_to_array($iterator));
$this->assertEquals($expected, array_values($values));
}
/**
* Same as assertOrderedIterator, but checks the order of groups of
* array elements.
*
* @param array $expected - an array of arrays. For any two subarrays
* $a and $b such that $a goes before $b in $expected, the method
* asserts that any element of $a goes before any element of $b
* in the sequence generated by $iterator
* @param \Traversable $iterator
*/
protected function assertOrderedIteratorForGroups($expected, \Traversable $iterator)
{
$values = array_values(array_map(function (\SplFileInfo $fileinfo) { return $fileinfo->getPathname(); }, iterator_to_array($iterator)));
foreach ($expected as $subarray) {
$temp = array();
while (count($values) && count($temp) < count($subarray)) {
$temp[] = array_shift($values);
}
sort($temp);
sort($subarray);
$this->assertEquals($subarray, $temp);
}
}
/**
* Same as IteratorTestCase::assertIterator with foreach usage.
*
* @param array $expected
* @param \Traversable $iterator
*/
protected function assertIteratorInForeach($expected, \Traversable $iterator)
{
$values = array();
foreach ($iterator as $file) {
$this->assertInstanceOf('Symfony\\Component\\Finder\\SplFileInfo', $file);
$values[] = $file->getPathname();
}
sort($values);
sort($expected);
$this->assertEquals($expected, array_values($values));
}
/**
* Same as IteratorTestCase::assertOrderedIterator with foreach usage.
*
* @param array $expected
* @param \Traversable $iterator
*/
protected function assertOrderedIteratorInForeach($expected, \Traversable $iterator)
{
$values = array();
foreach ($iterator as $file) {
$this->assertInstanceOf('Symfony\\Component\\Finder\\SplFileInfo', $file);
$values[] = $file->getPathname();
}
$this->assertEquals($expected, array_values($values));
}
}

View File

@@ -1,21 +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\Tests\Iterator;
class MockFileListIterator extends \ArrayIterator
{
public function __construct(array $filesArray = array())
{
$files = array_map(function ($file) { return new MockSplFileInfo($file); }, $filesArray);
parent::__construct($files);
}
}

View File

@@ -1,132 +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\Tests\Iterator;
class MockSplFileInfo extends \SplFileInfo
{
const TYPE_DIRECTORY = 1;
const TYPE_FILE = 2;
const TYPE_UNKNOWN = 3;
private $contents = null;
private $mode = null;
private $type = null;
private $relativePath = null;
private $relativePathname = null;
public function __construct($param)
{
if (is_string($param)) {
parent::__construct($param);
} elseif (is_array($param)) {
$defaults = array(
'name' => 'file.txt',
'contents' => null,
'mode' => null,
'type' => null,
'relativePath' => null,
'relativePathname' => null,
);
$defaults = array_merge($defaults, $param);
parent::__construct($defaults['name']);
$this->setContents($defaults['contents']);
$this->setMode($defaults['mode']);
$this->setType($defaults['type']);
$this->setRelativePath($defaults['relativePath']);
$this->setRelativePathname($defaults['relativePathname']);
} else {
throw new \RuntimeException(sprintf('Incorrect parameter "%s"', $param));
}
}
public function isFile()
{
if (null === $this->type) {
return false !== strpos($this->getFilename(), 'file');
}
return self::TYPE_FILE === $this->type;
}
public function isDir()
{
if (null === $this->type) {
return false !== strpos($this->getFilename(), 'directory');
}
return self::TYPE_DIRECTORY === $this->type;
}
public function isReadable()
{
if (null === $this->mode) {
return preg_match('/r\+/', $this->getFilename());
}
return preg_match('/r\+/', $this->mode);
}
public function getContents()
{
return $this->contents;
}
public function setContents($contents)
{
$this->contents = $contents;
}
public function setMode($mode)
{
$this->mode = $mode;
}
public function setType($type)
{
if (is_string($type)) {
switch ($type) {
case 'directory':
case 'd':
$this->type = self::TYPE_DIRECTORY;
break;
case 'file':
case 'f':
$this->type = self::TYPE_FILE;
break;
default:
$this->type = self::TYPE_UNKNOWN;
}
} else {
$this->type = $type;
}
}
public function setRelativePath($relativePath)
{
$this->relativePath = $relativePath;
}
public function setRelativePathname($relativePathname)
{
$this->relativePathname = $relativePathname;
}
public function getRelativePath()
{
return $this->relativePath;
}
public function getRelativePathname()
{
return $this->relativePathname;
}
}

View File

@@ -1,71 +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\Tests\Iterator;
use PHPUnit\Framework\TestCase;
use Symfony\Component\Finder\Iterator\MultiplePcreFilterIterator;
class MultiplePcreFilterIteratorTest extends TestCase
{
/**
* @dataProvider getIsRegexFixtures
*/
public function testIsRegex($string, $isRegex, $message)
{
$testIterator = new TestMultiplePcreFilterIterator();
$this->assertEquals($isRegex, $testIterator->isRegex($string), $message);
}
public function getIsRegexFixtures()
{
return array(
array('foo', false, 'string'),
array(' foo ', false, '" " is not a valid delimiter'),
array('\\foo\\', false, '"\\" is not a valid delimiter'),
array('afooa', false, '"a" is not a valid delimiter'),
array('//', false, 'the pattern should contain at least 1 character'),
array('/a/', true, 'valid regex'),
array('/foo/', true, 'valid regex'),
array('/foo/i', true, 'valid regex with a single modifier'),
array('/foo/imsxu', true, 'valid regex with multiple modifiers'),
array('#foo#', true, '"#" is a valid delimiter'),
array('{foo}', true, '"{,}" is a valid delimiter pair'),
array('[foo]', true, '"[,]" is a valid delimiter pair'),
array('(foo)', true, '"(,)" is a valid delimiter pair'),
array('<foo>', true, '"<,>" is a valid delimiter pair'),
array('*foo.*', false, '"*" is not considered as a valid delimiter'),
array('?foo.?', false, '"?" is not considered as a valid delimiter'),
);
}
}
class TestMultiplePcreFilterIterator extends MultiplePcreFilterIterator
{
public function __construct()
{
}
public function accept()
{
throw new \BadFunctionCallException('Not implemented');
}
public function isRegex($str)
{
return parent::isRegex($str);
}
public function toRegex($str)
{
throw new \BadFunctionCallException('Not implemented');
}
}

View File

@@ -1,82 +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\Tests\Iterator;
use Symfony\Component\Finder\Iterator\PathFilterIterator;
class PathFilterIteratorTest extends IteratorTestCase
{
/**
* @dataProvider getTestFilterData
*/
public function testFilter(\Iterator $inner, array $matchPatterns, array $noMatchPatterns, array $resultArray)
{
$iterator = new PathFilterIterator($inner, $matchPatterns, $noMatchPatterns);
$this->assertIterator($resultArray, $iterator);
}
public function getTestFilterData()
{
$inner = new MockFileListIterator();
//PATH: A/B/C/abc.dat
$inner[] = new MockSplFileInfo(array(
'name' => 'abc.dat',
'relativePathname' => 'A'.DIRECTORY_SEPARATOR.'B'.DIRECTORY_SEPARATOR.'C'.DIRECTORY_SEPARATOR.'abc.dat',
));
//PATH: A/B/ab.dat
$inner[] = new MockSplFileInfo(array(
'name' => 'ab.dat',
'relativePathname' => 'A'.DIRECTORY_SEPARATOR.'B'.DIRECTORY_SEPARATOR.'ab.dat',
));
//PATH: A/a.dat
$inner[] = new MockSplFileInfo(array(
'name' => 'a.dat',
'relativePathname' => 'A'.DIRECTORY_SEPARATOR.'a.dat',
));
//PATH: copy/A/B/C/abc.dat.copy
$inner[] = new MockSplFileInfo(array(
'name' => 'abc.dat.copy',
'relativePathname' => 'copy'.DIRECTORY_SEPARATOR.'A'.DIRECTORY_SEPARATOR.'B'.DIRECTORY_SEPARATOR.'C'.DIRECTORY_SEPARATOR.'abc.dat',
));
//PATH: copy/A/B/ab.dat.copy
$inner[] = new MockSplFileInfo(array(
'name' => 'ab.dat.copy',
'relativePathname' => 'copy'.DIRECTORY_SEPARATOR.'A'.DIRECTORY_SEPARATOR.'B'.DIRECTORY_SEPARATOR.'ab.dat',
));
//PATH: copy/A/a.dat.copy
$inner[] = new MockSplFileInfo(array(
'name' => 'a.dat.copy',
'relativePathname' => 'copy'.DIRECTORY_SEPARATOR.'A'.DIRECTORY_SEPARATOR.'a.dat',
));
return array(
array($inner, array('/^A/'), array(), array('abc.dat', 'ab.dat', 'a.dat')),
array($inner, array('/^A\/B/'), array(), array('abc.dat', 'ab.dat')),
array($inner, array('/^A\/B\/C/'), array(), array('abc.dat')),
array($inner, array('/A\/B\/C/'), array(), array('abc.dat', 'abc.dat.copy')),
array($inner, array('A'), array(), array('abc.dat', 'ab.dat', 'a.dat', 'abc.dat.copy', 'ab.dat.copy', 'a.dat.copy')),
array($inner, array('A/B'), array(), array('abc.dat', 'ab.dat', 'abc.dat.copy', 'ab.dat.copy')),
array($inner, array('A/B/C'), array(), array('abc.dat', 'abc.dat.copy')),
array($inner, array('copy/A'), array(), array('abc.dat.copy', 'ab.dat.copy', 'a.dat.copy')),
array($inner, array('copy/A/B'), array(), array('abc.dat.copy', 'ab.dat.copy')),
array($inner, array('copy/A/B/C'), array(), array('abc.dat.copy')),
);
}
}

View File

@@ -1,110 +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\Tests\Iterator;
abstract class RealIteratorTestCase extends IteratorTestCase
{
protected static $tmpDir;
protected static $files;
public static function setUpBeforeClass()
{
self::$tmpDir = realpath(sys_get_temp_dir()).DIRECTORY_SEPARATOR.'symfony_finder';
self::$files = array(
'.git/',
'.foo/',
'.foo/.bar',
'.foo/bar',
'.bar',
'test.py',
'foo/',
'foo/bar.tmp',
'test.php',
'toto/',
'toto/.git/',
'foo bar',
);
self::$files = self::toAbsolute(self::$files);
if (is_dir(self::$tmpDir)) {
self::tearDownAfterClass();
} else {
mkdir(self::$tmpDir);
}
foreach (self::$files as $file) {
if (DIRECTORY_SEPARATOR === $file[strlen($file) - 1]) {
mkdir($file);
} else {
touch($file);
}
}
file_put_contents(self::toAbsolute('test.php'), str_repeat(' ', 800));
file_put_contents(self::toAbsolute('test.py'), str_repeat(' ', 2000));
touch(self::toAbsolute('foo/bar.tmp'), strtotime('2005-10-15'));
touch(self::toAbsolute('test.php'), strtotime('2005-10-15'));
}
public static function tearDownAfterClass()
{
foreach (array_reverse(self::$files) as $file) {
if (DIRECTORY_SEPARATOR === $file[strlen($file) - 1]) {
@rmdir($file);
} else {
@unlink($file);
}
}
}
protected static function toAbsolute($files = null)
{
/*
* Without the call to setUpBeforeClass() property can be null.
*/
if (!self::$tmpDir) {
self::$tmpDir = realpath(sys_get_temp_dir()).DIRECTORY_SEPARATOR.'symfony_finder';
}
if (is_array($files)) {
$f = array();
foreach ($files as $file) {
if (is_array($file)) {
$f[] = self::toAbsolute($file);
} else {
$f[] = self::$tmpDir.DIRECTORY_SEPARATOR.str_replace('/', DIRECTORY_SEPARATOR, $file);
}
}
return $f;
}
if (is_string($files)) {
return self::$tmpDir.DIRECTORY_SEPARATOR.str_replace('/', DIRECTORY_SEPARATOR, $files);
}
return self::$tmpDir;
}
protected static function toAbsoluteFixtures($files)
{
$f = array();
foreach ($files as $file) {
$f[] = realpath(__DIR__.DIRECTORY_SEPARATOR.'..'.DIRECTORY_SEPARATOR.'Fixtures'.DIRECTORY_SEPARATOR.$file);
}
return $f;
}
}

View File

@@ -1,59 +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\Tests\Iterator;
use Symfony\Component\Finder\Iterator\RecursiveDirectoryIterator;
class RecursiveDirectoryIteratorTest extends IteratorTestCase
{
/**
* @group network
*/
public function testRewindOnFtp()
{
try {
$i = new RecursiveDirectoryIterator('ftp://speedtest.tele2.net/', \RecursiveDirectoryIterator::SKIP_DOTS);
} catch (\UnexpectedValueException $e) {
$this->markTestSkipped('Unsupported stream "ftp".');
}
$i->rewind();
$this->assertTrue(true);
}
/**
* @group network
*/
public function testSeekOnFtp()
{
try {
$i = new RecursiveDirectoryIterator('ftp://speedtest.tele2.net/', \RecursiveDirectoryIterator::SKIP_DOTS);
} catch (\UnexpectedValueException $e) {
$this->markTestSkipped('Unsupported stream "ftp".');
}
$contains = array(
'ftp://speedtest.tele2.net'.DIRECTORY_SEPARATOR.'1000GB.zip',
'ftp://speedtest.tele2.net'.DIRECTORY_SEPARATOR.'100GB.zip',
);
$actual = array();
$i->seek(0);
$actual[] = $i->getPathname();
$i->seek(1);
$actual[] = $i->getPathname();
$this->assertEquals($contains, $actual);
}
}

View File

@@ -1,69 +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\Tests\Iterator;
use Symfony\Component\Finder\Iterator\SizeRangeFilterIterator;
use Symfony\Component\Finder\Comparator\NumberComparator;
class SizeRangeFilterIteratorTest extends RealIteratorTestCase
{
/**
* @dataProvider getAcceptData
*/
public function testAccept($size, $expected)
{
$inner = new InnerSizeIterator(self::$files);
$iterator = new SizeRangeFilterIterator($inner, $size);
$this->assertIterator($expected, $iterator);
}
public function getAcceptData()
{
$lessThan1KGreaterThan05K = array(
'.foo',
'.git',
'foo',
'test.php',
'toto',
'toto/.git',
);
return array(
array(array(new NumberComparator('< 1K'), new NumberComparator('> 0.5K')), $this->toAbsolute($lessThan1KGreaterThan05K)),
);
}
}
class InnerSizeIterator extends \ArrayIterator
{
public function current()
{
return new \SplFileInfo(parent::current());
}
public function getFilename()
{
return parent::current();
}
public function isFile()
{
return $this->current()->isFile();
}
public function getSize()
{
return $this->current()->getSize();
}
}

View File

@@ -1,183 +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\Tests\Iterator;
use Symfony\Component\Finder\Iterator\SortableIterator;
class SortableIteratorTest extends RealIteratorTestCase
{
public function testConstructor()
{
try {
new SortableIterator(new Iterator(array()), 'foobar');
$this->fail('__construct() throws an \InvalidArgumentException exception if the mode is not valid');
} catch (\Exception $e) {
$this->assertInstanceOf('InvalidArgumentException', $e, '__construct() throws an \InvalidArgumentException exception if the mode is not valid');
}
}
/**
* @dataProvider getAcceptData
*/
public function testAccept($mode, $expected)
{
if (!is_callable($mode)) {
switch ($mode) {
case SortableIterator::SORT_BY_ACCESSED_TIME:
if ('\\' === DIRECTORY_SEPARATOR) {
touch(self::toAbsolute('.git'));
} else {
file_get_contents(self::toAbsolute('.git'));
}
sleep(1);
file_get_contents(self::toAbsolute('.bar'));
break;
case SortableIterator::SORT_BY_CHANGED_TIME:
file_put_contents(self::toAbsolute('test.php'), 'foo');
sleep(1);
file_put_contents(self::toAbsolute('test.py'), 'foo');
break;
case SortableIterator::SORT_BY_MODIFIED_TIME:
file_put_contents(self::toAbsolute('test.php'), 'foo');
sleep(1);
file_put_contents(self::toAbsolute('test.py'), 'foo');
break;
}
}
$inner = new Iterator(self::$files);
$iterator = new SortableIterator($inner, $mode);
if ($mode === SortableIterator::SORT_BY_ACCESSED_TIME
|| $mode === SortableIterator::SORT_BY_CHANGED_TIME
|| $mode === SortableIterator::SORT_BY_MODIFIED_TIME
) {
if ('\\' === DIRECTORY_SEPARATOR && SortableIterator::SORT_BY_MODIFIED_TIME !== $mode) {
$this->markTestSkipped('Sorting by atime or ctime is not supported on Windows');
}
$this->assertOrderedIteratorForGroups($expected, $iterator);
} else {
$this->assertOrderedIterator($expected, $iterator);
}
}
public function getAcceptData()
{
$sortByName = array(
'.bar',
'.foo',
'.foo/.bar',
'.foo/bar',
'.git',
'foo',
'foo bar',
'foo/bar.tmp',
'test.php',
'test.py',
'toto',
'toto/.git',
);
$sortByType = array(
'.foo',
'.git',
'foo',
'toto',
'toto/.git',
'.bar',
'.foo/.bar',
'.foo/bar',
'foo bar',
'foo/bar.tmp',
'test.php',
'test.py',
);
$customComparison = array(
'.bar',
'.foo',
'.foo/.bar',
'.foo/bar',
'.git',
'foo',
'foo bar',
'foo/bar.tmp',
'test.php',
'test.py',
'toto',
'toto/.git',
);
$sortByAccessedTime = array(
// For these two files the access time was set to 2005-10-15
array('foo/bar.tmp', 'test.php'),
// These files were created more or less at the same time
array(
'.git',
'.foo',
'.foo/.bar',
'.foo/bar',
'test.py',
'foo',
'toto',
'toto/.git',
'foo bar',
),
// This file was accessed after sleeping for 1 sec
array('.bar'),
);
$sortByChangedTime = array(
array(
'.git',
'.foo',
'.foo/.bar',
'.foo/bar',
'.bar',
'foo',
'foo/bar.tmp',
'toto',
'toto/.git',
'foo bar',
),
array('test.php'),
array('test.py'),
);
$sortByModifiedTime = array(
array(
'.git',
'.foo',
'.foo/.bar',
'.foo/bar',
'.bar',
'foo',
'foo/bar.tmp',
'toto',
'toto/.git',
'foo bar',
),
array('test.php'),
array('test.py'),
);
return array(
array(SortableIterator::SORT_BY_NAME, $this->toAbsolute($sortByName)),
array(SortableIterator::SORT_BY_TYPE, $this->toAbsolute($sortByType)),
array(SortableIterator::SORT_BY_ACCESSED_TIME, $this->toAbsolute($sortByAccessedTime)),
array(SortableIterator::SORT_BY_CHANGED_TIME, $this->toAbsolute($sortByChangedTime)),
array(SortableIterator::SORT_BY_MODIFIED_TIME, $this->toAbsolute($sortByModifiedTime)),
array(function (\SplFileInfo $a, \SplFileInfo $b) { return strcmp($a->getRealPath(), $b->getRealPath()); }, $this->toAbsolute($customComparison)),
);
}
}

View File

@@ -1,7 +1,7 @@
{
"name": "symfony/finder",
"type": "library",
"description": "Symfony Finder Component",
"description": "Finds files and directories via an intuitive fluent interface",
"keywords": [],
"homepage": "https://symfony.com",
"license": "MIT",
@@ -16,7 +16,7 @@
}
],
"require": {
"php": ">=5.5.9"
"php": ">=8.0.2"
},
"autoload": {
"psr-4": { "Symfony\\Component\\Finder\\": "" },
@@ -24,10 +24,5 @@
"/Tests/"
]
},
"minimum-stability": "dev",
"extra": {
"branch-alias": {
"dev-master": "3.3-dev"
}
}
"minimum-stability": "dev"
}

View File

@@ -1,30 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="http://schema.phpunit.de/4.1/phpunit.xsd"
backupGlobals="false"
colors="true"
bootstrap="vendor/autoload.php"
failOnRisky="true"
failOnWarning="true"
>
<php>
<ini name="error_reporting" value="-1" />
</php>
<testsuites>
<testsuite name="Symfony Finder Component Test Suite">
<directory>./Tests/</directory>
</testsuite>
</testsuites>
<filter>
<whitelist>
<directory>./</directory>
<exclude>
<directory>./Tests</directory>
<directory>./vendor</directory>
</exclude>
</whitelist>
</filter>
</phpunit>