m2mtech/flysystem-stream-wrapper
Adds a PHP stream wrapper for Flysystem v2/v3 so you can use fly:// paths with native file functions (file_put_contents, mkdir, etc.). Includes Symfony Lock-based locking plus options to ignore visibility errors and emulate dir metadata.
Installation
composer require m2mtech/flysystem-stream-wrapper
Register the service provider in config/app.php:
'providers' => [
// ...
M2MTech\FlysystemStreamWrapper\FlysystemStreamWrapperServiceProvider::class,
],
Basic Usage
Configure a Flysystem adapter (e.g., local, s3) and bind it to a stream wrapper name (e.g., myfilesystem):
use M2MTech\FlysystemStreamWrapper\FlysystemStreamWrapper;
use League\Flysystem\Filesystem;
$filesystem = new Filesystem(/* your adapter */);
FlysystemStreamWrapper::add('myfilesystem', $filesystem);
First Use Case
Access files via PHP’s built-in fopen() or file_get_contents():
$content = file_get_contents('myfilesystem://path/to/file.txt');
Dynamic Adapter Binding Bind adapters at runtime (e.g., in a service container or config loader):
$adapter = new S3Adapter([/* config */]);
$filesystem = new Filesystem($adapter);
FlysystemStreamWrapper::add('s3-bucket', $filesystem);
Laravel Integration Use Laravel’s service container to manage bindings:
$this->app->singleton('flysystem.my-bucket', fn() => new Filesystem($adapter));
FlysystemStreamWrapper::add('my-bucket', $this->app->make('flysystem.my-bucket'));
Stream Wrapper as a Proxy Useful for:
local:// for public/).s3:// for S3).encrypted:// via a custom adapter).Stream Wrapper in Blade Serve files directly in views:
<img src="{{ asset('myfilesystem://images/logo.png') }}" alt="Logo">
CacheAdapter) for performance.Storage::disk('myfilesystem')->url('path') for public URLs.str_replace('..', '', $path)).Stream Wrapper Naming Collisions
Avoid conflicts with PHP’s built-in wrappers (e.g., php://, zip://). Prefix custom names (e.g., app_).
Permissions and Ownership Stream wrappers inherit PHP’s file permissions. Ensure:
www-data) has access.Case Sensitivity Some adapters (e.g., S3) are case-sensitive. Normalize paths:
$path = strtolower($path);
Memory Limits
Large files may hit PHP’s memory_limit. Use streaming for big files:
$handle = fopen('myfilesystem://large-file.zip', 'rb');
while (!feof($handle)) {
echo fread($handle, 8192);
}
fclose($handle);
Error Handling Stream wrapper warnings (e.g., missing files) are now collected and handled gracefully due to fixes in v1.4.1. Ensure your error logging is configured to capture these.
stream_get_wrappers(); // List all registered wrappers.
$filesystem->getAdapter()->getLogger()->setHandler(new \Monolog\Handler\StreamHandler(storage_path('logs/flysystem.log')));
local:// for debugging before deploying to cloud storage.Custom Adapters Extend functionality by creating wrapper-specific adapters:
class EncryptedAdapter extends LocalAdapter {
public function read($path) {
return decrypt(parent::read($path));
}
}
Middleware Add logic before/after operations:
$filesystem->addPlugin(new class {
public function handleRead($path, $contents) {
return str_replace('secret', '****', $contents);
}
});
Fallback Adapters Implement fallback logic for missing files:
$filesystem->addPlugin(new class {
public function handleListContents($path) {
if (!$filesystem->has($path)) {
return ['fallback-file.txt' => new \League\Flysystem\FileAttributes()];
}
return $filesystem->listContents($path);
}
});
Configuration
Centralize bindings in config/filesystems.php:
'stream_wrappers' => [
'myfilesystem' => [
'adapter' => 's3',
'config' => [/* S3 config */],
],
],
Then load them in a service provider:
foreach (config('filesystems.stream_wrappers') as $name => $config) {
$adapter = new $config['adapter']($config['config']);
FlysystemStreamWrapper::add($name, new Filesystem($adapter));
}
How can I help you explore Laravel packages today?