php-standard-library/filesystem
Type-safe filesystem helpers for PHP with consistent exception handling. Provides safer wrappers for common file and directory operations, aiming for clearer intent and fewer runtime surprises. Part of the PHP Standard Library ecosystem.
Installation:
composer require php-standard-library/filesystem
No Laravel-specific config is needed—this is a standalone package.
First Use Case: Replace a manual file-read operation with type-safe code:
use PHPStandardLibrary\Filesystem\File;
try {
$content = File::read('/path/to/file.txt');
// Process content...
} catch (FileNotFoundException $e) {
Log::error("File missing: {$e->getMessage()}");
}
Where to Look First:
File, Directory, and PathResolver for 80% of use cases.FileNotFoundException, DirectoryNotFoundException, and PermissionDeniedException for error handling.FilesystemEvent if you need reactive workflows (e.g., audit logs).DIRECTORY_SEPARATOR hacks.PathResolver for project-relative paths:
$resolver = new PathResolver();
$path = $resolver->resolveRelativeToProjectRoot('storage/app/temp');
storage_path() calls in services:
// Before
$path = storage_path("app/{$id}.json");
// After
$path = (new PathResolver())->resolveRelativeToProjectRoot("app/{$id}.json");
// Write
File::write($path, $content, LockStrategy::NON_BLOCKING);
// Read
$content = File::read($path);
File::atomicWrite() for critical files (e.g., config):
File::atomicWrite($configPath, $newConfig);
Directory::ensure('/tmp/uploads')->create();
$files = Directory::listFilesRecursively('/path/to/dir');
$batch = new BatchProcessor();
$batch->process($files, 20, function ($file) {
// Process each file
});
$batch->process($files, 50, fn($file) => ProcessFileJob::dispatch($file));
$filesystem = new Filesystem();
$filesystem->on('fileCreated', function ($event) {
Log::info("File created: {$event->getPath()}");
});
File::write('/tmp/test.txt', 'content');
$filesystem->on('fileCreated', fn($event) =>
event(new \App\Events\FileCreated($event->getPath()))
);
AppServiceProvider:
$this->app->singleton(PathResolver::class, fn($app) =>
new PathResolver($app->basePath())
);
use PHPStandardLibrary\Filesystem\Directory;
class ExportCommand extends Command {
protected $signature = 'export:data';
public function handle() {
$exportDir = Directory::ensure(storage_path('exports'))->create();
// Export logic...
}
}
class ProcessFilesJob implements ShouldQueue {
public function handle() {
$files = Directory::listFilesRecursively(storage_path('uploads'));
$batch = new BatchProcessor();
$batch->process($files, 10, fn($file) => $this->processFile($file));
}
}
$resolver = $this->createMock(PathResolver::class);
$resolver->method('resolveRelativeToProjectRoot')->willReturn('/mock/path');
$this->expectException(FileNotFoundException::class);
File::read('/nonexistent/file.txt');
Event System Conflicts
FilesystemEvent dispatcher uses symfony/event-dispatcher, which may conflict with Laravel’s Illuminate/Events.composer.json:
"extra": {
"aliases": {
"symfony/event-dispatcher": "illuminate/events"
}
}
Or use Composer’s replace to avoid duplication.Windows Path Quirks
\\server\share) may not resolve correctly.if (str_starts_with($path, '\\\\')) {
throw new InvalidPathException("UNC paths not supported");
}
Batch Processor Overhead
BatchProcessor may slow down small datasets (<100 files).if (count($files) < 50) {
foreach ($files as $file) { ... }
} else {
$batch->process($files, 20, ...);
}
Permission Handling
PermissionDeniedException may not propagate as expected in shared hosting.File::write() with LockStrategy::NON_BLOCKING and retry logic:
File::write($path, $content, LockStrategy::NON_BLOCKING);
Event Dispatching in Loops
$filesystem->on('fileCreated', function ($event) {
if (rand(0, 100) < 10) { // 10% chance
Log::info("File created: {$event->getPath()}");
}
});
Path Resolution Issues
$path = (new PathResolver())->resolveRelativeToProjectRoot('storage/app/temp');
Log::debug("Resolved path: {$path}");
File Locking Conflicts
LockStrategy::NON_BLOCKING and check for FileLockedException:
try {
File::write($path, $content, LockStrategy::NON_BLOCKING);
} catch (FileLockedException $e) {
Log::warning("File locked, retrying...");
sleep(1);
retry();
}
Directory Traversal
Directory::listFilesRecursively() with depth limits:
$files = Directory::listFilesRecursively('/path/to/dir', maxDepth: 3);
Custom Exceptions
class InvalidMediaFileException extends FileException {}
Path Resolver Extensions
$resolver = new PathResolver();
$resolver->addRootDirectory('/custom/base/path');
Event Listeners
$filesystem->on('fileCreated', new LogFileCreationListener());
Batch Processor Strategies
$batch->setStrategy(new ParallelBatchStrategy(4));
Default Lock Strategies
LockStrategy::BLOCKING for writes, which may block in high-concurrency environments.File::setDefaultLockStrategy(LockStrategy::NON_BLOCKING);
Event Dispatcher Initialization
FilesystemEvent dispatcher requires explicit initialization:
$dispatcher = new EventDispatcher();
$filesystem = new Filesystem($dispatcher);
**Path
How can I help you explore Laravel packages today?