symfony/cache
Symfony Cache provides fast, low-overhead PSR-6 caching with adapters for common backends. Includes a PSR-16 bridge plus CacheInterface and TagAwareCacheInterface implementations via symfony/cache-contracts for flexible app caching.
Install the Package
composer require symfony/cache
Laravel already includes this package as a dependency of symfony/http-client or symfony/framework-bundle, so no additional installation is typically needed.
Basic PSR-6 Cache Usage
use Symfony\Contracts\Cache\CacheInterface;
use Symfony\Contracts\Cache\TagAwareCacheInterface;
use Symfony\Component\Cache\Adapter\FilesystemAdapter;
// Create a simple cache instance
$cache = new FilesystemAdapter(); // Stores in `storage/cache/`
// Store and retrieve data
$cache->set('key', 'value', 3600); // Expires in 1 hour
$value = $cache->get('key', function() {
return 'fallback-value'; // Computed if cache miss
});
Leverage Laravel’s Built-in Cache
Laravel’s Cache facade already uses Symfony’s cache under the hood. Configure it in config/cache.php:
'default' => env('CACHE_DRIVER', 'file'), // Options: file, redis, database, etc.
First Use Case: Caching API Responses
use Illuminate\Support\Facades\Cache;
$response = Cache::remember('api_products', now()->addHours(2), function() {
return Http::get('https://api.example.com/products')->json();
});
$cache = new FilesystemAdapter();
$cache = new RedisAdapter(
new \Redis(),
'cache_key_prefix',
0 // Timeout (0 = no timeout)
);
database driver in Laravel).
$cache = new DoctrineDbalAdapter(
$connection,
'cache_',
3600 // Default lifetime
);
$cache = new ChainAdapter([
new RedisAdapter($redis),
new FilesystemAdapter(),
]);
Useful for invalidating related cache entries (e.g., product categories).
$tagAwareCache = new TagAwareAdapter(
new FilesystemAdapter(),
'product_'
);
$tagAwareCache->set('product_123', $product, 3600, ['products', 'category_electronics']);
$tagAwareCache->invalidateTags(['products']); // Clears all tagged items
Laravel’s Cache facade implements PSR-16. Use it for lightweight caching:
Cache::put('key', 'value', 3600); // Set
$value = Cache::get('key'); // Get
Cache::forget('key'); // Delete
Prevent stampedes (thundering herds) with locks:
use Symfony\Component\Cache\Lock\LockFactory;
$lockFactory = new LockFactory($cache);
$lock = $lockFactory->createLock('expensive_operation', 10); // 10-second timeout
if ($lock->acquire(true)) { // Block until acquired
// Critical section
$lock->release();
}
Cache::tags(['products'])->put('product_123', $product, 3600);
Cache::tags(['products'])->flush(); // Invalidate all tagged items
CacheStore events:
Cache::store('file')->extend(function ($store) {
$store->extend('log', function ($item) {
if ($item->isHit()) {
Log::info('Cache hit: ' . $item->key());
}
return $item;
});
});
Extend Laravel’s cache configuration for domain-specific stores:
// config/cache.php
'stores' => [
'api' => [
'driver' => 'redis',
'key' => 'api_',
],
'views' => [
'driver' => 'file',
'path' => storage_path('framework/views'),
],
];
Use in code:
Cache::store('api')->get('products');
Tag Invalidation Race Conditions:
If multiple processes invalidate the same tag simultaneously, use invalidateTags() carefully. Consider wrapping in a lock:
$lock = $lockFactory->createLock('invalidate_products');
if ($lock->acquire()) {
Cache::tags(['products'])->flush();
$lock->release();
}
Redis Connection Issues: Ensure Redis DSN/auth is correctly passed to adapters. For clusters:
$redis = new \RedisCluster(
explode(',', env('REDIS_CLUSTER')),
['auth' => env('REDIS_PASSWORD')]
);
$cache = new RedisAdapter($redis, 'cache_');
Filesystem Permissions:
Laravel’s file driver requires storage/cache/ to be writable:
chmod -R 775 storage/cache/
Doctrine DBAL Schema Mismatches:
If using DoctrineDbalAdapter, ensure your database table matches the expected schema:
CREATE TABLE cache (
key VARCHAR(255) NOT NULL PRIMARY KEY,
value LONGBLOB NOT NULL,
expires_at BIGINT UNSIGNED NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
Enable Cache Logging:
$cache = new FilesystemAdapter(null, 0, null, null, true); // Enable debug
Or use Laravel’s logging:
Cache::store('file')->extend('log', function ($item) {
if ($item->isHit()) {
Log::debug('Cache hit: ' . $item->key());
}
return $item;
});
Check Cache Hit/Miss Rates:
Use Symfony’s CacheDataCollector in development:
$collector = new CacheDataCollector();
$cache = new CollectorAdapter($cache, $collector);
View stats in Symfony’s profiler or Laravel’s debugbar.
Clear Stale Cache: Laravel provides Artisan commands:
php artisan cache:clear # Clear application cache
php artisan cache:forget key # Remove a specific key
Avoid Large Serialized Data:
Large values (e.g., serialized models) can bloat cache storage. Use Cache::rememberForever() sparingly.
// Bad: Serializes entire model
Cache::put('user_123', $user, 3600);
// Better: Cache only IDs or lightweight data
Cache::put('user_123_email', $user->email, 3600);
Use null for Expiration:
Set null as the TTL to disable expiration (use cautiously):
$cache->set('persistent_key', $value, null);
Leverage getMultiple():
Reduce round trips for batch fetches:
$keys = ['key1', 'key2', 'key3'];
$values = Cache::get($keys); // Returns associative array
Cache Warming: Preload cache during low-traffic periods (e.g., cron job):
Cache::put('homepage_hero', $heroBanner, 86400); // Warm cache daily
Fallback Logic with get():
Use the callback parameter to compute defaults:
$value = Cache::get('expensive_key', function () {
return computeExpensiveValue();
});
Custom Cache Keys: Avoid collisions by namespacing keys:
$key = 'user_' . $userId . '_preferences';
Cache::put($key, $prefs, 3600);
How can I help you explore Laravel packages today?