kphoen/gaufrette-extras-bundle
A Symfony bundle that adds extra functionality and integrations on top of Gaufrette, the PHP filesystem abstraction library. It wires additional filesystem adapters, services, and configuration into Symfony apps, complementing KnpGaufretteBundle for flexible, framework-native file storage handling.
Accept up front that you cannot install this package into a modern Laravel app. kphoen/gaufrette-extras-bundle is a Symfony2 bundle: it ships a Bundle class, a DI extension, and a config tree designed to wire the kphoen/gaufrette-extras library into Symfony's container. It is marked [DEPRECATED], targets PHP 5.x/Symfony 2.x, has 3 stars and 0 dependents. Composer will almost certainly refuse to resolve symfony/http-kernel ^2.x against Laravel 10/11/12. What you can do is read it as a reference and port the ~200 lines of wiring into a Laravel service provider. That is the only realistic day-to-day use.
Read in this order:
kphoen/gaufrette-extras (the library, not the bundle). Its README and source are the actual substance: a ResolvableFilesystem decorator wrapping a Gaufrette FilesystemInterface, plus resolver implementations (prefix/host-based) that turn a storage key into a public URL. This is what you're porting.DependencyInjection/ extension (YAML/XML service definitions). This shows how the decorator and resolvers were wired together — the pattern to copy, not the code to run.composer.json files to confirm PHP/Symfony constraints before copying anything, so you understand why it's a non-starter.The first use case worth caring about: you store files in a backend (local disk, S3-compatible bucket, CDN origin) and need a single object that both performs filesystem operations and resolves a path to a public URL — rendering image links in an API response without hard-coding a CDN hostname in every controller and without leaking bucket internals.
Minimal Laravel-shaped port:
composer require gaufrette/gaufrette kphoen/gaufrette-extras
// app/Providers/GaufretteServiceProvider.php
public function register(): void
{
$this->app->singleton(ResolvableFilesystem::class, function ($app) {
$adapter = new LocalAdapter(storage_path('app/public'));
$filesystem = new Filesystem($adapter);
return new ResolvableFilesystem(
$filesystem,
new PrefixResolver(config('filesystems.public_url'))
);
});
}
Inject ResolvableFilesystem where needed. Before you write any of this, ask whether Storage::disk('public')->url($path) already answers the requirement — for most Laravel apps it does, and that is the correct first answer.
Wrap, don't replace, an existing disk. The valuable shape is the decorator: ResolvableFilesystem holds a Gaufrette FilesystemInterface, forwards read/write/exists/delete, and adds resolve($path). Bind it as a singleton and type-hint the decorator, not the underlying filesystem, so call sites get both capabilities from one dependency.
public function __construct(private ResolvableFilesystem $fs) {}
public function show(string $key): array
{
return [
'key' => $key,
'url' => $this->fs->resolve($key),
'size' => $this->fs->get($key)->getSize(),
];
}
Port the decorator onto Flysystem instead of Gaufrette. Gaufrette is a competing abstraction to Laravel's native Illuminate\Contracts\Filesystem\Filesystem (Flysystem-backed). Running both is a net-negative — two filesystem layers, two path-semantics conventions, and Gaufrette has no Octane story. Reimplement the ~40-line decorator against FilesystemInterface:
final class ResolvableStorage implements Filesystem
{
public function __construct(
private Filesystem $inner,
private UrlResolver $resolver,
) {}
public function resolve(string $path): string
{
return $this->resolver->resolve($path);
}
// forward everything else to $inner
public function exists(string $path): bool { return $this->inner->exists($path); }
// ...
}
Register it in config/filesystems.php as a custom disk or bind it via a provider. This preserves the pattern — one object for both I/O and URL resolution — while staying inside the abstraction Laravel maintains.
Workflow for a migration/cleanup ticket: inventory the adapters in use (local, S3, FTP, GridFS, Dropbox), map each to a Flysystem adapter, add one resolver per storage tier (public bucket vs. private/CDN), and wire them in filesystems.php. Zero new dependencies in the common case. Only scope a custom adapter if a genuinely missing backend is required — and even then, write it against the vendor SDK to implement League\Flysystem\FilesystemAdapter, not against Gaufrette.
Integration tip: keep URL resolution as a config-driven concern, not a hard-coded one. Expose the base URL via config('filesystems.disks.<name>.url') and inject it into the resolver. That makes swapping a local host for a CDN origin a one-line config change — exactly what the PrefixResolver did in the original.
Testing: because the decorator forwards to an interface, you can unit-test it against a fake Filesystem and a trivially stubbed resolver without touching real storage. Do that; it's the lowest-effort guard against path/URL drift.
[DEPRECATED] — no security patches, no PHP 8 compatibility work, no bug fixes. Anything you fork, you own, including EOL Symfony 2 transitive dependencies.symfony/* and php. --ignore-platform-reqs may let it install but won't make Symfony 2 DI internals work at runtime — and it definitely won't survive Laravel's boot or Octane's long-lived workers.composer audit/Dependabot will flag and block CI.gaufrette-extras-bundle is just the Symfony wiring. All the logic lives in kphoen/gaufrette-extras, which is itself unmaintained. Read the library, not the bundle.resolve() returns the wrong path or a double-slashed URL, check the prefix normalization in the resolver and the storage key format (leading slash, trailing slash). This is the single most common failure mode with prefix-based resolution and it's worth a unit test.How can I help you explore Laravel packages today?