smorken/array-cache
A lightweight in-memory array cache driver for Laravel. Store and retrieve cached values in a simple PHP array, ideal for testing, per-request caching, and avoiding external cache backends during development.
smorken/array-cache is an in-memory array cache for PHP and Laravel — effectively a place to stash values for the life of a single request/job without touching Redis, Memcached, or the filesystem. Treat it as a memoization layer, not as a real cache backend.
It is not published on Packagist, so register the VCS repository first:
composer config repositories.array-cache vcs https://bitbucket.org/scott_morken/array-cache.git
composer require smorken/array-cache
Bitbucket is private by default, so Composer needs credentials — an SSH key or an access token (composer config -g bitbucket-oauth...) — both locally and in CI.
Where to look first:
src/*ServiceProvider.php) — confirms whether it registers a cache driver alias (e.g. array, arraycache) or just binds a singleton class. This single file tells you whether you use it as Cache::store('...') or as an injected object.Illuminate\Contracts\Cache\Repository methods are actually implemented (get, put, remember, has, forget, flush, tags, TTL handling).php artisan vendor:publish to see defaults (prefix, TTL fallback).First use case — deduplicating identical work inside one request:
$settings = Cache::store('array')->remember(
'settings.all',
now()->addMinutes(5),
fn () => Setting::query()->pluck('value', 'key')->all()
);
Because the value lives only for the current process, the TTL is largely decorative; the win is that the closure runs once instead of N times when several services ask for the same thing.
Repository decorator (the highest-value pattern). Wrap a slow repository and put the array cache in front of it. This keeps the caching concern out of controllers and makes the lifetime of the memo explicit:
final class CachedUserRepository implements UserRepository
{
public function __construct(
private UserRepository $inner,
private Repository $cache,
) {}
public function find(int $id): ?User
{
return $this->cache->remember(
"users.{$id}",
now()->addHour(),
fn () => $this->inner->find($id)
);
}
}
Two-tier cache (array → Redis). Check the cheap store first, fall through to the persistent one, and hydrate the array so the second read in the same request is free:
function cached(string $key, Closure $callback)
{
$local = Cache::store('array');
if ($local->has($key)) {
return $local->get($key);
}
return $local->put($key, Cache::remember($key, 3600, $callback));
}
Per-request context object. Store the authenticated user's resolved permissions, tenant configuration, feature flags, or a formatted nav tree once and read it from every Blade partial and policy. This is where array caching normally removes the most queries.
Request-scoped injection. Register the store as a singleton in a service provider and inject Repository into services rather than calling the Cache facade statically — it makes swapping in a fake trivial.
Test isolation. Use it to make test suites fast: bind the array store as the default cache in the test case so nothing persists between tests, and reset it in setUp/tearDown (flush() if supported, otherwise re-instantiate the singleton via $this->app->forgetInstance(...)).
Warm it deliberately. In queue jobs and console commands that iterate over many models, prime the array cache in a loop before the hot path — e.g. Model::all()->each(fn ($m) => $cache->put("m.{$m->id}", $m)) — then let the loop body hit the cache instead of the DB.
Bust on write. Whenever you write through the decorator, forget() the corresponding key (and any list/index keys that would contain the changed row); there are no tags to fall back on unless the package implements them.
terminate() or an Octane RequestReceived/RequestTerminated listener, or bind it as a scoped (not singleton) instance.Cache::lock() semantics will not hold.null from a miss (has() may be implemented as isset()); if it conflates them, your "remember" closures will re-run on every access for nullable results.users.1 bleed between test methods if the container is reused. Set a prefix per test or flush in setUp.composer install. Vendor it, mirror it to a private Satis/Composer repository, or bake the token into the build secret store."dev-master#abcdef1") in composer.json and vendor the source, because a force-push or deleted repo breaks your build. Realistically, the Laravel array cache driver plus a small singleton binding reproduces this package's behaviour in ~20 lines, which is worth weighing before you take the dependency.->tags([...]) throws on stores that don't support them), flush(), increment/decrement, and rememberForever. Check the implementation — a thin array wrapper often implements only get/put/has/forget and will blow up on the rest.put/forget on the array store in local development; the trail is short because the store is empty at the start of every request.How can I help you explore Laravel packages today?