sebastian/file-filter
Filter and whitelist/blacklist files and directories in PHP projects. Extracted from phpunit/phpunit, this lightweight library helps build file lists while honoring include/exclude rules—useful for test runners, coverage tools, and CLI utilities.
Installation:
composer require sebastian/file-filter
Add as a dev dependency if only needed for testing:
composer require --dev sebastian/file-filter
First Use Case: Filter files in a directory (e.g., for test isolation or build steps):
use SebastianBergmann\FileFilter\Filter\DirectoryFilter;
use SebastianBergmann\FileFilter\Filter\PrefixFilter;
$filter = new DirectoryFilter(
new PrefixFilter('tests/Unit/'),
__DIR__ . '/tests'
);
$filteredFiles = $filter->getFiles();
Key Classes to Explore:
DirectoryFilter: Base class for filtering directories.PrefixFilter: Filter files by prefix (e.g., tests/Unit/).SuffixFilter: Filter files by suffix (e.g., .php).NotFilter: Invert another filter’s logic.AndFilter/OrFilter: Combine filters with logical operators.Test File Isolation: Dynamically filter test files based on environment or configuration:
$filter = new AndFilter(
new PrefixFilter('tests/Feature/'),
new NotFilter(new SuffixFilter('.skip.php'))
);
$testFiles = $filter->getFiles();
Build/Deployment Pipelines: Exclude files from artifact generation:
$excludeFilter = new OrFilter(
new SuffixFilter('.log'),
new SuffixFilter('.tmp')
);
$buildFiles = (new DirectoryFilter($excludeFilter, __DIR__ . '/src'))->getFiles();
Dynamic Filtering: Use runtime conditions (e.g., environment variables):
$prefix = getenv('TEST_PREFIX') ?: 'tests/Unit/';
$filter = new PrefixFilter($prefix);
Laravel Service Providers: Bind the filter to the container for reusable access:
$this->app->bind(FilterInterface::class, function () {
return new AndFilter(
new PrefixFilter('app/Tests/'),
new NotFilter(new SuffixFilter('.disabled.php'))
);
});
Artisan Commands:
Use filters to process files in commands (e.g., php artisan test:filter):
public function handle() {
$filter = resolve(FilterInterface::class);
$files = $filter->getFiles();
// Process files...
}
Event Listeners:
Filter files during events (e.g., files:updated):
public function handle() {
$filtered = (new DirectoryFilter(
new PrefixFilter('storage/logs/'),
storage_path('logs')
))->getFiles();
// Log filtered files...
}
Path Normalization:
strtolower() on paths if case-insensitive matching is needed.PrefixFilter('tests/') won’t match tests/ if trailing slashes differ. Use rtrim() or DIRECTORY_SEPARATOR for consistency:
new PrefixFilter(rtrim('tests/', '/') . DIRECTORY_SEPARATOR)
Performance:
getFiles() scans directories recursively. Cache results if used frequently:
$files = $filter->getFiles(); // Expensive; cache this!
Edge Cases:
RuntimeException. Validate paths first:
if (!is_dir($path)) throw new \InvalidArgumentException("Directory not found: $path");
Verify Filters:
Use getFiles() in twig or dd() to inspect filtered results:
dd((new DirectoryFilter(new PrefixFilter('tests/'), __DIR__))->getFiles());
Logical Errors:
Double-check AndFilter/OrFilter combinations. Test with dd():
$filter = new AndFilter(
new PrefixFilter('tests/'),
new SuffixFilter('.php')
);
dd($filter->getFiles()); // Ensure expected files are included/excluded.
Custom Filters:
Implement FilterInterface for domain-specific logic:
class SizeFilter implements FilterInterface {
public function getFiles() {
return array_filter(scandir($this->directory), fn($file) => filesize($file) > 1024);
}
}
Combine with Laravel:
Extend DirectoryFilter to use Laravel’s Filesystem:
class LaravelDirectoryFilter extends DirectoryFilter {
public function __construct(FilterInterface $filter, string $directory, Filesystem $files) {
parent::__construct($filter, $files->basePath($directory));
}
}
Configuration:
Store filter rules in config/file-filter.php:
return [
'test_prefix' => env('TEST_PREFIX', 'tests/Unit/'),
'excluded_suffixes' => ['.skip.php', '.bak'],
];
Then inject dynamically:
$filter = new AndFilter(
new PrefixFilter(config('file-filter.test_prefix')),
new NotFilter(new OrFilter(
array_map(fn($suffix) => new SuffixFilter($suffix), config('file-filter.excluded_suffixes'))
))
);
How can I help you explore Laravel packages today?