[
  {
    "path": ".gitattributes",
    "content": "/Tests export-ignore\n/phpunit.xml.dist export-ignore\n/.git* export-ignore\n"
  },
  {
    "path": ".github/PULL_REQUEST_TEMPLATE.md",
    "content": "Please do not submit any Pull Requests here. They will be closed.\n---\n\nPlease submit your PR here instead:\nhttps://github.com/symfony/symfony\n\nThis repository is what we call a \"subtree split\": a read-only subset of that main repository.\nWe're looking forward to your PR there!\n"
  },
  {
    "path": ".github/workflows/close-pull-request.yml",
    "content": "name: Close Pull Request\n\non:\n  pull_request_target:\n    types: [opened]\n\njobs:\n  run:\n    runs-on: ubuntu-latest\n    steps:\n    - uses: superbrothers/close-pull-request@v3\n      with:\n        comment: |\n          Thanks for your Pull Request! We love contributions.\n\n          However, you should instead open your PR on the main repository:\n          https://github.com/symfony/symfony\n\n          This repository is what we call a \"subtree split\": a read-only subset of that main repository.\n          We're looking forward to your PR there!\n"
  },
  {
    "path": ".gitignore",
    "content": "vendor/\ncomposer.lock\nphpunit.xml\n"
  },
  {
    "path": "CHANGELOG.md",
    "content": "CHANGELOG\n=========\n\n6.4\n---\n\n * Add early directory pruning to `Finder::filter()`\n\n6.2\n---\n\n * Add `Finder::sortByExtension()` and `Finder::sortBySize()`\n * Add `Finder::sortByCaseInsensitiveName()` to sort by name with case insensitive sorting methods\n\n6.0\n---\n\n * Remove `Comparator::setTarget()` and `Comparator::setOperator()`\n\n5.4.0\n-----\n\n * Deprecate `Comparator::setTarget()` and `Comparator::setOperator()`\n * Add a constructor to `Comparator` that allows setting target and operator\n * Finder's iterator has now `Symfony\\Component\\Finder\\SplFileInfo` inner type specified\n * Add recursive .gitignore files support\n\n5.0.0\n-----\n\n * added `$useNaturalSort` argument to `Finder::sortByName()`\n\n4.3.0\n-----\n\n * added Finder::ignoreVCSIgnored() to ignore files based on rules listed in .gitignore\n\n4.2.0\n-----\n\n * added $useNaturalSort option to Finder::sortByName() method\n * the `Finder::sortByName()` method will have a new `$useNaturalSort`\n   argument in version 5.0, not defining it is deprecated\n * added `Finder::reverseSorting()` to reverse the sorting\n\n4.0.0\n-----\n\n * removed `ExceptionInterface`\n * removed `Symfony\\Component\\Finder\\Iterator\\FilterIterator`\n\n3.4.0\n-----\n\n * deprecated `Symfony\\Component\\Finder\\Iterator\\FilterIterator`\n * added Finder::hasResults() method to check if any results were found\n\n3.3.0\n-----\n\n * added double-star matching to Glob::toRegex()\n\n3.0.0\n-----\n\n * removed deprecated classes\n\n2.8.0\n-----\n\n * deprecated adapters and related classes\n\n2.5.0\n-----\n * added support for GLOB_BRACE in the paths passed to Finder::in()\n\n2.3.0\n-----\n\n * added a way to ignore unreadable directories (via Finder::ignoreUnreadableDirs())\n * unified the way subfolders that are not executable are handled by always throwing an AccessDeniedException exception\n\n2.2.0\n-----\n\n * added Finder::path() and Finder::notPath() methods\n * added finder adapters to improve performance on specific platforms\n * added support for wildcard characters (glob patterns) in the paths passed\n   to Finder::in()\n\n2.1.0\n-----\n\n * added Finder::sortByAccessedTime(), Finder::sortByChangedTime(), and\n   Finder::sortByModifiedTime()\n * added Countable to Finder\n * added support for an array of directories as an argument to\n   Finder::exclude()\n * added searching based on the file content via Finder::contains() and\n   Finder::notContains()\n * added support for the != operator in the Comparator\n * [BC BREAK] filter expressions (used for file name and content) are no more\n   considered as regexps but glob patterns when they are enclosed in '*' or '?'\n"
  },
  {
    "path": "Comparator/Comparator.php",
    "content": "<?php\n\n/*\n * This file is part of the Symfony package.\n *\n * (c) Fabien Potencier <fabien@symfony.com>\n *\n * For the full copyright and license information, please view the LICENSE\n * file that was distributed with this source code.\n */\n\nnamespace Symfony\\Component\\Finder\\Comparator;\n\n/**\n * @author Fabien Potencier <fabien@symfony.com>\n */\nclass Comparator\n{\n    private string $operator;\n\n    public function __construct(\n        private string $target,\n        string $operator = '==',\n    ) {\n        if (!\\in_array($operator, ['>', '<', '>=', '<=', '==', '!='], true)) {\n            throw new \\InvalidArgumentException(\\sprintf('Invalid operator \"%s\".', $operator));\n        }\n\n        $this->operator = $operator;\n    }\n\n    /**\n     * Gets the target value.\n     */\n    public function getTarget(): string\n    {\n        return $this->target;\n    }\n\n    /**\n     * Gets the comparison operator.\n     */\n    public function getOperator(): string\n    {\n        return $this->operator;\n    }\n\n    /**\n     * Tests against the target.\n     */\n    public function test(mixed $test): bool\n    {\n        return match ($this->operator) {\n            '>' => $test > $this->target,\n            '>=' => $test >= $this->target,\n            '<' => $test < $this->target,\n            '<=' => $test <= $this->target,\n            '!=' => $test != $this->target,\n            default => $test == $this->target,\n        };\n    }\n}\n"
  },
  {
    "path": "Comparator/DateComparator.php",
    "content": "<?php\n\n/*\n * This file is part of the Symfony package.\n *\n * (c) Fabien Potencier <fabien@symfony.com>\n *\n * For the full copyright and license information, please view the LICENSE\n * file that was distributed with this source code.\n */\n\nnamespace Symfony\\Component\\Finder\\Comparator;\n\n/**\n * DateCompare compiles date comparisons.\n *\n * @author Fabien Potencier <fabien@symfony.com>\n */\nclass DateComparator extends Comparator\n{\n    /**\n     * @param string $test A comparison string\n     *\n     * @throws \\InvalidArgumentException If the test is not understood\n     */\n    public function __construct(string $test)\n    {\n        if (!preg_match('#^\\s*(==|!=|[<>]=?|after|since|before|until)?\\s*(.+?)\\s*$#i', $test, $matches)) {\n            throw new \\InvalidArgumentException(\\sprintf('Don\\'t understand \"%s\" as a date test.', $test));\n        }\n\n        try {\n            $date = new \\DateTimeImmutable($matches[2]);\n            $target = $date->format('U');\n        } catch (\\Exception) {\n            throw new \\InvalidArgumentException(\\sprintf('\"%s\" is not a valid date.', $matches[2]));\n        }\n\n        $operator = $matches[1] ?: '==';\n        if ('since' === $operator || 'after' === $operator) {\n            $operator = '>';\n        }\n\n        if ('until' === $operator || 'before' === $operator) {\n            $operator = '<';\n        }\n\n        parent::__construct($target, $operator);\n    }\n}\n"
  },
  {
    "path": "Comparator/NumberComparator.php",
    "content": "<?php\n\n/*\n * This file is part of the Symfony package.\n *\n * (c) Fabien Potencier <fabien@symfony.com>\n *\n * For the full copyright and license information, please view the LICENSE\n * file that was distributed with this source code.\n */\n\nnamespace Symfony\\Component\\Finder\\Comparator;\n\n/**\n * NumberComparator compiles a simple comparison to an anonymous\n * subroutine, which you can call with a value to be tested again.\n *\n * Now this would be very pointless, if NumberCompare didn't understand\n * magnitudes.\n *\n * The target value may use magnitudes of kilobytes (k, ki),\n * megabytes (m, mi), or gigabytes (g, gi). Those suffixed\n * with an i use the appropriate 2**n version in accordance with the\n * IEC standard: http://physics.nist.gov/cuu/Units/binary.html\n *\n * Based on the Perl Number::Compare module.\n *\n * @author    Fabien Potencier <fabien@symfony.com> PHP port\n * @author    Richard Clamp <richardc@unixbeard.net> Perl version\n * @copyright 2004-2005 Fabien Potencier <fabien@symfony.com>\n * @copyright 2002 Richard Clamp <richardc@unixbeard.net>\n *\n * @see http://physics.nist.gov/cuu/Units/binary.html\n */\nclass NumberComparator extends Comparator\n{\n    /**\n     * @param string|null $test A comparison string or null\n     *\n     * @throws \\InvalidArgumentException If the test is not understood\n     */\n    public function __construct(?string $test)\n    {\n        if (null === $test || !preg_match('#^\\s*(==|!=|[<>]=?)?\\s*([0-9\\.]+)\\s*([kmg]i?)?\\s*$#i', $test, $matches)) {\n            throw new \\InvalidArgumentException(\\sprintf('Don\\'t understand \"%s\" as a number test.', $test ?? 'null'));\n        }\n\n        $target = $matches[2];\n        if (!is_numeric($target)) {\n            throw new \\InvalidArgumentException(\\sprintf('Invalid number \"%s\".', $target));\n        }\n        if (isset($matches[3])) {\n            // magnitude\n            switch (strtolower($matches[3])) {\n                case 'k':\n                    $target *= 1000;\n                    break;\n                case 'ki':\n                    $target *= 1024;\n                    break;\n                case 'm':\n                    $target *= 1000000;\n                    break;\n                case 'mi':\n                    $target *= 1024 * 1024;\n                    break;\n                case 'g':\n                    $target *= 1000000000;\n                    break;\n                case 'gi':\n                    $target *= 1024 * 1024 * 1024;\n                    break;\n            }\n        }\n\n        parent::__construct($target, $matches[1] ?: '==');\n    }\n}\n"
  },
  {
    "path": "Exception/AccessDeniedException.php",
    "content": "<?php\n\n/*\n * This file is part of the Symfony package.\n *\n * (c) Fabien Potencier <fabien@symfony.com>\n *\n * For the full copyright and license information, please view the LICENSE\n * file that was distributed with this source code.\n */\n\nnamespace Symfony\\Component\\Finder\\Exception;\n\n/**\n * @author Jean-François Simon <jeanfrancois.simon@sensiolabs.com>\n */\nclass AccessDeniedException extends \\UnexpectedValueException\n{\n}\n"
  },
  {
    "path": "Exception/DirectoryNotFoundException.php",
    "content": "<?php\n\n/*\n * This file is part of the Symfony package.\n *\n * (c) Fabien Potencier <fabien@symfony.com>\n *\n * For the full copyright and license information, please view the LICENSE\n * file that was distributed with this source code.\n */\n\nnamespace Symfony\\Component\\Finder\\Exception;\n\n/**\n * @author Andreas Erhard <andreas.erhard@i-med.ac.at>\n */\nclass DirectoryNotFoundException extends \\InvalidArgumentException\n{\n}\n"
  },
  {
    "path": "Finder.php",
    "content": "<?php\n\n/*\n * This file is part of the Symfony package.\n *\n * (c) Fabien Potencier <fabien@symfony.com>\n *\n * For the full copyright and license information, please view the LICENSE\n * file that was distributed with this source code.\n */\n\nnamespace Symfony\\Component\\Finder;\n\nuse Symfony\\Component\\Finder\\Comparator\\DateComparator;\nuse Symfony\\Component\\Finder\\Comparator\\NumberComparator;\nuse Symfony\\Component\\Finder\\Exception\\DirectoryNotFoundException;\nuse Symfony\\Component\\Finder\\Iterator\\CustomFilterIterator;\nuse Symfony\\Component\\Finder\\Iterator\\DateRangeFilterIterator;\nuse Symfony\\Component\\Finder\\Iterator\\DepthRangeFilterIterator;\nuse Symfony\\Component\\Finder\\Iterator\\ExcludeDirectoryFilterIterator;\nuse Symfony\\Component\\Finder\\Iterator\\FilecontentFilterIterator;\nuse Symfony\\Component\\Finder\\Iterator\\FilenameFilterIterator;\nuse Symfony\\Component\\Finder\\Iterator\\LazyIterator;\nuse Symfony\\Component\\Finder\\Iterator\\SizeRangeFilterIterator;\nuse Symfony\\Component\\Finder\\Iterator\\SortableIterator;\n\n/**\n * Finder allows to build rules to find files and directories.\n *\n * It is a thin wrapper around several specialized iterator classes.\n *\n * All rules may be invoked several times.\n *\n * All methods return the current Finder object to allow chaining:\n *\n *     $finder = Finder::create()->files()->name('*.php')->in(__DIR__);\n *\n * @author Fabien Potencier <fabien@symfony.com>\n *\n * @implements \\IteratorAggregate<non-empty-string, SplFileInfo>\n */\nclass Finder implements \\IteratorAggregate, \\Countable\n{\n    public const IGNORE_VCS_FILES = 1;\n    public const IGNORE_DOT_FILES = 2;\n    public const IGNORE_VCS_IGNORED_FILES = 4;\n\n    private int $mode = 0;\n    private array $names = [];\n    private array $notNames = [];\n    private array $exclude = [];\n    private array $filters = [];\n    private array $pruneFilters = [];\n    private array $depths = [];\n    private array $sizes = [];\n    private bool $followLinks = false;\n    private bool $unixPaths = false;\n    private bool $reverseSorting = false;\n    private \\Closure|int|false $sort = false;\n    private int $ignore = 0;\n    /** @var list<string> */\n    private array $dirs = [];\n    private array $dates = [];\n    /** @var list<iterable<SplFileInfo|\\SplFileInfo|string>> */\n    private array $iterators = [];\n    private array $contains = [];\n    private array $notContains = [];\n    private array $paths = [];\n    private array $notPaths = [];\n    private bool $ignoreUnreadableDirs = false;\n\n    private static array $vcsPatterns = ['.svn', '_svn', 'CVS', '_darcs', '.arch-params', '.monotone', '.bzr', '.git', '.hg'];\n\n    public function __construct()\n    {\n        $this->ignore = static::IGNORE_VCS_FILES | static::IGNORE_DOT_FILES;\n    }\n\n    /**\n     * Creates a new Finder.\n     */\n    public static function create(): static\n    {\n        return new static();\n    }\n\n    /**\n     * Restricts the matching to directories only.\n     *\n     * @return $this\n     */\n    public function directories(): static\n    {\n        $this->mode = Iterator\\FileTypeFilterIterator::ONLY_DIRECTORIES;\n\n        return $this;\n    }\n\n    /**\n     * Restricts the matching to files only.\n     *\n     * @return $this\n     */\n    public function files(): static\n    {\n        $this->mode = Iterator\\FileTypeFilterIterator::ONLY_FILES;\n\n        return $this;\n    }\n\n    /**\n     * Adds tests for the directory depth.\n     *\n     * Usage:\n     *\n     *     $finder->depth('> 1') // the Finder will start matching at level 1.\n     *     $finder->depth('< 3') // the Finder will descend at most 3 levels of directories below the starting point.\n     *     $finder->depth(['>= 1', '< 3'])\n     *\n     * @param string|int|string[]|int[] $levels The depth level expression or an array of depth levels\n     *\n     * @return $this\n     *\n     * @see DepthRangeFilterIterator\n     * @see NumberComparator\n     */\n    public function depth(string|int|array $levels): static\n    {\n        foreach ((array) $levels as $level) {\n            $this->depths[] = new NumberComparator($level);\n        }\n\n        return $this;\n    }\n\n    /**\n     * Adds tests for file dates (last modified).\n     *\n     * The date must be something that strtotime() is able to parse:\n     *\n     *     $finder->date('since yesterday');\n     *     $finder->date('until 2 days ago');\n     *     $finder->date('> now - 2 hours');\n     *     $finder->date('>= 2005-10-15');\n     *     $finder->date(['>= 2005-10-15', '<= 2006-05-27']);\n     *\n     * @param string|string[] $dates A date range string or an array of date ranges\n     *\n     * @return $this\n     *\n     * @see strtotime\n     * @see DateRangeFilterIterator\n     * @see DateComparator\n     */\n    public function date(string|array $dates): static\n    {\n        foreach ((array) $dates as $date) {\n            $this->dates[] = new DateComparator($date);\n        }\n\n        return $this;\n    }\n\n    /**\n     * Adds rules that files must match.\n     *\n     * You can use patterns (delimited with / sign), globs or simple strings.\n     *\n     *     $finder->name('/\\.php$/')\n     *     $finder->name('*.php') // same as above, without dot files\n     *     $finder->name('test.php')\n     *     $finder->name(['test.py', 'test.php'])\n     *\n     * @param string|string[] $patterns A pattern (a regexp, a glob, or a string) or an array of patterns\n     *\n     * @return $this\n     *\n     * @see FilenameFilterIterator\n     */\n    public function name(string|array $patterns): static\n    {\n        $this->names = array_merge($this->names, (array) $patterns);\n\n        return $this;\n    }\n\n    /**\n     * Adds rules that files must not match.\n     *\n     * @param string|string[] $patterns A pattern (a regexp, a glob, or a string) or an array of patterns\n     *\n     * @return $this\n     *\n     * @see FilenameFilterIterator\n     */\n    public function notName(string|array $patterns): static\n    {\n        $this->notNames = array_merge($this->notNames, (array) $patterns);\n\n        return $this;\n    }\n\n    /**\n     * Adds tests that file contents must match.\n     *\n     * Strings or PCRE patterns can be used:\n     *\n     *     $finder->contains('Lorem ipsum')\n     *     $finder->contains('/Lorem ipsum/i')\n     *     $finder->contains(['dolor', '/ipsum/i'])\n     *\n     * @param string|string[] $patterns A pattern (string or regexp) or an array of patterns\n     *\n     * @return $this\n     *\n     * @see FilecontentFilterIterator\n     */\n    public function contains(string|array $patterns): static\n    {\n        $this->contains = array_merge($this->contains, (array) $patterns);\n\n        return $this;\n    }\n\n    /**\n     * Adds tests that file contents must not match.\n     *\n     * Strings or PCRE patterns can be used:\n     *\n     *     $finder->notContains('Lorem ipsum')\n     *     $finder->notContains('/Lorem ipsum/i')\n     *     $finder->notContains(['lorem', '/dolor/i'])\n     *\n     * @param string|string[] $patterns A pattern (string or regexp) or an array of patterns\n     *\n     * @return $this\n     *\n     * @see FilecontentFilterIterator\n     */\n    public function notContains(string|array $patterns): static\n    {\n        $this->notContains = array_merge($this->notContains, (array) $patterns);\n\n        return $this;\n    }\n\n    /**\n     * Adds rules that filenames must match.\n     *\n     * You can use patterns (delimited with / sign) or simple strings.\n     *\n     *     $finder->path('some/special/dir')\n     *     $finder->path('/some\\/special\\/dir/') // same as above\n     *     $finder->path(['some dir', 'another/dir'])\n     *\n     * Use only / as dirname separator.\n     *\n     * @param string|string[] $patterns A pattern (a regexp or a string) or an array of patterns\n     *\n     * @return $this\n     *\n     * @see FilenameFilterIterator\n     */\n    public function path(string|array $patterns): static\n    {\n        $this->paths = array_merge($this->paths, (array) $patterns);\n\n        return $this;\n    }\n\n    /**\n     * Adds rules that filenames must not match.\n     *\n     * You can use patterns (delimited with / sign) or simple strings.\n     *\n     *     $finder->notPath('some/special/dir')\n     *     $finder->notPath('/some\\/special\\/dir/') // same as above\n     *     $finder->notPath(['some/file.txt', 'another/file.log'])\n     *\n     * Use only / as dirname separator.\n     *\n     * @param string|string[] $patterns A pattern (a regexp or a string) or an array of patterns\n     *\n     * @return $this\n     *\n     * @see FilenameFilterIterator\n     */\n    public function notPath(string|array $patterns): static\n    {\n        $this->notPaths = array_merge($this->notPaths, (array) $patterns);\n\n        return $this;\n    }\n\n    /**\n     * Adds tests for file sizes.\n     *\n     *     $finder->size('> 10K');\n     *     $finder->size('<= 1Ki');\n     *     $finder->size(4);\n     *     $finder->size(['> 10K', '< 20K'])\n     *\n     * @param string|int|string[]|int[] $sizes A size range string or an integer or an array of size ranges\n     *\n     * @return $this\n     *\n     * @see SizeRangeFilterIterator\n     * @see NumberComparator\n     */\n    public function size(string|int|array $sizes): static\n    {\n        foreach ((array) $sizes as $size) {\n            $this->sizes[] = new NumberComparator($size);\n        }\n\n        return $this;\n    }\n\n    /**\n     * Excludes directories.\n     *\n     * Directories passed as argument must be relative to the ones defined with the `in()` method. For example:\n     *\n     *     $finder->in(__DIR__)->exclude('ruby');\n     *\n     * @param string|array $dirs A directory path or an array of directories\n     *\n     * @return $this\n     *\n     * @see ExcludeDirectoryFilterIterator\n     */\n    public function exclude(string|array $dirs): static\n    {\n        $this->exclude = array_merge($this->exclude, (array) $dirs);\n\n        return $this;\n    }\n\n    /**\n     * Excludes \"hidden\" directories and files (starting with a dot).\n     *\n     * This option is enabled by default.\n     *\n     * @return $this\n     *\n     * @see ExcludeDirectoryFilterIterator\n     */\n    public function ignoreDotFiles(bool $ignoreDotFiles): static\n    {\n        if ($ignoreDotFiles) {\n            $this->ignore |= static::IGNORE_DOT_FILES;\n        } else {\n            $this->ignore &= ~static::IGNORE_DOT_FILES;\n        }\n\n        return $this;\n    }\n\n    /**\n     * Forces the finder to ignore version control directories.\n     *\n     * This option is enabled by default.\n     *\n     * @return $this\n     *\n     * @see ExcludeDirectoryFilterIterator\n     */\n    public function ignoreVCS(bool $ignoreVCS): static\n    {\n        if ($ignoreVCS) {\n            $this->ignore |= static::IGNORE_VCS_FILES;\n        } else {\n            $this->ignore &= ~static::IGNORE_VCS_FILES;\n        }\n\n        return $this;\n    }\n\n    /**\n     * Forces Finder to obey .gitignore and ignore files based on rules listed there.\n     *\n     * This option is disabled by default.\n     *\n     * @return $this\n     */\n    public function ignoreVCSIgnored(bool $ignoreVCSIgnored): static\n    {\n        if ($ignoreVCSIgnored) {\n            $this->ignore |= static::IGNORE_VCS_IGNORED_FILES;\n        } else {\n            $this->ignore &= ~static::IGNORE_VCS_IGNORED_FILES;\n        }\n\n        return $this;\n    }\n\n    /**\n     * Adds VCS patterns.\n     *\n     * @see ignoreVCS()\n     *\n     * @param string|string[] $pattern VCS patterns to ignore\n     */\n    public static function addVCSPattern(string|array $pattern): void\n    {\n        foreach ((array) $pattern as $p) {\n            self::$vcsPatterns[] = $p;\n        }\n\n        self::$vcsPatterns = array_unique(self::$vcsPatterns);\n    }\n\n    /**\n     * Sorts files and directories by an anonymous function.\n     *\n     * The anonymous function receives two \\SplFileInfo instances to compare.\n     *\n     * This can be slow as all the matching files and directories must be retrieved for comparison.\n     *\n     * @return $this\n     *\n     * @see SortableIterator\n     */\n    public function sort(\\Closure $closure): static\n    {\n        $this->sort = $closure;\n\n        return $this;\n    }\n\n    /**\n     * Sorts files and directories by extension.\n     *\n     * This can be slow as all the matching files and directories must be retrieved for comparison.\n     *\n     * @return $this\n     *\n     * @see SortableIterator\n     */\n    public function sortByExtension(): static\n    {\n        $this->sort = SortableIterator::SORT_BY_EXTENSION;\n\n        return $this;\n    }\n\n    /**\n     * Sorts files and directories by name.\n     *\n     * This can be slow as all the matching files and directories must be retrieved for comparison.\n     *\n     * @return $this\n     *\n     * @see SortableIterator\n     */\n    public function sortByName(bool $useNaturalSort = false): static\n    {\n        $this->sort = $useNaturalSort ? SortableIterator::SORT_BY_NAME_NATURAL : SortableIterator::SORT_BY_NAME;\n\n        return $this;\n    }\n\n    /**\n     * Sorts files and directories by name case insensitive.\n     *\n     * This can be slow as all the matching files and directories must be retrieved for comparison.\n     *\n     * @return $this\n     *\n     * @see SortableIterator\n     */\n    public function sortByCaseInsensitiveName(bool $useNaturalSort = false): static\n    {\n        $this->sort = $useNaturalSort ? SortableIterator::SORT_BY_NAME_NATURAL_CASE_INSENSITIVE : SortableIterator::SORT_BY_NAME_CASE_INSENSITIVE;\n\n        return $this;\n    }\n\n    /**\n     * Sorts files and directories by size.\n     *\n     * This can be slow as all the matching files and directories must be retrieved for comparison.\n     *\n     * @return $this\n     *\n     * @see SortableIterator\n     */\n    public function sortBySize(): static\n    {\n        $this->sort = SortableIterator::SORT_BY_SIZE;\n\n        return $this;\n    }\n\n    /**\n     * Sorts files and directories by type (directories before files), then by name.\n     *\n     * This can be slow as all the matching files and directories must be retrieved for comparison.\n     *\n     * @return $this\n     *\n     * @see SortableIterator\n     */\n    public function sortByType(): static\n    {\n        $this->sort = SortableIterator::SORT_BY_TYPE;\n\n        return $this;\n    }\n\n    /**\n     * Sorts files and directories by the last accessed time.\n     *\n     * This is the time that the file was last accessed, read or written to.\n     *\n     * This can be slow as all the matching files and directories must be retrieved for comparison.\n     *\n     * @return $this\n     *\n     * @see SortableIterator\n     */\n    public function sortByAccessedTime(): static\n    {\n        $this->sort = SortableIterator::SORT_BY_ACCESSED_TIME;\n\n        return $this;\n    }\n\n    /**\n     * Reverses the sorting.\n     *\n     * @return $this\n     */\n    public function reverseSorting(): static\n    {\n        $this->reverseSorting = true;\n\n        return $this;\n    }\n\n    /**\n     * Sorts files and directories by the last inode changed time.\n     *\n     * This is the time that the inode information was last modified (permissions, owner, group or other metadata).\n     *\n     * On Windows, since inode is not available, changed time is actually the file creation time.\n     *\n     * This can be slow as all the matching files and directories must be retrieved for comparison.\n     *\n     * @return $this\n     *\n     * @see SortableIterator\n     */\n    public function sortByChangedTime(): static\n    {\n        $this->sort = SortableIterator::SORT_BY_CHANGED_TIME;\n\n        return $this;\n    }\n\n    /**\n     * Sorts files and directories by the last modified time.\n     *\n     * This is the last time the actual contents of the file were last modified.\n     *\n     * This can be slow as all the matching files and directories must be retrieved for comparison.\n     *\n     * @return $this\n     *\n     * @see SortableIterator\n     */\n    public function sortByModifiedTime(): static\n    {\n        $this->sort = SortableIterator::SORT_BY_MODIFIED_TIME;\n\n        return $this;\n    }\n\n    /**\n     * Filters the iterator with an anonymous function.\n     *\n     * The anonymous function receives a \\SplFileInfo and must return false\n     * to remove files.\n     *\n     * @param \\Closure(SplFileInfo): bool $closure\n     * @param bool                        $prune   Whether to skip traversing directories further\n     *\n     * @return $this\n     *\n     * @see CustomFilterIterator\n     */\n    public function filter(\\Closure $closure, bool $prune = false): static\n    {\n        $this->filters[] = $closure;\n\n        if ($prune) {\n            $this->pruneFilters[] = $closure;\n        }\n\n        return $this;\n    }\n\n    /**\n     * Forces the following of symlinks.\n     *\n     * @return $this\n     */\n    public function followLinks(): static\n    {\n        $this->followLinks = true;\n\n        return $this;\n    }\n\n    /**\n     * Force the use of UNIX paths when recursing directories.\n     *\n     * @return $this\n     */\n    public function useUnixPaths(): static\n    {\n        $this->unixPaths = true;\n\n        return $this;\n    }\n\n    /**\n     * Tells finder to ignore unreadable directories.\n     *\n     * By default, scanning unreadable directories content throws an AccessDeniedException.\n     *\n     * @return $this\n     */\n    public function ignoreUnreadableDirs(bool $ignore = true): static\n    {\n        $this->ignoreUnreadableDirs = $ignore;\n\n        return $this;\n    }\n\n    /**\n     * Searches files and directories which match defined rules.\n     *\n     * @param string|string[] $dirs A directory path or an array of directories\n     *\n     * @return $this\n     *\n     * @throws DirectoryNotFoundException if one of the directories does not exist\n     */\n    public function in(string|array $dirs): static\n    {\n        $resolvedDirs = [];\n\n        foreach ((array) $dirs as $dir) {\n            if (is_dir($dir)) {\n                $resolvedDirs[] = [$this->normalizeDir($dir)];\n            } elseif ($glob = glob($dir, (\\defined('GLOB_BRACE') ? \\GLOB_BRACE : 0) | \\GLOB_ONLYDIR | \\GLOB_NOSORT)) {\n                sort($glob);\n                $resolvedDirs[] = array_map($this->normalizeDir(...), $glob);\n            } else {\n                throw new DirectoryNotFoundException(\\sprintf('The \"%s\" directory does not exist.', $dir));\n            }\n        }\n\n        $this->dirs = array_merge($this->dirs, ...$resolvedDirs);\n\n        return $this;\n    }\n\n    /**\n     * Returns an Iterator for the current Finder configuration.\n     *\n     * This method implements the IteratorAggregate interface.\n     *\n     * @return \\Iterator<non-empty-string, SplFileInfo>\n     *\n     * @throws \\LogicException if the in() method has not been called\n     */\n    public function getIterator(): \\Iterator\n    {\n        if (!$this->dirs && !$this->iterators) {\n            throw new \\LogicException('You must call one of in() or append() methods before iterating over a Finder.');\n        }\n\n        if (1 === \\count($this->dirs) && !$this->iterators) {\n            $iterator = $this->searchInDirectory($this->dirs[0]);\n        } else {\n            $iterator = new \\AppendIterator();\n            foreach ($this->dirs as $dir) {\n                $iterator->append(new \\IteratorIterator(new LazyIterator(fn () => $this->searchInDirectory($dir))));\n            }\n\n            foreach ($this->iterators as $it) {\n                $iterator->append(new \\IteratorIterator(new LazyIterator(static function () use ($it) {\n                    foreach ($it as $file) {\n                        if (!$file instanceof \\SplFileInfo) {\n                            $file = new \\SplFileInfo($file);\n                        }\n                        $key = $file->getPathname();\n                        if (!$file instanceof SplFileInfo) {\n                            $file = new SplFileInfo($key, $file->getPath(), $key);\n                        }\n\n                        yield $key => $file;\n                    }\n                })));\n            }\n        }\n\n        if ($this->sort || $this->reverseSorting) {\n            $iterator = (new SortableIterator($iterator, $this->sort, $this->reverseSorting))->getIterator();\n        }\n\n        return $iterator;\n    }\n\n    /**\n     * Appends an existing set of files/directories to the finder.\n     *\n     * The set can be another Finder, an Iterator, an IteratorAggregate, or even a plain array.\n     *\n     * @param iterable<SplFileInfo|\\SplFileInfo|string> $iterator\n     *\n     * @return $this\n     */\n    public function append(iterable $iterator): static\n    {\n        $this->iterators[] = $iterator;\n\n        return $this;\n    }\n\n    /**\n     * Check if any results were found.\n     */\n    public function hasResults(): bool\n    {\n        foreach ($this->getIterator() as $_) {\n            return true;\n        }\n\n        return false;\n    }\n\n    /**\n     * Counts all the results collected by the iterators.\n     */\n    public function count(): int\n    {\n        return iterator_count($this->getIterator());\n    }\n\n    private function searchInDirectory(string $dir): \\Iterator\n    {\n        $exclude = $this->exclude;\n        $notPaths = $this->notPaths;\n\n        if ($this->pruneFilters) {\n            $exclude = array_merge($exclude, $this->pruneFilters);\n        }\n\n        if (static::IGNORE_VCS_FILES === (static::IGNORE_VCS_FILES & $this->ignore)) {\n            $exclude = array_merge($exclude, self::$vcsPatterns);\n        }\n\n        if (static::IGNORE_DOT_FILES === (static::IGNORE_DOT_FILES & $this->ignore)) {\n            $notPaths[] = '#(^|/)\\..+(/|$)#';\n        }\n\n        $minDepth = 0;\n        $maxDepth = \\PHP_INT_MAX;\n\n        foreach ($this->depths as $comparator) {\n            switch ($comparator->getOperator()) {\n                case '>':\n                    $minDepth = $comparator->getTarget() + 1;\n                    break;\n                case '>=':\n                    $minDepth = $comparator->getTarget();\n                    break;\n                case '<':\n                    $maxDepth = $comparator->getTarget() - 1;\n                    break;\n                case '<=':\n                    $maxDepth = $comparator->getTarget();\n                    break;\n                default:\n                    $minDepth = $maxDepth = $comparator->getTarget();\n            }\n        }\n\n        $flags = \\RecursiveDirectoryIterator::SKIP_DOTS;\n\n        if ($this->followLinks) {\n            $flags |= \\RecursiveDirectoryIterator::FOLLOW_SYMLINKS;\n        }\n\n        if ($this->unixPaths) {\n            $flags |= \\RecursiveDirectoryIterator::UNIX_PATHS;\n        }\n\n        $iterator = new Iterator\\RecursiveDirectoryIterator($dir, $flags, $this->ignoreUnreadableDirs);\n\n        if ($exclude) {\n            $iterator = new ExcludeDirectoryFilterIterator($iterator, $exclude);\n        }\n\n        $iterator = new \\RecursiveIteratorIterator($iterator, \\RecursiveIteratorIterator::SELF_FIRST);\n\n        if ($minDepth > 0 || $maxDepth < \\PHP_INT_MAX) {\n            $iterator = new DepthRangeFilterIterator($iterator, $minDepth, $maxDepth);\n        }\n\n        if ($this->mode) {\n            $iterator = new Iterator\\FileTypeFilterIterator($iterator, $this->mode);\n        }\n\n        if ($this->names || $this->notNames) {\n            $iterator = new FilenameFilterIterator($iterator, $this->names, $this->notNames);\n        }\n\n        if ($this->contains || $this->notContains) {\n            $iterator = new FilecontentFilterIterator($iterator, $this->contains, $this->notContains);\n        }\n\n        if ($this->sizes) {\n            $iterator = new SizeRangeFilterIterator($iterator, $this->sizes);\n        }\n\n        if ($this->dates) {\n            $iterator = new DateRangeFilterIterator($iterator, $this->dates);\n        }\n\n        if ($this->filters) {\n            $iterator = new CustomFilterIterator($iterator, $this->filters);\n        }\n\n        if ($this->paths || $notPaths) {\n            $iterator = new Iterator\\PathFilterIterator($iterator, $this->paths, $notPaths);\n        }\n\n        if (static::IGNORE_VCS_IGNORED_FILES === (static::IGNORE_VCS_IGNORED_FILES & $this->ignore)) {\n            $iterator = new Iterator\\VcsIgnoredFilterIterator($iterator, $dir);\n        }\n\n        return $iterator;\n    }\n\n    /**\n     * Normalizes given directory names by removing trailing slashes.\n     *\n     * Excluding: (s)ftp:// or ssh2.(s)ftp:// wrapper\n     */\n    private function normalizeDir(string $dir): string\n    {\n        if ('/' === $dir) {\n            return $dir;\n        }\n\n        $dir = rtrim($dir, '/'.\\DIRECTORY_SEPARATOR);\n\n        if (preg_match('#^(ssh2\\.)?s?ftp://#', $dir)) {\n            $dir .= '/';\n        }\n\n        return $dir;\n    }\n}\n"
  },
  {
    "path": "Gitignore.php",
    "content": "<?php\n\n/*\n * This file is part of the Symfony package.\n *\n * (c) Fabien Potencier <fabien@symfony.com>\n *\n * For the full copyright and license information, please view the LICENSE\n * file that was distributed with this source code.\n */\n\nnamespace Symfony\\Component\\Finder;\n\n/**\n * Gitignore matches against text.\n *\n * @author Michael Voříšek <vorismi3@fel.cvut.cz>\n * @author Ahmed Abdou <mail@ahmd.io>\n */\nclass Gitignore\n{\n    /**\n     * Returns a regexp which is the equivalent of the gitignore pattern.\n     *\n     * Format specification: https://git-scm.com/docs/gitignore#_pattern_format\n     */\n    public static function toRegex(string $gitignoreFileContent): string\n    {\n        return self::buildRegex($gitignoreFileContent, false);\n    }\n\n    public static function toRegexMatchingNegatedPatterns(string $gitignoreFileContent): string\n    {\n        return self::buildRegex($gitignoreFileContent, true);\n    }\n\n    private static function buildRegex(string $gitignoreFileContent, bool $inverted): string\n    {\n        $gitignoreFileContent = preg_replace('~(?<!\\\\\\\\)#[^\\n\\r]*~', '', $gitignoreFileContent);\n        $gitignoreLines = preg_split('~\\r\\n?|\\n~', $gitignoreFileContent);\n\n        $res = self::lineToRegex('');\n        foreach ($gitignoreLines as $line) {\n            $line = preg_replace('~(?<!\\\\\\\\)[ \\t]+$~', '', $line);\n\n            if (str_starts_with($line, '!')) {\n                $line = substr($line, 1);\n                $isNegative = true;\n            } else {\n                $isNegative = false;\n            }\n\n            if ('' !== $line) {\n                if ($isNegative xor $inverted) {\n                    $res = '(?!'.self::lineToRegex($line).'$)'.$res;\n                } else {\n                    $res = '(?:'.$res.'|'.self::lineToRegex($line).')';\n                }\n            }\n        }\n\n        return '~^(?:'.$res.')~s';\n    }\n\n    private static function lineToRegex(string $gitignoreLine): string\n    {\n        if ('' === $gitignoreLine) {\n            return '$f'; // always false\n        }\n\n        $slashPos = strpos($gitignoreLine, '/');\n        if (false !== $slashPos && \\strlen($gitignoreLine) - 1 !== $slashPos) {\n            if (0 === $slashPos) {\n                $gitignoreLine = substr($gitignoreLine, 1);\n            }\n            $isAbsolute = true;\n        } else {\n            $isAbsolute = false;\n        }\n\n        $regex = preg_quote(str_replace('\\\\', '', $gitignoreLine), '~');\n        $regex = preg_replace_callback('~\\\\\\\\\\[((?:\\\\\\\\!)?)([^\\[\\]]*)\\\\\\\\\\]~', static fn (array $matches): string => '['.('' !== $matches[1] ? '^' : '').str_replace('\\\\-', '-', $matches[2]).']', $regex);\n        $regex = preg_replace('~(?:(?:\\\\\\\\\\*){2,}(/?))+~', '(?:(?:(?!//).(?<!//))+$1)?', $regex);\n        $regex = preg_replace('~\\\\\\\\\\*~', '[^/]*', $regex);\n        $regex = preg_replace('~\\\\\\\\\\?~', '[^/]', $regex);\n\n        return ($isAbsolute ? '' : '(?:[^/]+/)*')\n            .$regex\n            .(!str_ends_with($gitignoreLine, '/') ? '(?:$|/)' : '');\n    }\n}\n"
  },
  {
    "path": "Glob.php",
    "content": "<?php\n\n/*\n * This file is part of the Symfony package.\n *\n * (c) Fabien Potencier <fabien@symfony.com>\n *\n * For the full copyright and license information, please view the LICENSE\n * file that was distributed with this source code.\n */\n\nnamespace Symfony\\Component\\Finder;\n\n/**\n * Glob matches globbing patterns against text.\n *\n *     if match_glob(\"foo.*\", \"foo.bar\") echo \"matched\\n\";\n *\n *     // prints foo.bar and foo.baz\n *     $regex = glob_to_regex(\"foo.*\");\n *     for (['foo.bar', 'foo.baz', 'foo', 'bar'] as $t)\n *     {\n *         if (/$regex/) echo \"matched: $car\\n\";\n *     }\n *\n * Glob implements glob(3) style matching that can be used to match\n * against text, rather than fetching names from a filesystem.\n *\n * Based on the Perl Text::Glob module.\n *\n * @author Fabien Potencier <fabien@symfony.com> PHP port\n * @author     Richard Clamp <richardc@unixbeard.net> Perl version\n * @copyright  2004-2005 Fabien Potencier <fabien@symfony.com>\n * @copyright  2002 Richard Clamp <richardc@unixbeard.net>\n */\nclass Glob\n{\n    /**\n     * Returns a regexp which is the equivalent of the glob pattern.\n     */\n    public static function toRegex(string $glob, bool $strictLeadingDot = true, bool $strictWildcardSlash = true, string $delimiter = '#'): string\n    {\n        $firstByte = true;\n        $escaping = false;\n        $inCurlies = 0;\n        $regex = '';\n        if ($unanchored = str_starts_with($glob, '**/')) {\n            $glob = '/'.$glob;\n        }\n        $sizeGlob = \\strlen($glob);\n        for ($i = 0; $i < $sizeGlob; ++$i) {\n            $car = $glob[$i];\n            if ($firstByte && $strictLeadingDot && '.' !== $car) {\n                $regex .= '(?=[^\\.])';\n            }\n\n            $firstByte = '/' === $car;\n\n            if ($firstByte && $strictWildcardSlash && isset($glob[$i + 2]) && '**' === $glob[$i + 1].$glob[$i + 2] && (!isset($glob[$i + 3]) || '/' === $glob[$i + 3])) {\n                $car = '[^/]++/';\n                if (!isset($glob[$i + 3])) {\n                    $car .= '?';\n                }\n\n                if ($strictLeadingDot) {\n                    $car = '(?=[^\\.])'.$car;\n                }\n\n                $car = '/(?:'.$car.')*';\n                $i += 2 + isset($glob[$i + 3]);\n\n                if ('/' === $delimiter) {\n                    $car = str_replace('/', '\\\\/', $car);\n                }\n            }\n\n            if ($delimiter === $car || '.' === $car || '(' === $car || ')' === $car || '|' === $car || '+' === $car || '^' === $car || '$' === $car) {\n                $regex .= \"\\\\$car\";\n            } elseif ('*' === $car) {\n                $regex .= $escaping ? '\\\\*' : ($strictWildcardSlash ? '[^/]*' : '.*');\n            } elseif ('?' === $car) {\n                $regex .= $escaping ? '\\\\?' : ($strictWildcardSlash ? '[^/]' : '.');\n            } elseif ('{' === $car) {\n                $regex .= $escaping ? '\\\\{' : '(';\n                if (!$escaping) {\n                    ++$inCurlies;\n                }\n            } elseif ('}' === $car && $inCurlies) {\n                $regex .= $escaping ? '}' : ')';\n                if (!$escaping) {\n                    --$inCurlies;\n                }\n            } elseif (',' === $car && $inCurlies) {\n                $regex .= $escaping ? ',' : '|';\n            } elseif ('\\\\' === $car) {\n                if ($escaping) {\n                    $regex .= '\\\\\\\\';\n                    $escaping = false;\n                } else {\n                    $escaping = true;\n                }\n\n                continue;\n            } else {\n                $regex .= $car;\n            }\n            $escaping = false;\n        }\n\n        if ($unanchored) {\n            $regex = substr_replace($regex, '?', 1 + ('/' === $delimiter) + ($strictLeadingDot ? \\strlen('(?=[^\\.])') : 0), 0);\n        }\n\n        return $delimiter.'^'.$regex.'$'.$delimiter;\n    }\n}\n"
  },
  {
    "path": "Iterator/CustomFilterIterator.php",
    "content": "<?php\n\n/*\n * This file is part of the Symfony package.\n *\n * (c) Fabien Potencier <fabien@symfony.com>\n *\n * For the full copyright and license information, please view the LICENSE\n * file that was distributed with this source code.\n */\n\nnamespace Symfony\\Component\\Finder\\Iterator;\n\n/**\n * CustomFilterIterator filters files by applying anonymous functions.\n *\n * The anonymous function receives a \\SplFileInfo and must return false\n * to remove files.\n *\n * @author Fabien Potencier <fabien@symfony.com>\n *\n * @extends \\FilterIterator<string, \\SplFileInfo>\n */\nclass CustomFilterIterator extends \\FilterIterator\n{\n    private array $filters = [];\n\n    /**\n     * @param \\Iterator<string, \\SplFileInfo> $iterator The Iterator to filter\n     * @param callable[]                      $filters  An array of PHP callbacks\n     *\n     * @throws \\InvalidArgumentException\n     */\n    public function __construct(\\Iterator $iterator, array $filters)\n    {\n        foreach ($filters as $filter) {\n            if (!\\is_callable($filter)) {\n                throw new \\InvalidArgumentException('Invalid PHP callback.');\n            }\n        }\n        $this->filters = $filters;\n\n        parent::__construct($iterator);\n    }\n\n    /**\n     * Filters the iterator values.\n     */\n    public function accept(): bool\n    {\n        $fileinfo = $this->current();\n\n        foreach ($this->filters as $filter) {\n            if (false === $filter($fileinfo)) {\n                return false;\n            }\n        }\n\n        return true;\n    }\n}\n"
  },
  {
    "path": "Iterator/DateRangeFilterIterator.php",
    "content": "<?php\n\n/*\n * This file is part of the Symfony package.\n *\n * (c) Fabien Potencier <fabien@symfony.com>\n *\n * For the full copyright and license information, please view the LICENSE\n * file that was distributed with this source code.\n */\n\nnamespace Symfony\\Component\\Finder\\Iterator;\n\nuse Symfony\\Component\\Finder\\Comparator\\DateComparator;\n\n/**\n * DateRangeFilterIterator filters out files that are not in the given date range (last modified dates).\n *\n * @author Fabien Potencier <fabien@symfony.com>\n *\n * @extends \\FilterIterator<string, \\SplFileInfo>\n */\nclass DateRangeFilterIterator extends \\FilterIterator\n{\n    private array $comparators = [];\n\n    /**\n     * @param \\Iterator<string, \\SplFileInfo> $iterator\n     * @param DateComparator[]                $comparators\n     */\n    public function __construct(\\Iterator $iterator, array $comparators)\n    {\n        $this->comparators = $comparators;\n\n        parent::__construct($iterator);\n    }\n\n    /**\n     * Filters the iterator values.\n     */\n    public function accept(): bool\n    {\n        $fileinfo = $this->current();\n\n        if (!file_exists($fileinfo->getPathname())) {\n            return false;\n        }\n\n        $filedate = $fileinfo->getMTime();\n        foreach ($this->comparators as $compare) {\n            if (!$compare->test($filedate)) {\n                return false;\n            }\n        }\n\n        return true;\n    }\n}\n"
  },
  {
    "path": "Iterator/DepthRangeFilterIterator.php",
    "content": "<?php\n\n/*\n * This file is part of the Symfony package.\n *\n * (c) Fabien Potencier <fabien@symfony.com>\n *\n * For the full copyright and license information, please view the LICENSE\n * file that was distributed with this source code.\n */\n\nnamespace Symfony\\Component\\Finder\\Iterator;\n\n/**\n * DepthRangeFilterIterator limits the directory depth.\n *\n * @author Fabien Potencier <fabien@symfony.com>\n *\n * @template-covariant TKey\n * @template-covariant TValue\n *\n * @extends \\FilterIterator<TKey, TValue>\n */\nclass DepthRangeFilterIterator extends \\FilterIterator\n{\n    private int $minDepth = 0;\n\n    /**\n     * @param \\RecursiveIteratorIterator<\\RecursiveIterator<TKey, TValue>> $iterator The Iterator to filter\n     * @param int                                                          $minDepth The min depth\n     * @param int                                                          $maxDepth The max depth\n     */\n    public function __construct(\\RecursiveIteratorIterator $iterator, int $minDepth = 0, int $maxDepth = \\PHP_INT_MAX)\n    {\n        $this->minDepth = $minDepth;\n        $iterator->setMaxDepth(\\PHP_INT_MAX === $maxDepth ? -1 : $maxDepth);\n\n        parent::__construct($iterator);\n    }\n\n    /**\n     * Filters the iterator values.\n     */\n    public function accept(): bool\n    {\n        return $this->getInnerIterator()->getDepth() >= $this->minDepth;\n    }\n}\n"
  },
  {
    "path": "Iterator/ExcludeDirectoryFilterIterator.php",
    "content": "<?php\n\n/*\n * This file is part of the Symfony package.\n *\n * (c) Fabien Potencier <fabien@symfony.com>\n *\n * For the full copyright and license information, please view the LICENSE\n * file that was distributed with this source code.\n */\n\nnamespace Symfony\\Component\\Finder\\Iterator;\n\nuse Symfony\\Component\\Finder\\SplFileInfo;\n\n/**\n * ExcludeDirectoryFilterIterator filters out directories.\n *\n * @author Fabien Potencier <fabien@symfony.com>\n *\n * @extends \\FilterIterator<string, SplFileInfo>\n *\n * @implements \\RecursiveIterator<string, SplFileInfo>\n */\nclass ExcludeDirectoryFilterIterator extends \\FilterIterator implements \\RecursiveIterator\n{\n    /** @var \\Iterator<string, SplFileInfo> */\n    private \\Iterator $iterator;\n    private bool $isRecursive;\n    /** @var array<string, true> */\n    private array $excludedDirs = [];\n    private ?string $excludedPattern = null;\n    /** @var list<callable(SplFileInfo):bool> */\n    private array $pruneFilters = [];\n\n    /**\n     * @param \\Iterator<string, SplFileInfo>          $iterator    The Iterator to filter\n     * @param list<string|callable(SplFileInfo):bool> $directories An array of directories to exclude\n     */\n    public function __construct(\\Iterator $iterator, array $directories)\n    {\n        $this->iterator = $iterator;\n        $this->isRecursive = $iterator instanceof \\RecursiveIterator;\n        $patterns = [];\n        foreach ($directories as $directory) {\n            if (!\\is_string($directory)) {\n                if (!\\is_callable($directory)) {\n                    throw new \\InvalidArgumentException('Invalid PHP callback.');\n                }\n\n                $this->pruneFilters[] = $directory;\n\n                continue;\n            }\n\n            $directory = rtrim($directory, '/');\n            if (!$this->isRecursive || str_contains($directory, '/')) {\n                $patterns[] = preg_quote($directory, '#');\n            } else {\n                $this->excludedDirs[$directory] = true;\n            }\n        }\n        if ($patterns) {\n            $this->excludedPattern = '#(?:^|/)(?:'.implode('|', $patterns).')(?:/|$)#';\n        }\n\n        parent::__construct($iterator);\n    }\n\n    /**\n     * Filters the iterator values.\n     */\n    public function accept(): bool\n    {\n        if ($this->isRecursive && isset($this->excludedDirs[$this->current()->getFilename()]) && $this->current()->isDir()) {\n            return false;\n        }\n\n        if ($this->excludedPattern) {\n            $path = $this->current()->isDir() ? $this->current()->getRelativePathname() : $this->current()->getRelativePath();\n            $path = str_replace('\\\\', '/', $path);\n\n            return !preg_match($this->excludedPattern, $path);\n        }\n\n        if ($this->pruneFilters && $this->hasChildren()) {\n            foreach ($this->pruneFilters as $pruneFilter) {\n                if (!$pruneFilter($this->current())) {\n                    return false;\n                }\n            }\n        }\n\n        return true;\n    }\n\n    public function hasChildren(): bool\n    {\n        return $this->isRecursive && $this->iterator->hasChildren();\n    }\n\n    public function getChildren(): self\n    {\n        $children = new self($this->iterator->getChildren(), []);\n        $children->excludedDirs = $this->excludedDirs;\n        $children->excludedPattern = $this->excludedPattern;\n\n        return $children;\n    }\n}\n"
  },
  {
    "path": "Iterator/FileTypeFilterIterator.php",
    "content": "<?php\n\n/*\n * This file is part of the Symfony package.\n *\n * (c) Fabien Potencier <fabien@symfony.com>\n *\n * For the full copyright and license information, please view the LICENSE\n * file that was distributed with this source code.\n */\n\nnamespace Symfony\\Component\\Finder\\Iterator;\n\n/**\n * FileTypeFilterIterator only keeps files, directories, or both.\n *\n * @author Fabien Potencier <fabien@symfony.com>\n *\n * @extends \\FilterIterator<string, \\SplFileInfo>\n */\nclass FileTypeFilterIterator extends \\FilterIterator\n{\n    public const ONLY_FILES = 1;\n    public const ONLY_DIRECTORIES = 2;\n\n    /**\n     * @param \\Iterator<string, \\SplFileInfo> $iterator The Iterator to filter\n     * @param int                             $mode     The mode (self::ONLY_FILES or self::ONLY_DIRECTORIES)\n     */\n    public function __construct(\n        \\Iterator $iterator,\n        private int $mode,\n    ) {\n        parent::__construct($iterator);\n    }\n\n    /**\n     * Filters the iterator values.\n     */\n    public function accept(): bool\n    {\n        $fileinfo = $this->current();\n        if (self::ONLY_DIRECTORIES === (self::ONLY_DIRECTORIES & $this->mode) && $fileinfo->isFile()) {\n            return false;\n        } elseif (self::ONLY_FILES === (self::ONLY_FILES & $this->mode) && $fileinfo->isDir()) {\n            return false;\n        }\n\n        return true;\n    }\n}\n"
  },
  {
    "path": "Iterator/FilecontentFilterIterator.php",
    "content": "<?php\n\n/*\n * This file is part of the Symfony package.\n *\n * (c) Fabien Potencier <fabien@symfony.com>\n *\n * For the full copyright and license information, please view the LICENSE\n * file that was distributed with this source code.\n */\n\nnamespace Symfony\\Component\\Finder\\Iterator;\n\nuse Symfony\\Component\\Finder\\SplFileInfo;\n\n/**\n * FilecontentFilterIterator filters files by their contents using patterns (regexps or strings).\n *\n * @author Fabien Potencier  <fabien@symfony.com>\n * @author Włodzimierz Gajda <gajdaw@gajdaw.pl>\n *\n * @extends MultiplePcreFilterIterator<string, SplFileInfo>\n */\nclass FilecontentFilterIterator extends MultiplePcreFilterIterator\n{\n    /**\n     * Filters the iterator values.\n     */\n    public function accept(): bool\n    {\n        if (!$this->matchRegexps && !$this->noMatchRegexps) {\n            return true;\n        }\n\n        $fileinfo = $this->current();\n\n        if ($fileinfo->isDir() || !$fileinfo->isReadable()) {\n            return false;\n        }\n\n        $content = $fileinfo->getContents();\n        if (!$content) {\n            return false;\n        }\n\n        return $this->isAccepted($content);\n    }\n\n    /**\n     * Converts string to regexp if necessary.\n     *\n     * @param string $str Pattern: string or regexp\n     */\n    protected function toRegex(string $str): string\n    {\n        return $this->isRegex($str) ? $str : '/'.preg_quote($str, '/').'/';\n    }\n}\n"
  },
  {
    "path": "Iterator/FilenameFilterIterator.php",
    "content": "<?php\n\n/*\n * This file is part of the Symfony package.\n *\n * (c) Fabien Potencier <fabien@symfony.com>\n *\n * For the full copyright and license information, please view the LICENSE\n * file that was distributed with this source code.\n */\n\nnamespace Symfony\\Component\\Finder\\Iterator;\n\nuse Symfony\\Component\\Finder\\Glob;\n\n/**\n * FilenameFilterIterator filters files by patterns (a regexp, a glob, or a string).\n *\n * @author Fabien Potencier <fabien@symfony.com>\n *\n * @extends MultiplePcreFilterIterator<string, \\SplFileInfo>\n */\nclass FilenameFilterIterator extends MultiplePcreFilterIterator\n{\n    /**\n     * Filters the iterator values.\n     */\n    public function accept(): bool\n    {\n        return $this->isAccepted($this->current()->getFilename());\n    }\n\n    /**\n     * Converts glob to regexp.\n     *\n     * PCRE patterns are left unchanged.\n     * Glob strings are transformed with Glob::toRegex().\n     *\n     * @param string $str Pattern: glob or regexp\n     */\n    protected function toRegex(string $str): string\n    {\n        return $this->isRegex($str) ? $str : Glob::toRegex($str);\n    }\n}\n"
  },
  {
    "path": "Iterator/LazyIterator.php",
    "content": "<?php\n\n/*\n * This file is part of the Symfony package.\n *\n * (c) Fabien Potencier <fabien@symfony.com>\n *\n * For the full copyright and license information, please view the LICENSE\n * file that was distributed with this source code.\n */\n\nnamespace Symfony\\Component\\Finder\\Iterator;\n\n/**\n * @author Jérémy Derussé <jeremy@derusse.com>\n *\n * @internal\n */\nclass LazyIterator implements \\IteratorAggregate\n{\n    private \\Closure $iteratorFactory;\n\n    public function __construct(callable $iteratorFactory)\n    {\n        $this->iteratorFactory = $iteratorFactory(...);\n    }\n\n    public function getIterator(): \\Traversable\n    {\n        yield from ($this->iteratorFactory)();\n    }\n}\n"
  },
  {
    "path": "Iterator/MultiplePcreFilterIterator.php",
    "content": "<?php\n\n/*\n * This file is part of the Symfony package.\n *\n * (c) Fabien Potencier <fabien@symfony.com>\n *\n * For the full copyright and license information, please view the LICENSE\n * file that was distributed with this source code.\n */\n\nnamespace Symfony\\Component\\Finder\\Iterator;\n\n/**\n * MultiplePcreFilterIterator filters files using patterns (regexps, globs or strings).\n *\n * @author Fabien Potencier <fabien@symfony.com>\n *\n * @template-covariant TKey\n * @template-covariant TValue\n *\n * @extends \\FilterIterator<TKey, TValue>\n */\nabstract class MultiplePcreFilterIterator extends \\FilterIterator\n{\n    protected array $matchRegexps = [];\n    protected array $noMatchRegexps = [];\n\n    /**\n     * @param \\Iterator<TKey, TValue> $iterator        The Iterator to filter\n     * @param string[]                $matchPatterns   An array of patterns that need to match\n     * @param string[]                $noMatchPatterns An array of patterns that need to not match\n     */\n    public function __construct(\\Iterator $iterator, array $matchPatterns, array $noMatchPatterns)\n    {\n        foreach ($matchPatterns as $pattern) {\n            $this->matchRegexps[] = $this->toRegex($pattern);\n        }\n\n        foreach ($noMatchPatterns as $pattern) {\n            $this->noMatchRegexps[] = $this->toRegex($pattern);\n        }\n\n        parent::__construct($iterator);\n    }\n\n    /**\n     * Checks whether the string is accepted by the regex filters.\n     *\n     * If there is no regexps defined in the class, this method will accept the string.\n     * Such case can be handled by child classes before calling the method if they want to\n     * apply a different behavior.\n     */\n    protected function isAccepted(string $string): bool\n    {\n        // should at least not match one rule to exclude\n        foreach ($this->noMatchRegexps as $regex) {\n            if (preg_match($regex, $string)) {\n                return false;\n            }\n        }\n\n        // should at least match one rule\n        if ($this->matchRegexps) {\n            foreach ($this->matchRegexps as $regex) {\n                if (preg_match($regex, $string)) {\n                    return true;\n                }\n            }\n\n            return false;\n        }\n\n        // If there is no match rules, the file is accepted\n        return true;\n    }\n\n    /**\n     * Checks whether the string is a regex.\n     */\n    protected function isRegex(string $str): bool\n    {\n        $availableModifiers = 'imsxuADUn';\n\n        if (preg_match('/^(.{3,}?)['.$availableModifiers.']*$/', $str, $m)) {\n            $start = substr($m[1], 0, 1);\n            $end = substr($m[1], -1);\n\n            if ($start === $end) {\n                return !preg_match('/[*?[:alnum:] \\\\\\\\]/', $start);\n            }\n\n            foreach ([['{', '}'], ['(', ')'], ['[', ']'], ['<', '>']] as $delimiters) {\n                if ($start === $delimiters[0] && $end === $delimiters[1]) {\n                    return true;\n                }\n            }\n        }\n\n        return false;\n    }\n\n    /**\n     * Converts string into regexp.\n     */\n    abstract protected function toRegex(string $str): string;\n}\n"
  },
  {
    "path": "Iterator/PathFilterIterator.php",
    "content": "<?php\n\n/*\n * This file is part of the Symfony package.\n *\n * (c) Fabien Potencier <fabien@symfony.com>\n *\n * For the full copyright and license information, please view the LICENSE\n * file that was distributed with this source code.\n */\n\nnamespace Symfony\\Component\\Finder\\Iterator;\n\nuse Symfony\\Component\\Finder\\SplFileInfo;\n\n/**\n * PathFilterIterator filters files by path patterns (e.g. some/special/dir).\n *\n * @author Fabien Potencier  <fabien@symfony.com>\n * @author Włodzimierz Gajda <gajdaw@gajdaw.pl>\n *\n * @extends MultiplePcreFilterIterator<string, SplFileInfo>\n */\nclass PathFilterIterator extends MultiplePcreFilterIterator\n{\n    /**\n     * Filters the iterator values.\n     */\n    public function accept(): bool\n    {\n        $filename = $this->current()->getRelativePathname();\n\n        if ('\\\\' === \\DIRECTORY_SEPARATOR) {\n            $filename = str_replace('\\\\', '/', $filename);\n        }\n\n        return $this->isAccepted($filename);\n    }\n\n    /**\n     * Converts strings to regexp.\n     *\n     * PCRE patterns are left unchanged.\n     *\n     * Default conversion:\n     *     'lorem/ipsum/dolor' ==>  'lorem\\/ipsum\\/dolor/'\n     *\n     * Use only / as directory separator (on Windows also).\n     *\n     * @param string $str Pattern: regexp or dirname\n     */\n    protected function toRegex(string $str): string\n    {\n        return $this->isRegex($str) ? $str : '/'.preg_quote($str, '/').'/';\n    }\n}\n"
  },
  {
    "path": "Iterator/RecursiveDirectoryIterator.php",
    "content": "<?php\n\n/*\n * This file is part of the Symfony package.\n *\n * (c) Fabien Potencier <fabien@symfony.com>\n *\n * For the full copyright and license information, please view the LICENSE\n * file that was distributed with this source code.\n */\n\nnamespace Symfony\\Component\\Finder\\Iterator;\n\nuse Symfony\\Component\\Finder\\Exception\\AccessDeniedException;\nuse Symfony\\Component\\Finder\\SplFileInfo;\n\n/**\n * Extends the \\RecursiveDirectoryIterator to support relative paths.\n *\n * @author Victor Berchet <victor@suumit.com>\n *\n * @extends \\RecursiveDirectoryIterator<string, SplFileInfo>\n */\nclass RecursiveDirectoryIterator extends \\RecursiveDirectoryIterator\n{\n    private bool $ignoreUnreadableDirs;\n    private bool $ignoreFirstRewind = true;\n\n    // these 3 properties take part of the performance optimization to avoid redoing the same work in all iterations\n    private string $rootPath;\n    private string $subPath;\n    private string $directorySeparator = '/';\n\n    /**\n     * @throws \\RuntimeException\n     */\n    public function __construct(string $path, int $flags, bool $ignoreUnreadableDirs = false)\n    {\n        if ($flags & (self::CURRENT_AS_PATHNAME | self::CURRENT_AS_SELF)) {\n            throw new \\RuntimeException('This iterator only support returning current as fileinfo.');\n        }\n\n        parent::__construct($path, $flags);\n        $this->ignoreUnreadableDirs = $ignoreUnreadableDirs;\n        $this->rootPath = $path;\n        if ('/' !== \\DIRECTORY_SEPARATOR && !($flags & self::UNIX_PATHS)) {\n            $this->directorySeparator = \\DIRECTORY_SEPARATOR;\n        }\n    }\n\n    /**\n     * Return an instance of SplFileInfo with support for relative paths.\n     */\n    public function current(): SplFileInfo\n    {\n        // the logic here avoids redoing the same work in all iterations\n\n        if (!isset($this->subPath)) {\n            $this->subPath = $this->getSubPath();\n        }\n        $subPathname = $this->subPath;\n        if ('' !== $subPathname) {\n            $subPathname .= $this->directorySeparator;\n        }\n        $subPathname .= $this->getFilename();\n        $basePath = $this->rootPath;\n\n        if ('/' !== $basePath && !str_ends_with($basePath, $this->directorySeparator) && !str_ends_with($basePath, '/')) {\n            $basePath .= $this->directorySeparator;\n        }\n\n        return new SplFileInfo($basePath.$subPathname, $this->subPath, $subPathname);\n    }\n\n    public function hasChildren(bool $allowLinks = false): bool\n    {\n        $hasChildren = parent::hasChildren($allowLinks);\n\n        if (!$hasChildren || !$this->ignoreUnreadableDirs) {\n            return $hasChildren;\n        }\n\n        try {\n            parent::getChildren();\n\n            return true;\n        } catch (\\UnexpectedValueException) {\n            // If directory is unreadable and finder is set to ignore it, skip children\n            return false;\n        }\n    }\n\n    /**\n     * @throws AccessDeniedException\n     */\n    public function getChildren(): \\RecursiveDirectoryIterator\n    {\n        try {\n            $children = parent::getChildren();\n\n            if ($children instanceof self) {\n                // parent method will call the constructor with default arguments, so unreadable dirs won't be ignored anymore\n                $children->ignoreUnreadableDirs = $this->ignoreUnreadableDirs;\n\n                // performance optimization to avoid redoing the same work in all children\n                $children->rootPath = $this->rootPath;\n            }\n\n            return $children;\n        } catch (\\UnexpectedValueException $e) {\n            throw new AccessDeniedException($e->getMessage(), $e->getCode(), $e);\n        }\n    }\n\n    public function next(): void\n    {\n        $this->ignoreFirstRewind = false;\n\n        parent::next();\n    }\n\n    public function rewind(): void\n    {\n        // some streams like FTP are not rewindable, ignore the first rewind after creation,\n        // as newly created DirectoryIterator does not need to be rewound\n        if ($this->ignoreFirstRewind) {\n            $this->ignoreFirstRewind = false;\n\n            return;\n        }\n\n        parent::rewind();\n    }\n}\n"
  },
  {
    "path": "Iterator/SizeRangeFilterIterator.php",
    "content": "<?php\n\n/*\n * This file is part of the Symfony package.\n *\n * (c) Fabien Potencier <fabien@symfony.com>\n *\n * For the full copyright and license information, please view the LICENSE\n * file that was distributed with this source code.\n */\n\nnamespace Symfony\\Component\\Finder\\Iterator;\n\nuse Symfony\\Component\\Finder\\Comparator\\NumberComparator;\n\n/**\n * SizeRangeFilterIterator filters out files that are not in the given size range.\n *\n * @author Fabien Potencier <fabien@symfony.com>\n *\n * @extends \\FilterIterator<string, \\SplFileInfo>\n */\nclass SizeRangeFilterIterator extends \\FilterIterator\n{\n    private array $comparators = [];\n\n    /**\n     * @param \\Iterator<string, \\SplFileInfo> $iterator\n     * @param NumberComparator[]              $comparators\n     */\n    public function __construct(\\Iterator $iterator, array $comparators)\n    {\n        $this->comparators = $comparators;\n\n        parent::__construct($iterator);\n    }\n\n    /**\n     * Filters the iterator values.\n     */\n    public function accept(): bool\n    {\n        $fileinfo = $this->current();\n        if (!$fileinfo->isFile()) {\n            return true;\n        }\n\n        $filesize = $fileinfo->getSize();\n        foreach ($this->comparators as $compare) {\n            if (!$compare->test($filesize)) {\n                return false;\n            }\n        }\n\n        return true;\n    }\n}\n"
  },
  {
    "path": "Iterator/SortableIterator.php",
    "content": "<?php\n\n/*\n * This file is part of the Symfony package.\n *\n * (c) Fabien Potencier <fabien@symfony.com>\n *\n * For the full copyright and license information, please view the LICENSE\n * file that was distributed with this source code.\n */\n\nnamespace Symfony\\Component\\Finder\\Iterator;\n\n/**\n * SortableIterator applies a sort on a given Iterator.\n *\n * @author Fabien Potencier <fabien@symfony.com>\n *\n * @implements \\IteratorAggregate<string, \\SplFileInfo>\n */\nclass SortableIterator implements \\IteratorAggregate\n{\n    public const SORT_BY_NONE = 0;\n    public const SORT_BY_NAME = 1;\n    public const SORT_BY_TYPE = 2;\n    public const SORT_BY_ACCESSED_TIME = 3;\n    public const SORT_BY_CHANGED_TIME = 4;\n    public const SORT_BY_MODIFIED_TIME = 5;\n    public const SORT_BY_NAME_NATURAL = 6;\n    public const SORT_BY_NAME_CASE_INSENSITIVE = 7;\n    public const SORT_BY_NAME_NATURAL_CASE_INSENSITIVE = 8;\n    public const SORT_BY_EXTENSION = 9;\n    public const SORT_BY_SIZE = 10;\n\n    /** @var \\Traversable<string, \\SplFileInfo> */\n    private \\Traversable $iterator;\n    private \\Closure|int $sort;\n\n    /**\n     * @param \\Traversable<string, \\SplFileInfo> $iterator\n     * @param int|callable                       $sort     The sort type (SORT_BY_NAME, SORT_BY_TYPE, or a PHP callback)\n     *\n     * @throws \\InvalidArgumentException\n     */\n    public function __construct(\\Traversable $iterator, int|callable $sort, bool $reverseOrder = false)\n    {\n        $this->iterator = $iterator;\n        $order = $reverseOrder ? -1 : 1;\n\n        if (self::SORT_BY_NAME === $sort) {\n            $this->sort = static fn (\\SplFileInfo $a, \\SplFileInfo $b) => $order * strcmp($a->getRealPath() ?: $a->getPathname(), $b->getRealPath() ?: $b->getPathname());\n        } elseif (self::SORT_BY_NAME_NATURAL === $sort) {\n            $this->sort = static fn (\\SplFileInfo $a, \\SplFileInfo $b) => $order * strnatcmp($a->getRealPath() ?: $a->getPathname(), $b->getRealPath() ?: $b->getPathname());\n        } elseif (self::SORT_BY_NAME_CASE_INSENSITIVE === $sort) {\n            $this->sort = static fn (\\SplFileInfo $a, \\SplFileInfo $b) => $order * strcasecmp($a->getRealPath() ?: $a->getPathname(), $b->getRealPath() ?: $b->getPathname());\n        } elseif (self::SORT_BY_NAME_NATURAL_CASE_INSENSITIVE === $sort) {\n            $this->sort = static fn (\\SplFileInfo $a, \\SplFileInfo $b) => $order * strnatcasecmp($a->getRealPath() ?: $a->getPathname(), $b->getRealPath() ?: $b->getPathname());\n        } elseif (self::SORT_BY_TYPE === $sort) {\n            $this->sort = static function (\\SplFileInfo $a, \\SplFileInfo $b) use ($order) {\n                if ($a->isDir() && $b->isFile()) {\n                    return -$order;\n                } elseif ($a->isFile() && $b->isDir()) {\n                    return $order;\n                }\n\n                return $order * strcmp($a->getRealPath() ?: $a->getPathname(), $b->getRealPath() ?: $b->getPathname());\n            };\n        } elseif (self::SORT_BY_ACCESSED_TIME === $sort) {\n            $this->sort = static fn (\\SplFileInfo $a, \\SplFileInfo $b) => $order * ($a->getATime() - $b->getATime());\n        } elseif (self::SORT_BY_CHANGED_TIME === $sort) {\n            $this->sort = static fn (\\SplFileInfo $a, \\SplFileInfo $b) => $order * ($a->getCTime() - $b->getCTime());\n        } elseif (self::SORT_BY_MODIFIED_TIME === $sort) {\n            $this->sort = static fn (\\SplFileInfo $a, \\SplFileInfo $b) => $order * ($a->getMTime() - $b->getMTime());\n        } elseif (self::SORT_BY_EXTENSION === $sort) {\n            $this->sort = static fn (\\SplFileInfo $a, \\SplFileInfo $b) => $order * strnatcmp($a->getExtension(), $b->getExtension());\n        } elseif (self::SORT_BY_SIZE === $sort) {\n            $this->sort = static fn (\\SplFileInfo $a, \\SplFileInfo $b) => $order * ($a->getSize() - $b->getSize());\n        } elseif (self::SORT_BY_NONE === $sort) {\n            $this->sort = $order;\n        } elseif (\\is_callable($sort)) {\n            $this->sort = $reverseOrder ? static fn (\\SplFileInfo $a, \\SplFileInfo $b) => -$sort($a, $b) : $sort(...);\n        } else {\n            throw new \\InvalidArgumentException('The SortableIterator takes a PHP callable or a valid built-in sort algorithm as an argument.');\n        }\n    }\n\n    public function getIterator(): \\Traversable\n    {\n        if (1 === $this->sort) {\n            yield from $this->iterator;\n\n            return;\n        }\n\n        $keys = $values = [];\n        foreach ($this->iterator as $key => $value) {\n            $keys[] = $key;\n            $values[] = $value;\n        }\n\n        if (-1 === $this->sort) {\n            for ($i = \\count($values) - 1; $i >= 0; --$i) {\n                yield $keys[$i] => $values[$i];\n            }\n\n            return;\n        }\n\n        uasort($values, $this->sort);\n\n        foreach ($values as $i => $v) {\n            yield $keys[$i] => $v;\n        }\n    }\n}\n"
  },
  {
    "path": "Iterator/VcsIgnoredFilterIterator.php",
    "content": "<?php\n\n/*\n * This file is part of the Symfony package.\n *\n * (c) Fabien Potencier <fabien@symfony.com>\n *\n * For the full copyright and license information, please view the LICENSE\n * file that was distributed with this source code.\n */\n\nnamespace Symfony\\Component\\Finder\\Iterator;\n\nuse Symfony\\Component\\Finder\\Gitignore;\n\n/**\n * @extends \\FilterIterator<string, \\SplFileInfo>\n */\nfinal class VcsIgnoredFilterIterator extends \\FilterIterator\n{\n    private string $baseDir;\n\n    /**\n     * @var array<string, array{0: string, 1: string}|null>\n     */\n    private array $gitignoreFilesCache = [];\n\n    /**\n     * @var array<string, bool>\n     */\n    private array $ignoredPathsCache = [];\n\n    /**\n     * @param \\Iterator<string, \\SplFileInfo> $iterator\n     */\n    public function __construct(\\Iterator $iterator, string $baseDir)\n    {\n        $this->baseDir = $this->normalizePath($baseDir);\n\n        foreach ([$this->baseDir, ...$this->parentDirectoriesUpwards($this->baseDir)] as $directory) {\n            if (@is_dir(\"{$directory}/.git\")) {\n                $this->baseDir = $directory;\n                break;\n            }\n        }\n\n        parent::__construct($iterator);\n    }\n\n    public function accept(): bool\n    {\n        $file = $this->current();\n\n        $fileRealPath = $this->normalizePath($file->getRealPath());\n\n        return !$this->isIgnored($fileRealPath);\n    }\n\n    private function isIgnored(string $fileRealPath): bool\n    {\n        if (is_dir($fileRealPath) && !str_ends_with($fileRealPath, '/')) {\n            $fileRealPath .= '/';\n        }\n\n        if (isset($this->ignoredPathsCache[$fileRealPath])) {\n            return $this->ignoredPathsCache[$fileRealPath];\n        }\n\n        $ignored = false;\n\n        foreach ($this->parentDirectoriesDownwards($fileRealPath) as $parentDirectory) {\n            if ($this->isIgnored($parentDirectory)) {\n                // rules in ignored directories are ignored, no need to check further.\n                break;\n            }\n\n            $fileRelativePath = substr($fileRealPath, \\strlen($parentDirectory) + 1);\n\n            if (null === $regexps = $this->readGitignoreFile(\"{$parentDirectory}/.gitignore\")) {\n                continue;\n            }\n\n            [$exclusionRegex, $inclusionRegex] = $regexps;\n\n            if (preg_match($exclusionRegex, $fileRelativePath)) {\n                $ignored = true;\n\n                continue;\n            }\n\n            if (preg_match($inclusionRegex, $fileRelativePath)) {\n                $ignored = false;\n            }\n        }\n\n        return $this->ignoredPathsCache[$fileRealPath] = $ignored;\n    }\n\n    /**\n     * @return list<string>\n     */\n    private function parentDirectoriesUpwards(string $from): array\n    {\n        $parentDirectories = [];\n\n        $parentDirectory = $from;\n\n        while (true) {\n            $newParentDirectory = \\dirname($parentDirectory);\n\n            // dirname('/') = '/'\n            if ($newParentDirectory === $parentDirectory) {\n                break;\n            }\n\n            $parentDirectories[] = $parentDirectory = $newParentDirectory;\n        }\n\n        return $parentDirectories;\n    }\n\n    private function parentDirectoriesUpTo(string $from, string $upTo): array\n    {\n        return array_filter(\n            $this->parentDirectoriesUpwards($from),\n            static fn (string $directory): bool => str_starts_with($directory, $upTo)\n        );\n    }\n\n    /**\n     * @return list<string>\n     */\n    private function parentDirectoriesDownwards(string $fileRealPath): array\n    {\n        return array_reverse(\n            $this->parentDirectoriesUpTo($fileRealPath, $this->baseDir)\n        );\n    }\n\n    /**\n     * @return array{0: string, 1: string}|null\n     */\n    private function readGitignoreFile(string $path): ?array\n    {\n        if (\\array_key_exists($path, $this->gitignoreFilesCache)) {\n            return $this->gitignoreFilesCache[$path];\n        }\n\n        if (!file_exists($path)) {\n            return $this->gitignoreFilesCache[$path] = null;\n        }\n\n        if (!is_file($path) || !is_readable($path)) {\n            throw new \\RuntimeException(\"The \\\"ignoreVCSIgnored\\\" option cannot be used by the Finder as the \\\"{$path}\\\" file is not readable.\");\n        }\n\n        $gitignoreFileContent = file_get_contents($path);\n\n        return $this->gitignoreFilesCache[$path] = [\n            Gitignore::toRegex($gitignoreFileContent),\n            Gitignore::toRegexMatchingNegatedPatterns($gitignoreFileContent),\n        ];\n    }\n\n    private function normalizePath(string $path): string\n    {\n        if ('\\\\' === \\DIRECTORY_SEPARATOR) {\n            return str_replace('\\\\', '/', $path);\n        }\n\n        return $path;\n    }\n}\n"
  },
  {
    "path": "LICENSE",
    "content": "Copyright (c) 2004-present Fabien Potencier\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is furnished\nto do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\nTHE SOFTWARE.\n"
  },
  {
    "path": "README.md",
    "content": "Finder Component\n================\n\nThe Finder component finds files and directories via an intuitive fluent\ninterface.\n\nResources\n---------\n\n * [Documentation](https://symfony.com/doc/current/components/finder.html)\n * [Contributing](https://symfony.com/doc/current/contributing/index.html)\n * [Report issues](https://github.com/symfony/symfony/issues) and\n   [send Pull Requests](https://github.com/symfony/symfony/pulls)\n   in the [main Symfony repository](https://github.com/symfony/symfony)\n"
  },
  {
    "path": "SplFileInfo.php",
    "content": "<?php\n\n/*\n * This file is part of the Symfony package.\n *\n * (c) Fabien Potencier <fabien@symfony.com>\n *\n * For the full copyright and license information, please view the LICENSE\n * file that was distributed with this source code.\n */\n\nnamespace Symfony\\Component\\Finder;\n\n/**\n * Extends \\SplFileInfo to support relative paths.\n *\n * @author Fabien Potencier <fabien@symfony.com>\n */\nclass SplFileInfo extends \\SplFileInfo\n{\n    /**\n     * @param string $file             The file name\n     * @param string $relativePath     The relative path\n     * @param string $relativePathname The relative path name\n     */\n    public function __construct(\n        string $file,\n        private string $relativePath,\n        private string $relativePathname,\n    ) {\n        parent::__construct($file);\n    }\n\n    /**\n     * Returns the relative path.\n     *\n     * This path does not contain the file name.\n     */\n    public function getRelativePath(): string\n    {\n        return $this->relativePath;\n    }\n\n    /**\n     * Returns the relative path name.\n     *\n     * This path contains the file name.\n     */\n    public function getRelativePathname(): string\n    {\n        return $this->relativePathname;\n    }\n\n    public function getFilenameWithoutExtension(): string\n    {\n        $filename = $this->getFilename();\n\n        return pathinfo($filename, \\PATHINFO_FILENAME);\n    }\n\n    /**\n     * Returns the contents of the file.\n     *\n     * @throws \\RuntimeException\n     */\n    public function getContents(): string\n    {\n        set_error_handler(static function ($type, $msg) use (&$error) { $error = $msg; });\n        try {\n            $content = file_get_contents($this->getPathname());\n        } finally {\n            restore_error_handler();\n        }\n        if (false === $content) {\n            throw new \\RuntimeException($error);\n        }\n\n        return $content;\n    }\n}\n"
  },
  {
    "path": "Tests/Comparator/ComparatorTest.php",
    "content": "<?php\n\n/*\n * This file is part of the Symfony package.\n *\n * (c) Fabien Potencier <fabien@symfony.com>\n *\n * For the full copyright and license information, please view the LICENSE\n * file that was distributed with this source code.\n */\n\nnamespace Symfony\\Component\\Finder\\Tests\\Comparator;\n\nuse PHPUnit\\Framework\\Attributes\\DataProvider;\nuse PHPUnit\\Framework\\TestCase;\nuse Symfony\\Component\\Finder\\Comparator\\Comparator;\n\nclass ComparatorTest extends TestCase\n{\n    public function testInvalidOperator()\n    {\n        $this->expectException(\\InvalidArgumentException::class);\n        $this->expectExceptionMessage('Invalid operator \"foo\".');\n\n        new Comparator('some target', 'foo');\n    }\n\n    #[DataProvider('provideMatches')]\n    public function testTestSucceeds(string $operator, string $target, string $testedValue)\n    {\n        $c = new Comparator($target, $operator);\n\n        $this->assertSame($target, $c->getTarget());\n        $this->assertSame($operator, $c->getOperator());\n\n        $this->assertTrue($c->test($testedValue));\n    }\n\n    public static function provideMatches(): array\n    {\n        return [\n            ['<', '1000', '500'],\n            ['<', '1000', '999'],\n            ['<=', '1000', '999'],\n            ['!=', '1000', '999'],\n            ['<=', '1000', '1000'],\n            ['==', '1000', '1000'],\n            ['>=', '1000', '1000'],\n            ['>=', '1000', '1001'],\n            ['>', '1000', '1001'],\n            ['>', '1000', '5000'],\n        ];\n    }\n\n    #[DataProvider('provideNonMatches')]\n    public function testTestFails(string $operator, string $target, string $testedValue)\n    {\n        $c = new Comparator($target, $operator);\n\n        $this->assertFalse($c->test($testedValue));\n    }\n\n    public static function provideNonMatches(): array\n    {\n        return [\n            ['>', '1000', '500'],\n            ['>=', '1000', '500'],\n            ['>', '1000', '1000'],\n            ['!=', '1000', '1000'],\n            ['<', '1000', '1000'],\n            ['<', '1000', '1500'],\n            ['<=', '1000', '1500'],\n        ];\n    }\n}\n"
  },
  {
    "path": "Tests/Comparator/DateComparatorTest.php",
    "content": "<?php\n\n/*\n * This file is part of the Symfony package.\n *\n * (c) Fabien Potencier <fabien@symfony.com>\n *\n * For the full copyright and license information, please view the LICENSE\n * file that was distributed with this source code.\n */\n\nnamespace Symfony\\Component\\Finder\\Tests\\Comparator;\n\nuse PHPUnit\\Framework\\Attributes\\DataProvider;\nuse PHPUnit\\Framework\\TestCase;\nuse Symfony\\Component\\Finder\\Comparator\\DateComparator;\n\nclass DateComparatorTest extends TestCase\n{\n    public function testConstructor()\n    {\n        try {\n            new DateComparator('foobar');\n            $this->fail('__construct() throws an \\InvalidArgumentException if the test expression is not valid.');\n        } catch (\\Exception $e) {\n            $this->assertInstanceOf(\\InvalidArgumentException::class, $e, '__construct() throws an \\InvalidArgumentException if the test expression is not valid.');\n        }\n\n        try {\n            new DateComparator('');\n            $this->fail('__construct() throws an \\InvalidArgumentException if the test expression is not valid.');\n        } catch (\\Exception $e) {\n            $this->assertInstanceOf(\\InvalidArgumentException::class, $e, '__construct() throws an \\InvalidArgumentException if the test expression is not valid.');\n        }\n    }\n\n    #[DataProvider('getTestData')]\n    public function testTest($test, $match, $noMatch)\n    {\n        $c = new DateComparator($test);\n\n        foreach ($match as $m) {\n            $this->assertTrue($c->test($m), '->test() tests a string against the expression');\n        }\n\n        foreach ($noMatch as $m) {\n            $this->assertFalse($c->test($m), '->test() tests a string against the expression');\n        }\n    }\n\n    public static function getTestData()\n    {\n        return [\n            ['< 2005-10-10', [strtotime('2005-10-09')], [strtotime('2005-10-15')]],\n            ['until 2005-10-10', [strtotime('2005-10-09')], [strtotime('2005-10-15')]],\n            ['before 2005-10-10', [strtotime('2005-10-09')], [strtotime('2005-10-15')]],\n            ['> 2005-10-10', [strtotime('2005-10-15')], [strtotime('2005-10-09')]],\n            ['after 2005-10-10', [strtotime('2005-10-15')], [strtotime('2005-10-09')]],\n            ['since 2005-10-10', [strtotime('2005-10-15')], [strtotime('2005-10-09')]],\n            ['!= 2005-10-10', [strtotime('2005-10-11')], [strtotime('2005-10-10')]],\n            ['2005-10-10', [strtotime('2005-10-10')], [strtotime('2005-10-11')]],\n        ];\n    }\n}\n"
  },
  {
    "path": "Tests/Comparator/NumberComparatorTest.php",
    "content": "<?php\n\n/*\n * This file is part of the Symfony package.\n *\n * (c) Fabien Potencier <fabien@symfony.com>\n *\n * For the full copyright and license information, please view the LICENSE\n * file that was distributed with this source code.\n */\n\nnamespace Symfony\\Component\\Finder\\Tests\\Comparator;\n\nuse PHPUnit\\Framework\\Attributes\\DataProvider;\nuse PHPUnit\\Framework\\TestCase;\nuse Symfony\\Component\\Finder\\Comparator\\NumberComparator;\n\nclass NumberComparatorTest extends TestCase\n{\n    #[DataProvider('getConstructorTestData')]\n    public function testConstructor($successes, $failures)\n    {\n        foreach ($successes as $s) {\n            new NumberComparator($s);\n        }\n\n        foreach ($failures as $f) {\n            try {\n                new NumberComparator($f);\n                $this->fail('__construct() throws an \\InvalidArgumentException if the test expression is not valid.');\n            } catch (\\Exception $e) {\n                $this->assertInstanceOf(\\InvalidArgumentException::class, $e, '__construct() throws an \\InvalidArgumentException if the test expression is not valid.');\n            }\n        }\n    }\n\n    #[DataProvider('getTestData')]\n    public function testTest($test, $match, $noMatch)\n    {\n        $c = new NumberComparator($test);\n\n        foreach ($match as $m) {\n            $this->assertTrue($c->test($m), '->test() tests a string against the expression');\n        }\n\n        foreach ($noMatch as $m) {\n            $this->assertFalse($c->test($m), '->test() tests a string against the expression');\n        }\n    }\n\n    public static function getTestData()\n    {\n        return [\n            ['< 1000', ['500', '999'], ['1000', '1500']],\n\n            ['< 1K', ['500', '999'], ['1000', '1500']],\n            ['<1k', ['500', '999'], ['1000', '1500']],\n            ['  < 1 K ', ['500', '999'], ['1000', '1500']],\n            ['<= 1K', ['1000'], ['1001']],\n            ['> 1K', ['1001'], ['1000']],\n            ['>= 1K', ['1000'], ['999']],\n\n            ['< 1KI', ['500', '1023'], ['1024', '1500']],\n            ['<= 1KI', ['1024'], ['1025']],\n            ['> 1KI', ['1025'], ['1024']],\n            ['>= 1KI', ['1024'], ['1023']],\n\n            ['1KI', ['1024'], ['1023', '1025']],\n            ['==1KI', ['1024'], ['1023', '1025']],\n\n            ['==1m', ['1000000'], ['999999', '1000001']],\n            ['==1mi', [1024 * 1024], [1024 * 1024 - 1, 1024 * 1024 + 1]],\n\n            ['==1g', ['1000000000'], ['999999999', '1000000001']],\n            ['==1gi', [1024 * 1024 * 1024], [1024 * 1024 * 1024 - 1, 1024 * 1024 * 1024 + 1]],\n\n            ['!= 1000', ['500', '999'], ['1000']],\n        ];\n    }\n\n    public static function getConstructorTestData()\n    {\n        return [\n            [\n                [\n                    '1', '0',\n                    '3.5', '33.55', '123.456', '123456.78',\n                    '.1', '.123',\n                    '.0', '0.0',\n                    '1.', '0.', '123.',\n                    '==1', '!=1', '<1', '>1', '<=1', '>=1',\n                    '==1k', '==1ki', '==1m', '==1mi', '==1g', '==1gi',\n                    '1k', '1ki', '1m', '1mi', '1g', '1gi',\n                ],\n                [\n                    null, '',\n                    ' ', 'foobar',\n                    '=1', '===1',\n                    '0 . 1', '123 .45', '234. 567',\n                    '..', '.0.', '0.1.2',\n                ],\n            ],\n        ];\n    }\n}\n"
  },
  {
    "path": "Tests/FinderOpenBasedirTest.php",
    "content": "<?php\n\n/*\n * This file is part of the Symfony package.\n *\n * (c) Fabien Potencier <fabien@symfony.com>\n *\n * For the full copyright and license information, please view the LICENSE\n * file that was distributed with this source code.\n */\n\nnamespace Symfony\\Component\\Finder\\Tests;\n\nuse PHPUnit\\Framework\\Attributes\\RunInSeparateProcess;\nuse Symfony\\Component\\Finder\\Finder;\n\nclass FinderOpenBasedirTest extends Iterator\\RealIteratorTestCase\n{\n    #[RunInSeparateProcess]\n    public function testIgnoreVCSIgnoredWithOpenBasedir()\n    {\n        $this->markTestIncomplete('Test case needs to be refactored so that PHPUnit can run it');\n\n        if (\\ini_get('open_basedir')) {\n            $this->markTestSkipped('Cannot test when open_basedir is set');\n        }\n\n        $finder = $this->buildFinder();\n        $this->assertSame(\n            $finder,\n            $finder\n                ->ignoreVCS(true)\n                ->ignoreDotFiles(true)\n                ->ignoreVCSIgnored(true)\n        );\n\n        $openBaseDir = \\dirname(__DIR__, 5).\\PATH_SEPARATOR.self::toAbsolute('gitignore/search_root');\n\n        if ($deprecationsFile = getenv('SYMFONY_DEPRECATIONS_SERIALIZE')) {\n            $openBaseDir .= \\PATH_SEPARATOR.$deprecationsFile;\n        }\n\n        $oldOpenBaseDir = ini_set('open_basedir', $openBaseDir);\n\n        try {\n            $this->assertIterator(self::toAbsolute([\n                'gitignore/search_root/b.txt',\n                'gitignore/search_root/c.txt',\n                'gitignore/search_root/dir',\n                'gitignore/search_root/dir/a.txt',\n                'gitignore/search_root/dir/c.txt',\n            ]), $finder->in(self::toAbsolute('gitignore/search_root'))->getIterator());\n        } finally {\n            ini_set('open_basedir', $oldOpenBaseDir);\n        }\n    }\n\n    protected function buildFinder()\n    {\n        return Finder::create()->exclude('gitignore');\n    }\n}\n"
  },
  {
    "path": "Tests/FinderTest.php",
    "content": "<?php\n\n/*\n * This file is part of the Symfony package.\n *\n * (c) Fabien Potencier <fabien@symfony.com>\n *\n * For the full copyright and license information, please view the LICENSE\n * file that was distributed with this source code.\n */\n\nnamespace Symfony\\Component\\Finder\\Tests;\n\nuse PHPUnit\\Framework\\Attributes\\DataProvider;\nuse PHPUnit\\Framework\\ExpectationFailedException;\nuse Symfony\\Component\\Finder\\Exception\\DirectoryNotFoundException;\nuse Symfony\\Component\\Finder\\Finder;\nuse Symfony\\Component\\Finder\\SplFileInfo;\n\nclass FinderTest extends Iterator\\RealIteratorTestCase\n{\n    use Iterator\\VfsIteratorTestTrait;\n\n    public function testCreate()\n    {\n        $this->assertInstanceOf(Finder::class, Finder::create());\n    }\n\n    public function testDirectories()\n    {\n        $finder = $this->buildFinder();\n        $this->assertSame($finder, $finder->directories());\n        $this->assertIterator($this->toAbsolute(['foo', 'qux', 'toto']), $finder->in(self::$tmpDir)->getIterator());\n\n        $finder = $this->buildFinder();\n        $finder->directories();\n        $finder->files();\n        $finder->directories();\n        $this->assertIterator($this->toAbsolute(['foo', 'qux', 'toto']), $finder->in(self::$tmpDir)->getIterator());\n    }\n\n    public function testFiles()\n    {\n        $finder = $this->buildFinder();\n        $this->assertSame($finder, $finder->files());\n        $this->assertIterator($this->toAbsolute(['foo/bar.tmp',\n            'test.php',\n            'test.py',\n            'foo bar',\n            'qux/baz_100_1.py',\n            'qux/baz_1_2.py',\n            'qux_0_1.php',\n            'qux_1000_1.php',\n            'qux_1002_0.php',\n            'qux_10_2.php',\n            'qux_12_0.php',\n            'qux_2_0.php',\n            'zebulon.php',\n            'Zephire.php',\n        ]), $finder->in(self::$tmpDir)->getIterator());\n\n        $finder = $this->buildFinder();\n        $finder->files();\n        $finder->directories();\n        $finder->files();\n        $this->assertIterator($this->toAbsolute(['foo/bar.tmp',\n            'test.php',\n            'test.py',\n            'foo bar',\n            'qux/baz_100_1.py',\n            'qux/baz_1_2.py',\n            'qux_0_1.php',\n            'qux_1000_1.php',\n            'qux_1002_0.php',\n            'qux_10_2.php',\n            'qux_12_0.php',\n            'qux_2_0.php',\n            'zebulon.php',\n            'Zephire.php',\n        ]), $finder->in(self::$tmpDir)->getIterator());\n    }\n\n    public function testRemoveTrailingSlash()\n    {\n        $finder = $this->buildFinder();\n\n        $expected = $this->toAbsolute([\n            'foo/bar.tmp',\n            'test.php',\n            'test.py',\n            'foo bar',\n            'qux/baz_100_1.py',\n            'qux/baz_1_2.py',\n            'qux_0_1.php',\n            'qux_1000_1.php',\n            'qux_1002_0.php',\n            'qux_10_2.php',\n            'qux_12_0.php',\n            'qux_2_0.php',\n            'zebulon.php',\n            'Zephire.php',\n        ]);\n        $in = self::$tmpDir.'//';\n\n        $this->assertIterator($expected, $finder->in($in)->files()->getIterator());\n    }\n\n    public function testSymlinksNotResolved()\n    {\n        if ('\\\\' === \\DIRECTORY_SEPARATOR) {\n            $this->markTestSkipped('symlinks are not supported on Windows');\n        }\n\n        $finder = $this->buildFinder();\n\n        symlink($this->toAbsolute('foo'), $this->toAbsolute('baz'));\n        $expected = $this->toAbsolute(['baz/bar.tmp']);\n        $in = self::$tmpDir.'/baz/';\n        try {\n            $this->assertIterator($expected, $finder->in($in)->files()->getIterator());\n            unlink($this->toAbsolute('baz'));\n        } catch (\\Exception $e) {\n            unlink($this->toAbsolute('baz'));\n            throw $e;\n        }\n    }\n\n    public function testBackPathNotNormalized()\n    {\n        $finder = $this->buildFinder();\n\n        $expected = $this->toAbsolute(['foo/../foo/bar.tmp']);\n        $in = self::$tmpDir.'/foo/../foo/';\n        $this->assertIterator($expected, $finder->in($in)->files()->getIterator());\n    }\n\n    public function testDepth()\n    {\n        $finder = $this->buildFinder();\n        $this->assertSame($finder, $finder->depth('< 1'));\n        $this->assertIterator($this->toAbsolute(['foo',\n            'test.php',\n            'test.py',\n            'toto',\n            'foo bar',\n            'qux',\n            'qux_0_1.php',\n            'qux_1000_1.php',\n            'qux_1002_0.php',\n            'qux_10_2.php',\n            'qux_12_0.php',\n            'qux_2_0.php',\n            'zebulon.php',\n            'Zephire.php',\n        ]), $finder->in(self::$tmpDir)->getIterator());\n\n        $finder = $this->buildFinder();\n        $this->assertSame($finder, $finder->depth('<= 0'));\n        $this->assertIterator($this->toAbsolute(['foo',\n            'test.php',\n            'test.py',\n            'toto',\n            'foo bar',\n            'qux',\n            'qux_0_1.php',\n            'qux_1000_1.php',\n            'qux_1002_0.php',\n            'qux_10_2.php',\n            'qux_12_0.php',\n            'qux_2_0.php',\n            'zebulon.php',\n            'Zephire.php',\n        ]), $finder->in(self::$tmpDir)->getIterator());\n\n        $finder = $this->buildFinder();\n        $this->assertSame($finder, $finder->depth('>= 1'));\n        $this->assertIterator($this->toAbsolute([\n            'foo/bar.tmp',\n            'qux/baz_100_1.py',\n            'qux/baz_1_2.py',\n        ]), $finder->in(self::$tmpDir)->getIterator());\n\n        $finder = $this->buildFinder();\n        $finder->depth('< 1')->depth('>= 1');\n        $this->assertIterator([], $finder->in(self::$tmpDir)->getIterator());\n    }\n\n    public function testDepthWithArrayParam()\n    {\n        $finder = $this->buildFinder();\n        $finder->depth(['>= 1', '< 2']);\n        $this->assertIterator($this->toAbsolute([\n            'foo/bar.tmp',\n            'qux/baz_100_1.py',\n            'qux/baz_1_2.py',\n        ]), $finder->in(self::$tmpDir)->getIterator());\n    }\n\n    public function testName()\n    {\n        $finder = $this->buildFinder();\n        $this->assertSame($finder, $finder->name('*.php'));\n        $this->assertIterator($this->toAbsolute([\n            'test.php',\n            'qux_0_1.php',\n            'qux_1000_1.php',\n            'qux_1002_0.php',\n            'qux_10_2.php',\n            'qux_12_0.php',\n            'qux_2_0.php',\n            'zebulon.php',\n            'Zephire.php',\n        ]), $finder->in(self::$tmpDir)->getIterator());\n\n        $finder = $this->buildFinder();\n        $finder->name('test.ph*');\n        $finder->name('test.py');\n        $this->assertIterator($this->toAbsolute(['test.php', 'test.py']), $finder->in(self::$tmpDir)->getIterator());\n\n        $finder = $this->buildFinder();\n        $finder->name('~^test~i');\n        $this->assertIterator($this->toAbsolute(['test.php', 'test.py']), $finder->in(self::$tmpDir)->getIterator());\n\n        $finder = $this->buildFinder();\n        $finder->name('~\\\\.php$~i');\n        $this->assertIterator($this->toAbsolute([\n            'test.php',\n            'qux_0_1.php',\n            'qux_1000_1.php',\n            'qux_1002_0.php',\n            'qux_10_2.php',\n            'qux_12_0.php',\n            'qux_2_0.php',\n            'zebulon.php',\n            'Zephire.php',\n        ]), $finder->in(self::$tmpDir)->getIterator());\n\n        $finder = $this->buildFinder();\n        $finder->name('test.p{hp,y}');\n        $this->assertIterator($this->toAbsolute(['test.php', 'test.py']), $finder->in(self::$tmpDir)->getIterator());\n    }\n\n    public function testNameWithArrayParam()\n    {\n        $finder = $this->buildFinder();\n        $finder->name(['test.php', 'test.py']);\n        $this->assertIterator($this->toAbsolute(['test.php', 'test.py']), $finder->in(self::$tmpDir)->getIterator());\n    }\n\n    public function testNotName()\n    {\n        $finder = $this->buildFinder();\n        $this->assertSame($finder, $finder->notName('*.php'));\n        $this->assertIterator($this->toAbsolute([\n            'foo',\n            'foo/bar.tmp',\n            'test.py',\n            'toto',\n            'foo bar',\n            'qux',\n            'qux/baz_100_1.py',\n            'qux/baz_1_2.py',\n        ]), $finder->in(self::$tmpDir)->getIterator());\n\n        $finder = $this->buildFinder();\n        $finder->notName('*.php');\n        $finder->notName('*.py');\n        $this->assertIterator($this->toAbsolute([\n            'foo',\n            'foo/bar.tmp',\n            'toto',\n            'foo bar',\n            'qux',\n        ]), $finder->in(self::$tmpDir)->getIterator());\n\n        $finder = $this->buildFinder();\n        $finder->name('test.ph*');\n        $finder->name('test.py');\n        $finder->notName('*.php');\n        $finder->notName('*.py');\n        $this->assertIterator([], $finder->in(self::$tmpDir)->getIterator());\n\n        $finder = $this->buildFinder();\n        $finder->name('test.ph*');\n        $finder->name('test.py');\n        $finder->notName('*.p{hp,y}');\n        $this->assertIterator([], $finder->in(self::$tmpDir)->getIterator());\n    }\n\n    public function testNotNameWithArrayParam()\n    {\n        $finder = $this->buildFinder();\n        $finder->notName(['*.php', '*.py']);\n        $this->assertIterator($this->toAbsolute([\n            'foo',\n            'foo/bar.tmp',\n            'toto',\n            'foo bar',\n            'qux',\n        ]), $finder->in(self::$tmpDir)->getIterator());\n    }\n\n    #[DataProvider('getRegexNameTestData')]\n    public function testRegexName($regex)\n    {\n        $finder = $this->buildFinder();\n        $finder->name($regex);\n        $this->assertIterator($this->toAbsolute([\n            'test.py',\n            'test.php',\n        ]), $finder->in(self::$tmpDir)->getIterator());\n    }\n\n    public function testSize()\n    {\n        $finder = $this->buildFinder();\n        $this->assertSame($finder, $finder->files()->size('< 1K')->size('> 500'));\n        $this->assertIterator($this->toAbsolute(['test.php']), $finder->in(self::$tmpDir)->getIterator());\n    }\n\n    public function testSizeWithArrayParam()\n    {\n        $finder = $this->buildFinder();\n        $this->assertSame($finder, $finder->files()->size(['< 1K', '> 500']));\n        $this->assertIterator($this->toAbsolute(['test.php']), $finder->in(self::$tmpDir)->getIterator());\n    }\n\n    public function testDate()\n    {\n        $finder = $this->buildFinder();\n        $this->assertSame($finder, $finder->files()->date('until last month'));\n        $this->assertIterator($this->toAbsolute(['foo/bar.tmp', 'test.php']), $finder->in(self::$tmpDir)->getIterator());\n    }\n\n    public function testDateWithArrayParam()\n    {\n        $finder = $this->buildFinder();\n        $this->assertSame($finder, $finder->files()->date(['>= 2005-10-15', 'until last month']));\n        $this->assertIterator($this->toAbsolute(['foo/bar.tmp', 'test.php']), $finder->in(self::$tmpDir)->getIterator());\n    }\n\n    public function testExclude()\n    {\n        $finder = $this->buildFinder();\n        $this->assertSame($finder, $finder->exclude('foo'));\n        $this->assertIterator($this->toAbsolute([\n            'test.php',\n            'test.py',\n            'toto',\n            'foo bar',\n            'qux',\n            'qux/baz_100_1.py',\n            'zebulon.php',\n            'Zephire.php',\n            'qux/baz_1_2.py',\n            'qux_0_1.php',\n            'qux_1000_1.php',\n            'qux_1002_0.php',\n            'qux_10_2.php',\n            'qux_12_0.php',\n            'qux_2_0.php',\n        ]), $finder->in(self::$tmpDir)->getIterator());\n    }\n\n    public function testIgnoreVCS()\n    {\n        $finder = $this->buildFinder();\n        $this->assertSame($finder, $finder->ignoreVCS(false)->ignoreDotFiles(false));\n        $this->assertIterator($this->toAbsolute([\n            '.git',\n            'foo',\n            'foo/bar.tmp',\n            'test.php',\n            'test.py',\n            'toto',\n            'toto/.git',\n            '.bar',\n            '.foo',\n            '.foo/.bar',\n            '.foo/bar',\n            'foo bar',\n            'qux',\n            'qux/baz_100_1.py',\n            'zebulon.php',\n            'Zephire.php',\n            'qux/baz_1_2.py',\n            'qux_0_1.php',\n            'qux_1000_1.php',\n            'qux_1002_0.php',\n            'qux_10_2.php',\n            'qux_12_0.php',\n            'qux_2_0.php',\n        ]), $finder->in(self::$tmpDir)->getIterator());\n\n        $finder = $this->buildFinder();\n        $finder->ignoreVCS(false)->ignoreVCS(false)->ignoreDotFiles(false);\n        $this->assertIterator($this->toAbsolute([\n            '.git',\n            'foo',\n            'foo/bar.tmp',\n            'test.php',\n            'test.py',\n            'toto',\n            'toto/.git',\n            '.bar',\n            '.foo',\n            '.foo/.bar',\n            '.foo/bar',\n            'foo bar',\n            'qux',\n            'qux/baz_100_1.py',\n            'zebulon.php',\n            'Zephire.php',\n            'qux/baz_1_2.py',\n            'qux_0_1.php',\n            'qux_1000_1.php',\n            'qux_1002_0.php',\n            'qux_10_2.php',\n            'qux_12_0.php',\n            'qux_2_0.php',\n        ]), $finder->in(self::$tmpDir)->getIterator());\n\n        $finder = $this->buildFinder();\n        $this->assertSame($finder, $finder->ignoreVCS(true)->ignoreDotFiles(false));\n        $this->assertIterator($this->toAbsolute([\n            'foo',\n            'foo/bar.tmp',\n            'test.php',\n            'test.py',\n            'toto',\n            '.bar',\n            '.foo',\n            '.foo/.bar',\n            '.foo/bar',\n            'foo bar',\n            'qux',\n            'qux/baz_100_1.py',\n            'zebulon.php',\n            'Zephire.php',\n            'qux/baz_1_2.py',\n            'qux_0_1.php',\n            'qux_1000_1.php',\n            'qux_1002_0.php',\n            'qux_10_2.php',\n            'qux_12_0.php',\n            'qux_2_0.php',\n        ]), $finder->in(self::$tmpDir)->getIterator());\n    }\n\n    public function testIgnoreVCSIgnored()\n    {\n        $finder = $this->buildFinder();\n        $this->assertSame(\n            $finder,\n            $finder\n                ->ignoreVCS(true)\n                ->ignoreDotFiles(true)\n                ->ignoreVCSIgnored(true)\n        );\n\n        $this->assertIterator(self::toAbsolute([\n            'gitignore/search_root/b.txt',\n            'gitignore/search_root/dir',\n            'gitignore/search_root/dir/a.txt',\n        ]), $finder->in(self::toAbsolute('gitignore/search_root'))->getIterator());\n    }\n\n    public function testIgnoreVCSIgnoredUpToFirstGitRepositoryRoot()\n    {\n        $finder = $this->buildFinder();\n        $this->assertSame(\n            $finder,\n            $finder\n                ->ignoreVCS(true)\n                ->ignoreDotFiles(true)\n                ->ignoreVCSIgnored(true)\n        );\n\n        $this->assertIterator(self::toAbsolute([\n            'gitignore/git_root/search_root/b.txt',\n            'gitignore/git_root/search_root/c.txt',\n            'gitignore/git_root/search_root/dir',\n            'gitignore/git_root/search_root/dir/a.txt',\n            'gitignore/git_root/search_root/dir/c.txt',\n        ]), $finder->in(self::toAbsolute('gitignore/git_root/search_root'))->getIterator());\n    }\n\n    public function testIgnoreVCSCanBeDisabledAfterFirstIteration()\n    {\n        $finder = $this->buildFinder();\n        $finder->in(self::$tmpDir);\n        $finder->ignoreDotFiles(false);\n\n        $this->assertIterator($this->toAbsolute([\n            'foo',\n            'foo/bar.tmp',\n            'qux',\n            'qux/baz_100_1.py',\n            'zebulon.php',\n            'Zephire.php',\n            'qux/baz_1_2.py',\n            'qux_0_1.php',\n            'qux_1000_1.php',\n            'qux_1002_0.php',\n            'qux_10_2.php',\n            'qux_12_0.php',\n            'qux_2_0.php',\n            'test.php',\n            'test.py',\n            'toto',\n            '.bar',\n            '.foo',\n            '.foo/.bar',\n            '.foo/bar',\n            'foo bar',\n        ]), $finder->getIterator());\n\n        $finder->ignoreVCS(false);\n        $this->assertIterator($this->toAbsolute([\n            '.git',\n            'foo',\n            'foo/bar.tmp',\n            'qux',\n            'qux/baz_100_1.py',\n            'zebulon.php',\n            'Zephire.php',\n            'qux/baz_1_2.py',\n            'qux_0_1.php',\n            'qux_1000_1.php',\n            'qux_1002_0.php',\n            'qux_10_2.php',\n            'qux_12_0.php',\n            'qux_2_0.php',\n            'test.php',\n            'test.py',\n            'toto',\n            'toto/.git',\n            '.bar',\n            '.foo',\n            '.foo/.bar',\n            '.foo/bar',\n            'foo bar',\n        ]), $finder->getIterator());\n    }\n\n    public function testIgnoreDotFiles()\n    {\n        $finder = $this->buildFinder();\n        $this->assertSame($finder, $finder->ignoreDotFiles(false)->ignoreVCS(false));\n        $this->assertIterator($this->toAbsolute([\n            '.git',\n            '.bar',\n            '.foo',\n            '.foo/.bar',\n            '.foo/bar',\n            'foo',\n            'foo/bar.tmp',\n            'test.php',\n            'test.py',\n            'toto',\n            'toto/.git',\n            'foo bar',\n            'qux',\n            'qux/baz_100_1.py',\n            'zebulon.php',\n            'Zephire.php',\n            'qux/baz_1_2.py',\n            'qux_0_1.php',\n            'qux_1000_1.php',\n            'qux_1002_0.php',\n            'qux_10_2.php',\n            'qux_12_0.php',\n            'qux_2_0.php',\n        ]), $finder->in(self::$tmpDir)->getIterator());\n\n        $finder = $this->buildFinder();\n        $finder->ignoreDotFiles(false)->ignoreDotFiles(false)->ignoreVCS(false);\n        $this->assertIterator($this->toAbsolute([\n            '.git',\n            '.bar',\n            '.foo',\n            '.foo/.bar',\n            '.foo/bar',\n            'foo',\n            'foo/bar.tmp',\n            'test.php',\n            'test.py',\n            'toto',\n            'toto/.git',\n            'foo bar',\n            'qux',\n            'qux/baz_100_1.py',\n            'zebulon.php',\n            'Zephire.php',\n            'qux/baz_1_2.py',\n            'qux_0_1.php',\n            'qux_1000_1.php',\n            'qux_1002_0.php',\n            'qux_10_2.php',\n            'qux_12_0.php',\n            'qux_2_0.php',\n        ]), $finder->in(self::$tmpDir)->getIterator());\n\n        $finder = $this->buildFinder();\n        $this->assertSame($finder, $finder->ignoreDotFiles(true)->ignoreVCS(false));\n        $this->assertIterator($this->toAbsolute([\n            'foo',\n            'foo/bar.tmp',\n            'test.php',\n            'test.py',\n            'toto',\n            'foo bar',\n            'qux',\n            'qux/baz_100_1.py',\n            'zebulon.php',\n            'Zephire.php',\n            'qux/baz_1_2.py',\n            'qux_0_1.php',\n            'qux_1000_1.php',\n            'qux_1002_0.php',\n            'qux_10_2.php',\n            'qux_12_0.php',\n            'qux_2_0.php',\n        ]), $finder->in(self::$tmpDir)->getIterator());\n    }\n\n    public function testIgnoreDotFilesCanBeDisabledAfterFirstIteration()\n    {\n        $finder = $this->buildFinder();\n        $finder->in(self::$tmpDir);\n\n        $this->assertIterator($this->toAbsolute([\n            'foo',\n            'foo/bar.tmp',\n            'qux',\n            'qux/baz_100_1.py',\n            'zebulon.php',\n            'Zephire.php',\n            'qux/baz_1_2.py',\n            'qux_0_1.php',\n            'qux_1000_1.php',\n            'qux_1002_0.php',\n            'qux_10_2.php',\n            'qux_12_0.php',\n            'qux_2_0.php',\n            'test.php',\n            'test.py',\n            'toto',\n            'foo bar',\n        ]), $finder->getIterator());\n\n        $finder->ignoreDotFiles(false);\n        $this->assertIterator($this->toAbsolute([\n            'foo',\n            'foo/bar.tmp',\n            'qux',\n            'qux/baz_100_1.py',\n            'zebulon.php',\n            'Zephire.php',\n            'qux/baz_1_2.py',\n            'qux_0_1.php',\n            'qux_1000_1.php',\n            'qux_1002_0.php',\n            'qux_10_2.php',\n            'qux_12_0.php',\n            'qux_2_0.php',\n            'test.php',\n            'test.py',\n            'toto',\n            '.bar',\n            '.foo',\n            '.foo/.bar',\n            '.foo/bar',\n            'foo bar',\n        ]), $finder->getIterator());\n    }\n\n    public function testSortByName()\n    {\n        $finder = $this->buildFinder();\n        $this->assertSame($finder, $finder->sortByName());\n        $this->assertOrderedIterator($this->toAbsolute([\n            'Zephire.php',\n            'foo',\n            'foo bar',\n            'foo/bar.tmp',\n            'qux',\n            'qux/baz_100_1.py',\n            'qux/baz_1_2.py',\n            'qux_0_1.php',\n            'qux_1000_1.php',\n            'qux_1002_0.php',\n            'qux_10_2.php',\n            'qux_12_0.php',\n            'qux_2_0.php',\n            'test.php',\n            'test.py',\n            'toto',\n            'zebulon.php',\n        ]), $finder->in(self::$tmpDir)->getIterator());\n    }\n\n    public function testSortByType()\n    {\n        $finder = $this->buildFinder();\n        $this->assertSame($finder, $finder->sortByType());\n        $this->assertOrderedIterator($this->toAbsolute([\n            'foo',\n            'qux',\n            'toto',\n            'Zephire.php',\n            'foo bar',\n            'foo/bar.tmp',\n            'qux/baz_100_1.py',\n            'qux/baz_1_2.py',\n            'qux_0_1.php',\n            'qux_1000_1.php',\n            'qux_1002_0.php',\n            'qux_10_2.php',\n            'qux_12_0.php',\n            'qux_2_0.php',\n            'test.php',\n            'test.py',\n            'zebulon.php',\n        ]), $finder->in(self::$tmpDir)->getIterator());\n    }\n\n    public function testSortByAccessedTime()\n    {\n        $finder = $this->buildFinder();\n        $this->assertSame($finder, $finder->sortByAccessedTime());\n        $this->assertIterator($this->toAbsolute([\n            'foo/bar.tmp',\n            'test.php',\n            'toto',\n            'test.py',\n            'foo',\n            'foo bar',\n            'qux',\n            'qux/baz_100_1.py',\n            'zebulon.php',\n            'Zephire.php',\n            'qux/baz_1_2.py',\n            'qux_0_1.php',\n            'qux_1000_1.php',\n            'qux_1002_0.php',\n            'qux_10_2.php',\n            'qux_12_0.php',\n            'qux_2_0.php',\n        ]), $finder->in(self::$tmpDir)->getIterator());\n    }\n\n    public function testSortByChangedTime()\n    {\n        $finder = $this->buildFinder();\n        $this->assertSame($finder, $finder->sortByChangedTime());\n        $this->assertIterator($this->toAbsolute([\n            'toto',\n            'test.py',\n            'test.php',\n            'foo/bar.tmp',\n            'foo',\n            'foo bar',\n            'qux',\n            'qux/baz_100_1.py',\n            'zebulon.php',\n            'Zephire.php',\n            'qux/baz_1_2.py',\n            'qux_0_1.php',\n            'qux_1000_1.php',\n            'qux_1002_0.php',\n            'qux_10_2.php',\n            'qux_12_0.php',\n            'qux_2_0.php',\n        ]), $finder->in(self::$tmpDir)->getIterator());\n    }\n\n    public function testSortByModifiedTime()\n    {\n        $finder = $this->buildFinder();\n        $this->assertSame($finder, $finder->sortByModifiedTime());\n        $this->assertIterator($this->toAbsolute([\n            'foo/bar.tmp',\n            'test.php',\n            'toto',\n            'test.py',\n            'foo',\n            'foo bar',\n            'qux',\n            'qux/baz_100_1.py',\n            'zebulon.php',\n            'Zephire.php',\n            'qux/baz_1_2.py',\n            'qux_0_1.php',\n            'qux_1000_1.php',\n            'qux_1002_0.php',\n            'qux_10_2.php',\n            'qux_12_0.php',\n            'qux_2_0.php',\n        ]), $finder->in(self::$tmpDir)->getIterator());\n    }\n\n    public function testReverseSorting()\n    {\n        $finder = $this->buildFinder();\n        $this->assertSame($finder, $finder->sortByName());\n        $this->assertSame($finder, $finder->reverseSorting());\n        $this->assertOrderedIteratorInForeach($this->toAbsolute([\n            'zebulon.php',\n            'toto',\n            'test.py',\n            'test.php',\n            'qux_2_0.php',\n            'qux_12_0.php',\n            'qux_10_2.php',\n            'qux_1002_0.php',\n            'qux_1000_1.php',\n            'qux_0_1.php',\n            'qux/baz_1_2.py',\n            'qux/baz_100_1.py',\n            'qux',\n            'foo/bar.tmp',\n            'foo bar',\n            'foo',\n            'Zephire.php',\n        ]), $finder->in(self::$tmpDir)->getIterator());\n    }\n\n    public function testSortByNameNatural()\n    {\n        $finder = $this->buildFinder();\n        $this->assertSame($finder, $finder->sortByName(true));\n        $this->assertOrderedIterator($this->toAbsolute([\n            'Zephire.php',\n            'foo',\n            'foo/bar.tmp',\n            'foo bar',\n            'qux',\n            'qux/baz_1_2.py',\n            'qux/baz_100_1.py',\n            'qux_0_1.php',\n            'qux_2_0.php',\n            'qux_10_2.php',\n            'qux_12_0.php',\n            'qux_1000_1.php',\n            'qux_1002_0.php',\n            'test.php',\n            'test.py',\n            'toto',\n            'zebulon.php',\n        ]), $finder->in(self::$tmpDir)->getIterator());\n\n        $finder = $this->buildFinder();\n        $this->assertSame($finder, $finder->sortByName(false));\n        $this->assertOrderedIterator($this->toAbsolute([\n            'Zephire.php',\n            'foo',\n            'foo bar',\n            'foo/bar.tmp',\n            'qux',\n            'qux/baz_100_1.py',\n            'qux/baz_1_2.py',\n            'qux_0_1.php',\n            'qux_1000_1.php',\n            'qux_1002_0.php',\n            'qux_10_2.php',\n            'qux_12_0.php',\n            'qux_2_0.php',\n            'test.php',\n            'test.py',\n            'toto',\n            'zebulon.php',\n        ]), $finder->in(self::$tmpDir)->getIterator());\n    }\n\n    public function testSortByNameCaseInsensitive()\n    {\n        $finder = $this->buildFinder();\n        $this->assertSame($finder, $finder->sortByCaseInsensitiveName(true));\n\n        $expected = ['foo'];\n\n        if ('\\\\' === \\DIRECTORY_SEPARATOR) {\n            $expected[] = 'foo bar';\n            $expected[] = 'foo/bar.tmp';\n        } else {\n            $expected[] = 'foo/bar.tmp';\n            $expected[] = 'foo bar';\n        }\n\n        $expected = array_merge($expected, [\n            'qux',\n            'qux/baz_1_2.py',\n            'qux/baz_100_1.py',\n            'qux_0_1.php',\n            'qux_2_0.php',\n            'qux_10_2.php',\n            'qux_12_0.php',\n            'qux_1000_1.php',\n            'qux_1002_0.php',\n            'test.php',\n            'test.py',\n            'toto',\n            'zebulon.php',\n            'Zephire.php',\n        ]);\n        $this->assertOrderedIterator($this->toAbsolute($expected), $finder->in(self::$tmpDir)->getIterator());\n\n        $finder = $this->buildFinder();\n        $this->assertSame($finder, $finder->sortByCaseInsensitiveName(false));\n        $this->assertOrderedIterator($this->toAbsolute([\n            'foo',\n            'foo bar',\n            'foo/bar.tmp',\n            'qux',\n            'qux/baz_100_1.py',\n            'qux/baz_1_2.py',\n            'qux_0_1.php',\n            'qux_1000_1.php',\n            'qux_1002_0.php',\n            'qux_10_2.php',\n            'qux_12_0.php',\n            'qux_2_0.php',\n            'test.php',\n            'test.py',\n            'toto',\n            'zebulon.php',\n            'Zephire.php',\n        ]), $finder->in(self::$tmpDir)->getIterator());\n    }\n\n    public function testSort()\n    {\n        $finder = $this->buildFinder();\n        $this->assertSame($finder, $finder->sort(static fn (\\SplFileInfo $a, \\SplFileInfo $b) => strcmp($a->getRealPath(), $b->getRealPath())));\n        $this->assertOrderedIterator($this->toAbsolute([\n            'Zephire.php',\n            'foo',\n            'foo bar',\n            'foo/bar.tmp',\n            'qux',\n            'qux/baz_100_1.py',\n            'qux/baz_1_2.py',\n            'qux_0_1.php',\n            'qux_1000_1.php',\n            'qux_1002_0.php',\n            'qux_10_2.php',\n            'qux_12_0.php',\n            'qux_2_0.php',\n            'test.php',\n            'test.py',\n            'toto',\n            'zebulon.php',\n        ]), $finder->in(self::$tmpDir)->getIterator());\n    }\n\n    public function testSortAcrossDirectories()\n    {\n        $finder = $this->buildFinder()\n            ->in([\n                self::$tmpDir,\n                self::$tmpDir.'/qux',\n                self::$tmpDir.'/foo',\n            ])\n            ->depth(0)\n            ->files()\n            ->filter(static fn (\\SplFileInfo $file): bool => '' !== $file->getExtension())\n            ->sort(static fn (\\SplFileInfo $a, \\SplFileInfo $b): int => strcmp($a->getExtension(), $b->getExtension()) ?: strcmp($a->getFilename(), $b->getFilename()))\n        ;\n\n        $this->assertOrderedIterator($this->toAbsolute([\n            'Zephire.php',\n            'qux_0_1.php',\n            'qux_1000_1.php',\n            'qux_1002_0.php',\n            'qux_10_2.php',\n            'qux_12_0.php',\n            'qux_2_0.php',\n            'test.php',\n            'zebulon.php',\n            'qux/baz_100_1.py',\n            'qux/baz_1_2.py',\n            'test.py',\n            'foo/bar.tmp',\n        ]), $finder->getIterator());\n    }\n\n    public function testFilter()\n    {\n        $finder = $this->buildFinder();\n        $this->assertSame($finder, $finder->filter(static fn (\\SplFileInfo $f) => str_contains($f, 'test')));\n        $this->assertIterator($this->toAbsolute(['test.php', 'test.py']), $finder->in(self::$tmpDir)->getIterator());\n    }\n\n    public function testFilterPrune()\n    {\n        $this->setupVfsProvider([\n            'x' => [\n                'a.php' => '',\n                'b.php' => '',\n                'd' => [\n                    'u.php' => '',\n                ],\n                'x' => [\n                    'd' => [\n                        'u2.php' => '',\n                    ],\n                ],\n            ],\n            'y' => [\n                'c.php' => '',\n            ],\n        ]);\n\n        $finder = $this->buildFinder();\n        $finder\n            ->in($this->vfsScheme.'://x')\n            ->filter(static fn (): bool => true, true) // does nothing\n            ->filter(function (\\SplFileInfo $file): bool {\n                $path = $this->stripSchemeFromVfsPath($file->getPathname());\n\n                $res = 'x/d' !== $path;\n\n                $this->vfsLog[] = [$path, 'exclude_filter', $res];\n\n                return $res;\n            }, true)\n            ->filter(static fn (): bool => true, true); // does nothing\n\n        $this->assertSameVfsIterator([\n            'x/a.php',\n            'x/b.php',\n            'x/x',\n            'x/x/d',\n            'x/x/d/u2.php',\n        ], $finder->getIterator());\n\n        // \"x/d\" directory must be pruned early\n        // \"x/x/d\" directory must not be pruned\n        $this->assertSame([\n            ['x', 'is_dir', true],\n            ['x', 'list_dir_open', ['a.php', 'b.php', 'd', 'x']],\n            ['x/a.php', 'is_dir', false],\n            ['x/a.php', 'exclude_filter', true],\n            ['x/b.php', 'is_dir', false],\n            ['x/b.php', 'exclude_filter', true],\n            ['x/d', 'is_dir', true],\n            ['x/d', 'exclude_filter', false],\n            ['x/x', 'is_dir', true],\n            ['x/x', 'exclude_filter', true], // from ExcludeDirectoryFilterIterator::accept() (prune directory filter)\n            ['x/x', 'exclude_filter', true], // from CustomFilterIterator::accept() (regular filter)\n            ['x/x', 'list_dir_open', ['d']],\n            ['x/x/d', 'is_dir', true],\n            ['x/x/d', 'exclude_filter', true],\n            ['x/x/d', 'list_dir_open', ['u2.php']],\n            ['x/x/d/u2.php', 'is_dir', false],\n            ['x/x/d/u2.php', 'exclude_filter', true],\n        ], $this->vfsLog);\n    }\n\n    public function testFollowLinks()\n    {\n        if ('\\\\' == \\DIRECTORY_SEPARATOR) {\n            $this->markTestSkipped('symlinks are not supported on Windows');\n        }\n\n        $finder = $this->buildFinder();\n        $this->assertSame($finder, $finder->followLinks());\n        $this->assertIterator($this->toAbsolute([\n            'foo',\n            'foo/bar.tmp',\n            'test.php',\n            'test.py',\n            'toto',\n            'foo bar',\n            'qux',\n            'qux/baz_100_1.py',\n            'zebulon.php',\n            'Zephire.php',\n            'qux/baz_1_2.py',\n            'qux_0_1.php',\n            'qux_1000_1.php',\n            'qux_1002_0.php',\n            'qux_10_2.php',\n            'qux_12_0.php',\n            'qux_2_0.php',\n        ]), $finder->in(self::$tmpDir)->getIterator());\n    }\n\n    public function testUseUnixPaths()\n    {\n        $fixturesDirectory = __DIR__.\\DIRECTORY_SEPARATOR.'Fixtures';\n\n        // Fix __DIR__ on Windows giving us backslashes.\n        $fixturesDirectory = str_replace('\\\\', '/', $fixturesDirectory);\n\n        $finder = $this->buildFinder();\n        $this->assertSame($finder, $finder->useUnixPaths());\n        foreach ($finder->in($fixturesDirectory) as $file) {\n            $this->assertStringNotContainsString('\\\\', $file->getPathname(), 'Paths should be in UNIX style.');\n        }\n    }\n\n    public function testIn()\n    {\n        $finder = $this->buildFinder();\n        $iterator = $finder->files()->name('*.php')->depth('< 1')->in([self::$tmpDir, __DIR__])->getIterator();\n\n        $expected = [\n            self::$tmpDir.\\DIRECTORY_SEPARATOR.'Zephire.php',\n            self::$tmpDir.\\DIRECTORY_SEPARATOR.'test.php',\n            __DIR__.\\DIRECTORY_SEPARATOR.'GitignoreTest.php',\n            __DIR__.\\DIRECTORY_SEPARATOR.'FinderOpenBasedirTest.php',\n            __DIR__.\\DIRECTORY_SEPARATOR.'FinderTest.php',\n            __DIR__.\\DIRECTORY_SEPARATOR.'GlobTest.php',\n            self::$tmpDir.\\DIRECTORY_SEPARATOR.'qux_0_1.php',\n            self::$tmpDir.\\DIRECTORY_SEPARATOR.'qux_1000_1.php',\n            self::$tmpDir.\\DIRECTORY_SEPARATOR.'qux_1002_0.php',\n            self::$tmpDir.\\DIRECTORY_SEPARATOR.'qux_10_2.php',\n            self::$tmpDir.\\DIRECTORY_SEPARATOR.'qux_12_0.php',\n            self::$tmpDir.\\DIRECTORY_SEPARATOR.'qux_2_0.php',\n            self::$tmpDir.\\DIRECTORY_SEPARATOR.'zebulon.php',\n        ];\n\n        $this->assertIterator($expected, $iterator);\n    }\n\n    public function testInWithNonExistentDirectory()\n    {\n        $this->expectException(DirectoryNotFoundException::class);\n        $finder = new Finder();\n        $finder->in('foobar');\n    }\n\n    public function testInWithNonExistentDirectoryLegacyException()\n    {\n        $this->expectException(\\InvalidArgumentException::class);\n        $finder = new Finder();\n        $finder->in('foobar');\n    }\n\n    public function testInWithGlob()\n    {\n        $finder = $this->buildFinder();\n        $finder->in([__DIR__.'/Fixtures/*/B/C/', __DIR__.'/Fixtures/*/*/B/C/'])->getIterator();\n\n        $this->assertIterator($this->toAbsoluteFixtures(['A/B/C/abc.dat', 'copy/A/B/C/abc.dat.copy']), $finder);\n    }\n\n    public function testInWithNonDirectoryGlob()\n    {\n        $this->expectException(\\InvalidArgumentException::class);\n        $finder = new Finder();\n        $finder->in(__DIR__.'/Fixtures/A/a*');\n    }\n\n    public function testInWithGlobBrace()\n    {\n        if (!\\defined('GLOB_BRACE')) {\n            $this->markTestSkipped('Glob brace is not supported on this system.');\n        }\n\n        $finder = $this->buildFinder();\n        $finder->in([__DIR__.'/Fixtures/{A,copy/A}/B/C'])->getIterator();\n\n        $this->assertIterator($this->toAbsoluteFixtures(['A/B/C/abc.dat', 'copy/A/B/C/abc.dat.copy']), $finder);\n    }\n\n    public function testGetIteratorWithoutIn()\n    {\n        $this->expectException(\\LogicException::class);\n        $finder = Finder::create();\n        $finder->getIterator();\n    }\n\n    public function testGetIterator()\n    {\n        $finder = $this->buildFinder();\n        $dirs = [];\n        foreach ($finder->directories()->in(self::$tmpDir) as $dir) {\n            $dirs[] = (string) $dir;\n        }\n\n        $expected = $this->toAbsolute(['foo', 'qux', 'toto']);\n\n        sort($dirs);\n        sort($expected);\n\n        $this->assertEquals($expected, $dirs, 'implements the \\IteratorAggregate interface');\n\n        $finder = $this->buildFinder();\n        $this->assertEquals(3, iterator_count($finder->directories()->in(self::$tmpDir)), 'implements the \\IteratorAggregate interface');\n\n        $finder = $this->buildFinder();\n        $a = iterator_to_array($finder->directories()->in(self::$tmpDir));\n        $a = array_values(array_map('strval', $a));\n        sort($a);\n        $this->assertEquals($expected, $a, 'implements the \\IteratorAggregate interface');\n    }\n\n    public function testRelativePath()\n    {\n        $finder = $this->buildFinder()->in(self::$tmpDir);\n\n        $paths = [];\n\n        foreach ($finder as $file) {\n            $paths[] = $file->getRelativePath();\n        }\n\n        $ref = ['', '', '', '', '', '', '', '', '', '', '', '', '', 'foo', 'qux', 'qux', ''];\n\n        sort($ref);\n        sort($paths);\n\n        $this->assertEquals($ref, $paths);\n    }\n\n    public function testRelativePathname()\n    {\n        $finder = $this->buildFinder()->in(self::$tmpDir)->sortByName();\n\n        $paths = [];\n\n        foreach ($finder as $file) {\n            $paths[] = $file->getRelativePathname();\n        }\n\n        $ref = [\n            'Zephire.php',\n            'test.php',\n            'toto',\n            'test.py',\n            'foo',\n            'foo'.\\DIRECTORY_SEPARATOR.'bar.tmp',\n            'foo bar',\n            'qux',\n            'qux'.\\DIRECTORY_SEPARATOR.'baz_100_1.py',\n            'qux'.\\DIRECTORY_SEPARATOR.'baz_1_2.py',\n            'qux_0_1.php',\n            'qux_1000_1.php',\n            'qux_1002_0.php',\n            'qux_10_2.php',\n            'qux_12_0.php',\n            'qux_2_0.php',\n            'zebulon.php',\n        ];\n\n        sort($paths);\n        sort($ref);\n\n        $this->assertEquals($ref, $paths);\n    }\n\n    public function testGetFilenameWithoutExtension()\n    {\n        $finder = $this->buildFinder()->in(self::$tmpDir)->sortByName();\n\n        $fileNames = [];\n\n        foreach ($finder as $file) {\n            $fileNames[] = $file->getFilenameWithoutExtension();\n        }\n\n        $ref = [\n            'Zephire',\n            'test',\n            'toto',\n            'test',\n            'foo',\n            'bar',\n            'foo bar',\n            'qux',\n            'baz_100_1',\n            'baz_1_2',\n            'qux_0_1',\n            'qux_1000_1',\n            'qux_1002_0',\n            'qux_10_2',\n            'qux_12_0',\n            'qux_2_0',\n            'zebulon',\n        ];\n\n        sort($fileNames);\n        sort($ref);\n\n        $this->assertEquals($ref, $fileNames);\n    }\n\n    public function testAppendWithAFinder()\n    {\n        $finder = $this->buildFinder();\n        $finder->files()->in(self::$tmpDir.\\DIRECTORY_SEPARATOR.'foo');\n\n        $finder1 = $this->buildFinder();\n        $finder1->directories()->in(self::$tmpDir);\n\n        $finder = $finder->append($finder1);\n\n        $this->assertIterator($this->toAbsolute(['foo', 'foo/bar.tmp', 'qux', 'toto']), $finder->getIterator());\n    }\n\n    public function testAppendWithAnArray()\n    {\n        $finder = $this->buildFinder();\n        $finder->files()->in(self::$tmpDir.\\DIRECTORY_SEPARATOR.'foo');\n\n        $finder->append($this->toAbsolute(['foo', 'toto']));\n\n        $this->assertIterator($this->toAbsolute(['foo', 'foo/bar.tmp', 'toto']), $finder->getIterator());\n    }\n\n    public function testAppendStandardizesItemsToBeSymfonySplFileInfo()\n    {\n        $finder1 = $this->buildFinder();\n        $finder1->files()->in(self::$tmpDir.\\DIRECTORY_SEPARATOR.'foo');\n\n        $finder2 = $this->buildFinder();\n        $finder2->directories()->in(self::$tmpDir);\n\n        $finder1->append($finder2);\n        $finder1->append($this->toAbsolute(['foo']));\n        $finder1->append(array_map(static fn ($item) => new \\SplFileInfo($item), $this->toAbsolute(['toto'])));\n\n        foreach ($finder1 as $item) {\n            $this->assertInstanceOf(SplFileInfo::class, $item);\n        }\n    }\n\n    public function testRelativePathWithoutAppend()\n    {\n        $this->setupVfsProvider([\n            'a' => [\n                'a1' => '',\n                'a2' => '',\n                'b' => [\n                    'b1' => '',\n                    'b2' => '',\n                    'c' => [\n                        'c1' => '',\n                        'c2' => '',\n                    ],\n                ],\n            ],\n        ]);\n\n        $dir = $this->vfsScheme.'://';\n\n        $finder = Finder::create()->sortByName()->in($dir.'a/b');\n        $this->assertSame(\n            [\n                ['key' => $dir.'a/b'.\\DIRECTORY_SEPARATOR.'b1', 'relativePathname' => 'b1'],\n                ['key' => $dir.'a/b'.\\DIRECTORY_SEPARATOR.'b2', 'relativePathname' => 'b2'],\n                ['key' => $dir.'a/b'.\\DIRECTORY_SEPARATOR.'c', 'relativePathname' => 'c'],\n                ['key' => $dir.'a/b'.\\DIRECTORY_SEPARATOR.'c'.\\DIRECTORY_SEPARATOR.'c1', 'relativePathname' => 'c'.\\DIRECTORY_SEPARATOR.'c1'],\n                ['key' => $dir.'a/b'.\\DIRECTORY_SEPARATOR.'c'.\\DIRECTORY_SEPARATOR.'c2', 'relativePathname' => 'c'.\\DIRECTORY_SEPARATOR.'c2'],\n            ],\n            self::formatForAssert($finder),\n        );\n    }\n\n    public function testRelativePathWithAppendedFinderForParentDirectory()\n    {\n        $this->setupVfsProvider([\n            'a' => [\n                'a1' => '',\n                'a2' => '',\n                'b' => [\n                    'b1' => '',\n                    'b2' => '',\n                    'c' => [\n                        'c1' => '',\n                        'c2' => '',\n                    ],\n                ],\n            ],\n        ]);\n\n        $dir = $this->vfsScheme.'://';\n\n        $finder = Finder::create()->sortByName(true)->in($dir.'a/b');\n        $finder->append(Finder::create()->in($dir.'a'));\n\n        $expected = [\n            ['key' => $dir.'a'.\\DIRECTORY_SEPARATOR.'a1', 'relativePathname' => 'a1'],\n            ['key' => $dir.'a'.\\DIRECTORY_SEPARATOR.'a2', 'relativePathname' => 'a2'],\n            ['key' => $dir.'a'.\\DIRECTORY_SEPARATOR.'b', 'relativePathname' => 'b'],\n            ['key' => $dir.'a/b'.\\DIRECTORY_SEPARATOR.'b1', 'relativePathname' => 'b1'],\n            ['key' => $dir.'a'.\\DIRECTORY_SEPARATOR.'b'.\\DIRECTORY_SEPARATOR.'b1', 'relativePathname' => 'b'.\\DIRECTORY_SEPARATOR.'b1'],\n            ['key' => $dir.'a/b'.\\DIRECTORY_SEPARATOR.'b2', 'relativePathname' => 'b2'],\n            ['key' => $dir.'a'.\\DIRECTORY_SEPARATOR.'b'.\\DIRECTORY_SEPARATOR.'b2', 'relativePathname' => 'b'.\\DIRECTORY_SEPARATOR.'b2'],\n            ['key' => $dir.'a/b'.\\DIRECTORY_SEPARATOR.'c', 'relativePathname' => 'c'],\n            ['key' => $dir.'a'.\\DIRECTORY_SEPARATOR.'b'.\\DIRECTORY_SEPARATOR.'c', 'relativePathname' => 'b'.\\DIRECTORY_SEPARATOR.'c'],\n            ['key' => $dir.'a/b'.\\DIRECTORY_SEPARATOR.'c'.\\DIRECTORY_SEPARATOR.'c1', 'relativePathname' => 'c'.\\DIRECTORY_SEPARATOR.'c1'],\n            ['key' => $dir.'a'.\\DIRECTORY_SEPARATOR.'b'.\\DIRECTORY_SEPARATOR.'c'.\\DIRECTORY_SEPARATOR.'c1', 'relativePathname' => 'b'.\\DIRECTORY_SEPARATOR.'c'.\\DIRECTORY_SEPARATOR.'c1'],\n            ['key' => $dir.'a/b'.\\DIRECTORY_SEPARATOR.'c'.\\DIRECTORY_SEPARATOR.'c2', 'relativePathname' => 'c'.\\DIRECTORY_SEPARATOR.'c2'],\n            ['key' => $dir.'a'.\\DIRECTORY_SEPARATOR.'b'.\\DIRECTORY_SEPARATOR.'c'.\\DIRECTORY_SEPARATOR.'c2', 'relativePathname' => 'b'.\\DIRECTORY_SEPARATOR.'c'.\\DIRECTORY_SEPARATOR.'c2'],\n        ];\n\n        if ('\\\\' === \\DIRECTORY_SEPARATOR) {\n            usort($expected, static fn ($a, $b) => $a['key'] <=> $b['key']);\n        }\n\n        $this->assertSame($expected, self::formatForAssert($finder));\n    }\n\n    public function testRelativePathWithAppendedFinderForChildDirectory()\n    {\n        $this->setupVfsProvider([\n            'a' => [\n                'a1' => '',\n                'a2' => '',\n                'b' => [\n                    'b1' => '',\n                    'b2' => '',\n                    'c' => [\n                        'c1' => '',\n                        'c2' => '',\n                    ],\n                ],\n            ],\n        ]);\n\n        $dir = $this->vfsScheme.'://';\n\n        $finder = Finder::create()->sortByName(true)->in($dir.'a/b');\n        $finder->append(Finder::create()->in($dir.'a/b/c'));\n\n        $expected = [\n            ['key' => $dir.'a/b'.\\DIRECTORY_SEPARATOR.'b1', 'relativePathname' => 'b1'],\n            ['key' => $dir.'a/b'.\\DIRECTORY_SEPARATOR.'b2', 'relativePathname' => 'b2'],\n            ['key' => $dir.'a/b'.\\DIRECTORY_SEPARATOR.'c', 'relativePathname' => 'c'],\n            ['key' => $dir.'a/b'.\\DIRECTORY_SEPARATOR.'c'.\\DIRECTORY_SEPARATOR.'c1', 'relativePathname' => 'c'.\\DIRECTORY_SEPARATOR.'c1'],\n            ['key' => $dir.'a/b/c'.\\DIRECTORY_SEPARATOR.'c1', 'relativePathname' => 'c1'],\n            ['key' => $dir.'a/b'.\\DIRECTORY_SEPARATOR.'c'.\\DIRECTORY_SEPARATOR.'c2', 'relativePathname' => 'c'.\\DIRECTORY_SEPARATOR.'c2'],\n            ['key' => $dir.'a/b/c'.\\DIRECTORY_SEPARATOR.'c2', 'relativePathname' => 'c2'],\n        ];\n\n        if ('\\\\' === \\DIRECTORY_SEPARATOR) {\n            usort($expected, static fn ($a, $b) => $a['key'] <=> $b['key']);\n        }\n\n        $this->assertSame($expected, self::formatForAssert($finder));\n    }\n\n    public function testRelativePathWithAppendedPaths()\n    {\n        $this->setupVfsProvider([\n            'a' => [\n                'a1' => '',\n                'a2' => '',\n                'b' => [\n                    'b1' => '',\n                    'b2' => '',\n                    'c' => [\n                        'c1' => '',\n                        'c2' => '',\n                    ],\n                ],\n            ],\n        ]);\n\n        $dir = $this->vfsScheme.'://';\n\n        $finder = Finder::create()->sortByName(true)->in($dir.'a/b');\n        $finder->append([$dir.'a/a1', $dir.'a/b/c/c1']);\n\n        $expected = [\n            ['key' => $dir.'a/a1', 'relativePathname' => $dir.'a/a1'],\n            ['key' => $dir.'a/b'.\\DIRECTORY_SEPARATOR.'b1', 'relativePathname' => 'b1'],\n            ['key' => $dir.'a/b'.\\DIRECTORY_SEPARATOR.'b2', 'relativePathname' => 'b2'],\n            ['key' => $dir.'a/b'.\\DIRECTORY_SEPARATOR.'c', 'relativePathname' => 'c'],\n            ['key' => $dir.'a/b'.\\DIRECTORY_SEPARATOR.'c'.\\DIRECTORY_SEPARATOR.'c1', 'relativePathname' => 'c'.\\DIRECTORY_SEPARATOR.'c1'],\n            ['key' => $dir.'a/b/c/c1', 'relativePathname' => $dir.'a/b/c/c1'],\n            ['key' => $dir.'a/b'.\\DIRECTORY_SEPARATOR.'c'.\\DIRECTORY_SEPARATOR.'c2', 'relativePathname' => 'c'.\\DIRECTORY_SEPARATOR.'c2'],\n        ];\n\n        if ('\\\\' === \\DIRECTORY_SEPARATOR) {\n            usort($expected, static fn ($a, $b) => $a['key'] <=> $b['key']);\n        }\n\n        $this->assertSame($expected, self::formatForAssert($finder));\n    }\n\n    public function testRelativePathWithAppendedOnEmptyFinder()\n    {\n        $this->setupVfsProvider([\n            'a' => [\n                'a1' => '',\n                'a2' => '',\n                'b' => [\n                    'b1' => '',\n                    'b2' => '',\n                    'c' => [\n                        'c1' => '',\n                        'c2' => '',\n                    ],\n                ],\n            ],\n        ]);\n\n        $dir = $this->vfsScheme.'://';\n\n        $finder = Finder::create()->sortByName();\n        $finder->append([$dir.'a/a1']);\n        $this->assertSame(\n            [\n                ['key' => $dir.'a/a1', 'relativePathname' => $dir.'a/a1'],\n            ],\n            self::formatForAssert($finder),\n        );\n    }\n\n    public function testAppendReturnsAFinder()\n    {\n        $this->assertInstanceOf(Finder::class, Finder::create()->append([]));\n    }\n\n    public function testAppendEmptyIterableAllowsIteration()\n    {\n        $finder = Finder::create()->files()->name('*.php')->append([]);\n\n        $this->assertSame([], iterator_to_array($finder->getIterator()));\n    }\n\n    public function testAppendDoesNotRequireIn()\n    {\n        $finder = $this->buildFinder();\n        $finder->in(self::$tmpDir.\\DIRECTORY_SEPARATOR.'foo');\n\n        $finder1 = Finder::create()->append($finder);\n\n        $this->assertIterator(iterator_to_array($finder->getIterator()), $finder1->getIterator());\n    }\n\n    public function testMultipleAppendCallsWithSorting()\n    {\n        $finder = $this->buildFinder()\n            ->sortByName()\n            ->append([self::$tmpDir.\\DIRECTORY_SEPARATOR.'qux_1000_1.php'])\n            ->append([self::$tmpDir.\\DIRECTORY_SEPARATOR.'qux_1002_0.php'])\n        ;\n\n        $this->assertOrderedIterator($this->toAbsolute(['qux_1000_1.php', 'qux_1002_0.php']), $finder->getIterator());\n    }\n\n    public function testCountDirectories()\n    {\n        $directory = Finder::create()->directories()->in(self::$tmpDir);\n        $i = 0;\n\n        foreach ($directory as $dir) {\n            ++$i;\n        }\n\n        $this->assertCount($i, $directory);\n    }\n\n    public function testCountFiles()\n    {\n        $files = Finder::create()->files()->in(__DIR__.\\DIRECTORY_SEPARATOR.'Fixtures');\n        $i = 0;\n\n        foreach ($files as $file) {\n            ++$i;\n        }\n\n        $this->assertCount($i, $files);\n    }\n\n    public function testCountWithoutIn()\n    {\n        $this->expectException(\\LogicException::class);\n        $finder = Finder::create()->files();\n        \\count($finder);\n    }\n\n    public function testHasResults()\n    {\n        $finder = $this->buildFinder();\n        $finder->in(__DIR__);\n        $this->assertTrue($finder->hasResults());\n    }\n\n    public function testNoResults()\n    {\n        $finder = $this->buildFinder();\n        $finder->in(__DIR__)->name('DoesNotExist');\n        $this->assertFalse($finder->hasResults());\n    }\n\n    #[DataProvider('getContainsTestData')]\n    public function testContains($matchPatterns, $noMatchPatterns, $expected)\n    {\n        $finder = $this->buildFinder();\n        $finder->in(__DIR__.\\DIRECTORY_SEPARATOR.'Fixtures')\n            ->name('*.txt')->sortByName()\n            ->contains($matchPatterns)\n            ->notContains($noMatchPatterns);\n\n        $this->assertIterator($this->toAbsoluteFixtures($expected), $finder);\n    }\n\n    public function testContainsOnDirectory()\n    {\n        $finder = $this->buildFinder();\n        $finder->in(__DIR__)\n            ->directories()\n            ->name('Fixtures')\n            ->contains('abc');\n        $this->assertIterator([], $finder);\n    }\n\n    public function testNotContainsOnDirectory()\n    {\n        $finder = $this->buildFinder();\n        $finder->in(__DIR__)\n            ->directories()\n            ->name('Fixtures')\n            ->notContains('abc');\n        $this->assertIterator([], $finder);\n    }\n\n    /**\n     * Searching in multiple locations involves AppendIterator which does an unnecessary rewind which leaves FilterIterator\n     * with inner FilesystemIterator in an invalid state.\n     *\n     * @see https://bugs.php.net/68557\n     */\n    public function testMultipleLocations()\n    {\n        $locations = [\n            self::$tmpDir.'/',\n            self::$tmpDir.'/toto/',\n        ];\n\n        // it is expected that there are test.py test.php in the tmpDir\n        $finder = new Finder();\n        $finder->in($locations)\n            // the default flag IGNORE_DOT_FILES fixes the problem indirectly\n            // so we set it to false for better isolation\n            ->ignoreDotFiles(false)\n            ->depth('< 1')->name('test.php');\n\n        $this->assertCount(1, $finder);\n    }\n\n    /**\n     * Searching in multiple locations with sub directories involves\n     * AppendIterator which does an unnecessary rewind which leaves\n     * FilterIterator with inner FilesystemIterator in an invalid state.\n     *\n     * @see https://bugs.php.net/68557\n     */\n    public function testMultipleLocationsWithSubDirectories()\n    {\n        $locations = [\n            __DIR__.'/Fixtures/one',\n            self::$tmpDir.\\DIRECTORY_SEPARATOR.'toto',\n        ];\n\n        $finder = $this->buildFinder();\n        $finder->in($locations)->depth('< 10')->name('*.neon');\n\n        $expected = [\n            __DIR__.'/Fixtures/one'.\\DIRECTORY_SEPARATOR.'b'.\\DIRECTORY_SEPARATOR.'c.neon',\n            __DIR__.'/Fixtures/one'.\\DIRECTORY_SEPARATOR.'b'.\\DIRECTORY_SEPARATOR.'d.neon',\n        ];\n\n        $this->assertIterator($expected, $finder);\n        $this->assertIteratorInForeach($expected, $finder);\n    }\n\n    /**\n     * Iterator keys must be the file pathname.\n     */\n    public function testIteratorKeys()\n    {\n        $finder = $this->buildFinder()->in(self::$tmpDir);\n        foreach ($finder as $key => $file) {\n            $this->assertEquals($file->getPathname(), $key);\n        }\n    }\n\n    public function testRegexSpecialCharsLocationWithPathRestrictionContainingStartFlag()\n    {\n        $finder = $this->buildFinder();\n        $finder->in(__DIR__.\\DIRECTORY_SEPARATOR.'Fixtures'.\\DIRECTORY_SEPARATOR.'r+e.gex[c]a(r)s')\n            ->path('/^dir/');\n\n        $expected = ['r+e.gex[c]a(r)s'.\\DIRECTORY_SEPARATOR.'dir', 'r+e.gex[c]a(r)s'.\\DIRECTORY_SEPARATOR.'dir'.\\DIRECTORY_SEPARATOR.'bar.dat'];\n        $this->assertIterator($this->toAbsoluteFixtures($expected), $finder);\n    }\n\n    public static function getContainsTestData()\n    {\n        return [\n            ['', '', []],\n            ['foo', 'bar', []],\n            ['', 'foobar', ['dolor.txt', 'ipsum.txt', 'lorem.txt']],\n            ['lorem ipsum dolor sit amet', 'foobar', ['lorem.txt']],\n            ['sit', 'bar', ['dolor.txt', 'ipsum.txt', 'lorem.txt']],\n            ['dolor sit amet', '@^L@m', ['dolor.txt', 'ipsum.txt']],\n            ['/^lorem ipsum dolor sit amet$/m', 'foobar', ['lorem.txt']],\n            ['lorem', 'foobar', ['lorem.txt']],\n            ['', 'lorem', ['dolor.txt', 'ipsum.txt']],\n            ['ipsum dolor sit amet', '/^IPSUM/m', ['lorem.txt']],\n            [['lorem', 'dolor'], [], ['lorem.txt', 'ipsum.txt', 'dolor.txt']],\n            ['', ['lorem', 'ipsum'], ['dolor.txt']],\n        ];\n    }\n\n    public static function getRegexNameTestData()\n    {\n        return [\n            ['~.*t\\\\.p.+~i'],\n            ['~t.*s~i'],\n        ];\n    }\n\n    #[DataProvider('getTestPathData')]\n    public function testPath($matchPatterns, $noMatchPatterns, array $expected)\n    {\n        $finder = $this->buildFinder();\n        $finder->in(__DIR__.\\DIRECTORY_SEPARATOR.'Fixtures')\n            ->path($matchPatterns)\n            ->notPath($noMatchPatterns);\n\n        $this->assertIterator($this->toAbsoluteFixtures($expected), $finder);\n    }\n\n    public static function getTestPathData()\n    {\n        return [\n            ['', '', []],\n            ['/^A\\/B\\/C/', '/C$/',\n                ['A'.\\DIRECTORY_SEPARATOR.'B'.\\DIRECTORY_SEPARATOR.'C'.\\DIRECTORY_SEPARATOR.'abc.dat'],\n            ],\n            ['/^A\\/B/', 'foobar',\n                [\n                    'A'.\\DIRECTORY_SEPARATOR.'B',\n                    'A'.\\DIRECTORY_SEPARATOR.'B'.\\DIRECTORY_SEPARATOR.'C',\n                    'A'.\\DIRECTORY_SEPARATOR.'B'.\\DIRECTORY_SEPARATOR.'ab.dat',\n                    'A'.\\DIRECTORY_SEPARATOR.'B'.\\DIRECTORY_SEPARATOR.'C'.\\DIRECTORY_SEPARATOR.'abc.dat',\n                ],\n            ],\n            ['A/B/C', 'foobar',\n                [\n                    'A'.\\DIRECTORY_SEPARATOR.'B'.\\DIRECTORY_SEPARATOR.'C',\n                    'A'.\\DIRECTORY_SEPARATOR.'B'.\\DIRECTORY_SEPARATOR.'C'.\\DIRECTORY_SEPARATOR.'abc.dat',\n                    'copy'.\\DIRECTORY_SEPARATOR.'A'.\\DIRECTORY_SEPARATOR.'B'.\\DIRECTORY_SEPARATOR.'C',\n                    'copy'.\\DIRECTORY_SEPARATOR.'A'.\\DIRECTORY_SEPARATOR.'B'.\\DIRECTORY_SEPARATOR.'C'.\\DIRECTORY_SEPARATOR.'abc.dat.copy',\n                ],\n            ],\n            ['A/B', 'foobar',\n                [\n                    // dirs\n                    'A'.\\DIRECTORY_SEPARATOR.'B',\n                    'A'.\\DIRECTORY_SEPARATOR.'B'.\\DIRECTORY_SEPARATOR.'C',\n                    'copy'.\\DIRECTORY_SEPARATOR.'A'.\\DIRECTORY_SEPARATOR.'B',\n                    'copy'.\\DIRECTORY_SEPARATOR.'A'.\\DIRECTORY_SEPARATOR.'B'.\\DIRECTORY_SEPARATOR.'C',\n                    // files\n                    'A'.\\DIRECTORY_SEPARATOR.'B'.\\DIRECTORY_SEPARATOR.'ab.dat',\n                    'A'.\\DIRECTORY_SEPARATOR.'B'.\\DIRECTORY_SEPARATOR.'C'.\\DIRECTORY_SEPARATOR.'abc.dat',\n                    'copy'.\\DIRECTORY_SEPARATOR.'A'.\\DIRECTORY_SEPARATOR.'B'.\\DIRECTORY_SEPARATOR.'ab.dat.copy',\n                    'copy'.\\DIRECTORY_SEPARATOR.'A'.\\DIRECTORY_SEPARATOR.'B'.\\DIRECTORY_SEPARATOR.'C'.\\DIRECTORY_SEPARATOR.'abc.dat.copy',\n                ],\n            ],\n            ['/^with space\\//', 'foobar',\n                [\n                    'with space'.\\DIRECTORY_SEPARATOR.'foo.txt',\n                ],\n            ],\n            [\n                '/^A/',\n                ['a.dat', 'abc.dat'],\n                [\n                    'A',\n                    'A'.\\DIRECTORY_SEPARATOR.'B',\n                    'A'.\\DIRECTORY_SEPARATOR.'B'.\\DIRECTORY_SEPARATOR.'C',\n                    'A'.\\DIRECTORY_SEPARATOR.'B'.\\DIRECTORY_SEPARATOR.'ab.dat',\n                ],\n            ],\n            [\n                ['/^A/', 'one'],\n                'foobar',\n                [\n                    'A',\n                    'A'.\\DIRECTORY_SEPARATOR.'B',\n                    'A'.\\DIRECTORY_SEPARATOR.'B'.\\DIRECTORY_SEPARATOR.'C',\n                    'A'.\\DIRECTORY_SEPARATOR.'a.dat',\n                    'A'.\\DIRECTORY_SEPARATOR.'B'.\\DIRECTORY_SEPARATOR.'ab.dat',\n                    'A'.\\DIRECTORY_SEPARATOR.'B'.\\DIRECTORY_SEPARATOR.'C'.\\DIRECTORY_SEPARATOR.'abc.dat',\n                    'one',\n                    'one'.\\DIRECTORY_SEPARATOR.'a',\n                    'one'.\\DIRECTORY_SEPARATOR.'b',\n                    'one'.\\DIRECTORY_SEPARATOR.'b'.\\DIRECTORY_SEPARATOR.'c.neon',\n                    'one'.\\DIRECTORY_SEPARATOR.'b'.\\DIRECTORY_SEPARATOR.'d.neon',\n                ],\n            ],\n        ];\n    }\n\n    public function testAccessDeniedException()\n    {\n        if ('\\\\' === \\DIRECTORY_SEPARATOR) {\n            $this->markTestSkipped('chmod is not supported on Windows');\n        }\n\n        $finder = $this->buildFinder();\n        $finder->files()->in(self::$tmpDir);\n\n        // make 'foo' directory non-readable\n        $testDir = self::$tmpDir.\\DIRECTORY_SEPARATOR.'foo';\n        chmod($testDir, 0o333);\n\n        if (false === $couldRead = is_readable($testDir)) {\n            try {\n                $this->assertIterator($this->toAbsolute(['foo bar', 'test.php', 'test.py']), $finder->getIterator());\n                $this->fail('Finder should throw an exception when opening a non-readable directory.');\n            } catch (\\Exception $e) {\n                $expectedExceptionClass = 'Symfony\\\\Component\\\\Finder\\\\Exception\\\\AccessDeniedException';\n                if ($e instanceof ExpectationFailedException) {\n                    $this->fail(\\sprintf(\"Expected exception:\\n%s\\nGot:\\n%s\\nWith comparison failure:\\n%s\", $expectedExceptionClass, 'PHPUnit\\Framework\\ExpectationFailedException', $e->getComparisonFailure()->getExpectedAsString()));\n                }\n\n                $this->assertInstanceOf($expectedExceptionClass, $e);\n            }\n        }\n\n        // restore original permissions\n        chmod($testDir, 0o777);\n        clearstatcache(true, $testDir);\n\n        if ($couldRead) {\n            $this->markTestSkipped('could read test files while test requires unreadable');\n        }\n    }\n\n    public function testIgnoredAccessDeniedException()\n    {\n        if ('\\\\' === \\DIRECTORY_SEPARATOR) {\n            $this->markTestSkipped('chmod is not supported on Windows');\n        }\n\n        $finder = $this->buildFinder();\n        $finder->files()->ignoreUnreadableDirs()->in(self::$tmpDir);\n\n        // make 'foo' directory non-readable\n        $testDir = self::$tmpDir.\\DIRECTORY_SEPARATOR.'foo';\n        chmod($testDir, 0o333);\n\n        if (false === ($couldRead = is_readable($testDir))) {\n            $this->assertIterator($this->toAbsolute([\n                'foo bar',\n                'test.php',\n                'test.py',\n                'qux/baz_100_1.py',\n                'zebulon.php',\n                'Zephire.php',\n                'qux/baz_1_2.py',\n                'qux_0_1.php',\n                'qux_1000_1.php',\n                'qux_1002_0.php',\n                'qux_10_2.php',\n                'qux_12_0.php',\n                'qux_2_0.php',\n            ]\n            ), $finder->getIterator());\n        }\n\n        // restore original permissions\n        chmod($testDir, 0o777);\n        clearstatcache(true, $testDir);\n\n        if ($couldRead) {\n            $this->markTestSkipped('could read test files while test requires unreadable');\n        }\n    }\n\n    protected function buildFinder()\n    {\n        return Finder::create()->exclude('gitignore');\n    }\n\n    private static function formatForAssert(Finder $finder): array\n    {\n        $data = [];\n        foreach ($finder as $key => $value) {\n            $data[] = ['key' => $key, 'relativePathname' => $value->getRelativePathname()];\n        }\n\n        return $data;\n    }\n}\n"
  },
  {
    "path": "Tests/Fixtures/.dot/a",
    "content": ""
  },
  {
    "path": "Tests/Fixtures/.dot/b/c.neon",
    "content": ""
  },
  {
    "path": "Tests/Fixtures/.dot/b/d.neon",
    "content": ""
  },
  {
    "path": "Tests/Fixtures/copy/A/B/C/abc.dat.copy",
    "content": ""
  },
  {
    "path": "Tests/Fixtures/copy/A/B/ab.dat.copy",
    "content": ""
  },
  {
    "path": "Tests/Fixtures/copy/A/a.dat.copy",
    "content": ""
  },
  {
    "path": "Tests/Fixtures/dolor.txt",
    "content": "dolor sit amet\nDOLOR SIT AMET"
  },
  {
    "path": "Tests/Fixtures/gitignore/.gitignore",
    "content": "c.txt\n"
  },
  {
    "path": "Tests/Fixtures/gitignore/git_root/search_root/.gitignore",
    "content": "/a.txt\n"
  },
  {
    "path": "Tests/Fixtures/gitignore/git_root/search_root/b.txt",
    "content": ""
  },
  {
    "path": "Tests/Fixtures/gitignore/git_root/search_root/dir/.gitignore",
    "content": "/b.txt\n"
  },
  {
    "path": "Tests/Fixtures/gitignore/git_root/search_root/dir/a.txt",
    "content": ""
  },
  {
    "path": "Tests/Fixtures/gitignore/search_root/.gitignore",
    "content": "/a.txt\n"
  },
  {
    "path": "Tests/Fixtures/gitignore/search_root/a.txt",
    "content": ""
  },
  {
    "path": "Tests/Fixtures/gitignore/search_root/b.txt",
    "content": ""
  },
  {
    "path": "Tests/Fixtures/gitignore/search_root/dir/.gitignore",
    "content": "/b.txt\n"
  },
  {
    "path": "Tests/Fixtures/gitignore/search_root/dir/a.txt",
    "content": ""
  },
  {
    "path": "Tests/Fixtures/gitignore/search_root/dir/b.txt",
    "content": ""
  },
  {
    "path": "Tests/Fixtures/ipsum.txt",
    "content": "ipsum dolor sit amet\nIPSUM DOLOR SIT AMET"
  },
  {
    "path": "Tests/Fixtures/lorem.txt",
    "content": "lorem ipsum dolor sit amet\nLOREM IPSUM DOLOR SIT AMET"
  },
  {
    "path": "Tests/Fixtures/one/.dot",
    "content": ".dot"
  },
  {
    "path": "Tests/Fixtures/one/a",
    "content": ""
  },
  {
    "path": "Tests/Fixtures/one/b/c.neon",
    "content": ""
  },
  {
    "path": "Tests/Fixtures/one/b/d.neon",
    "content": ""
  },
  {
    "path": "Tests/Fixtures/with space/foo.txt",
    "content": ""
  },
  {
    "path": "Tests/GitignoreTest.php",
    "content": "<?php\n\n/*\n * This file is part of the Symfony package.\n *\n * (c) Fabien Potencier <fabien@symfony.com>\n *\n * For the full copyright and license information, please view the LICENSE\n * file that was distributed with this source code.\n */\n\nnamespace Symfony\\Component\\Finder\\Tests;\n\nuse PHPUnit\\Framework\\Attributes\\DataProvider;\nuse PHPUnit\\Framework\\TestCase;\nuse Symfony\\Component\\Finder\\Gitignore;\n\n/**\n * @author Michael Voříšek <vorismi3@fel.cvut.cz>\n */\nclass GitignoreTest extends TestCase\n{\n    #[DataProvider('provider')]\n    #[DataProvider('providerExtended')]\n    public function testToRegex(array $gitignoreLines, array $matchingCases, array $nonMatchingCases)\n    {\n        $patterns = implode(\"\\n\", $gitignoreLines);\n\n        $regex = Gitignore::toRegex($patterns);\n        $this->assertSame($regex, Gitignore::toRegex(implode(\"\\r\\n\", $gitignoreLines)));\n        $this->assertSame($regex, Gitignore::toRegex(implode(\"\\r\", $gitignoreLines)));\n\n        foreach ($matchingCases as $matchingCase) {\n            $this->assertMatchesRegularExpression(\n                $regex,\n                $matchingCase,\n                \\sprintf(\n                    \"Failed asserting path:\\n%s\\nmatches gitignore patterns:\\n%s\",\n                    preg_replace('~^~m', '    ', $matchingCase),\n                    preg_replace('~^~m', '    ', $patterns)\n                )\n            );\n        }\n\n        foreach ($nonMatchingCases as $nonMatchingCase) {\n            $this->assertDoesNotMatchRegularExpression(\n                $regex,\n                $nonMatchingCase,\n                \\sprintf(\"Failed asserting path:\\n%s\\nNOT matching gitignore patterns:\\n%s\",\n                    preg_replace('~^~m', '    ', $nonMatchingCase),\n                    preg_replace('~^~m', '    ', $patterns)\n                )\n            );\n        }\n    }\n\n    public static function provider(): array\n    {\n        $cases = [\n            [\n                [''],\n                [],\n                ['a', 'a/b', 'a/b/c', 'aa', 'm.txt', '.txt'],\n            ],\n            [\n                ['a', 'X'],\n                ['a', 'a/b', 'a/b/c', 'X', 'b/a', 'b/c/a', 'a/X', 'a/X/y', 'b/a/X/y'],\n                ['A', 'x', 'aa', 'm.txt', '.txt', 'aa/b', 'b/aa'],\n            ],\n            [\n                ['/a', 'x', 'd/'],\n                ['a', 'a/b', 'a/b/c', 'x', 'a/x', 'a/x/y', 'b/a/x/y', 'd/', 'd/u', 'e/d/', 'e/d/u'],\n                ['b/a', 'b/c/a', 'aa', 'm.txt', '.txt', 'aa/b', 'b/aa', 'e/d'],\n            ],\n            [\n                ['a/', 'x'],\n                ['a/b', 'a/b/c', 'x', 'a/x', 'a/x/y', 'b/a/x/y'],\n                ['a', 'b/a', 'b/c/a', 'aa', 'm.txt', '.txt', 'aa/b', 'b/aa'],\n            ],\n            [\n                ['*'],\n                ['a', 'a/b', 'a/b/c', 'aa', 'm.txt', '.txt'],\n                [],\n            ],\n            [\n                ['/*'],\n                ['a', 'a/b', 'a/b/c', 'aa', 'm.txt', '.txt'],\n                [],\n            ],\n            [\n                ['/a', 'm/*', 'o/**', 'p/**/', 'x**y'],\n                ['a', 'a/b', 'a/b/c', 'm/', 'o/', 'p/', 'xy', 'xuy', 'x/y', 'x/u/y', 'xu/y', 'x/uy', 'xu/uy'],\n                ['aa', 'm', 'b/m', 'b/m/', 'o', 'b/o', 'b/o/', 'p', 'b/p', 'b/p/'],\n            ],\n            [\n                ['a', '!x'],\n                ['a', 'a/b', 'a/b/c', 'b/a', 'b/c/a'],\n                ['x', 'aa', 'm.txt', '.txt', 'aa/b', 'b/aa'],\n            ],\n            [\n                ['a', '!a/', 'b', '!b/b'],\n                ['a', 'a/x', 'x/a', 'x/a/x', 'b', 'b'],\n                ['a/', 'x/a/', 'bb', 'b/b', 'bb'],\n            ],\n            [\n                ['[a-c]', 'x[C-E][][o]', 'g-h'],\n                ['a', 'b', 'c', 'xDo', 'g-h'],\n                ['A', 'xdo', 'u', 'g', 'h'],\n            ],\n            [\n                ['a?', '*/??b?'],\n                ['ax', 'x/xxbx'],\n                ['a', 'axy', 'xxax', 'x/xxax', 'x/y/xxax'],\n            ],\n            [\n                [' ', ' \\ ', '  \\  ', '/a ', '/b/c \\ '],\n                ['  ', '   ', 'x/  ', 'x/   ', 'a', 'a/x', 'b/c  '],\n                [' ', '    ', 'x/ ', 'x/    ', 'a ', 'b/c   '],\n            ],\n            [\n                ['#', ' #', '/ #', '  #', '/  #', '  \\ #', '   \\  #', 'a #', 'a  #', 'a  \\ #', 'a   \\  #'],\n                ['   ', '    ', 'a', 'a   ', 'a    '],\n                [' ', '  ', 'a ', 'a  '],\n            ],\n            [\n                [\"\\t\", \"\\t\\\\\\t\", \" \\t\\\\\\t \", \"\\t#\", \"a\\t#\", \"a\\t\\t#\", \"a \\t#\", \"a\\t\\t\\\\\\t#\", \"a \\t\\t\\\\\\t\\t#\"],\n                [\"\\t\\t\", \" \\t\\t\", 'a', \"a\\t\\t\\t\", \"a \\t\\t\\t\"],\n                [\"\\t\", \"\\t\\t \", \" \\t\\t \", \"a\\t\", 'a ', \"a \\t\", \"a\\t\\t\"],\n            ],\n            [\n                [' a', 'b ', '\\ ', 'c\\ '],\n                [' a', 'b', ' ', 'c '],\n                ['a', 'b ', 'c'],\n            ],\n            [\n                ['#a', '\\#b', '\\#/'],\n                ['#b', '#/'],\n                ['#a', 'a', 'b'],\n            ],\n            [\n                ['*', '!!', '!!*x', '\\!!b'],\n                ['a', '!!', '!!b'],\n                ['!', '!x', '!xx'],\n            ],\n            [\n                [\n                    '*',\n                    '!/bin',\n                    '!/bin/bash',\n                ],\n                ['bin/cat', 'abc/bin/cat'],\n                ['bin/bash'],\n            ],\n            [\n                ['fi#le.txt'],\n                [],\n                ['#file.txt'],\n            ],\n            [\n                [\n                    '/bin/',\n                    '/usr/local/',\n                    '!/bin/bash',\n                    '!/usr/local/bin/bash',\n                ],\n                ['bin/cat'],\n                ['bin/bash'],\n            ],\n            [\n                ['*.py[co]'],\n                ['file.pyc', 'file.pyc'],\n                ['filexpyc', 'file.pycx', 'file.py'],\n            ],\n            [\n                ['dir1/**/dir2/'],\n                ['dir1/dir2/', 'dir1/dirA/dir2/', 'dir1/dirA/dirB/dir2/'],\n                ['dir1dir2/', 'dir1xdir2/', 'dir1/xdir2/', 'dir1x/dir2/'],\n            ],\n            [\n                ['dir1/*/dir2/'],\n                ['dir1/dirA/dir2/'],\n                ['dir1/dirA/dirB/dir2/'],\n            ],\n            [\n                ['/*.php'],\n                ['file.php'],\n                ['app/file.php'],\n            ],\n            [\n                ['\\#file.txt'],\n                ['#file.txt'],\n                [],\n            ],\n            [\n                ['*.php'],\n                ['app/file.php', 'file.php'],\n                ['file.phps', 'file.phps', 'filephps'],\n            ],\n            [\n                ['app/cache/'],\n                ['app/cache/file.txt', 'app/cache/dir1/dir2/file.txt'],\n                ['a/app/cache/file.txt'],\n            ],\n            [\n                ['#IamComment', '/app/cache/'],\n                ['app/cache/file.txt', 'app/cache/subdir/ile.txt'],\n                ['a/app/cache/file.txt', '#IamComment', 'IamComment'],\n            ],\n            [\n                ['/app/cache/', '#LastLineIsComment'],\n                ['app/cache/file.txt', 'app/cache/subdir/ile.txt'],\n                ['a/app/cache/file.txt', '#LastLineIsComment', 'LastLineIsComment'],\n            ],\n            [\n                ['/app/cache/', '\\#file.txt', '#LastLineIsComment'],\n                ['app/cache/file.txt', 'app/cache/subdir/ile.txt', '#file.txt'],\n                ['a/app/cache/file.txt', '#LastLineIsComment', 'LastLineIsComment'],\n            ],\n            [\n                ['/app/cache/', '\\#file.txt', '#IamComment', 'another_file.txt'],\n                ['app/cache/file.txt', 'app/cache/subdir/ile.txt', '#file.txt', 'another_file.txt'],\n                ['a/app/cache/file.txt', 'IamComment', '#IamComment'],\n            ],\n            [\n                [\n                    '/app/**',\n                    '!/app/bin',\n                    '!/app/bin/test',\n                ],\n                ['app/test/file', 'app/bin/file'],\n                ['app/bin/test'],\n            ],\n            [\n                [\n                    '/app/*/img',\n                    '!/app/*/img/src',\n                ],\n                ['app/a/img', 'app/a/img/x', 'app/a/img/src/x'],\n                ['app/a/img/src', 'app/a/img/src/'],\n            ],\n            [\n                [\n                    'app/**/img',\n                    '!/app/**/img/src',\n                ],\n                ['app/a/img', 'app/a/img/x', 'app/a/img/src/x', 'app/a/b/img', 'app/a/b/img/x', 'app/a/b/img/src/x', 'app/a/b/c/img'],\n                ['app/a/img/src', 'app/a/b/img/src', 'app/a/c/b/img/src'],\n            ],\n            [\n                [\n                    '/*',\n                    '!/foo',\n                    '/foo/*',\n                    '!/foo/bar',\n                ],\n                ['bar', 'foo/ba', 'foo/barx', 'x/foo/bar'],\n                ['foo', 'foo/bar'],\n            ],\n            [\n                [\n                    '/example/**',\n                    '!/example/example.txt',\n                    '!/example/packages',\n                ],\n                ['example/test', 'example/example.txt2', 'example/packages/foo.yaml'],\n                ['example/example.txt', 'example/packages', 'example/packages/'],\n            ],\n            // based on https://www.atlassian.com/git/tutorials/saving-changes/gitignore\n            [\n                ['**/logs'],\n                ['logs/debug.log', 'logs/monday/foo.bar'],\n                [],\n            ],\n            [\n                ['**/logs/debug.log'],\n                ['logs/debug.log', 'build/logs/debug.log'],\n                ['logs/build/debug.log'],\n            ],\n            [\n                ['*.log'],\n                ['debug.log', 'foo.log', '.log', 'logs/debug.log'],\n                [],\n            ],\n            [\n                [\n                    '*.log',\n                    '!important.log',\n                ],\n                ['debug.log', 'trace.log'],\n                ['important.log', 'logs/important.log'],\n            ],\n            [\n                [\n                    '*.log',\n                    '!important/*.log',\n                    'trace.*',\n                ],\n                ['debug.log', 'important/trace.log'],\n                ['important/debug.log'],\n            ],\n            [\n                ['/debug.log'],\n                ['debug.log'],\n                ['logs/debug.log'],\n            ],\n            [\n                ['debug.log'],\n                ['debug.log', 'logs/debug.log'],\n                [],\n            ],\n            [\n                ['debug?.log'],\n                ['debug0.log', 'debugg.log'],\n                ['debug10.log'],\n            ],\n            [\n                ['debug[0-9].log'],\n                ['debug0.log', 'debug1.log'],\n                ['debug10.log'],\n            ],\n            [\n                ['debug[01].log'],\n                ['debug0.log', 'debug1.log'],\n                ['debug2.log', 'debug01.log'],\n            ],\n            [\n                ['debug[!01].log'],\n                ['debug2.log'],\n                ['debug0.log', 'debug1.log', 'debug01.log'],\n            ],\n            [\n                ['debug[a-z].log'],\n                ['debuga.log', 'debugb.log'],\n                ['debug1.log'],\n            ],\n            [\n                ['logs'],\n                ['logs', 'logs/debug.log', 'logs/latest/foo.bar', 'build/logs', 'build/logs/debug.log'],\n                [],\n            ],\n            [\n                ['logs/'],\n                ['logs/debug.log', 'logs/latest/foo.bar', 'build/logs/foo.bar', 'build/logs/latest/debug.log'],\n                [],\n            ],\n            [\n                [\n                    'logs/',\n                    '!logs/important.log',\n                ],\n                ['logs/debug.log'/* must be pruned on traversal 'logs/important.log' */],\n                [],\n            ],\n            [\n                ['logs/**/debug.log'],\n                ['logs/debug.log', 'logs/monday/debug.log', 'logs/monday/pm/debug.log'],\n                [],\n            ],\n            [\n                ['logs/*day/debug.log'],\n                ['logs/monday/debug.log', 'logs/tuesday/debug.log'],\n                ['logs/latest/debug.log'],\n            ],\n            [\n                ['logs/debug.log'],\n                ['logs/debug.log'],\n                ['debug.log', 'build/logs/debug.log'],\n            ],\n            [\n                ['*/vendor/*'],\n                ['a/vendor/', 'a/vendor/b', 'a/vendor/b/c'],\n                ['a', 'vendor', 'vendor/', 'a/vendor', 'a/b/vendor', 'a/b/vendor/c'],\n            ],\n            [\n                ['**/vendor/**'],\n                ['vendor/', 'vendor/a', 'vendor/a/b', 'a/b/vendor/c/d'],\n                ['a', 'vendor', 'a/vendor', 'a/b/vendor'],\n            ],\n            [\n                ['***/***/vendor/*****/*****'],\n                ['vendor/', 'vendor/a', 'vendor/a/b', 'a/b/vendor/c/d'],\n                ['a', 'vendor', 'a/vendor', 'a/b/vendor'],\n            ],\n            [\n                ['**vendor**'],\n                ['vendor', 'vendor/', 'vendor/a', 'vendor/a/b', 'a/vendor', 'a/b/vendor', 'a/b/vendor/c/d'],\n                ['a'],\n            ],\n        ];\n\n        return $cases;\n    }\n\n    public static function providerExtended(): array\n    {\n        $basicCases = self::provider();\n\n        $cases = [];\n        foreach ($basicCases as $case) {\n            $cases[] = [\n                array_merge(['never'], $case[0], ['!never']),\n                $case[1],\n                $case[2],\n            ];\n\n            $cases[] = [\n                array_merge(['!*'], $case[0]),\n                $case[1],\n                $case[2],\n            ];\n\n            $cases[] = [\n                array_merge(['*', '!*'], $case[0]),\n                $case[1],\n                $case[2],\n            ];\n\n            $cases[] = [\n                array_merge(['never', '**/never2', 'never3/**'], $case[0]),\n                $case[1],\n                $case[2],\n            ];\n\n            $cases[] = [\n                array_merge(['!never', '!**/never2', '!never3/**'], $case[0]),\n                $case[1],\n                $case[2],\n            ];\n\n            $lines = [];\n            for ($i = 0; $i < 30; ++$i) {\n                foreach ($case[0] as $line) {\n                    $lines[] = $line;\n                }\n            }\n            $cases[] = [\n                array_merge(['!never', '!**/never2', '!never3/**'], $lines),\n                $case[1],\n                $case[2],\n            ];\n        }\n\n        return $cases;\n    }\n\n    #[DataProvider('provideNegatedPatternsCases')]\n    public function testToRegexMatchingNegatedPatterns(array $gitignoreLines, array $matchingCases, array $nonMatchingCases)\n    {\n        $patterns = implode(\"\\n\", $gitignoreLines);\n\n        $regex = Gitignore::toRegexMatchingNegatedPatterns($patterns);\n        $this->assertSame($regex, Gitignore::toRegexMatchingNegatedPatterns(implode(\"\\r\\n\", $gitignoreLines)));\n        $this->assertSame($regex, Gitignore::toRegexMatchingNegatedPatterns(implode(\"\\r\", $gitignoreLines)));\n\n        foreach ($matchingCases as $matchingCase) {\n            $this->assertMatchesRegularExpression(\n                $regex,\n                $matchingCase,\n                \\sprintf(\n                    \"Failed asserting path:\\n%s\\nmatches gitignore negated patterns:\\n%s\",\n                    preg_replace('~^~m', '    ', $matchingCase),\n                    preg_replace('~^~m', '    ', $patterns)\n                )\n            );\n        }\n\n        foreach ($nonMatchingCases as $nonMatchingCase) {\n            $this->assertDoesNotMatchRegularExpression(\n                $regex,\n                $nonMatchingCase,\n                \\sprintf(\"Failed asserting path:\\n%s\\nNOT matching gitignore negated patterns:\\n%s\",\n                    preg_replace('~^~m', '    ', $nonMatchingCase),\n                    preg_replace('~^~m', '    ', $patterns)\n                )\n            );\n        }\n    }\n\n    public static function provideNegatedPatternsCases(): iterable\n    {\n        yield [\n            [''],\n            [],\n            ['a', 'a/b', 'a/b/c', 'aa', 'm.txt', '.txt'],\n        ];\n\n        yield [\n            ['!a', '!X'],\n            ['a', 'a/b', 'a/b/c', 'X', 'b/a', 'b/c/a', 'a/X', 'a/X/y', 'b/a/X/y'],\n            ['A', 'x', 'aa', 'm.txt', '.txt', 'aa/b', 'b/aa'],\n        ];\n\n        yield [\n            ['!/a', '!x', '!d/'],\n            ['a', 'a/b', 'a/b/c', 'x', 'a/x', 'a/x/y', 'b/a/x/y', 'd/', 'd/u', 'e/d/', 'e/d/u'],\n            ['b/a', 'b/c/a', 'aa', 'm.txt', '.txt', 'aa/b', 'b/aa', 'e/d'],\n        ];\n\n        yield [\n            ['!a/', '!x'],\n            ['a/b', 'a/b/c', 'x', 'a/x', 'a/x/y', 'b/a/x/y'],\n            ['a', 'b/a', 'b/c/a', 'aa', 'm.txt', '.txt', 'aa/b', 'b/aa'],\n        ];\n\n        yield [\n            ['!*'],\n            ['a', 'a/b', 'a/b/c', 'aa', 'm.txt', '.txt'],\n            [],\n        ];\n\n        yield [\n            ['!/*'],\n            ['a', 'a/b', 'a/b/c', 'aa', 'm.txt', '.txt'],\n            [],\n        ];\n\n        yield [\n            ['!/a', '!m/*', '!o/**', '!p/**/', '!x**y'],\n            ['a', 'a/b', 'a/b/c', 'm/', 'o/', 'p/', 'xy', 'xuy', 'x/y', 'x/u/y', 'xu/y', 'x/uy', 'xu/uy'],\n            ['aa', 'm', 'b/m', 'b/m/', 'o', 'b/o', 'b/o/', 'p', 'b/p', 'b/p/'],\n        ];\n\n        yield [\n            ['!a', 'x'],\n            ['a', 'a/b', 'a/b/c', 'b/a', 'b/c/a'],\n            ['x', 'aa', 'm.txt', '.txt', 'aa/b', 'b/aa'],\n        ];\n\n        yield [\n            ['!a', 'a/', '!b', 'b/b'],\n            ['a', 'a/x', 'x/a', 'x/a/x', 'b', 'b'],\n            ['a/', 'x/a/', 'bb', 'b/b', 'bb'],\n        ];\n\n        yield [\n            ['![a-c]', '!x[C-E][][o]', '!g-h'],\n            ['a', 'b', 'c', 'xDo', 'g-h'],\n            ['A', 'xdo', 'u', 'g', 'h'],\n        ];\n\n        yield [\n            ['!a?', '!*/??b?'],\n            ['ax', 'x/xxbx'],\n            ['a', 'axy', 'xxax', 'x/xxax', 'x/y/xxax'],\n        ];\n\n        yield [\n            ['! ', '! \\ ', '!  \\  ', '!/a ', '!/b/c \\ '],\n            ['  ', '   ', 'x/  ', 'x/   ', 'a', 'a/x', 'b/c  '],\n            [' ', '    ', 'x/ ', 'x/    ', 'a ', 'b/c   '],\n        ];\n\n        yield [\n            ['!\\#', '! #', '!/ #', '!  #', '!/  #', '!  \\ #', '!   \\  #', '!a #', '!a  #', '!a  \\ #', '!a   \\  #'],\n            ['   ', '    ', 'a', 'a   ', 'a    '],\n            [' ', '  ', 'a ', 'a  '],\n        ];\n\n        yield [\n            [\"!\\t\", \"!\\t\\\\\\t\", \"! \\t\\\\\\t \", \"!\\t#\", \"!a\\t#\", \"!a\\t\\t#\", \"!a \\t#\", \"!a\\t\\t\\\\\\t#\", \"!a \\t\\t\\\\\\t\\t#\"],\n            [\"\\t\\t\", \" \\t\\t\", 'a', \"a\\t\\t\\t\", \"a \\t\\t\\t\"],\n            [\"\\t\", \"\\t\\t \", \" \\t\\t \", \"a\\t\", 'a ', \"a \\t\", \"a\\t\\t\"],\n        ];\n\n        yield [\n            ['! a', '!b ', '!\\ ', '!c\\ '],\n            [' a', 'b', ' ', 'c '],\n            ['a', 'b ', 'c'],\n        ];\n\n        yield [\n            ['!\\#a', '!\\#b', '!\\#/'],\n            ['#a', '#b', '#/'],\n            ['a', 'b'],\n        ];\n\n        yield [\n            ['*', '!!', '!!*x', '\\!!b'],\n            ['!', '!x', '!xx'],\n            ['a', '!!', '!!b'],\n        ];\n\n        yield [\n            [\n                '*',\n                '!/bin',\n                '!/bin/bash',\n            ],\n            ['bin/bash', 'bin/cat'],\n            ['abc/bin/cat'],\n        ];\n\n        yield [\n            ['!fi#le.txt'],\n            [],\n            ['#file.txt'],\n        ];\n\n        yield [\n            [\n                '/bin/',\n                '/usr/local/',\n                '!/bin/bash',\n                '!/usr/local/bin/bash',\n            ],\n            ['bin/bash'],\n            ['bin/cat'],\n        ];\n\n        yield [\n            ['!*.py[co]'],\n            ['file.pyc', 'file.pyc'],\n            ['filexpyc', 'file.pycx', 'file.py'],\n        ];\n\n        yield [\n            ['!dir1/**/dir2/'],\n            ['dir1/dir2/', 'dir1/dirA/dir2/', 'dir1/dirA/dirB/dir2/'],\n            ['dir1dir2/', 'dir1xdir2/', 'dir1/xdir2/', 'dir1x/dir2/'],\n        ];\n\n        yield [\n            ['!dir1/*/dir2/'],\n            ['dir1/dirA/dir2/'],\n            ['dir1/dirA/dirB/dir2/'],\n        ];\n\n        yield [\n            ['!/*.php'],\n            ['file.php'],\n            ['app/file.php'],\n        ];\n\n        yield [\n            ['!\\#file.txt'],\n            ['#file.txt'],\n            [],\n        ];\n\n        yield [\n            ['!*.php'],\n            ['app/file.php', 'file.php'],\n            ['file.phps', 'file.phps', 'filephps'],\n        ];\n\n        yield [\n            ['!app/cache/'],\n            ['app/cache/file.txt', 'app/cache/dir1/dir2/file.txt'],\n            ['a/app/cache/file.txt'],\n        ];\n\n        yield [\n            ['#IamComment', '!/app/cache/'],\n            ['app/cache/file.txt', 'app/cache/subdir/ile.txt'],\n            ['a/app/cache/file.txt', '#IamComment', 'IamComment'],\n        ];\n\n        yield [\n            ['!/app/cache/', '#LastLineIsComment'],\n            ['app/cache/file.txt', 'app/cache/subdir/ile.txt'],\n            ['a/app/cache/file.txt', '#LastLineIsComment', 'LastLineIsComment'],\n        ];\n\n        yield [\n            ['!/app/cache/', '!\\#file.txt', '#LastLineIsComment'],\n            ['app/cache/file.txt', 'app/cache/subdir/ile.txt', '#file.txt'],\n            ['a/app/cache/file.txt', '#LastLineIsComment', 'LastLineIsComment'],\n        ];\n\n        yield [\n            ['!/app/cache/', '!\\#file.txt', '#IamComment', '!another_file.txt'],\n            ['app/cache/file.txt', 'app/cache/subdir/ile.txt', '#file.txt', 'another_file.txt'],\n            ['a/app/cache/file.txt', 'IamComment', '#IamComment'],\n        ];\n\n        yield [\n            [\n                '/app/**',\n                '!/app/bin',\n                '!/app/bin/test',\n            ],\n            ['app/bin/file', 'app/bin/test'],\n            ['app/test/file'],\n        ];\n\n        yield [\n            [\n                '/app/*/img',\n                '!/app/*/img/src',\n            ],\n            ['app/a/img/src', 'app/a/img/src/', 'app/a/img/src/x'],\n            ['app/a/img', 'app/a/img/x'],\n        ];\n\n        yield [\n            [\n                'app/**/img',\n                '!/app/**/img/src',\n            ],\n            ['app/a/img/src', 'app/a/b/img/src', 'app/a/c/b/img/src', 'app/a/img/src/x', 'app/a/b/img/src/x'],\n            ['app/a/img', 'app/a/img/x', 'app/a/b/img', 'app/a/b/img/x', 'app/a/b/c/img'],\n        ];\n\n        yield [\n            [\n                '/*',\n                '!/foo',\n                '/foo/*',\n                '!/foo/bar',\n            ],\n            ['foo', 'foo/bar'],\n            ['bar', 'foo/ba', 'foo/barx', 'x/foo/bar'],\n        ];\n\n        yield [\n            [\n                '/example/**',\n                '!/example/example.txt',\n                '!/example/packages',\n            ],\n            ['example/example.txt', 'example/packages', 'example/packages/', 'example/packages/foo.yaml'],\n            ['example/test', 'example/example.txt2'],\n        ];\n\n        // based on https://www.atlassian.com/git/tutorials/saving-changes/gitignore\n        yield [\n            ['!**/logs'],\n            ['logs/debug.log', 'logs/monday/foo.bar'],\n            [],\n        ];\n\n        yield [\n            ['!**/logs/debug.log'],\n            ['logs/debug.log', 'build/logs/debug.log'],\n            ['logs/build/debug.log'],\n        ];\n\n        yield [\n            ['!*.log'],\n            ['debug.log', 'foo.log', '.log', 'logs/debug.log'],\n            [],\n        ];\n\n        yield [\n            [\n                '*.log',\n                '!important.log',\n            ],\n            ['important.log', 'logs/important.log'],\n            ['debug.log', 'trace.log'],\n        ];\n\n        yield [\n            [\n                '*.log',\n                '!important/*.log',\n                'trace.*',\n            ],\n            ['important/debug.log'],\n            ['debug.log', 'important/trace.log'],\n        ];\n\n        yield [\n            ['!/debug.log'],\n            ['debug.log'],\n            ['logs/debug.log'],\n        ];\n\n        yield [\n            ['!debug.log'],\n            ['debug.log', 'logs/debug.log'],\n            [],\n        ];\n\n        yield [\n            ['!debug?.log'],\n            ['debug0.log', 'debugg.log'],\n            ['debug10.log'],\n        ];\n\n        yield [\n            ['!debug[0-9].log'],\n            ['debug0.log', 'debug1.log'],\n            ['debug10.log'],\n        ];\n\n        yield [\n            ['!debug[01].log'],\n            ['debug0.log', 'debug1.log'],\n            ['debug2.log', 'debug01.log'],\n        ];\n\n        yield [\n            ['!debug[!01].log'],\n            ['debug2.log'],\n            ['debug0.log', 'debug1.log', 'debug01.log'],\n        ];\n\n        yield [\n            ['!debug[a-z].log'],\n            ['debuga.log', 'debugb.log'],\n            ['debug1.log'],\n        ];\n\n        yield [\n            ['!logs'],\n            ['logs', 'logs/debug.log', 'logs/latest/foo.bar', 'build/logs', 'build/logs/debug.log'],\n            [],\n        ];\n\n        yield [\n            ['!logs/'],\n            ['logs/debug.log', 'logs/latest/foo.bar', 'build/logs/foo.bar', 'build/logs/latest/debug.log'],\n            [],\n        ];\n\n        yield [\n            [\n                'logs/',\n                '!logs/important.log',\n            ],\n            [],\n            ['logs/debug.log'/* must be pruned on traversal 'logs/important.log' */],\n        ];\n\n        yield [\n            ['!logs/**/debug.log'],\n            ['logs/debug.log', 'logs/monday/debug.log', 'logs/monday/pm/debug.log'],\n            [],\n        ];\n\n        yield [\n            ['!logs/*day/debug.log'],\n            ['logs/monday/debug.log', 'logs/tuesday/debug.log'],\n            ['logs/latest/debug.log'],\n        ];\n\n        yield [\n            ['!logs/debug.log'],\n            ['logs/debug.log'],\n            ['debug.log', 'build/logs/debug.log'],\n        ];\n\n        yield [\n            ['!*/vendor/*'],\n            ['a/vendor/', 'a/vendor/b', 'a/vendor/b/c'],\n            ['a', 'vendor', 'vendor/', 'a/vendor', 'a/b/vendor', 'a/b/vendor/c'],\n        ];\n\n        yield [\n            ['!**/vendor/**'],\n            ['vendor/', 'vendor/a', 'vendor/a/b', 'a/b/vendor/c/d'],\n            ['a', 'vendor', 'a/vendor', 'a/b/vendor'],\n        ];\n\n        yield [\n            ['!***/***/vendor/*****/*****'],\n            ['vendor/', 'vendor/a', 'vendor/a/b', 'a/b/vendor/c/d'],\n            ['a', 'vendor', 'a/vendor', 'a/b/vendor'],\n        ];\n\n        yield [\n            ['!**vendor**'],\n            ['vendor', 'vendor/', 'vendor/a', 'vendor/a/b', 'a/vendor', 'a/b/vendor', 'a/b/vendor/c/d'],\n            ['a'],\n        ];\n    }\n}\n"
  },
  {
    "path": "Tests/GlobTest.php",
    "content": "<?php\n\n/*\n * This file is part of the Symfony package.\n *\n * (c) Fabien Potencier <fabien@symfony.com>\n *\n * For the full copyright and license information, please view the LICENSE\n * file that was distributed with this source code.\n */\n\nnamespace Symfony\\Component\\Finder\\Tests;\n\nuse PHPUnit\\Framework\\TestCase;\nuse Symfony\\Component\\Finder\\Finder;\nuse Symfony\\Component\\Finder\\Glob;\n\nclass GlobTest extends TestCase\n{\n    public function testGlobToRegexDelimiters()\n    {\n        $this->assertEquals('#^(?=[^\\.])\\#$#', Glob::toRegex('#'));\n        $this->assertEquals('#^\\.[^/]*$#', Glob::toRegex('.*'));\n        $this->assertEquals('^\\.[^/]*$', Glob::toRegex('.*', true, true, ''));\n        $this->assertEquals('/^\\.[^/]*$/', Glob::toRegex('.*', true, true, '/'));\n    }\n\n    public function testGlobToRegexDoubleStarStrictDots()\n    {\n        $finder = new Finder();\n        $finder->ignoreDotFiles(false);\n        $regex = Glob::toRegex('/**/*.neon');\n\n        foreach ($finder->in(__DIR__) as $k => $v) {\n            $k = str_replace(\\DIRECTORY_SEPARATOR, '/', $k);\n            if (preg_match($regex, substr($k, \\strlen(__DIR__)))) {\n                $match[] = substr($k, 10 + \\strlen(__DIR__));\n            }\n        }\n        sort($match);\n\n        $this->assertSame(['one/b/c.neon', 'one/b/d.neon'], $match);\n    }\n\n    public function testGlobToRegexDoubleStarNonStrictDots()\n    {\n        $finder = new Finder();\n        $finder->ignoreDotFiles(false);\n        $regex = Glob::toRegex('/**/*.neon', false);\n\n        foreach ($finder->in(__DIR__) as $k => $v) {\n            $k = str_replace(\\DIRECTORY_SEPARATOR, '/', $k);\n            if (preg_match($regex, substr($k, \\strlen(__DIR__)))) {\n                $match[] = substr($k, 10 + \\strlen(__DIR__));\n            }\n        }\n        sort($match);\n\n        $this->assertSame(['.dot/b/c.neon', '.dot/b/d.neon', 'one/b/c.neon', 'one/b/d.neon'], $match);\n    }\n\n    public function testGlobToRegexDoubleStarWithoutLeadingSlash()\n    {\n        $finder = new Finder();\n        $finder->ignoreDotFiles(false);\n        $regex = Glob::toRegex('/Fixtures/one/**');\n\n        foreach ($finder->in(__DIR__) as $k => $v) {\n            $k = str_replace(\\DIRECTORY_SEPARATOR, '/', $k);\n            if (preg_match($regex, substr($k, \\strlen(__DIR__)))) {\n                $match[] = substr($k, 10 + \\strlen(__DIR__));\n            }\n        }\n        sort($match);\n\n        $this->assertSame(['one/a', 'one/b', 'one/b/c.neon', 'one/b/d.neon'], $match);\n    }\n\n    public function testGlobToRegexDoubleStarWithoutLeadingSlashNotStrictLeadingDot()\n    {\n        $finder = new Finder();\n        $finder->ignoreDotFiles(false);\n        $regex = Glob::toRegex('/Fixtures/one/**', false);\n\n        foreach ($finder->in(__DIR__) as $k => $v) {\n            $k = str_replace(\\DIRECTORY_SEPARATOR, '/', $k);\n            if (preg_match($regex, substr($k, \\strlen(__DIR__)))) {\n                $match[] = substr($k, 10 + \\strlen(__DIR__));\n            }\n        }\n        sort($match);\n\n        $this->assertSame(['one/.dot', 'one/a', 'one/b', 'one/b/c.neon', 'one/b/d.neon'], $match);\n    }\n\n    public function testGlobToRegexDoubleStarMatchesRootFiles()\n    {\n        $regex = Glob::toRegex('**/*.txt');\n\n        $this->assertSame(1, preg_match($regex, 'file.txt'));\n        $this->assertSame(1, preg_match($regex, 'foo/file.txt'));\n        $this->assertSame(1, preg_match($regex, 'foo/bar/baz.txt'));\n        $this->assertSame(1, preg_match($regex, '/foo/bar.txt'));\n        $this->assertSame(0, preg_match($regex, './foo/bar.txt'));\n        $this->assertSame(0, preg_match($regex, 'foo/.bar/bar.txt'));\n        $this->assertSame(0, preg_match($regex, '.file.txt'));\n        $this->assertSame(0, preg_match($regex, 'foo/bar/baz.php'));\n\n        $regex = Glob::toRegex('**/*.txt', false);\n\n        $this->assertSame(1, preg_match($regex, './foo/bar.txt'));\n        $this->assertSame(1, preg_match($regex, 'foo/.bar/bar.txt'));\n        $this->assertSame(1, preg_match($regex, '.file.txt'));\n    }\n}\n"
  },
  {
    "path": "Tests/Iterator/CustomFilterIteratorTest.php",
    "content": "<?php\n\n/*\n * This file is part of the Symfony package.\n *\n * (c) Fabien Potencier <fabien@symfony.com>\n *\n * For the full copyright and license information, please view the LICENSE\n * file that was distributed with this source code.\n */\n\nnamespace Symfony\\Component\\Finder\\Tests\\Iterator;\n\nuse PHPUnit\\Framework\\Attributes\\DataProvider;\nuse Symfony\\Component\\Finder\\Iterator\\CustomFilterIterator;\n\nclass CustomFilterIteratorTest extends IteratorTestCase\n{\n    public function testWithInvalidFilter()\n    {\n        $this->expectException(\\InvalidArgumentException::class);\n        new CustomFilterIterator(new Iterator(), ['foo']);\n    }\n\n    #[DataProvider('getAcceptData')]\n    public function testAccept($filters, $expected)\n    {\n        $inner = new Iterator(['test.php', 'test.py', 'foo.php']);\n\n        $iterator = new CustomFilterIterator($inner, $filters);\n\n        $this->assertIterator($expected, $iterator);\n    }\n\n    public static function getAcceptData()\n    {\n        return [\n            [[static fn (\\SplFileInfo $fileinfo) => false], []],\n            [[static fn (\\SplFileInfo $fileinfo) => str_starts_with($fileinfo, 'test')], ['test.php', 'test.py']],\n            [['is_dir'], []],\n        ];\n    }\n}\n"
  },
  {
    "path": "Tests/Iterator/DateRangeFilterIteratorTest.php",
    "content": "<?php\n\n/*\n * This file is part of the Symfony package.\n *\n * (c) Fabien Potencier <fabien@symfony.com>\n *\n * For the full copyright and license information, please view the LICENSE\n * file that was distributed with this source code.\n */\n\nnamespace Symfony\\Component\\Finder\\Tests\\Iterator;\n\nuse PHPUnit\\Framework\\Attributes\\DataProvider;\nuse Symfony\\Component\\Finder\\Comparator\\DateComparator;\nuse Symfony\\Component\\Finder\\Iterator\\DateRangeFilterIterator;\n\nclass DateRangeFilterIteratorTest extends RealIteratorTestCase\n{\n    #[DataProvider('getAcceptData')]\n    public function testAccept($size, $expected)\n    {\n        $files = self::$files;\n        $files[] = static::toAbsolute('doesnotexist');\n        $inner = new Iterator($files);\n\n        $iterator = new DateRangeFilterIterator($inner, $size);\n\n        $this->assertIterator($expected, $iterator);\n    }\n\n    public static function getAcceptData()\n    {\n        $since20YearsAgo = [\n            '.git',\n            'test.py',\n            'foo',\n            'foo/bar.tmp',\n            'test.php',\n            'toto',\n            'toto/.git',\n            '.bar',\n            '.foo',\n            '.foo/.bar',\n            'foo bar',\n            '.foo/bar',\n            'qux',\n            'qux/baz_100_1.py',\n            'zebulon.php',\n            'Zephire.php',\n            'qux/baz_1_2.py',\n            'qux_0_1.php',\n            'qux_1000_1.php',\n            'qux_1002_0.php',\n            'qux_10_2.php',\n            'qux_12_0.php',\n            'qux_2_0.php',\n        ];\n\n        $since2MonthsAgo = [\n            '.git',\n            'test.py',\n            'foo',\n            'toto',\n            'toto/.git',\n            '.bar',\n            '.foo',\n            '.foo/.bar',\n            'foo bar',\n            '.foo/bar',\n            'qux',\n            'qux/baz_100_1.py',\n            'zebulon.php',\n            'Zephire.php',\n            'qux/baz_1_2.py',\n            'qux_0_1.php',\n            'qux_1000_1.php',\n            'qux_1002_0.php',\n            'qux_10_2.php',\n            'qux_12_0.php',\n            'qux_2_0.php',\n        ];\n\n        $untilLastMonth = [\n            'foo/bar.tmp',\n            'test.php',\n        ];\n\n        return [\n            [[new DateComparator('since 20 years ago')], static::toAbsolute($since20YearsAgo)],\n            [[new DateComparator('since 2 months ago')], static::toAbsolute($since2MonthsAgo)],\n            [[new DateComparator('until last month')], static::toAbsolute($untilLastMonth)],\n        ];\n    }\n}\n"
  },
  {
    "path": "Tests/Iterator/DepthRangeFilterIteratorTest.php",
    "content": "<?php\n\n/*\n * This file is part of the Symfony package.\n *\n * (c) Fabien Potencier <fabien@symfony.com>\n *\n * For the full copyright and license information, please view the LICENSE\n * file that was distributed with this source code.\n */\n\nnamespace Symfony\\Component\\Finder\\Tests\\Iterator;\n\nuse PHPUnit\\Framework\\Attributes\\DataProvider;\nuse Symfony\\Component\\Finder\\Iterator\\DepthRangeFilterIterator;\n\nclass DepthRangeFilterIteratorTest extends RealIteratorTestCase\n{\n    #[DataProvider('getAcceptData')]\n    public function testAccept($minDepth, $maxDepth, $expected)\n    {\n        $inner = new \\RecursiveIteratorIterator(new \\RecursiveDirectoryIterator($this->toAbsolute(), \\FilesystemIterator::SKIP_DOTS), \\RecursiveIteratorIterator::SELF_FIRST);\n\n        $iterator = new DepthRangeFilterIterator($inner, $minDepth, $maxDepth);\n\n        $actual = array_keys(iterator_to_array($iterator));\n        sort($expected);\n        sort($actual);\n        $this->assertEquals($expected, $actual);\n    }\n\n    public static function getAcceptData()\n    {\n        $lessThan1 = [\n            '.git',\n            'test.py',\n            'foo',\n            'test.php',\n            'toto',\n            '.foo',\n            '.bar',\n            'foo bar',\n            'qux',\n            'qux_0_1.php',\n            'qux_1000_1.php',\n            'qux_1002_0.php',\n            'qux_10_2.php',\n            'qux_12_0.php',\n            'qux_2_0.php',\n            'zebulon.php', 'Zephire.php',\n        ];\n\n        $lessThanOrEqualTo1 = [\n            '.git',\n            'test.py',\n            'foo',\n            'foo/bar.tmp',\n            'test.php',\n            'toto',\n            'toto/.git',\n            '.foo',\n            '.foo/.bar',\n            '.bar',\n            'foo bar',\n            '.foo/bar',\n            'qux',\n            'qux/baz_100_1.py',\n            'qux/baz_1_2.py',\n            'qux_0_1.php',\n            'qux_1000_1.php',\n            'qux_1002_0.php',\n            'qux_10_2.php',\n            'qux_12_0.php',\n            'qux_2_0.php',\n            'zebulon.php', 'Zephire.php',\n        ];\n\n        $graterThanOrEqualTo1 = [\n            'toto/.git',\n            'foo/bar.tmp',\n            '.foo/.bar',\n            '.foo/bar',\n            'qux/baz_100_1.py',\n            'qux/baz_1_2.py',\n        ];\n\n        $equalTo1 = [\n            'toto/.git',\n            'foo/bar.tmp',\n            '.foo/.bar',\n            '.foo/bar',\n            'qux/baz_100_1.py',\n            'qux/baz_1_2.py',\n        ];\n\n        return [\n            [0, 0, self::toAbsolute($lessThan1)],\n            [0, 1, self::toAbsolute($lessThanOrEqualTo1)],\n            [2, \\PHP_INT_MAX, []],\n            [1, \\PHP_INT_MAX, self::toAbsolute($graterThanOrEqualTo1)],\n            [1, 1, self::toAbsolute($equalTo1)],\n        ];\n    }\n}\n"
  },
  {
    "path": "Tests/Iterator/ExcludeDirectoryFilterIteratorTest.php",
    "content": "<?php\n\n/*\n * This file is part of the Symfony package.\n *\n * (c) Fabien Potencier <fabien@symfony.com>\n *\n * For the full copyright and license information, please view the LICENSE\n * file that was distributed with this source code.\n */\n\nnamespace Symfony\\Component\\Finder\\Tests\\Iterator;\n\nuse PHPUnit\\Framework\\Attributes\\DataProvider;\nuse Symfony\\Component\\Finder\\Iterator\\ExcludeDirectoryFilterIterator;\nuse Symfony\\Component\\Finder\\Iterator\\RecursiveDirectoryIterator;\n\nclass ExcludeDirectoryFilterIteratorTest extends RealIteratorTestCase\n{\n    #[DataProvider('getAcceptData')]\n    public function testAccept($directories, $expected)\n    {\n        $inner = new \\RecursiveIteratorIterator(new RecursiveDirectoryIterator($this->toAbsolute(), \\FilesystemIterator::SKIP_DOTS), \\RecursiveIteratorIterator::SELF_FIRST);\n\n        $iterator = new ExcludeDirectoryFilterIterator($inner, $directories);\n\n        $this->assertIterator($expected, $iterator);\n    }\n\n    public static function getAcceptData()\n    {\n        $foo = [\n            '.bar',\n            '.foo',\n            '.foo/.bar',\n            '.foo/bar',\n            '.git',\n            'test.py',\n            'test.php',\n            'toto',\n            'toto/.git',\n            'foo bar',\n            'qux',\n            'qux/baz_100_1.py',\n            'zebulon.php',\n            'Zephire.php',\n            'qux/baz_1_2.py',\n            'qux_0_1.php',\n            'qux_1000_1.php',\n            'qux_1002_0.php',\n            'qux_10_2.php',\n            'qux_12_0.php',\n            'qux_2_0.php',\n        ];\n\n        $fo = [\n            '.bar',\n            '.foo',\n            '.foo/.bar',\n            '.foo/bar',\n            '.git',\n            'test.py',\n            'foo',\n            'foo/bar.tmp',\n            'test.php',\n            'toto',\n            'toto/.git',\n            'foo bar',\n            'qux',\n            'qux/baz_100_1.py',\n            'zebulon.php',\n            'Zephire.php',\n            'qux/baz_1_2.py',\n            'qux_0_1.php',\n            'qux_1000_1.php',\n            'qux_1002_0.php',\n            'qux_10_2.php',\n            'qux_12_0.php',\n            'qux_2_0.php',\n        ];\n\n        $toto = [\n            '.bar',\n            '.foo',\n            '.foo/.bar',\n            '.foo/bar',\n            '.git',\n            'test.py',\n            'foo',\n            'foo/bar.tmp',\n            'test.php',\n            'foo bar',\n            'qux',\n            'qux/baz_100_1.py',\n            'zebulon.php',\n            'Zephire.php',\n            'qux/baz_1_2.py',\n            'qux_0_1.php',\n            'qux_1000_1.php',\n            'qux_1002_0.php',\n            'qux_10_2.php',\n            'qux_12_0.php',\n            'qux_2_0.php',\n        ];\n\n        return [\n            [['foo'], self::toAbsolute($foo)],\n            [['fo'], self::toAbsolute($fo)],\n            [['toto/'], self::toAbsolute($toto)],\n        ];\n    }\n}\n"
  },
  {
    "path": "Tests/Iterator/FileTypeFilterIteratorTest.php",
    "content": "<?php\n\n/*\n * This file is part of the Symfony package.\n *\n * (c) Fabien Potencier <fabien@symfony.com>\n *\n * For the full copyright and license information, please view the LICENSE\n * file that was distributed with this source code.\n */\n\nnamespace Symfony\\Component\\Finder\\Tests\\Iterator;\n\nuse PHPUnit\\Framework\\Attributes\\DataProvider;\nuse Symfony\\Component\\Finder\\Iterator\\FileTypeFilterIterator;\n\nclass FileTypeFilterIteratorTest extends RealIteratorTestCase\n{\n    #[DataProvider('getAcceptData')]\n    public function testAccept($mode, $expected)\n    {\n        $inner = new InnerTypeIterator(self::$files);\n\n        $iterator = new FileTypeFilterIterator($inner, $mode);\n\n        $this->assertIterator($expected, $iterator);\n    }\n\n    public static function getAcceptData()\n    {\n        $onlyFiles = [\n            'test.py',\n            'foo/bar.tmp',\n            'test.php',\n            '.bar',\n            '.foo/.bar',\n            '.foo/bar',\n            'foo bar',\n            'qux/baz_100_1.py',\n            'zebulon.php',\n            'Zephire.php',\n            'qux/baz_1_2.py',\n            'qux_0_1.php',\n            'qux_1000_1.php',\n            'qux_1002_0.php',\n            'qux_10_2.php',\n            'qux_12_0.php',\n            'qux_2_0.php',\n        ];\n\n        $onlyDirectories = [\n            '.git',\n            'foo',\n            'qux',\n            'toto',\n            'toto/.git',\n            '.foo',\n        ];\n\n        return [\n            [FileTypeFilterIterator::ONLY_FILES, self::toAbsolute($onlyFiles)],\n            [FileTypeFilterIterator::ONLY_DIRECTORIES, self::toAbsolute($onlyDirectories)],\n        ];\n    }\n}\n\nclass InnerTypeIterator extends \\ArrayIterator\n{\n    public function current(): \\SplFileInfo\n    {\n        return new \\SplFileInfo(parent::current());\n    }\n\n    public function isFile(): bool\n    {\n        return $this->current()->isFile();\n    }\n\n    public function isDir(): bool\n    {\n        return $this->current()->isDir();\n    }\n}\n"
  },
  {
    "path": "Tests/Iterator/FilecontentFilterIteratorTest.php",
    "content": "<?php\n\n/*\n * This file is part of the Symfony package.\n *\n * (c) Fabien Potencier <fabien@symfony.com>\n *\n * For the full copyright and license information, please view the LICENSE\n * file that was distributed with this source code.\n */\n\nnamespace Symfony\\Component\\Finder\\Tests\\Iterator;\n\nuse PHPUnit\\Framework\\Attributes\\DataProvider;\nuse Symfony\\Component\\Finder\\Iterator\\FilecontentFilterIterator;\n\nclass FilecontentFilterIteratorTest extends IteratorTestCase\n{\n    public function testAccept()\n    {\n        $inner = new MockFileListIterator(['test.txt']);\n        $iterator = new FilecontentFilterIterator($inner, [], []);\n        $this->assertIterator(['test.txt'], $iterator);\n    }\n\n    public function testDirectory()\n    {\n        $inner = new MockFileListIterator(['directory']);\n        $iterator = new FilecontentFilterIterator($inner, ['directory'], []);\n        $this->assertIterator([], $iterator);\n    }\n\n    public function testUnreadableFile()\n    {\n        $inner = new MockFileListIterator(['file r-']);\n        $iterator = new FilecontentFilterIterator($inner, ['file r-'], []);\n        $this->assertIterator([], $iterator);\n    }\n\n    #[DataProvider('getTestFilterData')]\n    public function testFilter(\\Iterator $inner, array $matchPatterns, array $noMatchPatterns, array $resultArray)\n    {\n        $iterator = new FilecontentFilterIterator($inner, $matchPatterns, $noMatchPatterns);\n        $this->assertIterator($resultArray, $iterator);\n    }\n\n    public static function getTestFilterData()\n    {\n        $inner = new MockFileListIterator();\n\n        $inner[] = new MockSplFileInfo([\n            'name' => 'a.txt',\n            'contents' => 'Lorem ipsum...',\n            'type' => 'file',\n            'mode' => 'r+', ]\n        );\n\n        $inner[] = new MockSplFileInfo([\n            'name' => 'b.yml',\n            'contents' => 'dolor sit...',\n            'type' => 'file',\n            'mode' => 'r+', ]\n        );\n\n        $inner[] = new MockSplFileInfo([\n            'name' => 'some/other/dir/third.php',\n            'contents' => 'amet...',\n            'type' => 'file',\n            'mode' => 'r+', ]\n        );\n\n        $inner[] = new MockSplFileInfo([\n            'name' => 'unreadable-file.txt',\n            'contents' => '',\n            'type' => 'file',\n            'mode' => 'r+', ]\n        );\n\n        return [\n            [$inner, ['.'], [], ['a.txt', 'b.yml', 'some/other/dir/third.php']],\n            [$inner, ['ipsum'], [], ['a.txt']],\n            [$inner, ['i', 'amet'], ['Lorem', 'amet'], ['b.yml']],\n        ];\n    }\n}\n"
  },
  {
    "path": "Tests/Iterator/FilenameFilterIteratorTest.php",
    "content": "<?php\n\n/*\n * This file is part of the Symfony package.\n *\n * (c) Fabien Potencier <fabien@symfony.com>\n *\n * For the full copyright and license information, please view the LICENSE\n * file that was distributed with this source code.\n */\n\nnamespace Symfony\\Component\\Finder\\Tests\\Iterator;\n\nuse PHPUnit\\Framework\\Attributes\\DataProvider;\nuse Symfony\\Component\\Finder\\Iterator\\FilenameFilterIterator;\n\nclass FilenameFilterIteratorTest extends IteratorTestCase\n{\n    #[DataProvider('getAcceptData')]\n    public function testAccept($matchPatterns, $noMatchPatterns, $expected)\n    {\n        $inner = new InnerNameIterator(['test.php', 'test.py', 'foo.php']);\n\n        $iterator = new FilenameFilterIterator($inner, $matchPatterns, $noMatchPatterns);\n\n        $this->assertIterator($expected, $iterator);\n    }\n\n    public static function getAcceptData()\n    {\n        return [\n            [['test.*'], [], ['test.php', 'test.py']],\n            [[], ['test.*'], ['foo.php']],\n            [['*.php'], ['test.*'], ['foo.php']],\n            [['*.php', '*.py'], ['foo.*'], ['test.php', 'test.py']],\n            [['/\\.php$/'], [], ['test.php', 'foo.php']],\n            [[], ['/\\.php$/'], ['test.py']],\n        ];\n    }\n}\n"
  },
  {
    "path": "Tests/Iterator/InnerNameIterator.php",
    "content": "<?php\n\n/*\n * This file is part of the Symfony package.\n *\n * (c) Fabien Potencier <fabien@symfony.com>\n *\n * For the full copyright and license information, please view the LICENSE\n * file that was distributed with this source code.\n */\n\nnamespace Symfony\\Component\\Finder\\Tests\\Iterator;\n\nclass InnerNameIterator extends \\ArrayIterator\n{\n    public function current(): \\SplFileInfo\n    {\n        return new \\SplFileInfo(parent::current());\n    }\n\n    public function getFilename()\n    {\n        return parent::current();\n    }\n}\n"
  },
  {
    "path": "Tests/Iterator/Iterator.php",
    "content": "<?php\n\n/*\n * This file is part of the Symfony package.\n *\n * (c) Fabien Potencier <fabien@symfony.com>\n *\n * For the full copyright and license information, please view the LICENSE\n * file that was distributed with this source code.\n */\n\nnamespace Symfony\\Component\\Finder\\Tests\\Iterator;\n\nclass Iterator implements \\Iterator\n{\n    protected array $values = [];\n\n    public function __construct(array $values = [])\n    {\n        foreach ($values as $value) {\n            $this->attach(new \\SplFileInfo($value));\n        }\n        $this->rewind();\n    }\n\n    public function attach(\\SplFileInfo $fileinfo): void\n    {\n        $this->values[] = $fileinfo;\n    }\n\n    public function rewind(): void\n    {\n        reset($this->values);\n    }\n\n    public function valid(): bool\n    {\n        return false !== $this->current();\n    }\n\n    public function next(): void\n    {\n        next($this->values);\n    }\n\n    public function current(): mixed\n    {\n        return current($this->values);\n    }\n\n    public function key(): mixed\n    {\n        return key($this->values);\n    }\n}\n"
  },
  {
    "path": "Tests/Iterator/IteratorTestCase.php",
    "content": "<?php\n\n/*\n * This file is part of the Symfony package.\n *\n * (c) Fabien Potencier <fabien@symfony.com>\n *\n * For the full copyright and license information, please view the LICENSE\n * file that was distributed with this source code.\n */\n\nnamespace Symfony\\Component\\Finder\\Tests\\Iterator;\n\nuse PHPUnit\\Framework\\TestCase;\n\nabstract class IteratorTestCase extends TestCase\n{\n    protected function assertIterator($expected, \\Traversable $iterator)\n    {\n        // set iterator_to_array $use_key to false to avoid values merge\n        // this made FinderTest::testAppendWithAnArray() fail with GnuFinderAdapter\n        $values = array_map(static fn (\\SplFileInfo $fileinfo) => str_replace('/', \\DIRECTORY_SEPARATOR, $fileinfo->getPathname()), iterator_to_array($iterator, false));\n\n        $expected = array_map(static fn ($path) => str_replace('/', \\DIRECTORY_SEPARATOR, $path), $expected);\n\n        sort($values);\n        sort($expected);\n\n        $this->assertEquals($expected, array_values($values));\n    }\n\n    protected function assertOrderedIterator($expected, \\Traversable $iterator)\n    {\n        $values = array_map(static fn (\\SplFileInfo $fileinfo) => str_replace('/', \\DIRECTORY_SEPARATOR, $fileinfo->getPathname()), iterator_to_array($iterator));\n        $expected = array_map(static fn ($path) => str_replace('/', \\DIRECTORY_SEPARATOR, $path), $expected);\n\n        $this->assertEquals($expected, array_values($values));\n    }\n\n    /**\n     * Same as assertOrderedIterator, but checks the order of groups of\n     * array elements.\n     *\n     * @param array $expected an array of arrays. For any two subarrays\n     *                        $a and $b such that $a goes before $b in $expected, the method\n     *                        asserts that any element of $a goes before any element of $b\n     *                        in the sequence generated by $iterator\n     */\n    protected function assertOrderedIteratorForGroups(array $expected, \\Traversable $iterator)\n    {\n        $values = array_values(array_map(static fn (\\SplFileInfo $fileinfo) => $fileinfo->getPathname(), iterator_to_array($iterator)));\n\n        foreach ($expected as $subarray) {\n            $temp = [];\n            while (\\count($values) && \\count($temp) < \\count($subarray)) {\n                $temp[] = array_shift($values);\n            }\n            sort($temp);\n            sort($subarray);\n            $this->assertEquals($subarray, $temp);\n        }\n    }\n\n    /**\n     * Same as IteratorTestCase::assertIterator with foreach usage.\n     */\n    protected function assertIteratorInForeach(array $expected, \\Traversable $iterator)\n    {\n        $values = [];\n        foreach ($iterator as $file) {\n            $this->assertInstanceOf(\\Symfony\\Component\\Finder\\SplFileInfo::class, $file);\n            $values[] = $file->getPathname();\n        }\n\n        sort($values);\n        sort($expected);\n\n        $this->assertEquals($expected, array_values($values));\n    }\n\n    /**\n     * Same as IteratorTestCase::assertOrderedIterator with foreach usage.\n     */\n    protected function assertOrderedIteratorInForeach(array $expected, \\Traversable $iterator)\n    {\n        $values = [];\n        foreach ($iterator as $file) {\n            $this->assertInstanceOf(\\Symfony\\Component\\Finder\\SplFileInfo::class, $file);\n            $values[] = $file->getPathname();\n        }\n\n        $this->assertEquals($expected, array_values($values));\n    }\n}\n"
  },
  {
    "path": "Tests/Iterator/LazyIteratorTest.php",
    "content": "<?php\n\n/*\n * This file is part of the Symfony package.\n *\n * (c) Fabien Potencier <fabien@symfony.com>\n *\n * For the full copyright and license information, please view the LICENSE\n * file that was distributed with this source code.\n */\n\nnamespace Symfony\\Component\\Finder\\Tests\\Iterator;\n\nuse PHPUnit\\Framework\\TestCase;\nuse Symfony\\Component\\Finder\\Iterator\\LazyIterator;\n\nclass LazyIteratorTest extends TestCase\n{\n    public function testLazy()\n    {\n        new LazyIterator(function () {\n            $this->markTestFailed('lazyIterator should not be called');\n        });\n\n        $this->expectNotToPerformAssertions();\n    }\n\n    public function testDelegate()\n    {\n        $iterator = new LazyIterator(static fn () => new Iterator(['foo', 'bar']));\n\n        $this->assertCount(2, iterator_to_array($iterator));\n    }\n\n    public function testInnerDestructedAtTheEnd()\n    {\n        $count = 0;\n        $iterator = new LazyIterator(static function () use (&$count) {\n            ++$count;\n\n            return new Iterator(['foo', 'bar']);\n        });\n\n        foreach ($iterator as $x) {\n        }\n        $this->assertSame(1, $count);\n        foreach ($iterator as $x) {\n        }\n        $this->assertSame(2, $count);\n    }\n}\n"
  },
  {
    "path": "Tests/Iterator/MockFileListIterator.php",
    "content": "<?php\n\n/*\n * This file is part of the Symfony package.\n *\n * (c) Fabien Potencier <fabien@symfony.com>\n *\n * For the full copyright and license information, please view the LICENSE\n * file that was distributed with this source code.\n */\n\nnamespace Symfony\\Component\\Finder\\Tests\\Iterator;\n\nclass MockFileListIterator extends \\ArrayIterator\n{\n    public function __construct(array $filesArray = [])\n    {\n        $files = array_map(static fn ($file) => new MockSplFileInfo($file), $filesArray);\n        parent::__construct($files);\n    }\n}\n"
  },
  {
    "path": "Tests/Iterator/MockSplFileInfo.php",
    "content": "<?php\n\n/*\n * This file is part of the Symfony package.\n *\n * (c) Fabien Potencier <fabien@symfony.com>\n *\n * For the full copyright and license information, please view the LICENSE\n * file that was distributed with this source code.\n */\n\nnamespace Symfony\\Component\\Finder\\Tests\\Iterator;\n\nclass MockSplFileInfo extends \\SplFileInfo\n{\n    public const TYPE_DIRECTORY = 1;\n    public const TYPE_FILE = 2;\n    public const TYPE_UNKNOWN = 3;\n\n    private ?string $contents = null;\n    private ?string $mode = null;\n    private ?int $type = null;\n    private ?string $relativePath = null;\n    private ?string $relativePathname = null;\n\n    public function __construct($param)\n    {\n        if (\\is_string($param)) {\n            parent::__construct($param);\n        } elseif (\\is_array($param)) {\n            $defaults = [\n                'name' => 'file.txt',\n                'contents' => null,\n                'mode' => null,\n                'type' => null,\n                'relativePath' => null,\n                'relativePathname' => null,\n            ];\n            $defaults = array_merge($defaults, $param);\n            parent::__construct($defaults['name']);\n            $this->setContents($defaults['contents']);\n            $this->setMode($defaults['mode']);\n            $this->setType($defaults['type']);\n            $this->setRelativePath($defaults['relativePath']);\n            $this->setRelativePathname($defaults['relativePathname']);\n        } else {\n            throw new \\RuntimeException(\\sprintf('Incorrect parameter \"%s\"', $param));\n        }\n    }\n\n    public function isFile(): bool\n    {\n        if (null === $this->type) {\n            return str_contains($this->getFilename(), 'file');\n        }\n\n        return self::TYPE_FILE === $this->type;\n    }\n\n    public function isDir(): bool\n    {\n        if (null === $this->type) {\n            return str_contains($this->getFilename(), 'directory');\n        }\n\n        return self::TYPE_DIRECTORY === $this->type;\n    }\n\n    public function isReadable(): bool\n    {\n        return (bool) preg_match('/r\\+/', $this->mode ?? $this->getFilename());\n    }\n\n    public function getContents()\n    {\n        return $this->contents;\n    }\n\n    public function setContents($contents)\n    {\n        $this->contents = $contents;\n    }\n\n    public function setMode($mode)\n    {\n        $this->mode = $mode;\n    }\n\n    public function setType($type)\n    {\n        if (\\is_string($type)) {\n            $this->type = match ($type) {\n                'directory',\n                'd' => self::TYPE_DIRECTORY,\n                'file',\n                'f' => self::TYPE_FILE,\n                default => self::TYPE_UNKNOWN,\n            };\n        } else {\n            $this->type = $type;\n        }\n    }\n\n    public function setRelativePath($relativePath)\n    {\n        $this->relativePath = $relativePath;\n    }\n\n    public function setRelativePathname($relativePathname)\n    {\n        $this->relativePathname = $relativePathname;\n    }\n\n    public function getRelativePath()\n    {\n        return $this->relativePath;\n    }\n\n    public function getRelativePathname()\n    {\n        return $this->relativePathname;\n    }\n}\n"
  },
  {
    "path": "Tests/Iterator/MultiplePcreFilterIteratorTest.php",
    "content": "<?php\n\n/*\n * This file is part of the Symfony package.\n *\n * (c) Fabien Potencier <fabien@symfony.com>\n *\n * For the full copyright and license information, please view the LICENSE\n * file that was distributed with this source code.\n */\n\nnamespace Symfony\\Component\\Finder\\Tests\\Iterator;\n\nuse PHPUnit\\Framework\\Attributes\\DataProvider;\nuse PHPUnit\\Framework\\TestCase;\nuse Symfony\\Component\\Finder\\Iterator\\MultiplePcreFilterIterator;\n\nclass MultiplePcreFilterIteratorTest extends TestCase\n{\n    #[DataProvider('getIsRegexFixtures')]\n    public function testIsRegex($string, $isRegex, $message)\n    {\n        $testIterator = new TestMultiplePcreFilterIterator();\n        $this->assertEquals($isRegex, $testIterator->isRegex($string), $message);\n    }\n\n    public static function getIsRegexFixtures()\n    {\n        yield ['foo', false, 'string'];\n        yield [' foo ', false, '\" \" is not a valid delimiter'];\n        yield ['\\\\foo\\\\', false, '\"\\\\\" is not a valid delimiter'];\n        yield ['afooa', false, '\"a\" is not a valid delimiter'];\n        yield ['//', false, 'the pattern should contain at least 1 character'];\n        yield ['/a/', true, 'valid regex'];\n        yield ['/foo/', true, 'valid regex'];\n        yield ['/foo/i', true, 'valid regex with a single modifier'];\n        yield ['/foo/imsxu', true, 'valid regex with multiple modifiers'];\n        yield ['#foo#', true, '\"#\" is a valid delimiter'];\n        yield ['{foo}', true, '\"{,}\" is a valid delimiter pair'];\n        yield ['[foo]', true, '\"[,]\" is a valid delimiter pair'];\n        yield ['(foo)', true, '\"(,)\" is a valid delimiter pair'];\n        yield ['<foo>', true, '\"<,>\" is a valid delimiter pair'];\n        yield ['*foo.*', false, '\"*\" is not considered as a valid delimiter'];\n        yield ['?foo.?', false, '\"?\" is not considered as a valid delimiter'];\n        yield ['/foo/n', true, 'valid regex with the no-capture modifier'];\n    }\n}\n\nclass TestMultiplePcreFilterIterator extends MultiplePcreFilterIterator\n{\n    public function __construct()\n    {\n    }\n\n    public function accept(): bool\n    {\n        throw new \\BadFunctionCallException('Not implemented');\n    }\n\n    public function isRegex(string $str): bool\n    {\n        return parent::isRegex($str);\n    }\n\n    public function toRegex(string $str): string\n    {\n        throw new \\BadFunctionCallException('Not implemented');\n    }\n}\n"
  },
  {
    "path": "Tests/Iterator/PathFilterIteratorTest.php",
    "content": "<?php\n\n/*\n * This file is part of the Symfony package.\n *\n * (c) Fabien Potencier <fabien@symfony.com>\n *\n * For the full copyright and license information, please view the LICENSE\n * file that was distributed with this source code.\n */\n\nnamespace Symfony\\Component\\Finder\\Tests\\Iterator;\n\nuse PHPUnit\\Framework\\Attributes\\DataProvider;\nuse Symfony\\Component\\Finder\\Iterator\\PathFilterIterator;\n\nclass PathFilterIteratorTest extends IteratorTestCase\n{\n    #[DataProvider('getTestFilterData')]\n    public function testFilter(\\Iterator $inner, array $matchPatterns, array $noMatchPatterns, array $resultArray)\n    {\n        $iterator = new PathFilterIterator($inner, $matchPatterns, $noMatchPatterns);\n        $this->assertIterator($resultArray, $iterator);\n    }\n\n    public static function getTestFilterData()\n    {\n        $inner = new MockFileListIterator();\n\n        // PATH:   A/B/C/abc.dat\n        $inner[] = new MockSplFileInfo([\n            'name' => 'abc.dat',\n            'relativePathname' => 'A'.\\DIRECTORY_SEPARATOR.'B'.\\DIRECTORY_SEPARATOR.'C'.\\DIRECTORY_SEPARATOR.'abc.dat',\n        ]);\n\n        // PATH:   A/B/ab.dat\n        $inner[] = new MockSplFileInfo([\n            'name' => 'ab.dat',\n            'relativePathname' => 'A'.\\DIRECTORY_SEPARATOR.'B'.\\DIRECTORY_SEPARATOR.'ab.dat',\n        ]);\n\n        // PATH:   A/a.dat\n        $inner[] = new MockSplFileInfo([\n            'name' => 'a.dat',\n            'relativePathname' => 'A'.\\DIRECTORY_SEPARATOR.'a.dat',\n        ]);\n\n        // PATH:   copy/A/B/C/abc.dat.copy\n        $inner[] = new MockSplFileInfo([\n            'name' => 'abc.dat.copy',\n            'relativePathname' => 'copy'.\\DIRECTORY_SEPARATOR.'A'.\\DIRECTORY_SEPARATOR.'B'.\\DIRECTORY_SEPARATOR.'C'.\\DIRECTORY_SEPARATOR.'abc.dat',\n        ]);\n\n        // PATH:   copy/A/B/ab.dat.copy\n        $inner[] = new MockSplFileInfo([\n            'name' => 'ab.dat.copy',\n            'relativePathname' => 'copy'.\\DIRECTORY_SEPARATOR.'A'.\\DIRECTORY_SEPARATOR.'B'.\\DIRECTORY_SEPARATOR.'ab.dat',\n        ]);\n\n        // PATH:   copy/A/a.dat.copy\n        $inner[] = new MockSplFileInfo([\n            'name' => 'a.dat.copy',\n            'relativePathname' => 'copy'.\\DIRECTORY_SEPARATOR.'A'.\\DIRECTORY_SEPARATOR.'a.dat',\n        ]);\n\n        return [\n            [$inner, ['/^A/'],       [], ['abc.dat', 'ab.dat', 'a.dat']],\n            [$inner, ['/^A\\/B/'],    [], ['abc.dat', 'ab.dat']],\n            [$inner, ['/^A\\/B\\/C/'], [], ['abc.dat']],\n            [$inner, ['/A\\/B\\/C/'], [], ['abc.dat', 'abc.dat.copy']],\n\n            [$inner, ['A'],      [], ['abc.dat', 'ab.dat', 'a.dat', 'abc.dat.copy', 'ab.dat.copy', 'a.dat.copy']],\n            [$inner, ['A/B'],    [], ['abc.dat', 'ab.dat', 'abc.dat.copy', 'ab.dat.copy']],\n            [$inner, ['A/B/C'], [], ['abc.dat', 'abc.dat.copy']],\n\n            [$inner, ['copy/A'],      [], ['abc.dat.copy', 'ab.dat.copy', 'a.dat.copy']],\n            [$inner, ['copy/A/B'],    [], ['abc.dat.copy', 'ab.dat.copy']],\n            [$inner, ['copy/A/B/C'], [], ['abc.dat.copy']],\n        ];\n    }\n}\n"
  },
  {
    "path": "Tests/Iterator/RealIteratorTestCase.php",
    "content": "<?php\n\n/*\n * This file is part of the Symfony package.\n *\n * (c) Fabien Potencier <fabien@symfony.com>\n *\n * For the full copyright and license information, please view the LICENSE\n * file that was distributed with this source code.\n */\n\nnamespace Symfony\\Component\\Finder\\Tests\\Iterator;\n\nuse Symfony\\Component\\Filesystem\\Filesystem;\nuse Symfony\\Component\\Finder\\Tests\\FinderTest;\n\nabstract class RealIteratorTestCase extends IteratorTestCase\n{\n    protected static string $tmpDir;\n    protected static array $files;\n\n    public static function setUpBeforeClass(): void\n    {\n        self::$tmpDir = realpath(sys_get_temp_dir()).\\DIRECTORY_SEPARATOR.'symfony_finder';\n\n        self::$files = [\n            '.git/',\n            '.foo/',\n            '.foo/.bar',\n            '.foo/bar',\n            '.bar',\n            'test.py',\n            'foo/',\n            'foo/bar.tmp',\n            'test.php',\n            'toto/',\n            'toto/.git/',\n            'foo bar',\n            'qux_0_1.php',\n            'qux_2_0.php',\n            'qux_10_2.php',\n            'qux_12_0.php',\n            'qux_1000_1.php',\n            'qux_1002_0.php',\n            'qux/',\n            'qux/baz_1_2.py',\n            'qux/baz_100_1.py',\n            'zebulon.php',\n            'Zephire.php',\n        ];\n\n        if (FinderTest::class === static::class) {\n            self::$files[] = 'gitignore/';\n        }\n\n        self::$files = self::toAbsolute(self::$files);\n\n        if (is_dir(self::$tmpDir)) {\n            self::tearDownAfterClass();\n        }\n        mkdir(self::$tmpDir);\n\n        foreach (self::$files as $file) {\n            if (\\DIRECTORY_SEPARATOR === $file[\\strlen($file) - 1]) {\n                mkdir($file);\n            } else {\n                touch($file);\n            }\n        }\n\n        file_put_contents(self::toAbsolute('test.php'), str_repeat(' ', 800));\n        file_put_contents(self::toAbsolute('test.py'), str_repeat(' ', 2000));\n\n        $oneYearAgo = strtotime('-1 year');\n        touch(self::toAbsolute('foo/bar.tmp'), $oneYearAgo);\n        touch(self::toAbsolute('test.php'), $oneYearAgo);\n\n        if (FinderTest::class === static::class) {\n            $fs = new Filesystem();\n            $fs->mirror(__DIR__.'/../Fixtures/gitignore', self::toAbsolute('gitignore'));\n\n            foreach ([\n                'gitignore/search_root/a.txt',\n                'gitignore/search_root/c.txt',\n                'gitignore/search_root/dir/b.txt',\n                'gitignore/search_root/dir/c.txt',\n                'gitignore/git_root/search_root/a.txt',\n                'gitignore/git_root/search_root/c.txt',\n                'gitignore/git_root/search_root/dir/b.txt',\n                'gitignore/git_root/search_root/dir/c.txt',\n            ] as $file) {\n                $fs->touch(self::toAbsolute($file));\n            }\n\n            $fs->mkdir(self::toAbsolute('gitignore/git_root/.git'));\n        }\n    }\n\n    public static function tearDownAfterClass(): void\n    {\n        (new Filesystem())->remove(self::$tmpDir);\n    }\n\n    protected static function toAbsolute($files = null)\n    {\n        /*\n         * Without the call to setUpBeforeClass() property can be null.\n         */\n        self::$tmpDir ??= realpath(sys_get_temp_dir()).\\DIRECTORY_SEPARATOR.'symfony_finder';\n\n        if (\\is_array($files)) {\n            $f = [];\n            foreach ($files as $file) {\n                if (\\is_array($file)) {\n                    $f[] = self::toAbsolute($file);\n                } else {\n                    $f[] = self::$tmpDir.\\DIRECTORY_SEPARATOR.str_replace('/', \\DIRECTORY_SEPARATOR, $file);\n                }\n            }\n\n            return $f;\n        }\n\n        if (\\is_string($files)) {\n            return self::$tmpDir.\\DIRECTORY_SEPARATOR.str_replace('/', \\DIRECTORY_SEPARATOR, $files);\n        }\n\n        return self::$tmpDir;\n    }\n\n    protected static function toAbsoluteFixtures($files)\n    {\n        $f = [];\n        foreach ($files as $file) {\n            $f[] = realpath(__DIR__.\\DIRECTORY_SEPARATOR.'..'.\\DIRECTORY_SEPARATOR.'Fixtures'.\\DIRECTORY_SEPARATOR.$file);\n        }\n\n        return $f;\n    }\n}\n"
  },
  {
    "path": "Tests/Iterator/RecursiveDirectoryIteratorTest.php",
    "content": "<?php\n\n/*\n * This file is part of the Symfony package.\n *\n * (c) Fabien Potencier <fabien@symfony.com>\n *\n * For the full copyright and license information, please view the LICENSE\n * file that was distributed with this source code.\n */\n\nnamespace Symfony\\Component\\Finder\\Tests\\Iterator;\n\nuse PHPUnit\\Framework\\Attributes\\Group;\nuse Symfony\\Component\\Finder\\Iterator\\RecursiveDirectoryIterator;\n\nclass RecursiveDirectoryIteratorTest extends IteratorTestCase\n{\n    protected function setUp(): void\n    {\n        if (!\\in_array('ftp', stream_get_wrappers(), true) || !\\ini_get('allow_url_fopen')) {\n            $this->markTestSkipped('Unsupported stream \"ftp\".');\n        }\n    }\n\n    #[Group('network')]\n    #[Group('integration')]\n    public function testRewindOnFtp()\n    {\n        if (!getenv('INTEGRATION_FTP_URL')) {\n            self::markTestSkipped('INTEGRATION_FTP_URL env var is not defined.');\n        }\n\n        $i = new RecursiveDirectoryIterator(getenv('INTEGRATION_FTP_URL').\\DIRECTORY_SEPARATOR, \\RecursiveDirectoryIterator::SKIP_DOTS);\n\n        $i->rewind();\n\n        $this->expectNotToPerformAssertions();\n    }\n\n    #[Group('network')]\n    #[Group('integration')]\n    public function testSeekOnFtp()\n    {\n        if (!getenv('INTEGRATION_FTP_URL')) {\n            self::markTestSkipped('INTEGRATION_FTP_URL env var is not defined.');\n        }\n\n        $ftpUrl = getenv('INTEGRATION_FTP_URL');\n\n        $i = new RecursiveDirectoryIterator($ftpUrl.\\DIRECTORY_SEPARATOR, \\RecursiveDirectoryIterator::SKIP_DOTS);\n\n        $contains = [\n            $ftpUrl.\\DIRECTORY_SEPARATOR.'pub',\n            $ftpUrl.\\DIRECTORY_SEPARATOR.'readme.txt',\n        ];\n        $actual = [];\n\n        $i->seek(0);\n        $actual[] = $i->getPathname();\n\n        $i->seek(1);\n        $actual[] = $i->getPathname();\n\n        $this->assertEquals($contains, $actual);\n    }\n\n    public function testTrailingDirectorySeparatorIsStripped()\n    {\n        $fixturesDirectory = __DIR__.'/../Fixtures/';\n        $actual = [];\n\n        foreach (new RecursiveDirectoryIterator($fixturesDirectory, RecursiveDirectoryIterator::SKIP_DOTS) as $file) {\n            $actual[] = $file->getPathname();\n        }\n\n        sort($actual);\n\n        $expected = [\n            $fixturesDirectory.'.dot',\n            $fixturesDirectory.'A',\n            $fixturesDirectory.'copy',\n            $fixturesDirectory.'dolor.txt',\n            $fixturesDirectory.'gitignore',\n            $fixturesDirectory.'ipsum.txt',\n            $fixturesDirectory.'lorem.txt',\n            $fixturesDirectory.'one',\n            $fixturesDirectory.'r+e.gex[c]a(r)s',\n            $fixturesDirectory.'with space',\n        ];\n\n        $this->assertEquals($expected, $actual);\n    }\n}\n"
  },
  {
    "path": "Tests/Iterator/SizeRangeFilterIteratorTest.php",
    "content": "<?php\n\n/*\n * This file is part of the Symfony package.\n *\n * (c) Fabien Potencier <fabien@symfony.com>\n *\n * For the full copyright and license information, please view the LICENSE\n * file that was distributed with this source code.\n */\n\nnamespace Symfony\\Component\\Finder\\Tests\\Iterator;\n\nuse PHPUnit\\Framework\\Attributes\\DataProvider;\nuse Symfony\\Component\\Finder\\Comparator\\NumberComparator;\nuse Symfony\\Component\\Finder\\Iterator\\SizeRangeFilterIterator;\n\nclass SizeRangeFilterIteratorTest extends RealIteratorTestCase\n{\n    #[DataProvider('getAcceptData')]\n    public function testAccept($size, $expected)\n    {\n        $inner = new InnerSizeIterator(self::$files);\n\n        $iterator = new SizeRangeFilterIterator($inner, $size);\n\n        $this->assertIterator($expected, $iterator);\n    }\n\n    public static function getAcceptData()\n    {\n        $lessThan1KGreaterThan05K = [\n            '.foo',\n            '.git',\n            'foo',\n            'qux',\n            'test.php',\n            'toto',\n            'toto/.git',\n        ];\n\n        return [\n            [[new NumberComparator('< 1K'), new NumberComparator('> 0.5K')], self::toAbsolute($lessThan1KGreaterThan05K)],\n        ];\n    }\n}\n\nclass InnerSizeIterator extends \\ArrayIterator\n{\n    public function current(): \\SplFileInfo\n    {\n        return new \\SplFileInfo(parent::current());\n    }\n\n    public function getFilename(): string\n    {\n        return parent::current();\n    }\n\n    public function isFile(): bool\n    {\n        return $this->current()->isFile();\n    }\n\n    public function getSize(): int\n    {\n        return $this->current()->getSize();\n    }\n}\n"
  },
  {
    "path": "Tests/Iterator/SortableIteratorTest.php",
    "content": "<?php\n\n/*\n * This file is part of the Symfony package.\n *\n * (c) Fabien Potencier <fabien@symfony.com>\n *\n * For the full copyright and license information, please view the LICENSE\n * file that was distributed with this source code.\n */\n\nnamespace Symfony\\Component\\Finder\\Tests\\Iterator;\n\nuse PHPUnit\\Framework\\Attributes\\DataProvider;\nuse Symfony\\Component\\Finder\\Iterator\\SortableIterator;\n\nclass SortableIteratorTest extends RealIteratorTestCase\n{\n    public function testConstructor()\n    {\n        try {\n            new SortableIterator(new Iterator([]), -255);\n            $this->fail('__construct() throws an \\InvalidArgumentException exception if the mode is not valid');\n        } catch (\\Exception $e) {\n            $this->assertInstanceOf(\\InvalidArgumentException::class, $e, '__construct() throws an \\InvalidArgumentException exception if the mode is not valid');\n        }\n    }\n\n    #[DataProvider('getAcceptData')]\n    public function testAccept($mode, $expected)\n    {\n        if (!\\is_callable($mode)) {\n            switch ($mode) {\n                case SortableIterator::SORT_BY_ACCESSED_TIME:\n                    touch(self::toAbsolute('.git'));\n                    sleep(1);\n                    touch(self::toAbsolute('.bar'), time());\n                    break;\n                case SortableIterator::SORT_BY_CHANGED_TIME:\n                    sleep(1);\n                    file_put_contents(self::toAbsolute('test.php'), 'foo');\n                    sleep(1);\n                    file_put_contents(self::toAbsolute('test.py'), 'foo');\n                    break;\n                case SortableIterator::SORT_BY_MODIFIED_TIME:\n                    file_put_contents(self::toAbsolute('test.php'), 'foo');\n                    sleep(1);\n                    file_put_contents(self::toAbsolute('test.py'), 'foo');\n                    break;\n            }\n        }\n\n        $inner = new Iterator(self::$files);\n\n        $iterator = new SortableIterator($inner, $mode);\n\n        if (SortableIterator::SORT_BY_ACCESSED_TIME === $mode\n            || SortableIterator::SORT_BY_CHANGED_TIME === $mode\n            || SortableIterator::SORT_BY_MODIFIED_TIME === $mode\n        ) {\n            if ('\\\\' === \\DIRECTORY_SEPARATOR && SortableIterator::SORT_BY_MODIFIED_TIME !== $mode) {\n                $this->markTestSkipped('Sorting by atime or ctime is not supported on Windows');\n            }\n            $this->assertOrderedIteratorForGroups($expected, $iterator);\n        } else {\n            $this->assertOrderedIterator($expected, $iterator);\n        }\n    }\n\n    public static function getAcceptData()\n    {\n        $sortByName = [\n            '.bar',\n            '.foo',\n            '.foo/.bar',\n            '.foo/bar',\n            '.git',\n            'Zephire.php',\n            'foo',\n            'foo bar',\n            'foo/bar.tmp',\n            'qux',\n            'qux/baz_100_1.py',\n            'qux/baz_1_2.py',\n            'qux_0_1.php',\n            'qux_1000_1.php',\n            'qux_1002_0.php',\n            'qux_10_2.php',\n            'qux_12_0.php',\n            'qux_2_0.php',\n            'test.php',\n            'test.py',\n            'toto',\n            'toto/.git',\n            'zebulon.php',\n        ];\n\n        $sortByType = [\n            '.foo',\n            '.git',\n            'foo',\n            'qux',\n            'toto',\n            'toto/.git',\n            '.bar',\n            '.foo/.bar',\n            '.foo/bar',\n            'Zephire.php',\n            'foo bar',\n            'foo/bar.tmp',\n            'qux/baz_100_1.py',\n            'qux/baz_1_2.py',\n            'qux_0_1.php',\n            'qux_1000_1.php',\n            'qux_1002_0.php',\n            'qux_10_2.php',\n            'qux_12_0.php',\n            'qux_2_0.php',\n            'test.php',\n            'test.py',\n            'zebulon.php',\n        ];\n\n        $sortByAccessedTime = [\n            // For these two files the access time was set to 2005-10-15\n            ['foo/bar.tmp', 'test.php'],\n            // These files were created more or less at the same time\n            [\n                '.git',\n                '.foo',\n                '.foo/.bar',\n                '.foo/bar',\n                'test.py',\n                'Zephire.php',\n                'foo',\n                'toto',\n                'toto/.git',\n                'foo bar',\n                'qux',\n                'qux/baz_100_1.py',\n                'qux/baz_1_2.py',\n                'qux_0_1.php',\n                'qux_1000_1.php',\n                'qux_1002_0.php',\n                'qux_10_2.php',\n                'qux_12_0.php',\n                'qux_2_0.php',\n                'zebulon.php',\n            ],\n            // This file was accessed after sleeping for 1 sec\n            ['.bar'],\n        ];\n\n        $sortByChangedTime = [\n            [\n                '.git',\n                '.foo',\n                '.foo/.bar',\n                '.foo/bar',\n                '.bar',\n                'Zephire.php',\n                'foo',\n                'foo/bar.tmp',\n                'toto',\n                'toto/.git',\n                'foo bar',\n                'qux',\n                'qux/baz_100_1.py',\n                'qux/baz_1_2.py',\n                'qux_0_1.php',\n                'qux_1000_1.php',\n                'qux_1002_0.php',\n                'qux_10_2.php',\n                'qux_12_0.php',\n                'qux_2_0.php',\n                'zebulon.php',\n            ],\n            ['test.php'],\n            ['test.py'],\n        ];\n\n        $sortByModifiedTime = [\n            [\n                '.git',\n                '.foo',\n                '.foo/.bar',\n                '.foo/bar',\n                '.bar',\n                'Zephire.php',\n                'foo',\n                'foo/bar.tmp',\n                'toto',\n                'toto/.git',\n                'foo bar',\n                'qux',\n                'qux/baz_100_1.py',\n                'qux/baz_1_2.py',\n                'qux_0_1.php',\n                'qux_1000_1.php',\n                'qux_1002_0.php',\n                'qux_10_2.php',\n                'qux_12_0.php',\n                'qux_2_0.php',\n                'zebulon.php',\n            ],\n            ['test.php'],\n            ['test.py'],\n        ];\n\n        $sortByNameNatural = [\n            '.bar',\n            '.foo',\n            '.foo/.bar',\n            '.foo/bar',\n            '.git',\n            'Zephire.php',\n            'foo',\n            'foo/bar.tmp',\n            'foo bar',\n            'qux',\n            'qux/baz_1_2.py',\n            'qux/baz_100_1.py',\n            'qux_0_1.php',\n            'qux_2_0.php',\n            'qux_10_2.php',\n            'qux_12_0.php',\n            'qux_1000_1.php',\n            'qux_1002_0.php',\n            'test.php',\n            'test.py',\n            'toto',\n            'toto/.git',\n            'zebulon.php',\n        ];\n\n        $customComparison = [\n            '.bar',\n            '.foo',\n            '.foo/.bar',\n            '.foo/bar',\n            '.git',\n            'Zephire.php',\n            'foo',\n            'foo bar',\n            'foo/bar.tmp',\n            'qux',\n            'qux/baz_100_1.py',\n            'qux/baz_1_2.py',\n            'qux_0_1.php',\n            'qux_1000_1.php',\n            'qux_1002_0.php',\n            'qux_10_2.php',\n            'qux_12_0.php',\n            'qux_2_0.php',\n            'test.php',\n            'test.py',\n            'toto',\n            'toto/.git',\n            'zebulon.php',\n        ];\n\n        return [\n            [SortableIterator::SORT_BY_NAME, self::toAbsolute($sortByName)],\n            [SortableIterator::SORT_BY_TYPE, self::toAbsolute($sortByType)],\n            [SortableIterator::SORT_BY_ACCESSED_TIME, self::toAbsolute($sortByAccessedTime)],\n            [SortableIterator::SORT_BY_CHANGED_TIME, self::toAbsolute($sortByChangedTime)],\n            [SortableIterator::SORT_BY_MODIFIED_TIME, self::toAbsolute($sortByModifiedTime)],\n            [SortableIterator::SORT_BY_NAME_NATURAL, self::toAbsolute($sortByNameNatural)],\n            [static fn (\\SplFileInfo $a, \\SplFileInfo $b) => strcmp($a->getRealPath(), $b->getRealPath()), self::toAbsolute($customComparison)],\n        ];\n    }\n}\n"
  },
  {
    "path": "Tests/Iterator/VcsIgnoredFilterIteratorTest.php",
    "content": "<?php\n\n/*\n * This file is part of the Symfony package.\n *\n * (c) Fabien Potencier <fabien@symfony.com>\n *\n * For the full copyright and license information, please view the LICENSE\n * file that was distributed with this source code.\n */\n\nnamespace Symfony\\Component\\Finder\\Tests\\Iterator;\n\nuse PHPUnit\\Framework\\Attributes\\DataProvider;\nuse Symfony\\Component\\Finder\\Finder;\nuse Symfony\\Component\\Finder\\Iterator\\VcsIgnoredFilterIterator;\n\nclass VcsIgnoredFilterIteratorTest extends IteratorTestCase\n{\n    private string $tmpDir;\n\n    protected function setUp(): void\n    {\n        $this->tmpDir = realpath(sys_get_temp_dir()).\\DIRECTORY_SEPARATOR.'symfony_finder_vcs_ignored';\n        mkdir($this->tmpDir);\n    }\n\n    protected function tearDown(): void\n    {\n        $this->removeDirectory($this->tmpDir);\n    }\n\n    /**\n     * @param array<string, string> $gitIgnoreFiles\n     */\n    #[DataProvider('getAcceptData')]\n    public function testAccept(array $gitIgnoreFiles, array $otherFileNames, array $expectedResult, string $baseDir = '')\n    {\n        $otherFileNames = $this->toAbsolute($otherFileNames);\n        foreach ($otherFileNames as $path) {\n            if (str_ends_with($path, '/')) {\n                mkdir($path);\n            } else {\n                touch($path);\n            }\n        }\n\n        foreach ($gitIgnoreFiles as $path => $content) {\n            file_put_contents(\"{$this->tmpDir}/{$path}\", $content);\n        }\n\n        $inner = new InnerNameIterator($otherFileNames);\n\n        $baseDir = $this->tmpDir.('' !== $baseDir ? '/'.$baseDir : '');\n        $iterator = new VcsIgnoredFilterIterator($inner, $baseDir);\n\n        $this->assertIterator($this->toAbsolute($expectedResult), $iterator);\n    }\n\n    public static function getAcceptData(): iterable\n    {\n        yield 'simple file' => [\n            [\n                '.gitignore' => 'a.txt',\n            ],\n            [\n                'a.txt',\n                'b.txt',\n                'dir/',\n                'dir/a.txt',\n            ],\n            [\n                'b.txt',\n                'dir',\n            ],\n        ];\n\n        yield 'simple file - .gitignore and in() from repository root' => [\n            [\n                '.gitignore' => 'a.txt',\n            ],\n            [\n                '.git',\n                'a.txt',\n                'b.txt',\n                'dir/',\n                'dir/a.txt',\n            ],\n            [\n                '.git',\n                'b.txt',\n                'dir',\n            ],\n        ];\n\n        yield 'nested git repositories only consider .gitignore files of the most inner repository' => [\n            [\n                '.gitignore' => \"nested/*\\na.txt\",\n                'nested/.gitignore' => 'c.txt',\n                'nested/dir/.gitignore' => 'f.txt',\n            ],\n            [\n                '.git',\n                'a.txt',\n                'b.txt',\n                'nested/',\n                'nested/.git',\n                'nested/c.txt',\n                'nested/d.txt',\n                'nested/dir/',\n                'nested/dir/e.txt',\n                'nested/dir/f.txt',\n            ],\n            [\n                '.git',\n                'a.txt',\n                'b.txt',\n                'nested',\n                'nested/.git',\n                'nested/d.txt',\n                'nested/dir',\n                'nested/dir/e.txt',\n            ],\n            'nested',\n        ];\n\n        yield 'simple file at root' => [\n            [\n                '.gitignore' => '/a.txt',\n            ],\n            [\n                'a.txt',\n                'b.txt',\n                'dir/',\n                'dir/a.txt',\n            ],\n            [\n                'b.txt',\n                'dir',\n                'dir/a.txt',\n            ],\n        ];\n\n        yield 'directory' => [\n            [\n                '.gitignore' => 'dir/',\n            ],\n            [\n                'a.txt',\n                'dir/',\n                'dir/a.txt',\n                'dir/b.txt',\n            ],\n            [\n                'a.txt',\n            ],\n        ];\n\n        yield 'directory matching a file' => [\n            [\n                '.gitignore' => 'dir.txt/',\n            ],\n            [\n                'dir.txt',\n            ],\n            [\n                'dir.txt',\n            ],\n        ];\n\n        yield 'directory at root' => [\n            [\n                '.gitignore' => '/dir/',\n            ],\n            [\n                'dir/',\n                'dir/a.txt',\n                'other/',\n                'other/dir/',\n                'other/dir/b.txt',\n            ],\n            [\n                'other',\n                'other/dir',\n                'other/dir/b.txt',\n            ],\n        ];\n\n        yield 'simple file in nested .gitignore' => [\n            [\n                'nested/.gitignore' => 'a.txt',\n            ],\n            [\n                'a.txt',\n                'nested/',\n                'nested/a.txt',\n                'nested/nested/',\n                'nested/nested/a.txt',\n            ],\n            [\n                'a.txt',\n                'nested',\n                'nested/nested',\n            ],\n        ];\n\n        yield 'simple file at root of nested .gitignore' => [\n            [\n                'nested/.gitignore' => '/a.txt',\n            ],\n            [\n                'a.txt',\n                'nested/',\n                'nested/a.txt',\n                'nested/nested/',\n                'nested/nested/a.txt',\n            ],\n            [\n                'a.txt',\n                'nested',\n                'nested/nested',\n                'nested/nested/a.txt',\n            ],\n        ];\n\n        yield 'directory in nested .gitignore' => [\n            [\n                'nested/.gitignore' => 'dir/',\n            ],\n            [\n                'a.txt',\n                'dir/',\n                'dir/a.txt',\n                'nested/',\n                'nested/dir/',\n                'nested/dir/a.txt',\n                'nested/nested/',\n                'nested/nested/dir/',\n                'nested/nested/dir/a.txt',\n            ],\n            [\n                'a.txt',\n                'dir',\n                'dir/a.txt',\n                'nested',\n                'nested/nested',\n            ],\n        ];\n\n        yield 'directory matching a file in nested .gitignore' => [\n            [\n                'nested/.gitignore' => 'dir.txt/',\n            ],\n            [\n                'dir.txt',\n                'nested/',\n                'nested/dir.txt',\n            ],\n            [\n                'dir.txt',\n                'nested',\n                'nested/dir.txt',\n            ],\n        ];\n\n        yield 'directory at root of nested .gitignore' => [\n            [\n                'nested/.gitignore' => '/dir/',\n            ],\n            [\n                'a.txt',\n                'dir/',\n                'dir/a.txt',\n                'nested/',\n                'nested/dir/',\n                'nested/dir/a.txt',\n                'nested/nested/',\n                'nested/nested/dir/',\n                'nested/nested/dir/a.txt',\n            ],\n            [\n                'a.txt',\n                'dir',\n                'dir/a.txt',\n                'nested',\n                'nested/nested',\n                'nested/nested/dir',\n                'nested/nested/dir/a.txt',\n            ],\n        ];\n\n        yield 'negated pattern in nested .gitignore' => [\n            [\n                '.gitignore' => '*.txt',\n                'nested/.gitignore' => \"!a.txt\\ndir/\",\n            ],\n            [\n                'a.txt',\n                'b.txt',\n                'nested/',\n                'nested/a.txt',\n                'nested/b.txt',\n                'nested/dir/',\n                'nested/dir/a.txt',\n                'nested/dir/b.txt',\n            ],\n            [\n                'nested',\n                'nested/a.txt',\n            ],\n        ];\n\n        yield 'negated pattern in ignored nested .gitignore' => [\n            [\n                '.gitignore' => \"*.txt\\n/nested/\",\n                'nested/.gitignore' => \"!a.txt\\ndir/\",\n            ],\n            [\n                'a.txt',\n                'b.txt',\n                'nested/',\n                'nested/a.txt',\n                'nested/b.txt',\n                'nested/dir/',\n                'nested/dir/a.txt',\n                'nested/dir/b.txt',\n            ],\n            [],\n        ];\n\n        yield 'directory pattern negated in a subdirectory' => [\n            [\n                '.gitignore' => 'c/',\n                'a/.gitignore' => '!c/',\n            ],\n            [\n                'a/',\n                'a/b/',\n                'a/b/c/',\n                'a/b/c/d.txt',\n            ],\n            [\n                'a',\n                'a/b',\n                'a/b/c',\n                'a/b/c/d.txt',\n            ],\n        ];\n\n        yield 'file included from subdirectory with everything excluded' => [\n            [\n                '.gitignore' => \"/a/**\\n!/a/b.txt\",\n            ],\n            [\n                'a/',\n                'a/a.txt',\n                'a/b.txt',\n                'a/c.txt',\n            ],\n            [\n                'a/b.txt',\n            ],\n        ];\n    }\n\n    public function testAcceptAtRootDirectory()\n    {\n        $inner = new InnerNameIterator([__FILE__]);\n\n        $iterator = new VcsIgnoredFilterIterator($inner, '/');\n\n        $this->assertIterator([__FILE__], $iterator);\n    }\n\n    private function toAbsolute(array $files): array\n    {\n        foreach ($files as &$path) {\n            $path = \"{$this->tmpDir}/{$path}\";\n        }\n\n        return $files;\n    }\n\n    private function removeDirectory(string $dir): void\n    {\n        foreach ((new Finder())->in($dir)->ignoreDotFiles(false)->depth('< 1') as $file) {\n            $path = $file->getRealPath();\n\n            if ($file->isDir()) {\n                $this->removeDirectory($path);\n            } else {\n                unlink($path);\n            }\n        }\n\n        rmdir($dir);\n    }\n}\n"
  },
  {
    "path": "Tests/Iterator/VfsIteratorTestTrait.php",
    "content": "<?php\n\n/*\n * This file is part of the Symfony package.\n *\n * (c) Fabien Potencier <fabien@symfony.com>\n *\n * For the full copyright and license information, please view the LICENSE\n * file that was distributed with this source code.\n */\n\nnamespace Symfony\\Component\\Finder\\Tests\\Iterator;\n\ntrait VfsIteratorTestTrait\n{\n    private static int $vfsNextSchemeIndex = 0;\n\n    /** @var array<string, \\Closure(string, 'list_dir_open'|'list_dir_rewind'|'is_dir'): (list<string>|bool)> */\n    public static array $vfsProviders;\n\n    protected string $vfsScheme;\n\n    /** @var list<array{string, string, mixed}> */\n    protected array $vfsLog = [];\n\n    protected function setUp(): void\n    {\n        parent::setUp();\n\n        $this->vfsScheme = 'symfony-finder-vfs-test-'.++self::$vfsNextSchemeIndex;\n\n        $vfsWrapperClass = \\get_class(new class {\n            /** @var array<string, \\Closure(string, 'list_dir_open'|'list_dir_rewind'|'is_dir'): (list<string>|bool)> */\n            public static array $vfsProviders = [];\n\n            /** @var resource */\n            public $context;\n\n            private string $scheme;\n\n            private string $dirPath;\n\n            /** @var list<string> */\n            private array $dirData;\n\n            private function parsePathAndSetScheme(string $url): string\n            {\n                $urlArr = parse_url($url);\n                \\assert(\\is_array($urlArr));\n                \\assert(isset($urlArr['scheme']));\n                \\assert(isset($urlArr['host']));\n\n                $this->scheme = $urlArr['scheme'];\n\n                return str_replace(\\DIRECTORY_SEPARATOR, '/', $urlArr['host'].($urlArr['path'] ?? ''));\n            }\n\n            public function processListDir(bool $fromRewind): bool\n            {\n                $providerFx = self::$vfsProviders[$this->scheme];\n                $data = $providerFx($this->dirPath, 'list_dir'.($fromRewind ? '_rewind' : '_open'));\n                \\assert(\\is_array($data));\n                $this->dirData = $data;\n\n                return true;\n            }\n\n            public function dir_opendir(string $url): bool\n            {\n                $this->dirPath = $this->parsePathAndSetScheme($url);\n\n                return $this->processListDir(false);\n            }\n\n            public function dir_readdir(): string|false\n            {\n                return array_shift($this->dirData) ?? false;\n            }\n\n            public function dir_closedir(): bool\n            {\n                unset($this->dirPath);\n                unset($this->dirData);\n\n                return true;\n            }\n\n            public function dir_rewinddir(): bool\n            {\n                return $this->processListDir(true);\n            }\n\n            /**\n             * @return array<string, mixed>\n             */\n            public function stream_stat(): array\n            {\n                return [];\n            }\n\n            /**\n             * @return array<string, mixed>\n             */\n            public function url_stat(string $url): array\n            {\n                $path = $this->parsePathAndSetScheme($url);\n                $providerFx = self::$vfsProviders[$this->scheme];\n                $isDir = $providerFx($path, 'is_dir');\n                \\assert(\\is_bool($isDir));\n\n                return ['mode' => $isDir ? 0o040755 : 0o100644];\n            }\n        });\n        self::$vfsProviders = &$vfsWrapperClass::$vfsProviders;\n\n        stream_wrapper_register($this->vfsScheme, $vfsWrapperClass);\n    }\n\n    protected function tearDown(): void\n    {\n        stream_wrapper_unregister($this->vfsScheme);\n\n        parent::tearDown();\n    }\n\n    /**\n     * @param array<string, mixed> $data\n     */\n    protected function setupVfsProvider(array $data): void\n    {\n        self::$vfsProviders[$this->vfsScheme] = function (string $path, string $op) use ($data) {\n            $pathArr = explode('/', $path);\n            $fileEntry = $data;\n            while (($name = array_shift($pathArr)) !== null) {\n                if (!isset($fileEntry[$name])) {\n                    $fileEntry = false;\n\n                    break;\n                }\n\n                $fileEntry = $fileEntry[$name];\n            }\n\n            if ('list_dir_open' === $op || 'list_dir_rewind' === $op) {\n                /** @var list<string> $res */\n                $res = array_keys($fileEntry);\n            } elseif ('is_dir' === $op) {\n                $res = \\is_array($fileEntry);\n            } else {\n                throw new \\Exception('Unexpected operation type');\n            }\n\n            $this->vfsLog[] = [$path, $op, $res];\n\n            return $res;\n        };\n    }\n\n    protected function stripSchemeFromVfsPath(string $url): string\n    {\n        $urlArr = parse_url($url);\n        \\assert(\\is_array($urlArr));\n        \\assert($urlArr['scheme'] === $this->vfsScheme);\n        \\assert(isset($urlArr['host']));\n\n        return str_replace(\\DIRECTORY_SEPARATOR, '/', $urlArr['host'].($urlArr['path'] ?? ''));\n    }\n\n    protected function assertSameVfsIterator(array $expected, \\Traversable $iterator)\n    {\n        $values = array_map(fn (\\SplFileInfo $fileinfo) => $this->stripSchemeFromVfsPath($fileinfo->getPathname()), iterator_to_array($iterator));\n\n        $this->assertEquals($expected, array_values($values));\n    }\n}\n"
  },
  {
    "path": "composer.json",
    "content": "{\n    \"name\": \"symfony/finder\",\n    \"type\": \"library\",\n    \"description\": \"Finds files and directories via an intuitive fluent interface\",\n    \"keywords\": [],\n    \"homepage\": \"https://symfony.com\",\n    \"license\": \"MIT\",\n    \"authors\": [\n        {\n            \"name\": \"Fabien Potencier\",\n            \"email\": \"fabien@symfony.com\"\n        },\n        {\n            \"name\": \"Symfony Community\",\n            \"homepage\": \"https://symfony.com/contributors\"\n        }\n    ],\n    \"require\": {\n        \"php\": \">=8.4\"\n    },\n    \"require-dev\": {\n        \"symfony/filesystem\": \"^7.4|^8.0\"\n    },\n    \"autoload\": {\n        \"psr-4\": { \"Symfony\\\\Component\\\\Finder\\\\\": \"\" },\n        \"exclude-from-classmap\": [\n            \"/Tests/\"\n        ]\n    },\n    \"minimum-stability\": \"dev\"\n}\n"
  },
  {
    "path": "phpunit.xml.dist",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n\n<phpunit xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:noNamespaceSchemaLocation=\"https://schema.phpunit.de/11.3/phpunit.xsd\"\n         backupGlobals=\"false\"\n         colors=\"true\"\n         bootstrap=\"vendor/autoload.php\"\n         failOnDeprecation=\"true\"\n         failOnRisky=\"true\"\n         failOnWarning=\"true\"\n>\n    <php>\n        <ini name=\"error_reporting\" value=\"-1\" />\n    </php>\n\n    <testsuites>\n        <testsuite name=\"Symfony Finder Component Test Suite\">\n            <directory>./Tests/</directory>\n        </testsuite>\n    </testsuites>\n\n    <source ignoreSuppressionOfDeprecations=\"true\">\n        <include>\n            <directory>./</directory>\n        </include>\n        <exclude>\n            <directory>./Tests</directory>\n            <directory>./vendor</directory>\n        </exclude>\n    </source>\n\n    <extensions>\n        <bootstrap class=\"Symfony\\Bridge\\PhpUnit\\SymfonyExtension\" />\n    </extensions>\n</phpunit>\n"
  }
]