symfony/finder
Symfony Finder component provides a fluent API to locate and iterate files and directories. Filter by name, extension, size, date, contents, or path; search multiple locations; and traverse recursively with sorting and ignore rules for flexible filesystem searches.
Installation:
composer require symfony/finder
Laravel already includes this as a dependency (via symfony/console), so no additional installation is needed in most cases.
First Use Case: Locate all PHP files in a directory and its subdirectories:
use Symfony\Component\Finder\Finder;
$finder = new Finder();
$files = $finder->files()->in(__DIR__.'/src')->name('*.php');
foreach ($files as $file) {
echo $file->getRealPath() . "\n";
}
Where to Look First:
Illuminate\Filesystem\Filesystem leverages Finder internally).Basic File Discovery:
$finder = new Finder();
$finder->files()->in('/path/to/dir')->name('*.{php,js}');
Directory Filtering:
$finder->directories()->in('/path')->depth('== 2')->name('/vendor/');
Combining Conditions:
$finder->files()
->in(__DIR__)
->name('*.md')
->notName('README.md')
->ignoreDotFiles(true)
->sortByModifiedTime();
Lazy Iteration with Collections:
use Illuminate\Support\Collection;
$collection = Collection::make(iterator_to_array($finder->files()->in(storage_path('app'))));
$collection->each(fn ($file) => Log::info($file->getPathname()));
Integration with Laravel Artisan:
use Symfony\Component\Finder\Finder;
use Illuminate\Console\Command;
class OptimizeAssets extends Command
{
protected $signature = 'assets:optimize';
protected $description = 'Optimize all assets in the public directory';
public function handle()
{
$finder = new Finder();
$files = $finder->files()->in(public_path())->name('*.{png,jpg}');
foreach ($files as $file) {
$this->optimizeImage($file->getRealPath());
}
}
}
Dependency Injection in Laravel:
use Symfony\Component\Finder\Finder;
use Illuminate\Support\ServiceProvider;
class AppServiceProvider extends ServiceProvider
{
public function register()
{
$this->app->singleton(Finder::class, function () {
return new Finder();
});
}
}
Then inject Finder into controllers/services:
public function __construct(private Finder $finder) {}
Custom Sorting:
$finder->files()->sortByName()->reverseSorting();
// Or with custom comparator:
$finder->files()->sort(function ($a, $b) {
return $a->getMTime() <=> $b->getMTime();
});
Excluding Vendor Directories:
$finder->files()->in(__DIR__)->exclude(['vendor', 'node_modules']);
Handling Symlinks:
$finder->files()->followLinks()->in('/path/with/symlinks');
Filtering by File Size:
$finder->files()->size('> 1M')->in(storage_path('logs'));
Asset Processing Pipeline:
Finder to locate all images in /public/assets, then process them with Laravel’s Intervention Image or Imagick.$images = $finder->files()->in(public_path('assets'))->name('*.jpg');
foreach ($images as $image) {
Image::make($image->getRealPath())->resize(800, 600)->save();
}
Dynamic Configuration Loading:
.php files from /config/dynamic and merge them into the app’s config:
$configFiles = $finder->files()->in(config_path('dynamic'))->name('*.php');
foreach ($configFiles as $file) {
$configs[$file->getFilenameWithoutExtension()] = require $file->getRealPath();
}
Test Fixture Management:
tests/Fixtures and load them for testing:
$fixtures = $finder->files()->in(base_path('tests/Fixtures'))->name('*.json');
foreach ($fixtures as $fixture) {
$data = json_decode(file_get_contents($fixture->getRealPath()), true);
// Use $data in tests
}
Log File Rotation:
$oldLogs = $finder->files()
->in(storage_path('logs'))
->date('> 30 days ago')
->name('*.log');
foreach ($oldLogs as $log) {
Storage::disk('s3')->put('backups/' . $log->getFilename(), file_get_contents($log->getRealPath()));
$log->delete();
}
Localization Workflow:
/resources/lang:
$untranslated = $finder->files()
->in(resource_path('lang'))
->name('*.php')
->not(function (\SplFileInfo $file) {
return file_exists($file->getPathname() . '.en.php');
});
Combine with Laravel’s Storage Facade:
use Illuminate\Support\Facades\Storage;
$files = (new Finder())->files()->in(Storage::disk('s3')->path('uploads'));
Use with Laravel’s Filesystem Manager:
$files = (new Finder())->files()->in($this->files->getAdapter()->getDriver()->getPathPrefix());
Leverage in Service Providers:
public function boot()
{
$routes = (new Finder())->files()->in(base_path('routes'))->name('*.php');
foreach ($routes as $routeFile) {
require $routeFile->getRealPath();
}
}
Custom Artisan Commands:
$this->command('files:cleanup')
->describe('Remove old temporary files')
->action(function () {
$finder = new Finder();
$tempFiles = $finder->files()
->in(storage_path('framework/views'))
->date('> 1 day ago');
foreach ($tempFiles as $file) {
$file->delete();
}
});
Testing:
Finder in unit tests:
$finder = $this->createMock(Finder::class);
$finder->method('files')->willReturn(new ArrayIterator([$mockFile]));
$this->app->instance(Finder::class, $finder);
Glob Pattern Quirks:
*test*) may not work as expected. Use Finder::create() with explicit paths or anchor patterns (e.g., */test*).Finder::create()->in('/path')->name('/test/') for anchored patterns.Empty Iterators:
$files = $finder->files()->in('/nonexistent')->name('*.php');
if ($files->count() === 0) {
// Handle empty result
}
Case Sensitivity:
name('*.PHP') may not match file.php. Use ignoreCase(true) or stick to lowercase:
$finder->files()->name('*.php')->ignoreCase(true);
Performance with Large Directories:
foreach ($finder->files()->in('/large/dir') as $file) {
// Process one file at a time
}
Symlink Handling:
Finder does not follow symlinks. Use followLinks() to traverse symlinked directories:
$finder->files()->followLinks()->in('/path/with/symlinks');
Path Separators:
How can I help you explore Laravel packages today?