phpunit/php-file-iterator
php-file-iterator provides a simple, dependency-free way to filter and iterate over files by suffix, prefix, or custom filters, powering PHPUnit's test file discovery. Install it via Composer to find and list files matching patterns in any PHP project.
Architecture fit. phpunit/php-file-iterator is a single-purpose utility: a FilterIterator that walks a directory tree and yields files matching suffix, prefix, and exclusion rules, plus a Factory that resolves glob/globstar patterns into iterators. It has no framework coupling — no service provider, no container bindings, no configuration surface, no persistence, no I/O beyond RecursiveDirectoryIterator. That makes it architecturally harmless but also architecturally redundant for a Laravel codebase: Laravel already ships symfony/finder transitively via laravel/framework, and Finder covers the same problem space with a richer API and a far larger public documentation corpus. The realistic architecture fit is therefore not "add this to our application" but "this is already in our dependency graph as a PHPUnit implementation detail, and we should govern its version rather than adopt its API."
Integration feasibility. Trivially feasible mechanically — one composer require --dev phpunit/php-file-iterator, zero wiring. The constraint is not effort but intent. In practice the package arrives as a transitive dependency of phpunit/phpunit through phpunit/php-code-coverage; the Dependents: 0 figure in the supplied data is a Packagist reverse-dependency artifact and should not be read as "no one uses this." Feasibility for direct use is real but narrow: custom test runners, static-analysis prelinters, code-generation scanners, or CI tooling that needs to enumerate PHP files before parsing. Feasibility for application runtime use is effectively nil — using a PHPUnit-internal utility to enumerate files in a request path introduces a dev-only dependency into production bootstrap for no gain over Finder.
Technical risk. Low severity, moderate churn:
** globstar resolution bug, and pruned hidden directories instead of traversing and discarding. Several of these are correctness fixes to filtering — meaning pinning to an older minor can silently produce the wrong file set, which for a test-discovery or coverage tool is a quiet, hard-to-diagnose failure.readme and releases only. There is no API reference, no migration guide, and no documented behavioral contract for globstar, case sensitivity, or exclusion-prefix matching. Correct usage is learned from source and tests.Key questions to resolve before committing engineering time:
composer.lock transitively, and at which major version?Iterator or Factory directly, versus only consuming it through PHPUnit?**) semantics, or are simple suffix/prefix filters sufficient?Stack fit. Poor as a first-class Laravel dependency; acceptable as a governed dev-time transitive. The decision rule should be: this package is permitted in require-dev only when pulled by PHPUnit, and direct usage requires a written justification against Symfony Finder.
Migration path. Two directions, depending on inventory outcome:
phpunit/phpunit and never pin it independently, since independent pinning is exactly how the traversal-correctness fixes in 7.0.2 get stranded behind a newer PHPUnit.FileEnumerator adapter exposing one method (filesIn(string $path, array $filters): iterable) implemented over this package today and over symfony/finder as the target. Migrate call sites to the adapter, then retire the dependency by swapping the implementation — a bounded change inside one class. Finder's in(), name(), notPath(), and ignoreVCS() cover the documented feature surface here almost one-to-one; the only genuinely non-obvious translation is globstar, which Finder expresses via ->path() regexes rather than ** pattern syntax.Compatibility. Two axes must be verified per release, not assumed:
Lockfile is the source of truth. Add a CI check that fails when php-file-iterator resolves to a major version newer than the one bundled with our PHPUnit minor, to catch silent drift.
Sequencing.
PHPUnit\File\Iterator / Factory references to establish transitive-versus-direct.Maintenance. Near-zero in the transitive case — no configuration, no runtime footprint, no service to operate. The only recurring cost is upgrade coordination with PHPUnit and PHP. In the direct-usage case the cost shifts to behavioral maintenance: undocumented glob, prefix-exclusion, and case-sensitivity semantics become institutional knowledge held by whoever last debugged a missing-test incident.
Support. Upstream is a GitHub-issues, no-LTS, no-SLA project in the PHPUnit organization. Support model is "fix released, then consumed at the next PHPUnit bump." There is no vendor to escalate to. Acceptable because the blast radius is bounded to test discovery and build tooling, and because co-release with PHPUnit means the ecosystem absorbs breakage quickly.
Scaling. Traversal is lazy and streaming — memory is O(1) in file count, so it scales fine to large repositories. Cost is dominated by filesystem I/O and stat calls, and 7.0.1 explicitly removed per-file realpath() resolution during prefix/suffix filtering, which is a meaningful throughput improvement on network and overlay filesystems. The scaling ceiling is not the iterator; it is the number of times a tool re-walks the tree. Avoid re-instantiating filters inside loops — the Iterator is immutable, with chained methods returning new instances, so a naive addExclude() in a loop allocates a fresh chain each pass.
Failure modes to watch:
** requires path separators on both sides; patterns like **.php are not recursive. A malformed pattern yields an empty or truncated set rather than an error.require rather than require-dev, a PHPUnit-internal package ships to production and becomes subject to runtime upgrade constraints it was never designed for.Ramp-up. The API is learnable in under an hour (Iterator with addSuffix/addPrefix/addExclude, and Factory::getIterator() for globs). The realistic ramp-up is on the semantics, not the API: a new engineer will not intuit globstar boundaries, Windows separator behavior, or exclusion-prefix matching from the README alone. Mitigate by keeping exactly one file-traversal idiom in the codebase — Symfony Finder — and by documenting the adapter contract if direct usage is ever sanctioned. Onboarding cost is near zero precisely to the degree that no one on the team ever calls this package directly.
How can I help you explore Laravel packages today?