php-standard-library/filesystem
Type-safe filesystem utilities for PHP Standard Library. Perform common file and directory operations with consistent APIs and proper exception handling, improving safety and clarity over raw PHP functions. Documentation and contribution links included.
Installation
composer require php-standard-library/filesystem
Add the service provider to config/app.php (if using Laravel):
'providers' => [
// ...
PhpStandardLibrary\Filesystem\FilesystemServiceProvider::class,
],
Publish config (if available):
php artisan vendor:publish --provider="PhpStandardLibrary\Filesystem\FilesystemServiceProvider"
First Use Case Resolve and normalize paths in a Laravel controller:
use PhpStandardLibrary\Filesystem\Facades\Filesystem;
$resolvedPath = Filesystem::path()->resolve('/var/www/storage', 'logs/app.log');
$normalizedPath = Filesystem::path()->normalize($resolvedPath);
PhpStandardLibrary\Filesystem\Facades\Filesystem (for quick access).PhpStandardLibrary\Filesystem\Path (path manipulation).PhpStandardLibrary\Filesystem\File (file operations).PhpStandardLibrary\Filesystem\Directory (directory operations).vendor/php-standard-library/filesystem/README.md for API reference.$path = Filesystem::path()->join('storage', 'logs', 'app.log');
$absolutePath = Filesystem::path()->resolve(base_path(), 'app/Config.php');
// Write
Filesystem::file()->put($path, 'Hello, world!');
// Read
$content = Filesystem::file()->get($path);
try {
Filesystem::file()->copy($source, $destination);
} catch (\PhpStandardLibrary\Filesystem\Exceptions\FileOperationException $e) {
Log::error($e->getMessage());
}
Filesystem::directory()->ensure($path);
$files = Filesystem::directory()->files($directory, true);
if (Filesystem::file()->exists($path) && Filesystem::file()->isReadable($path)) {
$size = Filesystem::file()->size($path);
}
Storage facade for custom logic:
// Instead of:
Storage::put('file.txt', 'content');
// Use:
Filesystem::file()->put(storage_path('app/file.txt'), 'content');
FilesystemManager):
// config/filesystems.php
'disks' => [
'custom' => [
'driver' => 'php-standard-library',
'root' => storage_path('app'),
],
],
ensure() for directories before writing files to avoid race conditions.FileNotFoundException, PermissionDeniedException).Path methods over raw DIRECTORY_SEPARATOR for cross-platform compatibility.Path Resolution Quirks
resolve() uses the current working directory as the base if no absolute path is provided.
// Avoid:
$path = Filesystem::path()->resolve('relative/path'); // May fail if CWD is unexpected.
// Prefer:
$path = Filesystem::path()->resolve(base_path(), 'relative/path');
join() or resolve(). Use normalize() to clean them:
$cleanPath = Filesystem::path()->normalize('/path/with/trailing//slashes/');
Permission Handling
ensure() on the parent:
$parent = dirname($path);
Filesystem::directory()->ensure($parent);
Filesystem::file()->put($path, 'content');
isSymlink() to check or disable following with resolve(..., false).Edge Cases in File Operations
file()->append() or file()->writeStream() for memory efficiency.file()->get($path, 'binary')).Laravel-Specific Issues
base_path() or storage_path() are dynamic.Filesystem facade in tests to avoid filesystem I/O:
$this->mock(Filesystem::class)->shouldReceive('file')->andReturnSelf()
->shouldReceive('exists')->andReturn(true);
Enable Verbose Errors
Set the debug config option to true to get detailed exceptions:
// config/filesystem.php
'debug' => env('FILESYSTEM_DEBUG', false),
Log Path Operations Wrap path resolutions in logs for debugging:
$path = Filesystem::path()->resolve($base, $relative);
Log::debug('Resolved path', ['path' => $path, 'base' => $base, 'relative' => $relative]);
Check File Permissions
Use file()->isWritable() or directory()->isWritable() to diagnose permission issues:
if (!Filesystem::file()->isWritable($path)) {
throw new \RuntimeException("Cannot write to {$path}. Check permissions.");
}
Custom Path Resolver
Extend PhpStandardLibrary\Filesystem\Path to add domain-specific logic:
class CustomPath extends \PhpStandardLibrary\Filesystem\Path
{
public function resolveWithBase(string $base, string $path): string
{
return parent::resolve($this->normalize($base), $path);
}
}
Filesystem Events Listen for file/directory operations via Laravel events (if the package supports them):
// Example (hypothetical)
Filesystem::file()->listen(function ($event) {
Log::info("File event: {$event->type}", ['path' => $event->path]);
});
Override Default Behavior Bind your own implementations to the container:
$app->bind(\PhpStandardLibrary\Filesystem\Contracts\File::class, function ($app) {
return new CustomFile();
});
exists() checks: The package optimizes these internally, but chaining them (e.g., if (exists() && isReadable())) may still hit the filesystem twice.directory()->files() with true for recursion, but limit depth if performance is critical:
$files = Filesystem::directory()->files($dir, true, 2); // Max depth = 2
How can I help you explore Laravel packages today?