In-Memory
Zero-config default. Fast and simple, but not shared across processes or nodes.
Winter Boot’s caching abstraction lets you add caching behaviour to any bean method without writing a single line of cache-management code. Under the hood the framework uses Aspect-Oriented Programming (AOP): when it detects a caching attribute on a public method it generates a transparent proxy that intercepts calls to that method and handles cache lookups, storage, and eviction automatically. You choose the caching backend — in-memory, shared local KV store, or distributed Redis — by wiring a CacheManager bean, and the attributes remain identical regardless of the backend.
Before any caching attributes take effect, annotate your main application class with #[EnableCaching]. This tells Winter Boot to scan all beans for caching attributes and create the necessary AOP proxies.
use dev\winterframework\stereotype\cache\EnableCaching;use dev\winterframework\stereotype\WinterBootApplication;
#[WinterBootApplication]#[EnableCaching]class MyApplication { // application entry point}#[Cacheable] is a method-level attribute. The first time a method is called for a given cache key, Winter Boot executes the method and stores the return value in the cache. On subsequent calls with the same key the cached value is returned directly and the method body is not executed.
cacheNames string|string[] (default: "default")
One or more cache container names where the result will be stored. Accepts a single string or an array of strings.
key string (default: Method name + arguments)
The key under which the value is cached. Supports #{param} expression interpolation.
keyGenerator string (default: Framework managed)
Bean name of a class implementing KeyGenerator for custom key generation logic.
cacheManager string (default: Framework managed)
Bean name of a class implementing CacheManager. Use when you have multiple managers registered.
cacheResolver string (default: Framework managed)
Bean name of a class implementing CacheResolver for dynamic cache resolution at runtime.
condition string (default: "")
SpEL-style expression. When non-empty, caching only applies if the expression evaluates to true.
unless string (default: "")
SpEL-style expression. When non-empty, the result is not cached if the expression evaluates to true.
use dev\winterframework\cache\stereotype\Cacheable;
// Default cache container (in-memory, key = method name + arguments)#[Cacheable]public function getExpensiveCalculationResult(): mixed{ // Complex, time-consuming calculation return 'some_calculated_value';}use dev\winterframework\cache\stereotype\Cacheable;
// Single named cache container#[Cacheable('product-prices')]public function getProductPrice(string $productId): mixed{ // Fetch price from database or external API return '19.99';}use dev\winterframework\cache\stereotype\Cacheable;
// Multiple named cache containers — result is stored in both#[Cacheable(cacheNames: ['user-sessions', 'api-tokens'])]public function getUserSessionData(string $userId): mixed{ return ['session_id' => 'abc', 'token' => 'xyz'];}#[CachePut] is a method-level attribute that always executes the underlying method and then writes the fresh return value to the cache. Use it when you need to keep the cache up to date after a write operation. #[CachePut] accepts the same options as #[Cacheable].
use dev\winterframework\cache\stereotype\CachePut;
#[CachePut('product-prices')]public function updateProductPrice(string $productId, float $newPrice): mixed{ // Persist new price to the database first… return $newPrice; // return value is written back to the cache}#[CacheEvict] is a method-level attribute that removes one or more entries from the cache when the annotated method is called. You can target a specific entry using the key option or flush an entire cache container by setting allEntries: true.
allEntries bool (default: false)
When true, all entries in the specified cache(s) are removed rather than just the entry matching key.
beforeInvocation bool (default: false)
When true, the cache is cleared before the method executes. The default clears the cache after successful execution.
use dev\winterframework\cache\stereotype\CacheEvict;
#[CacheEvict( cacheNames: 'stock-prices', cacheManager: 'redisCacheManager', allEntries: true, beforeInvocation: false)]public function clearAllStockPricesCache(): void{ // All entries in the "stock-prices" cache are removed after this method returns.}Winter Boot ships with multiple cache backend options. Choose the one that fits your deployment topology.
In-Memory
Zero-config default. Fast and simple, but not shared across processes or nodes.
SharedKvCache
Shared across all processes on the same node. Ideal for single-node deployments.
RedisCache
Distributed cache shared across the entire cluster. Requires winter-data-redis.
Out of the box, Winter Boot registers a SimpleCacheManager backed by a fast PHP in-memory store. No additional configuration is required — add #[Cacheable] to your methods and caching works immediately.
SharedKvCache is backed by a local Key-Value store that is shared across all processes on the same node. It is a good fit for single-node deployments or for data that does not need to be distributed across a cluster.
CacheConfiguration controls the eviction and expiry policy per cache container:
maximumSize int (default: PHP_INT_MAX - 1)
Maximum number of entries before LRU eviction kicks in.
expireAfterWriteMs int (default: -1)
Milliseconds after which a written entry expires. -1 means entries never expire.
expireAfterAccessMs int (default: -1)
Milliseconds after the last access after which an entry expires.
use dev\winterframework\stereotype\Configuration;use dev\winterframework\stereotype\Bean;use dev\winterframework\cache\impl\SharedKvCache;use dev\winterframework\cache\CacheConfiguration;use dev\winterframework\cache\impl\SimpleCacheManager;use dev\winterframework\cache\CacheManager;use dev\winterframework\data\kv\KvTemplate;
#[Configuration]class CacheConfig{ #[Bean] public function getCacheManager(KvTemplate $kvTemplate): CacheManager { $cache = new SharedKvCache( $kvTemplate, 'stock-prices', // unique cache name CacheConfiguration::get( maximumSize: 5000, // LRU eviction after 5 000 entries expireAfterWriteMs: 600_000, // entries expire after 10 minutes ) );
$manager = new SimpleCacheManager(); $manager->addCache($cache);
return $manager; }}For multi-node deployments where all nodes must share the same cached data, RedisCache provides a distributed cache backed by Redis. It is available in the winter-data-redis module.
Define the Redis CacheManager bean
use dev\winterframework\stereotype\Configuration;use dev\winterframework\stereotype\Bean;use dev\winterframework\data\redis\cache\RedisCache;use dev\winterframework\cache\CacheConfiguration;use dev\winterframework\cache\impl\SimpleCacheManager;use dev\winterframework\cache\CacheManager;use dev\winterframework\data\redis\PhpRedisTemplate;
#[Configuration]class CacheConfig{ #[Bean('redisCacheManager')] public function getRedisCacheManager(PhpRedisTemplate $redisTpl): CacheManager { $pricesCache = new RedisCache( $redisTpl, 'stock-prices', CacheConfiguration::get( maximumSize: 5000, expireAfterWriteMs: 600_000, ) );
$manager = new SimpleCacheManager(); $manager->addCache($pricesCache);
return $manager; }}Reference the bean name in your caching attributes
use dev\winterframework\cache\stereotype\Cacheable;use dev\winterframework\cache\stereotype\CacheEvict;
#[Cacheable(cacheNames: 'stock-prices', cacheManager: 'redisCacheManager')]public function getStockPrice(string $symbol): mixed{ // Only called on a cache miss; result is stored in Redis return $this->stockApi->fetchPrice($symbol);}
#[CacheEvict(cacheNames: 'stock-prices', cacheManager: 'redisCacheManager', allEntries: true)]public function refreshAllStockPrices(): void{ // Flushes the entire "stock-prices" cache in Redis after execution}