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

Think Cache Laravel Package

topthink/think-cache

ThinkCache is the caching component for the ThinkPHP framework, offering a unified API across file, Redis, Memcached, and other drivers. It supports tags, expiration, and multiple cache stores, and can be used standalone via Composer in any PHP project.

View on GitHub
Deep Wiki
Context7

Getting Started

This is the cache layer that ThinkPHP's think\Cache manager loads its drivers from. It is not a Laravel package, and it does not try to be — there is no service provider, no facade, no config publishing. You get a set of driver classes you instantiate yourself.

First decision, before installing anything: in a Laravel app, Illuminate\Cache already gives you more than this package does (tags, atomic locks, Cache::remember, event hooks, array driver for tests, Redis cluster support). Only reach for topthink/think-cache when you have a concrete interop problem: a legacy ThinkPHP service you're migrating from, a shared Redis keyspace that ThinkPHP code also writes to, or a vendor package that type-hints think\cache\Driver.

composer require topthink/think-cache

Then look at three files, in this order:

  1. src/Driver.php — the abstract base (renamed CacheStore in recent tags; check which you have). This defines the whole contract: get, set, delete, has, clear, inc, dec, pull, remember, tag, and the protected getCacheKey() / getExpireTime() helpers.
  2. src/driver/File.php — the reference driver. If you can read this one you can read them all.
  3. src/TagSet.php — how tag bookkeeping is faked on top of a flat keyspace. Worth understanding before you rely on it.

First use case — a direct driver instantiation, no framework:

use think\cache\driver\File;

$cache = new File([
    'expire'        => 3600,          // default TTL, seconds
    'prefix'        => 'app:',        // per-instance, NOT global
    'path'          => __DIR__ . '/runtime/cache',
    'cache_subdir'  => true,
    'hash_type'     => 'md5',
    'data_compress' => false,
]);

$cache->set('user:42', ['name' => 'Ada'], 600);
$cache->get('user:42', 'fallback');
$cache->has('user:42');

The Laravel equivalent is Cache::store('file')->put('user:42', $data, 600) — same shape, more features.

Implementation Patterns

Construct drivers per store, mirror them by config. There's no container magic in standalone use. The cleanest pattern is a small factory that reads your existing cache config and returns a driver, so call sites never new a driver directly:

function tp_cache(string $store = 'redis'): \think\cache\Driver
{
    static $instances = [];
    if (!isset($instances[$store])) {
        $cfg = config("cache.tp.{$store}");
        $class = '\think\cache\driver\\' . ucfirst($store);
        $instances[$store] = new $class($cfg);
    }
    return $instances[$store];
}

Use remember instead of hand-rolled get/set/miss. It accepts a closure and handles the miss path, which is the one thing that actually saves you time day to day:

$stats = $cache->remember('dashboard:stats', function () use ($repo) {
    return $repo->aggregate();
}, 300);

pull is your one-shot token/dequeue primitive. pull($key) reads and deletes in one call — handy for invite tokens, one-time nonces, and "consume once" flags. Note it is not atomic across processes; see Gotchas.

Tags, if you must, but scope them. $cache->tag('user-42')->set(...) returns a TagSet; later $cache->tag('user-42')->clear(). Treat tag names as namespaced constants and keep tag cardinality low — every tagged write touches the tag index key.

Bridging into Laravel. Do it once, behind Illuminate\Contracts\Cache\Store, and never let a think\ class leak into application code:

// app/Cache/ThinkCacheStore.php implements Illuminate\Contracts\Cache\Store
// delegate get/put/forget/flush/increment/decrement to the think driver,
// mapping TTL minutes -> seconds and returning false on miss.

Register it in a service provider:

Cache::extend('tp-redis', fn ($app) => Cache::repository(
    new ThinkCacheStore(new \think\cache\driver\Redis($app['config']['cache.tp.redis']))
));

Now Cache::store('tp-redis')->get(...) works and you can delete the adapter the day the last ThinkPHP dependency goes away. If you can't justify the adapter, that's a strong signal you don't need the package.

Migrating off ThinkPHP. Do it driver-by-driver, not key-by-key. Set the same prefix and expire in both stacks, dual-write for one release, and read from Laravel. The File driver's layout will not match Laravel's file store — for a file-based migration, just let the old cache go cold.

Gotchas and Tips

  • prefix is per-instance, not per-server. Two drivers constructed with different prefixes cannot see each other's keys. When a "cache miss that shouldn't be a miss" appears, dump getCacheKey($name) first — it is the single fastest debug step for this package.
  • File::clear() deletes everything under path. There is no per-prefix clear. Never point it at a shared storage/ or runtime/ directory; give it its own subdirectory.
  • TTL 0 / null means "never expire" in most drivers, and a negative TTL often means "delete now". Read the driver before passing a computed TTL that can go negative — a $ttl = $expiresAt - time() is a classic footgun.
  • inc/dec on the File driver is read-modify-write, not atomic. No flock guarantee. If you need counters that survive concurrency (rate limits, view counts), use the Redis driver or don't use this package.
  • Tag cleanup leaks. Tag membership is stored in the cache itself; if an entry evicts but the tag index survives (or vice versa), tag()->clear() leaves orphans behind. Fine for short TTLs, not fine for unbounded keyspaces.
  • get($key, $default) returns the default only on a genuine miss, not on a deserialization failure — corrupted payloads can come back as false indistinguishable from a hit. Check the File driver's serialize handling if you store objects.
  • Redis driver expects ext-redis. Match the extension your driver version instantiates; a PECL/predis mismatch shows up as a fatal at construction, not as a cache miss.
  • The think\ namespace is shared. If a ThinkPHP app or another topthink/* package is also installed, keep versions compatible and pin exact tags — this package has one maintainer lineage, a small star count and effectively zero independent dependents, so it gets no third-party bug pressure.
  • PSR-16 is not guaranteed. The method names look PSR-16-ish, but signatures (set($name, $value, $expire = null) with an int, has() semantics, extra inc/dec/pull) mean you should not type-hint Psr\SimpleCache\CacheInterface without verifying the installed version.
  • Extension point: subclass think\cache\Driver and implement get/set/delete/has/clear. Override inc/dec only if you can make them atomic. That's the supported seam for adding an array/in-memory driver for tests — otherwise use File with a temp path per test.
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.
skyraptor/chatter
edlugz/sasapay
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