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

Cache Bundle Laravel Package

kitano/cache-bundle

A Symfony/PHP bundle that adds annotation-driven caching to service methods, making it easy to cache expensive results with configurable keys, providers, and lifetimes while keeping application code clean.

View on GitHub
Deep Wiki
Context7

Getting Started

Read the provenance before writing a line of code. The README is an explicit redirect to TheBigBrainsCompany/TbbcCacheBundle (Composer: tbbc/cache-bundle). kitano/cache-bundle targets Symfony 2.1–2.3 / PHP 5.3, wires annotation-driven caching through jms/aop-bundle, and has 14 stars with 0 dependents. You are not "getting started" with a package — you are inheriting an abandoned artifact. Do one of two things: (a) install tbbc/cache-bundle instead, which is the named successor, or (b) extract the pattern and reimplement on symfony/cache (PSR-6/PSR-16), which is what a new build should do regardless.

If you are on a legacy Symfony 2.x app that already uses it, the minimal wiring is:

composer require kitano/cache-bundle
// app/AppKernel.php
new Kitano\CacheBundle\KitanoCacheBundle(),
# app/config/config.yml
kitano_cache:
    provider:
        type: doctrine
        options:
            service: my_cache_provider   # a Doctrine\Common\Cache\Cache service
# app/config/config_dev.yml, config_test.yml
kitano_cache:
    provider:
        type: array          # in-memory, per-request; keeps tests hermetic

The array provider in dev/test is the single most important config choice you will make — it keeps the cache from leaking between test runs and stops you debugging stale data during development.

Where to look first. In the source tree, in this order: the DI extension (DependencyInjection/KitanoCacheExtension.php) defines every provider type and option key you can configure; the compiler passes reveal how annotated services get proxied and are the usual source of boot-time errors; the cache manager and adapter classes define key-generation and eviction semantics, which is the part worth copying. Do not start at the annotations — the annotation is the ergonomic veneer, the key-generation contract underneath is the actual payload.

First use case. Pick a read path that is expensive, tolerant of staleness, and has an obvious key: a catalog lookup, a config-derived pricing calculation, a rendered fragment summary. Wrap the service method with @Cache and a TTL, and verify via logging that the miss-then-hit transition works across separate requests (not just within one). If you can't demonstrate a cross-request hit, the AOP weaving isn't active and nothing else you configure matters.

Implementation Patterns

Annotation-first caching on service methods. The bundle resolves @Cache on service methods through Doctrine's annotation reader and JMS AOP, so the annotated method is proxied and the cached result is returned on hit. The day-to-day pattern is: keep the cached method pure and deterministic with respect to its arguments, and derive the key from those arguments only. Any state read inside the method that isn't an argument (clock, current user, request locale, feature flag) is an invisible key component — either pass it in as an argument or encode it explicitly in the key.

Explicit TTL per method, never a global default. A single blanket TTL is how you end up with a 5-minute cache on something that needed 5 seconds and a 5-second cache on a reference table that never changes. Assign TTL per method based on a per-surface staleness SLA: pricing and inventory need short TTLs or explicit eviction; static reference data can be long-lived. Write the SLA into the annotation and into a comment, because six months from now nobody will remember why it's 30 seconds.

Separate read caching from write invalidation. Use a @Cache-style annotation on read methods and an evict-style annotation on the write path that mutates the same data. The pattern that works is co-locating the cache key builder so the writer can compute the same key the reader used. If key generation is duplicated across read and write paths, you will get silent orphaned keys and stale reads — this is the most common failure in any method-level cache.

Namespacing discipline as a hard rule. Prefix every generated key with app:env:domain:entity:id (application name, environment, bounded context). The bundle does not enforce a namespace, and a shared Redis instance across staging and production — or across two services — will collide. Bake the prefix into your key-generation configuration, not into each annotation string.

Provider selection mirrors the lifecycle of your data. Doctrine-style providers for object-graph data the app already loads; Redis/Memcached for shared cross-node caches; the array provider exclusively for dev/test. Do not let the provider choice be implicit — make it explicit in config_*.yml per environment, and fail fast if a production container boots with array configured.

Wrap at the service boundary, not the controller. The bundle's value is caching the result of a method call, so the annotation belongs on the service method that does the expensive work. Caching at the controller level defeats the abstraction and couples the cache to HTTP semantics. If you need HTTP-level caching, this is the wrong layer — use Symfony's HTTP cache.

Decorator as the escape hatch. Where annotations can't reach (third-party or final classes, closures, methods with non-scalar arguments), wrap the service in a decorator that calls the cache manager directly. Keep the decorator's key builder identical to the annotated methods' so invalidation behaves uniformly across both call paths.

