Installation:
composer require theseer/directoryscanner
No additional configuration is needed—autoload via Composer.
First Use Case: Scan a directory recursively and filter files by extension:
use Theseer\DirectoryScanner\DirectoryScanner;
$scanner = new DirectoryScanner();
$files = $scanner->scan(['/path/to/directory', '*.php']);
foreach ($files as $file) {
echo $file->getPathname() . "\n";
}
Key Classes:
DirectoryScanner: Core class for scanning.File: Represents scanned files (extends SplFileInfo).Directory: Represents scanned directories (extends SplFileInfo).Where to Look First:
Basic Scanning:
$scanner = new DirectoryScanner();
$results = $scanner->scan(['/path/to/directory', '*.{php,js}']);
*.php, **/*.md for recursive subdirectories).File/Directory objects (not raw strings).Filtering:
$files = $scanner->scan(['/path/to/directory', '*']);
$filtered = array_filter($files, fn($file) => $file->getExtension() === 'php');
Integration with Laravel:
$this->app->singleton(DirectoryScanner::class, fn() => new DirectoryScanner());
use Theseer\DirectoryScanner\DirectoryScanner;
class ScanCommand extends Command {
protected $signature = 'scan:files {directory}';
public function handle(DirectoryScanner $scanner) {
$files = $scanner->scan([$this->argument('directory'), '*.php']);
$this->info(count($files) . ' files found.');
}
}
Performance Optimization:
**/*.php) to avoid scanning all files.$cacheKey = 'scanned_files_' . md5($directory);
$files = cache()->remember($cacheKey, now()->addHours(1), fn() =>
$scanner->scan([$directory, '*.php'])
);
Async Processing:
class ScanJob implements ShouldQueue {
public function handle(DirectoryScanner $scanner) {
$files = $scanner->scan([storage_path('app'), '*.jpg']);
// Process files asynchronously
}
}
Laravel Filesystem Integration:
Storage facade for cloud storage:
$path = Storage::path('public/uploads');
$files = $scanner->scan([$path, '*.png']);
Case Sensitivity:
$filtered = array_filter($files, fn($file) =>
strtolower($file->getExtension()) === 'php'
);
Symbolic Links:
DirectoryScanner follows symlinks, which can lead to infinite loops or unexpected results.$scanner = new DirectoryScanner();
$scanner->ignoreSymlinks(true);
Permission Issues:
try {
$files = $scanner->scan([$directory, '*']);
} catch (\RuntimeException $e) {
Log::error('Scan failed: ' . $e->getMessage());
}
Memory Limits:
memory_limit.$iterator = $scanner->scan([$directory, '*']);
foreach ($iterator as $file) {
// Process one file at a time
}
Path Handling:
realpath() for consistency:
$absolutePath = realpath($directory);
$files = $scanner->scan([$absolutePath, '*']);
Enable Verbose Output:
$scanner->setVerbose(true);
Check Glob Patterns:
glob('/path/to/directory/*.php')) to validate they match expected files.Validate File Objects:
File objects are properly instantiated:
foreach ($files as $file) {
if (!$file instanceof \Theseer\DirectoryScanner\File) {
throw new \RuntimeException('Invalid file object');
}
}
Custom Filters:
$scanner->addFilter(function($file) {
return $file->getSize() > 1024; // Filter files > 1KB
});
Event Dispatching:
FileScanned):
$scanner = new DirectoryScanner();
$scanner->on('scan.complete', function($files) {
event(new FilesScanned($files));
});
Laravel Service Provider:
$this->app->singleton(DirectoryScanner::class, function() {
$scanner = new DirectoryScanner();
$scanner->ignoreSymlinks(true);
return $scanner;
});
Use with Storage Facade:
Storage for cloud storage support:
$path = Storage::disk('s3')->path('uploads');
$files = $scanner->scan([$path, '*.jpg']);
Queue Background Scans:
ScanJob::dispatch($directory, '*.php')->onQueue('scans');
Integration with Eloquent:
$files = $scanner->scan([storage_path('app'), '*.pdf']);
foreach ($files as $file) {
$model->attachFile($file->getPathname());
}
Testing:
$mockScanner = Mockery::mock(DirectoryScanner::class);
$mockScanner->shouldReceive('scan')
->once()
->andReturn([new \Theseer\DirectoryScanner\File('/fake/path/file.txt')]);
How can I help you explore Laravel packages today?