league/flysystem
League/Flysystem is a filesystem abstraction for PHP that provides a consistent API for local disks and cloud storage like S3. Swap adapters without changing your app, with support for reading/writing files, directories, visibility, and streams.
Strengths:
FilesystemInterface) for interacting with local and remote storage (S3, SFTP, FTP, GCS, Azure, etc.), aligning with Laravel’s dependency injection and service container patterns. This reduces vendor lock-in and simplifies future migrations.AwsS3V3, GoogleCloudStorage, Local, ZipArchive), making it ideal for multi-cloud or hybrid storage architectures. Laravel’s ecosystem (e.g., spatie/laravel-medialibrary) already leverages Flysystem, ensuring compatibility.MountManager (for mounting multiple filesystems under a single interface) and DecoratedAdapter (for cross-cutting concerns like logging or caching) fit Laravel’s modular design.Weaknesses:
WebDAV, SFTP) have edge cases (e.g., path encoding, directory creation races) that require configuration tuning. Laravel’s config system can mitigate this via adapter-specific settings.Storage facade uses Flysystem under the hood, third-party packages (e.g., spatie/laravel-flysystem) are needed for advanced features like disk mounting or custom adapters. This adds a minor dependency.Storage::disk() already uses Flysystem, so integrating additional adapters (e.g., GoogleCloudStorage, Azure) is seamless. Example:
$filesystem = Storage::build([
'driver' => 'google',
'bucket' => 'my-bucket',
// Flysystem-specific config
'visibility' => 'public',
]);
MountManager can be bound to Laravel’s IoC container for dynamic filesystem routing (e.g., route-based storage switching).FileWritten, DirectoryCreated) can be mapped to Laravel’s event system for auditing or notifications.File::put()) with Flysystem’s FilesystemInterface in services. Use Laravel’s Storage facade as a bridge during transition.local driver with Flysystem’s Local adapter for consistent behavior across environments (e.g., dev/staging/prod).InMemoryFilesystem for unit tests (replaces Storage::fake() in some cases).aws-sdk, google/cloud-storage) may conflict with Laravel’s dependencies. Use replace in composer.json or platform-check to avoid version clashes.SFTP) throw custom exceptions (e.g., ConnectionException). Laravel’s exception handler can normalize these into Handler responses.config caching to toggle Flysystem features (e.g., checksums, temporary URLs) during rollout.Local adapter).MetadataDirective).local, s3)? For example, does the system require SFTP, Azure, or WebDAV?Storage facade adds ~5–10% overhead.config/filesystems.php can centralize this.Illuminate/Filesystem and Storage components. The FilesystemManager and Filesystem classes are thin wrappers around Flysystem’s FilesystemInterface.spatie/laravel-medialibrary uses Flysystem for storage.spatie/laravel-backup supports Flysystem adapters for backup destinations.spatie/laravel-cache-filesystem uses Flysystem for cache storage.Mockery or Flysystem’s InMemoryFilesystem can replace Laravel’s Storage::fake() for isolated tests.league/flysystem-bundle), so integration with Symfony apps is straightforward.laravel/framework package’s Storage facade or manually instantiate Flysystem.Phase 1: Adopt Flysystem for Remote Storage
Aws\S3\S3Client) with Flysystem’s AwsS3V3 adapter.// Before
$s3 = new Aws\S3\S3Client([...]);
$s3->putObject([...]);
// After
$filesystem = Storage::disk('s3')->driver();
$filesystem->write('file.txt', 'content');
Storage::disks() to configure adapters in config/filesystems.php.Phase 2: Standardize Local Storage
Storage::disk('local') with Flysystem’s Local adapter for consistent behavior (e.g., path normalization, visibility handling).$local = Storage::build([
'driver' => 'local',
'root' => storage_path('app'),
'visibility' => 'public', // Retain visibility on copy/move
]);
Phase 3: Advanced Features
$mountManager = new MountManager([
'local' => new LocalAdapter(...),
's3' => new AwsS3V3Adapter(...),
]);
$mountManager->mount('backup', $mountManager->createMount('s3', 'backup-bucket'));
$adapter = new LoggingAdapter(new LocalAdapter(...));
$filesystem = new Filesystem($adapter);
$url = Storage::disk('s3')->temporaryUrl('file.txt', now()->addMinutes(10));
Phase 4: Custom Adapters
How can I help you explore Laravel packages today?