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

Laminas Cache Laravel Package

laminas/laminas-cache

Laminas Cache is a PHP caching component with PSR-6 and PSR-16 compliant adapters, multiple storage backends such as filesystem, memory, Redis, and more, plus cache patterns and integration for Laminas and Mezzio applications.

View on GitHub
Deep Wiki
Context7

Getting Started

Minimal Setup in Laravel

  1. Install core + one adapter (adapters are split packages since 3.x):

    composer require laminas/laminas-cache laminas/laminas-cache-storage-adapter-filesystem
    

    (Swap in -redis, -memcached, -apcu, -blackhole, etc. as needed. Nothing is bundled by default — a missing adapter package is the #1 first-run error.)

  2. Create the adapter through the factory, not new:

    use Laminas\Cache\Service\StorageAdapterFactoryInterface;
    
    $factory = app(StorageAdapterFactoryInterface::class);
    
    $storage = $factory->create('filesystem', [
        'cache_dir' => storage_path('framework/cache/laminas'),
        'namespace' => 'myapp',
        'ttl'       => 3600,
    ]);
    

    The standalone new Filesystem([...]) snippet that used to live in the package README was removed in 3.14.1 (#418, fixing #285) precisely because it no longer worked as written. Build adapters via StorageAdapterFactoryInterface (or the StorageAdapterAbstractServiceFactory in a PSR container) so options, plugin manager, and adapter resolution are wired correctly.

  3. First use case — cache an expensive query result:

    use Laminas\Cache\Storage\StorageInterface;
    
    public function __construct(private StorageInterface $cache) {}
    
    public function index()
    {
        $key = 'homepage.stats.v1';
        if (! $this->cache->hasItem($key)) {
            $this->cache->setItem($key, $this->computeStats(), 300);
        }
        return view('home', ['stats' => $this->cache->getItem($key)]);
    }
    
  4. PSR-16 (if you want the standard interface):

    use Laminas\Cache\Psr\SimpleCache\SimpleCacheDecorator;
    
    $psr16 = new SimpleCacheDecorator($storage);
    $psr16->set('psr_key', ['a' => 1], 3600);
    $psr16->get('psr_key', 'fallback');
    

    Use CacheItemPoolDecorator for PSR-6.


Implementation Patterns

1. Bind once in a service provider

// AppServiceProvider::register()
$this->app->singleton(StorageInterface::class, function ($app) {
    return $app->make(StorageAdapterFactoryInterface::class)->create('filesystem', [
        'cache_dir' => storage_path('framework/cache/laminas'),
        'namespace' => config('app.name'),
    ]);
});

Type-hint StorageInterface (not a concrete adapter) so tests can swap in Laminas\Cache\Storage\Adapter\Memory or a BlackHole adapter.

2. Register as a real Laravel cache driver

Keeps Cache:: calls and Blade/queued-job code unchanged while backing them with Laminas:

// AppServiceProvider::boot()
Cache::extend('laminas', function ($app) {
    $storage = $app->make(StorageInterface::class);
    return Cache::repository(new class(new SimpleCacheDecorator($storage)) {
        // adapt a PSR-16 store to Illuminate\Contracts\Cache\Store
    });
});

Only worth it if you need an adapter Laravel doesn't ship (e.g. dba, mongodb via laminas-cache-storage-adapter-mongodb, session). Otherwise use Laravel's cache and skip the bridge entirely.

3. Cache-aside with a small repository wrapper

Keep key-building and TTL policy in one class instead of scattering md5() calls:

class LaminasCache
{
    public function __construct(private StorageInterface $storage) {}

    public function remember(string $bucket, string $id, int $ttl, callable $compute): mixed
    {
        $key = sprintf('%s:%s', $bucket, $id);
        $value = $this->storage->getItem($key, $success);
        if ($success) {
            return $value;
        }
        $value = $compute();
        $this->storage->setItem($key, $value, $ttl);
        return $value;
    }
}

getItem()'s by-ref $success flag is the reliable hit/miss signal — a cached false, 0, or empty array is otherwise indistinguishable from a miss.

4. Invalidation without tags (see Gotchas)

Laminas has no tag API. Use a versioned namespace per bucket:

public function invalidateBucket(string $bucket): void
{
    $this->storage->incrementItem("version:$bucket", 1); // or setItem to a random value
}
// key becomes "v{$version}:{$bucket}:{$id}"

Bumping the version orphans old entries; let the adapter's TTL/clearExpiredByFactor reclaim them.

5. Two-tier (fast + durable)

Wire a hot APCu/Redis store and a filesystem fallback in a small composite, or simply prefer Redis in production and Memory in tests. Both are cheap to configure through the same factory:

$factory->create('redis', [
    'host' => '127.0.0.1', 'port' => 6379,
    'namespace' => 'myapp', 'ttl' => 3600,
]);

6. Batch I/O in jobs

$items = $storage->getItems(['k1', 'k2', 'k3']); // returns found keys only
$storage->setItems($rows, 600);                   // one round trip

Prefer these over foreach when warming cache in an artisan command or queue worker.

7. Plugin stack for non-string data

Attach once, at construction, not per call:

$storage->addPlugin(new \Laminas\Cache\Storage\Plugin\Serializer());
$storage->addPlugin(new \Laminas\Cache\Storage\Plugin\ExceptionHandler());

ExceptionHandler converts adapter exceptions into false returns + logged throws — handy in request paths, dangerous if you rely on setItem() failures being fatal.


Gotchas and Tips

1. No tag support — don't copy Laravel idioms

There is no setItem($k, $v, $ttl, ['tags']) and no clean(['tag']). The 4th parameter doesn't exist; clean() clears everything (or is not part of the storage API at all). Use prefixed/versioned keys (§4 above) or keep tag invalidation in Laravel's own cache.

2. Adapters are optional packages

new Filesystem(...) fatals if laminas-cache-storage-adapter-filesystem isn't installed. Same for every other adapter. The factory throws a clear "adapter not found" — always a composer problem, never a code problem.

3. Serialization

  • Scalars are stored natively; anything else (arrays, objects, DTOs) needs the Serializer plugin (or an adapter-level serializer option for Redis).
  • Closures, resources, PDO handles, and circular references will throw Laminas\Cache\Exception\ExceptionInterface. Catch it at the boundary, or you'll 500 inside a request that thought it was just caching.
  • Attaching Serializer twice double-encodes values and produces baffling "string instead of object" bugs.

4. TTL semantics

  • Integer = seconds; null = adapter default / no expiry; DateInterval = exact point in time. Fractional/floats get truncated.
  • 0 is not universally "forever" — some adapters treat it as an immediate miss. Use null for no-expiry.
  • Filesystem and APCu honour TTL lazily — expired entries still occupy space until read or swept. Enable ClearExpiredByFactor/OptimizeByFactor if you write a lot of short-TTL keys.

5. PSR-16 details that differ from Laravel

  • get($key, $default) takes a value, not a closure. No remember() in PSR-16 — implement it yourself.
  • delete(), setMultiple(), clear() return bool; check them or you'll silently lose writes.
  • set() of null is legal; distinguish "cached null" from "miss" via has() or the by-ref $success flag.

6. PHP 8.5 / release hygiene

3.14.1 replaced internal calls to methods deprecated in PHP 8.5 (#414). If you're on PHP 8.5, pin >=3.14.1 and re-run your test suite — otherwise expect deprecation noise (and future breakage) from laminas-cache internals under E_ALL.

7. Debugging

  • Wrap calls in a try { } catch (\Laminas\Cache\Exception\ExceptionInterface $e) and log adapter name + key; core exceptions carry both.
  • getMetadata($key) returns per-key metadata (ttl, mtime, filespec on filesystem) — useful for "why is this key gone?".
  • For connection/writable-dir issues the failure is at adapter construction, not at setItem(); test the factory in tinker first.

8. Common errors & fixes

Error Cause Fix
Adapter … could not be found Adapter package not installed composer require laminas/laminas-cache-storage-adapter-*
Serialization of 'Closure' is not allowed Caching a closure/resource Cache resolved values only; add Serializer for arrays/objects
Cache directory … is not writable Filesystem perms chmod -R 775 storage/framework/cache and check the namespace subdir
Redis connection refused Bad host/port/auth Verify phpredis/predis presence and host/port/password options
Stale data after "clearing" Expected clean(['tag']) Use versioned key prefixes
Value comes back as a string Double Serializer plugin Attach the plugin exactly once

9. Performance

  • Keys are opaque byte strings: md5()/hash() long, user-controlled inputs to stay under adapter limits and avoid injection into keyspaces.
  • Set a namespace per app/environment so local, staging, and production sharing a Redis instance don't collide.
  • Warm hot keys from a scheduled command after deploys; a cold distributed cache is a self-inflicted thundering herd.
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.
abbasudo/artisan-route
mirvan/ippanel
ignibyte/aic-framework
glavweb/data-schema-bundle
glavweb/datagrid-bundle
tacticmedia/scim-server
uni-method/frame
neika-ai/sdk
adachsoft/php-code-style
pharaonic/laravel-notification-dependencies
beautinow/translug
dev-brainiac/bot-cloaked
octoberrain/html
octoberrain/extension
octoberrain/exception
octoberrain/contracts
adachsoft/api-version-bundle
cgv/localized-resource
noith/settings
kda/laravel-package