Porting the pattern to Laravel or modern Symfony. If you take one thing from this bundle, take the ergonomics of declarative method caching and reimplement them on the native contract:

// Laravel — thin Cacheable trait
trait Cacheable
{
    public function remember(string $key, int $ttl, Closure $fn)
    {
        $key = sprintf('%s:%s:%s', config('app.name'), app()->environment(), $key);
        return Cache::tags([$this->cacheTag()])->remember($key, $ttl, $fn);
    }
    protected function cacheTag(): string { return class_basename($this); }
}

That replaces the bundle's entire value proposition in a dozen lines, on a maintained dependency, with tag-based invalidation the bundle does not provide.

Migration/retirement path. If you're maintaining a legacy app, mark the bundle as tech debt with a named owner, pin the exact Composer version, and stop adding new @Cache annotations. Budget the migration to symfony/cache (or Laravel's Cache facade) per module as those modules are touched anyway — do not schedule a big-bang rewrite of the caching layer; do it opportunistically.

Gotchas and Tips

The annotation reader is the hidden dependency. jms/aop-bundle and Doctrine's annotation reader do the proxying and metadata extraction. On PHP 8.x you will hit annotation-reader API drift and dynamic-property deprecations before you hit anything cache-specific. If the annotation reader won't parse the annotation, the cache silently doesn't engage — no error, just no caching. Add a log line on cache miss/hit while validating, or you will not know the difference between "cached" and "broken."

Service proxying changes identity. Annotated services are wrapped in proxies, so instanceof checks, type-hinted setter methods, and === comparisons against get('service') can behave unexpectedly. If a service that used to satisfy a type hint now fails it, check whether it was annotated for caching. Prefer interface type hints; avoid comparing service objects by identity.

Key collisions across environments. This is the #1 production-only bug with this class of bundle. Two environments sharing a Redis instance, or a feature branch hitting the shared cache, will read each other's entries. The fix is environment-scoped prefixes at the key-builder level, plus separate cache backends per environment where feasible. Verify by grepping your cache for keys without the env prefix.

Cache invalidation is not transactional. If you update the database and then evict the cache, and the eviction fails or the process crashes in between, you have a stale entry that lives for the full TTL. There is no stampede protection and no negative-caching story in this bundle — cold-start thundering herd is entirely unmitigated. For hot keys, add a lock (Symfony Lock, Redis SET NX) around the recompute, or accept that a cold cache will spike your latency and size your capacity for it.

Do not use it for anything security-sensitive without reading the adapter. Deserialization of cached objects and credential handling for backend connections are attack-relevant surfaces, and there is no upstream security process. If your cached payloads contain user data, cache them as scalars or explicitly-serialized, validated structures, not raw objects.

The array provider is per-request only. Useful for tests and dev, useless for anything else. If a production config resolves to array (a merged-config mistake is enough), everything appears to work and nothing is cached. Add a boot-time assertion in the kernel or a config test that fails if array is active outside dev/test.

Compiler passes fail at container boot, not at the call site. AOP weaving errors surface when the container compiles, often with an error pointing at the compiler pass rather than your service. When debugging, clear the container cache (rm -rf var/cache/*) first — stale compiled containers produce errors that have nothing to do with your last change.

Successor check before adoption. If the requirement is genuinely annotation-driven method caching on a Symfony app, evaluate tbbc/cache-bundle — but as a separate decision against its own maintenance status, not as a continuation of this one. If the requirement is tag-based invalidation, multi-backend portability, or any observability (hit ratio, key cardinality, invalidation lag), this bundle covers none of it and you should be on symfony/cache (or Laravel's Cache) from the start.

Debugging stale reads, in order. (1) Dump the resolved key — is the env prefix present? (2) Confirm the annotation is actually being read (hit/miss logging). (3) Check the backend directly (redis-cli GET <key>) to confirm the write side is evicting the same key the read side builds. (4) Check TTLs on the specific key — a stale read is more often a TTL that's far longer than anyone remembers setting than a broken eviction.

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.
middlewares/firewall
qubiqx/montapacking
nyoncode/laravel-package-console
pnscripts/filament-broadquill-lite
pnscripts/laravel-sentfold-lite
vleap/mx-sdk-laravel-internal
twidpay/utils
htetmyathlaing/scout-elasticsearch-driver
artisan-toolbox/front-awesome
ls/client-assistant
users-au/laravel-client
robertmarney/lara-hierarchial-collections
simonschaufi/rule-doc-generator
emissaoja/emissaoja
blackbricksoftware/gravityforms-civicrm-form-processor
al-rahabi/fancy-crud
jinas/dhivatar-component
mltstephane/laravel-quality
bernskiold/laravel-talentlms
bernskioldmedia/laravel-form-architect