Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Extras Laravel Package

gaufrette/extras

Gaufrette Extras provides additional adapters and plugins for the Gaufrette filesystem abstraction library, helping PHP developers connect applications to cloud storage, archive formats, and other filesystem backends through a consistent API.

Deep Wiki
Context7

Getting Started

Install and inspect.

composer require gaufrette/extras

Two classes matter on day one:

  • Gaufrette\Extras\Resolvable\ResolvableFilesystem – a FilesystemInterface implementation that delegates every operation to whichever underlying filesystem owns the key.
  • Gaufrette\Extras\Resolvable\Resolver\Resolver – the built-in router that maps a scheme://key path to a registered Gaufrette\Filesystem.

First use case: one injected filesystem for many backends. Instead of injecting $localFilesystem into your upload service, $s3Filesystem into your CDN service, and so on, inject a single ResolvableFilesystem and address targets by scheme.

use Gaufrette\Filesystem;
use Gaufrette\Adapter\Local;
use Gaufrette\Adapter\AwsS3;
use Gaufrette\Extras\Resolvable\ResolvableFilesystem;
use Gaufrette\Extras\Resolvable\Resolver\Resolver;

$resolver = new Resolver([
    'local' => new Filesystem(new Local('/var/app/storage')),
    's3'    => new Filesystem(new AwsS3($s3Client, 'bucket')),
]);

$filesystem = new ResolvableFilesystem($resolver);

$filesystem->write('local://cache/report.json', $json);
$filesystem->read('s3://assets/logo.png');

Read the source of Resolver::resolve() first — that is the entire contract you are programming against. Everything else is delegation.

Implementation Patterns

Registry service. Wrap the resolver in your DI container and register named filesystems as tagged services. The scheme names then become a small, documented vocabulary (local://, s3://, uploads://) rather than a stringly-typed free-for-all.

// config/services.php
$container->set(ResolvableFilesystem::class, function ($c) {
    return new ResolvableFilesystem(new Resolver([
        'local'   => $c->get('fs.local'),
        's3'      => $c->get('fs.s3'),
        'uploads' => $c->get('fs.uploads'),
    ], $c->get('fs.local'))); // optional default filesystem
});

Type-hint Gaufrette\FilesystemInterface (or ResolvableFilesystem where you need scheme awareness) in services that already work with Gaufrette. Existing code keeps working: unprefixed keys fall through to the default.

Lazy resolution for expensive adapters. Don't construct every filesystem eagerly if a request only touches one scheme. Either construct adapter objects lazily inside the resolver array, or implement Gaufrette\Extras\Resolvable\Resolvable yourself and instantiate on first hit.

Custom routing logic. The resolver interface (resolve($path) returning a Resolved with getFilesystem() and getKey()) is the extension point. Implement it for tenant routing, sharding, or feature-flagged cutovers:

final class TenantResolver implements Resolvable
{
    public function __construct(private array $filesystems) {}

    public function resolve(string $path): Resolved
    {
        [$scheme, $key] = explode('://', $path, 2) + [1 => $path];
        // e.g. tenant1 -> filesystems['tenant1']
        return new Resolved($this->filesystems[$scheme], $key);
    }
}

Migration/fallback pattern. Compose a resolver that tries the new backend, catches "not found" from the underlying filesystem, and falls back to the legacy one. This lets you move buckets or tenants without changing call sites.

Stream wrapper integration. If your codebase is fopen-oriented, register the resolvable filesystem as a stream wrapper once at bootstrap and then use scheme://key natively in templates, file_get_contents(), and third-party libraries.

Testing. Unit-test the resolver in isolation (scheme → filesystem + key) with in-memory adapters, and integration-test the ResolvableFilesystem against a real local adapter. Because the public surface is FilesystemInterface, anything consuming it can be tested with a stub Filesystem.

Gotchas and Tips

  • Last release 2017 — pin deliberately. Check the Gaufrette major/minor you are on (FilesystemInterface has churned across Gaufrette releases). Run composer why gaufrette/extras and lock compatible versions; do not assume PHP 8.2+ compatibility without testing. If you hit fatal errors, a two-method custom Resolvable implementation is often cheaper than waiting for an update.
  • Scheme parsing is literal. foo://bar means scheme foo, key bar. Keys containing ://, Windows drive letters, or URLs stored as object keys will be misrouted. If you accept arbitrary user keys, encode the separator or use a custom resolver that only splits on the first :// and requires the scheme to be in an allow-list.
  • Do not trust client-supplied schemes. Validate the scheme against your registry before resolution; otherwise a path like s3://private/... becomes an authorization bypass in code that expected local://. Resolution is routing, not access control.
  • UnresolvablePathException (from the resolver) is what unknown schemes throw. Catch it at your boundary and convert to a 400/404 domain exception; don't let it bubble as a 500.
  • Listings are per-filesystem. listKeys() / keys() operate on one resolved filesystem — the resolver has no cross-scheme notion of directories. Aggregate listings yourself if you need a virtual root.
  • Directory semantics vary by adapter. The resolvable wrapper delegates mkdir, has, rename straight through; renaming across schemes is not supported and will look like a missing file on the source or destination. Copy + delete explicitly for cross-backend moves.
  • No caching of resolution. Cheap for arrays, but if your custom resolver does I/O (DB lookup for tenant, etc.), memoize it per request.
  • Debug by logging resolution, not operations. A single Log::debug('resolved', [$path, get_class($resolved->getFilesystem())]) in a decorator around the resolver saves hours versus staring at adapter exceptions.
  • Extension points to remember: Resolvable for custom routing, the Resolved value object for tests, and decoration of ResolvableFilesystem itself for cross-cutting concerns (logging, metrics, retries) — since it is just another FilesystemInterface, wrapping it is trivial.
  • Streaming/large files. The wrapper does not buffer or copy; reads and writes pass through to the adapter, so Gaufrette's adapter-level behavior (e.g., S3 read() loading whole objects) applies unchanged.
  • Watch for abandoned-package drift in docs. Example code circulating online predates current Gaufrette APIs; verify constructor signatures against the src/Resolvable directory of the installed version rather than blog posts.
Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
diephp/sequences
users-au/socialite-provider
twidpay/twid-logger
mrnewport/laravel-plaid
mrnewport/laravel-repo
mrnewport/laravel-docsign
mrnewport/laravel-stow
mrnewport/laravel-flow
mrnewport/laravel-priceable
mrnewport/laravel-nestedterms
fredmeng/laravel-apidoc-generator
itstructure/laravel-multilingual-tools
pdustdar/elasticsearch
markupai/php-sdk
rlacerda83/lumem-microservice-shipment
uni-method/symfony-lib
tacticmedia/scim-server-bundle
neika-ai/symfony-bundle
mattleads/agent-publishing-bundle
glavweb/framework-bundle