sebastian/finder-facade
A convenience wrapper around Symfony Finder that simplifies locating files in PHP projects. It provides a fluent, facade-style API for common search patterns, filtering, and exclusions, making file discovery easier for tools such as PHPUnit and code coverage.
sebastian/finder-facade is a tiny wrapper around symfony/finder that collapses the common “find files by path, name, and exclusion list” setup into one constructor plus findFiles(). It is archived and unmaintained, so treat it as a convenience for existing code, not a new strategic dependency.
Install it (dev-only is usually appropriate):
composer require --dev sebastian/finder-facade
composer show sebastian/finder-facade
Look first at vendor/sebastian/finder-facade/src/FinderFacade.php. It is a single, short class — reading it takes a minute and tells you exactly which Finder defaults are applied. The README is minimal; the source is the real documentation.
Public API shape:
use SebastianBergmann\FinderFacade\FinderFacade;
$facade = new FinderFacade(
[app_path()], // paths to search
['*.php'], // file name patterns
['Console/Kernel.php'] // exclusions
);
$files = $facade->findFiles(); // array of Symfony\Component\Finder\SplFileInfo
A good first use case is an Artisan command that lists candidate PHP files for an audit, code-generation step, or static-analysis pass:
$files = (new FinderFacade(
[app_path()],
['*.php'],
['Console/Kernel.php', 'vendor']
))->findFiles();
foreach ($files as $file) {
$this->line($file->getRealPath());
}
Older releases may expose a findFiles(true) mode that returns path strings; check the installed version before relying on it. In current usage, expect SplFileInfo objects and call getRealPath() or getPathname() yourself.
Wrap it in a small application service. Avoid scattering FinderFacade construction through commands and jobs. A scanner service gives you one place to normalize paths, sort results, and handle missing directories.
namespace App\Support;
use SebastianBergmann\FinderFacade\FinderFacade;
final class PhpFileScanner
{
/** @return list<string> */
public function scan(
array $paths,
array $names = ['*.php'],
array $exclude = []
): array {
return collect((new FinderFacade($paths, $names, $exclude))->findFiles())
->map(fn ($file) => $file->getRealPath())
->sort()
->values()
->all();
}
}
Bind it as a singleton in a service provider if you want to inject it into commands, jobs, or other services:
$this->app->singleton(\App\Support\PhpFileScanner::class);
Drive it from config, not hard-coded paths. A config/scanner.php file keeps environment-specific exclusions out of the command:
return [
'paths' => [app_path(), database_path('migrations')],
'names' => ['*.php'],
'exclude' => ['vendor', 'node_modules', 'storage'],
];
Then combine config defaults with CLI overrides:
$paths = $this->option('path') ?: config('scanner.paths');
$names = $this->option('name') ?: config('scanner.names');
$exclude = $this->option('exclude') ?: config('scanner.exclude');
Use it for discovery, then hand off to Laravel for content. The facade only finds files. Use File::get(), SplFileInfo::getContents(), or token_get_all() once you have the list. Typical day-to-day uses in a Laravel app:
app/ to build a class map or registration list.migrate:status-style report.Make output deterministic. Finder does not promise a stable order, and filesystem order varies between machines. Always sort() or use Symfony Finder’s sortByName() directly if the order matters for diffs, snapshots, or generated documentation.
Test with real fixture directories. A tests/Fixtures/scanner/ tree works well. Laravel’s Storage::fake() uses Flysystem and is invisible to Symfony Finder, so it is the wrong tool here.
Know when to drop the wrapper. If you need ignoreDotFiles(false), followLinks(), depth(), date(), sortByName(), or streaming iteration, use Symfony\Component\Finder\Finder directly. The facade is deliberately narrow and exposes no extension hooks beyond subclassing or copying the class.
symfony/finder directly. The facade adds almost no behavior beyond defaulting to files, ignoring VCS directories, and applying names/exclusions. Migrating is usually a five-line change.findFiles() is eager. It returns a full array, so very large directory trees consume memory. For big repositories, use Symfony Finder’s iterator or add chunking around the results.DirectoryNotFoundException when iteration begins. Wrap scans in a try/catch and validate paths with is_dir() before presenting user-supplied options.AccessDeniedException. This matters when scanning mounted volumes, node_modules, or shared hosting directories.ignoreDotFiles(false). If you need .env.example, .github workflows, or other dotfiles, use Finder directly.followLinks(). Symlinked packages or deployment layouts may appear empty.notPath() or exclude() in Symfony Finder; semantics differ, and patterns are relative to each in() root. Test exclusions explicitly rather than assuming vendor behaves like a global ignore.base_path(), app_path(), database_path(), etc.Illuminate\Filesystem\Filesystem::allFiles() is fine for simple recursive listing, but it lacks name-pattern and exclusion filtering. Symfony Finder is the closer replacement.symfony/finder constraint may lag behind the Symfony major version used by your Laravel release. Run composer why symfony/finder and composer why-not sebastian/finder-facade when resolution fails.NOASSERTION; check the repository LICENSE file before redistributing or bundling. Sebastian Bergmann packages are typically BSD-3-Clause, but verify rather than assume.findFiles() returns an array, inspect it directly in Tinker or a command with dump(array_map(fn ($f) => $f->getRealPath(), $files)). If the list is empty, check path existence, dotfile handling, name patterns, and exclusions in that order.*.PHP may match on macOS but not Linux. Prefer lowercase extensions in patterns and test in the same environment as production.FinderFacade class into your app and adapt it, or use Symfony Finder directly. Subclassing gives you little because the Finder instance is created internally and not exposed.How can I help you explore Laravel packages today?