Make large cache payloads feel lightweight.
Automatic compression, driver-safe chunking, write deduplication, and self-healing recovery behind Laravel's familiar cache API.
Illuminate\Contracts\Cache\Repository; review the explicit clearManaged() and flush() scopes when migrating.
What is SmartCache?
SmartCache wraps Laravel's caching layer with transparent optimizations while keeping ordinary cache operations familiar:
📦 Intelligent Compression
Automatic gzip compression for large values written through SmartCache, when compression is beneficial.
🧩 Smart Chunking
Breaks large arrays/objects into manageable pieces while preserving associative and sparse numeric keys.
🔒 Atomic Locks
Prevent cache stampede and race conditions with distributed locking.
⚡ Cache Memoization
In-memory caching eliminates repeated lookups within a request or job.
🎯 Adaptive Compression
Auto-optimize compression levels based on data characteristics.
💾 Lazy Loading
On-demand chunk loading keeps memory usage low for large datasets.
📡 Cache Events
Monitor and react to cache operations in real-time.
🔄 Gradual Adoption
Keep Laravel Cache alongside SmartCache and migrate only the keys that benefit from optimization.
🧬 Write Deduplication
Skips redundant writes when content is unchanged — saves I/O for frequently refreshed data.
🩹 Self-Healing Cache
Corrupted entries are auto-evicted; callback-based reads can regenerate them cleanly.
🎯 Conditional Caching
Cache only when a condition is met — filter out empty or invalid responses with rememberIf().
🔐 Encryption
Automatically encrypt sensitive cached data with pattern matching.
📁 Namespacing
Group cache keys by namespace for better organization and bulk operations.
⏱️ TTL Jitter
Prevent thundering herd with random TTL variation.
🔌 Circuit Breaker
Protect your app from cache backend failures with automatic fallbacks.
Production Safety for Large Data
SmartCache is designed for the failure modes that appear when cache entries become too large to treat as ordinary values: huge Eloquent result sets, API responses, reports, dashboards, and Redis or Memcached payloads that need to stay fast without becoming fragile.
🔑 Key-Preserving Restore
Chunked payloads keep associative keys and sparse numeric keys intact, so ID-keyed arrays do not come back reindexed.
🩹 Partial Chunk Recovery
If a chunk is missing, SmartCache treats the entry as a miss, evicts the broken metadata, and lets remember() regenerate clean data.
⭕ Valid Null Caching
Stored null values remain valid cached values and are not confused with cache misses.
When SmartCache Fits
- Large Eloquent results, reports, dashboards, or API payloads put pressure on the cache backend.
- You need consistent tag or dependency invalidation across Laravel cache drivers.
- You want diagnostics, corruption recovery, and measurable optimization behavior without maintaining a parallel cache layer.
When Native Laravel Cache Is Enough
- Most values are small and the native cache already meets latency and operational requirements.
- Payloads are already compressed or encrypted and will not benefit from gzip.
- The application cannot budget additional metadata keys for tracking, deduplication, metrics, tags, dependencies, or chunks.
Quick Start
use SmartCache\Facades\SmartCache;
// Familiar Laravel cache operations
SmartCache::put('users', $users, 3600);
$users = SmartCache::get('users');
Scales With Your Application
- Incremental: Change one facade import only where optimization is useful
- Advanced patterns: SWR, stampede protection, dependency-based invalidation
- Observability: Real-time monitoring, cost-aware eviction, performance analysis
- Production controls: Circuit breakers, encryption at rest, dashboards, HTTP management APIs
📦 Installation
Requirements
- ✓ PHP 8.1–8.5
- ✓ Laravel 8.0 - 13.x (fully tested on Laravel 8, 9, 10, 11, 12, and 13)
- ✓ Any Laravel-supported cache driver (Redis, File, Database, Array, Memcached)
- 💡 Recommended: PHP extension
ext-zlibfor gzip compression. JSON is always enabled in PHP 8+.
Installation Steps
composer require iazaran/smart-cache
# Clear application configuration cache to register the provider
php artisan optimize:clear
Configuration (Optional)
SmartCache works out-of-the-box with sensible defaults. For advanced customization:
php artisan vendor:publish --tag=smart-cache-config
[
'compression' => 1024 * 50, // 50KB - compress data larger than this
'chunking' => 1024 * 100, // 100KB - chunk arrays larger than this
],
/*
|--------------------------------------------------------------------------
| Strategies
|--------------------------------------------------------------------------
|
| Configure which optimization strategies are enabled and their options.
|
*/
'strategies' => [
'compression' => [
'enabled' => true,
'level' => 6, // 0-9 (higher = better compression but slower)
],
'chunking' => [
'enabled' => true,
'chunk_size' => 1000, // Items per chunk for arrays/collections
],
],
/*
|--------------------------------------------------------------------------
| Performance Monitoring
|--------------------------------------------------------------------------
|
| Enable performance monitoring to track cache hit/miss ratios,
| optimization impact, and operation durations.
|
*/
'monitoring' => [
'enabled' => true,
'metrics_ttl' => 3600, // How long to keep metrics in cache (seconds)
'recent_entries_limit' => 100, // Number of recent operations to track
],
/*
|--------------------------------------------------------------------------
| Performance Warnings
|--------------------------------------------------------------------------
|
| Configure thresholds for performance warnings and recommendations.
|
*/
'warnings' => [
'hit_ratio_threshold' => 70, // Warn if hit ratio below this percentage
'optimization_ratio_threshold' => 20, // Warn if optimization usage below this
'slow_write_threshold' => 0.1, // Warn if writes slower than this (seconds)
],
/*
|--------------------------------------------------------------------------
| Cache Drivers
|--------------------------------------------------------------------------
|
| Configure which cache drivers should use which optimization strategies.
| Set to null to use the global strategies configuration.
|
*/
'drivers' => [
'redis' => null, // Use global settings
'file' => [
'compression' => true,
'chunking' => true,
],
'memcached' => [
'compression' => false, // Memcached has its own compression
'chunking' => true,
],
],
/*
|--------------------------------------------------------------------------
| Fallback
|--------------------------------------------------------------------------
|
| Configure fallback behavior if optimizations fail or are incompatible.
|
*/
'fallback' => [
'enabled' => true,
'log_errors' => true,
],
];
Service Provider
SmartCache automatically registers its service provider. No additional configuration is needed for Laravel 8+.
💻 Basic Usage
Familiar Laravel API
SmartCache implements Laravel's cache repository contract, so ordinary cache operations remain familiar. Migrate incrementally and review cache-clearing calls because SmartCache 1.x gives clear() managed-only semantics:
use SmartCache\Facades\SmartCache;
// Basic caching (just like Laravel Cache)
SmartCache::put('user_data', $userData, 3600);
$userData = SmartCache::get('user_data');
// Helper function (just like cache() helper)
smart_cache(['products' => $products], 3600);
$products = smart_cache('products');
// Remember pattern (just like Cache::remember)
$users = SmartCache::remember('users', 3600, function() {
return User::all();
});
// Check if key exists
if (SmartCache::has('users')) {
$users = SmartCache::get('users');
}
// Forget a key
SmartCache::forget('users');
// Clear all cache
SmartCache::flush();
Using Different Cache Drivers
Use different cache drivers while maintaining all SmartCache optimizations:
// Use Redis with all SmartCache optimizations (compression, chunking, etc.)
SmartCache::store('redis')->put('key', $value, 3600);
SmartCache::store('redis')->get('key');
// Use Memcached with optimizations
SmartCache::store('memcached')->remember('users', 3600, fn() => User::all());
// Use file cache with optimizations
SmartCache::store('file')->put('config', $config, 86400);
// Chain multiple operations on a specific store
$redisCache = SmartCache::store('redis');
$redisCache->put('users', $users, 3600);
$redisCache->put('products', $products, 3600);
// For raw access to Laravel's cache (bypasses SmartCache optimizations)
SmartCache::repository('redis')->put('key', $value, 3600);
store() method returns a SmartCache instance, so all optimization strategies (compression, chunking, encryption, etc.) continue to work. Use repository() if you need direct access to Laravel's cache without SmartCache optimizations.
Illuminate\Contracts\Cache\Repository interface, and store() returns another SmartCache repository. The 1.x clear() method intentionally clears only SmartCache-managed keys; use flush() for the entire underlying store.
Automatic Optimization
SmartCache automatically optimizes your data when beneficial:
// Large array - automatically chunked
$largeArray = range(1, 10000);
SmartCache::put('large_data', $largeArray, 3600);
// Data is automatically chunked for better performance
// Large string - automatically compressed
$largeString = str_repeat('Hello World! ', 1000);
SmartCache::put('large_text', $largeString, 3600);
// Data is automatically compressed to save space
Cache Tags
Group related cache entries for easy management:
// Store with tags
SmartCache::tags(['users', 'profiles'])->put('user_1', $user1, 3600);
SmartCache::tags(['users', 'profiles'])->put('user_2', $user2, 3600);
// Clear all cache with specific tags
SmartCache::tags(['users'])->flush(); // Clears both user_1 and user_2
// Clear cache with multiple tags
SmartCache::tags(['users', 'profiles'])->flush();
Cache Locking
Prevent cache stampede with atomic locks:
$lock = SmartCache::lock('expensive_operation', 10);
if ($lock->get()) {
try {
// Perform expensive operation
$result = expensiveOperation();
SmartCache::put('expensive_result', $result, 3600);
} finally {
$lock->release();
}
} else {
// Wait for the lock to be released
$result = SmartCache::get('expensive_result');
}
⚡ Optimization Features
Intelligent Compression
SmartCache automatically compresses large data to reduce storage requirements:
// Large data automatically compressed
$largeData = [
'products' => Product::with('images', 'reviews')->get(),
'categories' => Category::with('children')->get(),
'settings' => Setting::all()
];
SmartCache::put('catalog_data', $largeData, 3600);
// Automatically compressed with gzip
Smart Chunking
Large arrays and objects are automatically chunked for better performance:
// Large array automatically chunked
$users = User::with('profile', 'posts')->get(); // 10,000+ records
SmartCache::put('all_users', $users, 3600);
// Automatically split into manageable chunks
Strategy Selection
SmartCache automatically chooses the best optimization strategy:
| Data Type | Size | Strategy Applied | Benefit |
|---|---|---|---|
| Large Arrays (5000+ items) | Any | Chunking | Better memory usage, faster access |
| Text/Strings | >50KB | Compression | Significant size reduction |
| Mixed Objects | >50KB | Compression | Optimal serialization |
| API Responses | >100KB | Chunking + Compression | Best performance |
| Small Data | <50KB | None | Fastest performance |
🧠 Smart Serialization
Auto-select best serialization method:
// Automatically chooses:
// - JSON for simple arrays (fastest)
// - igbinary for complex data (if available)
// - PHP serialize as fallback
SmartCache::put('data', $value, 3600);
// Best method selected automatically
📏 Smart Chunk Sizing
Auto-calculate optimal chunk sizes based on cache driver:
// Enable smart chunk sizing
config(['smart-cache.strategies.chunking.smart_sizing' => true]);
// Automatically calculates optimal chunk size based on:
// - Cache driver limits (Redis: 512MB, Memcached: 1MB)
// - Data structure complexity
// - Serialization overhead
SmartCache::put('large_array', $data, 3600);
// Optimal chunk size calculated automatically
Configuration Options
// config/smart-cache.php
return [
'thresholds' => [
'compression' => 1024 * 50, // 50KB
'chunking' => 1024 * 100, // 100KB
],
'strategies' => [
'compression' => [
'enabled' => true,
'mode' => 'fixed', // 'fixed' or 'adaptive'
'level' => 6, // 1-9 (higher = better compression)
],
'chunking' => [
'enabled' => true,
'chunk_size' => 1000, // Items per chunk
'lazy_loading' => false, // Enable for memory savings
'smart_sizing' => false, // Auto-calculate chunk size
],
],
'events' => [
'enabled' => false, // Enable for monitoring
'dispatch' => [
'tag_flushed' => true,
],
],
'metadata_lock' => [
'enabled' => true, // Lock tag/dependency metadata when supported
'ttl' => 5,
'wait' => 1,
],
'model_invalidation' => [
'after_commit' => true, // Flush after DB commit when inside a transaction
],
'monitoring' => [
'enabled' => true,
'metrics_ttl' => 3600,
],
];
What to Expect
🌊 SWR Patterns
swr(), stale(), and refreshAhead() execute a due refresh callback synchronously before returning the stale value. Use asyncSwr() with a queue worker when callers must not wait for regeneration.
What is SWR?
SWR (Stale-While-Revalidate) separates a value's fresh and stale windows. SmartCache 1.x offers synchronous stale refresh through swr(), stale(), and refreshAhead(), plus queue-based refresh through asyncSwr():
- 🔄 Fresh and stale windows - Control how long a value is fresh and how long it remains eligible to serve.
- 🛡️ Stale-on-error behavior - A failed due refresh can leave the prior stale value available.
- 🚫 Optional single-flight locking - Reduce concurrent regeneration when the cache store supports locks.
Basic SWR Usage
// A due refresh runs synchronously before this method returns stale data
$apiData = SmartCache::swr('github_repos', function() {
return Http::get('https://api.github.com/user/repos')->json();
}, 300, 900); // 5min fresh, 15min stale
Extended Stale Serving
For slowly changing data, serve stale data for extended periods:
// Extended stale serving for site configuration
$siteConfig = SmartCache::stale('site_config', function() {
return Config::fromDatabase();
}, 3600, 86400); // 1hour fresh, 24hour stale
Refresh-Ahead Caching
Proactively refresh cache before expiration:
// Refresh-ahead for expensive computations
$analytics = SmartCache::refreshAhead('daily_analytics', function() {
return Analytics::generateReport();
}, 1800, 300); // 30min TTL, 5min refresh window
Queue-Based Background Refresh
Return stale data immediately and refresh asynchronously via a queued job:
// Returns stale data instantly, dispatches a background job to refresh
// Pass a "Class@method" string or an invokable class — Closures are rejected
// since v1.12.0 because Laravel's queue serializer cannot persist them.
$data = SmartCache::asyncSwr('dashboard_stats', RefreshDashboardStats::class, 300, 900, 'cache-refresh');
swr(), stale() and refreshAhead() run a due refresh callback synchronously in the current PHP process before the method returns the stale value. Use asyncSwr() with a queue worker for non-blocking, out-of-process regeneration.
Single-Flight Refresh v1.12.0
Prevent stampedes during the stale window by serialising concurrent refreshes through an opportunistic non-blocking lock:
// config/smart-cache.php
'swr' => [
'single_flight' => true, // default false (BC)
],
When enabled and the underlying store implements LockProvider (redis, memcached, database, dynamodb, file), only one worker regenerates a stale entry; concurrent workers continue to serve the stale value without piling up redundant callback executions. Default false preserves the historical behaviour.
Real-World Examples
API Gateway Cache
class ApiController
{
public function getProducts()
{
return SmartCache::swr('api_products', function() {
return Product::with('images', 'reviews')->get();
}, 300, 900); // 5min fresh, 15min stale
}
public function getUserProfile($userId)
{
return SmartCache::stale("user_profile_{$userId}", function() use ($userId) {
return User::with('profile', 'settings')->find($userId);
}, 1800, 3600); // 30min fresh, 1hour stale
}
}
E-commerce Product Recommendations
class RecommendationService
{
public function getRecommendations($userId)
{
return SmartCache::refreshAhead(
"recommendations_{$userId}",
function() use ($userId) {
return $this->aiEngine->generateRecommendations($userId);
},
3600, 600 // 1hour TTL, 10min refresh window
);
}
}
📊 Performance Monitoring
Real-Time Metrics
Get comprehensive performance metrics about your cache usage:
// Get all performance metrics
$metrics = SmartCache::getPerformanceMetrics();
/*
Returns:
[
'cache_efficiency' => [
'hit_ratio' => 0.943,
'miss_ratio' => 0.057,
'total_requests' => 1000
],
'optimization_impact' => [
'compression_savings' => 0.68,
'chunking_benefits' => 0.23,
'total_size_reduction' => 0.72
],
'operation_timing' => [
'average_get_time' => 0.023,
'average_put_time' => 0.045,
'total_operations' => 5000
]
]
*/
Health Analysis
Get automated analysis and recommendations:
// Automated performance analysis
$analysis = SmartCache::analyzePerformance();
/*
Returns:
[
'overall_health' => 'good', // good, warning, critical
'recommendations' => [
'Consider increasing cache TTL for frequently accessed data',
'Enable compression for large text data'
],
'issues' => [],
'score' => 85
]
*/
HTTP Command Execution
Manage cache via HTTP API (perfect for web-based admin panels):
// Get available commands
$commands = SmartCache::getAvailableCommands();
// Execute commands via HTTP
$status = SmartCache::executeCommand('status');
$clearResult = SmartCache::executeCommand('clear');
$specificClear = SmartCache::executeCommand('clear', [
'key' => 'expensive_computation',
'force' => true
]);
CLI Commands
# Quick status overview
php artisan smart-cache:status
# Read-only production audit
php artisan smart-cache:audit --driver=redis
# Benchmark your real cache driver and payload shapes
php artisan smart-cache:bench --driver=redis --iterations=5
# Detailed analysis with recommendations
php artisan smart-cache:status --force
# Clear all SmartCache managed keys
php artisan smart-cache:clear
# Clear specific key
php artisan smart-cache:clear expensive_api_call
# Force clear any cache key
php artisan smart-cache:clear --force
🔧 Advanced Features
🔒 Atomic Locks
Prevent multiple processes from regenerating expensive cache simultaneously (prevents race conditions):
// Prevent cache stampede with distributed locks
$lock = SmartCache::lock('expensive_operation', 10);
if ($lock->get()) {
// Only one process executes this
$data = expensiveApiCall();
SmartCache::put('api_data', $data, 3600);
$lock->release();
}
// Or use callback pattern for automatic release
SmartCache::lock('regenerate_cache', 30)->get(function() {
return regenerateExpensiveData();
});
⚡ Cache Memoization
Hold retrieved values in memory for the duration of a request or job, eliminating repeated cache lookups:
// Get memoized cache instance
$memo = SmartCache::memo();
// First call hits cache, subsequent calls are instant (from memory)
$users = $memo->remember('users', 3600, fn() => User::all());
$users = $memo->get('users'); // From memory — no cache round-trip
$users = $memo->get('users'); // Still from memory
// Perfect for loops and repeated access
foreach ($products as $product) {
$category = $memo->get("category_{$product->category_id}");
// First access hits cache, subsequent accesses are instant
}
// Get memoization statistics
$stats = $memo->getMemoizationStats();
// Returns: memoized_count, missing_count, total_memory
📡 Cache Events
Monitor and react to cache operations in real-time:
// Enable events in config
config(['smart-cache.events.enabled' => true]);
// Listen to cache operations
use SmartCache\Events\{CacheHit, CacheMissed, KeyWritten, KeyForgotten, OptimizationApplied, TagFlushed};
Event::listen(CacheHit::class, function ($event) {
Log::info("Cache hit: {$event->key}", ['tags' => $event->tags]);
});
Event::listen(CacheMissed::class, function ($event) {
Log::warning("Cache miss: {$event->key}");
});
Event::listen(OptimizationApplied::class, function ($event) {
Log::info("Optimized {$event->key}: {$event->strategy} - {$event->ratio}% reduction");
});
Event::listen(TagFlushed::class, function ($event) {
Log::notice("Flushed {$event->tag}", [
'keys' => $event->keyCount,
'source' => $event->source, // manual, model, or model_helper
]);
});
🔢 Batch Operations
Optimize multiple cache operations:
// Retrieve multiple keys at once
$values = SmartCache::many(['key1', 'key2', 'key3']);
// Store multiple keys at once
SmartCache::putMany([
'key1' => 'value1',
'key2' => 'value2',
'key3' => 'value3',
], 3600);
// Delete multiple keys at once
SmartCache::deleteMultiple(['key1', 'key2', 'key3']);
🎯 Adaptive Compression
Auto-optimize compression levels based on data characteristics:
// Enable adaptive compression in config
config(['smart-cache.strategies.compression.mode' => 'adaptive']);
// Automatically selects optimal compression level based on:
// - Data compressibility (sample-based analysis)
// - Access frequency (hot data = faster compression)
// - Data size (larger data = higher compression)
SmartCache::put('hot_data', $frequentlyAccessed, 3600); // Uses level 3-4 (faster)
SmartCache::put('cold_data', $rarelyAccessed, 3600); // Uses level 7-9 (smaller)
// Get compression statistics
$stats = SmartCache::getCompressionStats($optimizedValue);
// Returns: level, original_size, compressed_size, ratio, savings_bytes, savings_percent
💾 Lazy Loading
Load large datasets on-demand, keeping only the active chunk in memory:
// Enable lazy loading in config
config(['smart-cache.strategies.chunking.lazy_loading' => true]);
// Large datasets return LazyChunkedCollection
$largeDataset = SmartCache::get('100k_records');
// Chunks loaded on-demand (max 3 in memory at once)
foreach ($largeDataset as $record) {
processRecord($record);
// Only loads chunks as needed
}
// Access specific items
$item = $largeDataset[50000]; // Only loads the chunk containing this item
// Collection methods work too
$filtered = $largeDataset->filter(fn($item) => $item->active);
$mapped = $largeDataset->map(fn($item) => $item->name);
🧠 Smart Serialization
Auto-select best serialization method for optimal performance:
// Automatically chooses best method:
// - JSON for simple arrays (fastest, most compact)
// - igbinary for complex data (if available)
// - PHP serialize as fallback
SmartCache::put('simple_data', ['key' => 'value'], 3600); // Uses JSON
SmartCache::put('complex_data', $objectGraph, 3600); // Uses igbinary/PHP
// Get serialization statistics
$stats = SmartCache::getSerializationStats($optimizedValue);
// Returns: method, original_size, serialized_size, ratio
Smart Cache Invalidation
Dependency Tracking
Create cache hierarchies where invalidating a parent clears all children:
// Create cache dependencies
SmartCache::dependsOn('user_posts', 'user_profile');
SmartCache::dependsOn('user_stats', 'user_profile');
// Invalidate parent - children cleared automatically
SmartCache::invalidate('user_profile');
// This will also clear 'user_posts' and 'user_stats'
Pattern-Based Invalidation
Clear cache entries using wildcards and regex patterns:
// Pattern-based clearing
SmartCache::flushPatterns([
'user_*', // All user keys
'api_v2_*', // All API v2 cache
'/product_\d+/' // Regex: product_123, product_456
]);
Model Auto-Invalidation
Automatically clear cache when Eloquent models change:
use SmartCache\Traits\CacheInvalidation;
class User extends Model
{
use CacheInvalidation;
protected function cacheInvalidation(): array
{
return [
'keys' => ['user_{id}_profile', 'user_{id}_posts'],
'tags' => ['users', 'user_{id}', 'team_{team_id}'],
'patterns' => ['users_list_*'],
'dependencies' => ['team_{team_id}_summary'],
];
}
}
// Cache automatically cleared when user changes!
$user = User::find(1);
$user->update(['name' => 'New Name']); // Flushes after the transaction commits
// Event-blind writes should flush explicitly.
User::where('status', 'inactive')->update(['archived' => true]);
User::flushCacheTags(['users']);
saveQuietly(), query-builder update() / delete(), upsert(), mass insert(), or raw SQL. Use Model::flushCacheTags() or SmartCache::flushTags() in those paths.
When an Eloquent event fires inside a database transaction, SmartCache defers invalidation until the outer transaction commits by default. This avoids flushing cache and then letting another request re-cache pre-commit data. Disable with smart-cache.model_invalidation.after_commit = false if your application needs immediate invalidation.
Dependency Map Cookbook
Declare tags for the data used to build the response, not for the controller or route name. Use coarse tags for list endpoints such as products, add instance tags for detail endpoints such as product_123, and prefer over-tagging to under-tagging. For opaque endpoints, capture Laravel's query log in a test and compare the selected tables with the cache tags you declared.
Custom Optimization Strategies
Create custom optimization strategies for your specific needs:
use SmartCache\Contracts\OptimizationStrategy;
class JsonCompressionStrategy implements OptimizationStrategy
{
public function shouldApply(mixed $value, array $context = []): bool
{
return is_array($value) &&
json_encode($value, JSON_UNESCAPED_UNICODE) !== false &&
strlen(json_encode($value)) > 10240; // 10KB threshold
}
public function optimize(mixed $value, array $context = []): mixed
{
$json = json_encode($value, JSON_UNESCAPED_UNICODE);
return [
'_sc_json_compressed' => true,
'data' => gzcompress($json, 9)
];
}
public function restore(mixed $value, array $context = []): mixed
{
if (is_array($value) && ($value['_sc_json_compressed'] ?? false)) {
return json_decode(gzuncompress($value['data']), true);
}
return $value;
}
public function getIdentifier(): string
{
return 'json_compression';
}
}
// Register your custom strategy
SmartCache::addStrategy(new JsonCompressionStrategy());
🛡️ Best Practices & Troubleshooting
- Binary Data: If caching already compressed objects like images, video, or encrypted data, disable
compressionfor those specific cache keys as they waste CPU cycles without yielding size reductions. - Memory Limits with Chunking: Large multi-megabyte datasets automatically trigger the 'chunking' strategy. For arrays over 100,000 items, verify
memory_limitinphp.inior enablelazy_loadingvia config to avoid server crashes. - Provider Not Found: Laravel aggressively caches service providers and aliases. Always run
php artisan optimize:clearafter upgrading or installing this package if encountering "Class 'SmartCache' not found". - IDE Autocomplete: Modern IDEs (PhpStorm, VSCode) completely resolve
SmartCache::magical methods automatically via our included Facade PHPDocs without needingide-helpergenerated files.
✨ Extended Features
🔐 Encryption Strategy
Encrypt sensitive cached data automatically:
// Configure encryption in config/smart-cache.php → strategies.encryption
'encryption' => [
'enabled' => true,
'keys' => ['user_token_abc123'], // Exact cache-key match
'patterns' => ['/^user_/', '/^payment_/'], // Regex patterns
],
// Data matching patterns is automatically encrypted
SmartCache::put('user_123_data', $sensitiveData, 3600);
// Automatically encrypted at rest
// Decryption is automatic on retrieval
$data = SmartCache::get('user_123_data');
// Returns decrypted data
📁 Cache Namespacing
Group cache keys by namespace for better organization:
// Set active namespace
SmartCache::namespace('api_v2');
// All operations now use this namespace
SmartCache::put('users', $users, 3600); // Actually stores 'api_v2:users'
SmartCache::get('users'); // Gets 'api_v2:users'
// Flush entire namespace
SmartCache::flushNamespace('api_v2');
// Get all keys in namespace
$keys = SmartCache::getNamespaceKeys('api_v2');
// Disable namespace temporarily
SmartCache::withoutNamespace()->put('global_key', $value, 3600);
⏱️ TTL Jitter
Prevent thundering herd by adding random variation to TTL:
// Enable jitter globally in config
'jitter' => [
'enabled' => true,
'percentage' => 0.1, // 10% variation
],
// Or enable per-operation
SmartCache::withJitter(0.15)->put('key', $value, 3600);
// TTL will be 3060-3540 seconds (±15%)
// Use jitter-specific methods
SmartCache::putWithJitter('key', $value, 3600, 0.2);
SmartCache::rememberWithJitter('key', 3600, fn() => $value, 0.1);
🔌 Circuit Breaker
Protect your application from cache backend failures:
// Configure circuit breaker
'circuit_breaker' => [
'enabled' => true,
'failure_threshold' => 5, // Open after 5 failures
'recovery_timeout' => 30, // Try again after 30 seconds
'success_threshold' => 3, // Close after 3 successes
],
// Check if cache is available
if (SmartCache::isAvailable()) {
$data = SmartCache::get('key');
} else {
$data = $this->fallbackSource();
}
// Use fallback automatically
$data = SmartCache::withFallback(
fn() => SmartCache::get('key'),
fn() => $this->fallbackSource()
);
// Get circuit breaker statistics
$stats = SmartCache::getCircuitBreakerStats();
// Returns: state, failures, successes, last_failure_time
🚦 Rate Limiting & Stampede Protection
Prevent cache stampede with probabilistic early expiration:
// Throttle cache regeneration
SmartCache::throttle('expensive_key', 10, 60, function() {
return expensiveOperation();
}); // Max 10 regenerations per 60 seconds
// Stampede protection with XFetch algorithm
$data = SmartCache::rememberWithStampedeProtection(
'popular_key',
3600,
fn() => expensiveQuery(),
1.0 // Beta factor (higher = earlier refresh)
);
// Configure rate limiter
'rate_limiter' => [
'enabled' => true,
'window' => 60,
'max_attempts' => 10,
],
🔥 Cache Warming
Pre-populate cache with frequently accessed data:
// Create a cache warmer
use SmartCache\Contracts\CacheWarmer;
class ProductCacheWarmer implements CacheWarmer
{
public function warm(): array
{
$products = Product::with('images')->get();
SmartCache::put('all_products', $products, 3600);
return ['keys' => 1];
}
public function getName(): string
{
return 'products';
}
public function getDescription(): string
{
return 'Warms the product cache';
}
}
// Register in config/smart-cache.php
'warmers' => [
'products' => App\CacheWarmers\ProductCacheWarmer::class,
],
// Run cache warming command
php artisan smart-cache:warm
// Or warm specific warmers
php artisan smart-cache:warm --warmer=products
🧹 Orphan Chunk Cleanup
Automatically clean up orphaned chunks from expired data:
// Run cleanup command
php artisan smart-cache:cleanup-chunks
// Schedule automatic cleanup
// In app/Console/Kernel.php
$schedule->command('smart-cache:cleanup-chunks')->daily();
// Cleanup is also triggered automatically when:
// - A chunked key is forgotten
// - A chunked key expires and is accessed
📊 Cache Statistics Dashboard
Visual dashboard for monitoring cache performance:
// Enable dashboard in config
'dashboard' => [
'enabled' => true,
'prefix' => 'smart-cache',
'middleware' => ['web', 'auth'],
],
// Access dashboard at:
// GET /smart-cache/dashboard
// JSON API endpoints:
// GET /smart-cache/statistics - Performance metrics
// GET /smart-cache/health - Health check
// GET /smart-cache/keys - Managed keys list
🔄 Background Refresh
Queue-based background cache refresh:
use App\Cache\RefreshExpensiveValue;
use SmartCache\Facades\SmartCache;
// Pass a serializable invokable class or a "Class@method" string.
SmartCache::refreshAsync('expensive_key', RefreshExpensiveValue::class, 3600, 'cache-refresh');
// Serve a stale value immediately and refresh it through the queue.
SmartCache::asyncSwr('key', RefreshExpensiveValue::class, 300, 900, 'cache-refresh');
refreshAsync() and asyncSwr() reject Closures because Laravel queue drivers cannot serialize them safely. Ensure a queue worker is running for the selected queue.
🧠 Cost-Aware Caching
Inspired by the GreedyDual-Size algorithm, every remember() call automatically tracks regeneration cost, access frequency, and data size to compute a value score — letting you make data-driven eviction decisions.
score = (cost × ln(1 + access_count) × decay) / size. Expensive, frequently accessed, small entries score highest. Cheap, rarely used, large entries score lowest.
// Automatic — just use remember() as usual
$report = SmartCache::remember('analytics', 3600, function () {
return AnalyticsService::generateReport(); // 800ms callback
});
$users = SmartCache::remember('users', 3600, function () {
return User::all(); // 5ms callback
});
// See which cache entries are most valuable
$report = SmartCache::getCacheValueReport();
// [
// ['key' => 'analytics', 'cost_ms' => 800, 'access_count' => 47, 'score' => 92.4],
// ['key' => 'users', 'cost_ms' => 5, 'access_count' => 120, 'score' => 14.1],
// ]
// Get value score for a specific key
$meta = SmartCache::cacheValue('analytics');
// ['cost_ms' => 800, 'access_count' => 47, 'size_bytes' => 4096, 'score' => 92.4]
// When you need to free space, find the least valuable entries
$evictable = SmartCache::suggestEvictions(5);
// Returns the 5 lowest-score keys — safe to remove first
🧬 Write Deduplication (Cache DNA)
SmartCache hashes every value before writing. When the stored content is identical to what is already cached, the write is skipped entirely — eliminating redundant I/O for frequently refreshed but rarely changing data such as configuration, feature flags, and rate-limit counters.
// Frequent cron refreshes? Only the first write hits the store.
SmartCache::put('app_config', Config::all(), 3600);
// Second call with the same data skips the value rewrite; metadata may refresh
SmartCache::put('app_config', Config::all(), 3600);
// When data actually changes, the write proceeds normally
SmartCache::put('app_config', $updatedConfig, 3600); // written
xxh128 hash of the serialized value is stored alongside each entry as _sc_dna:{key}. On subsequent writes, SmartCache compares the hash — if identical, the value rewrite is skipped. The DNA metadata may still be refreshed, shares the data TTL, and is cleaned up on forget().
// Enable/disable in config/smart-cache.php
'deduplication' => [
'enabled' => true, // enabled by default
],
🎯 Conditional Caching (rememberIf)
Cache values only when a condition is met. The callback always executes, but the result is stored only if the condition returns true. This is useful for filtering out empty, error, or invalid responses from external APIs.
// Only cache non-empty API responses
$data = SmartCache::rememberIf('external_api', 3600,
fn() => Http::get('https://api.example.com/data')->json(),
fn($value) => !empty($value) && isset($value['status'])
);
// Only cache results above a minimum size
$report = SmartCache::rememberIf('analytics_report', 7200,
fn() => AnalyticsService::generate(),
fn($result) => count($result) > 0
);
// Combined with cost-aware caching — generation cost is still tracked
$users = SmartCache::rememberIf('active_users', 1800,
fn() => User::where('active', true)->get(),
fn($users) => $users->isNotEmpty()
);
remember() with an if check, rememberIf() integrates with cost tracking and deduplication out of the box.
🩹 Self-Healing Cache
Corrupted or unrestorable cache entries are automatically evicted instead of being returned. Combined with remember() or rememberIf(), the callback can regenerate the entry on the next read. This also covers partial chunk loss in very large cached arrays.
// If 'report' is corrupted, SmartCache evicts it silently
// and the callback regenerates it on the next call
$report = SmartCache::remember('report', 3600, fn() => Analytics::generate());
// Self-healing also cleans up associated DNA hashes
// and broken chunk metadata from large entries
// and logs the eviction for observability:
// "SmartCache self-healing: evicted corrupted key [report]"
// Enable/disable in config/smart-cache.php
'self_healing' => [
'enabled' => true, // enabled by default
],
CompressionStrategy::restore() now explicitly validates the base64, gzip and unserialize steps and throws RuntimeException on any failure (no more silent E_NOTICEs reaching application logs). The self-healing eviction was extended to drop the full footprint of a corrupted entry: wrapper key, SWR metadata (_sc_meta:{key}), Cache DNA hash (_sc_dna:{key}), managed-keys index entry, and — for chunked values — every chunk key referenced by chunk_keys plus the orphan-chunk registry entry.
🚀 Laravel Octane / Long-Running Workers v1.12.0
SmartCache is registered as a singleton, which means per-request state (active tags, namespaces, in-memory metric buffers) would normally leak between requests on Laravel Octane, Swoole, FrankenPHP or RoadRunner. Since v1.12.0 the service provider's terminating() hook calls SmartCache::reset() and OrphanChunkCleanupService::flush() at the end of every request, so there is nothing extra to configure.
// If you embed SmartCache in your own long-running runtime, call reset() between iterations:
app(\SmartCache\Contracts\SmartCache::class)->reset();
Operational Footprint
SmartCache stores metadata beside application values in the selected cache store. Depending on enabled features, keys can include _sc_managed_keys, _sc_dna:{key}, _sc_meta:{key}, _sc_chunk_*, _sc_tag_*, _sc_dependencies, _sc_cost_metadata, and _sc_performance_metrics.
- Use a dedicated cache store or Laravel cache prefix when multiple applications share infrastructure.
- Set
managed_keys.max_trackedfor high-cardinality workloads after considering the effect on managed-only bulk clearing. - Run
php artisan smart-cache:auditbefore and after production cache changes.
Configuration Reference
// config/smart-cache.php
return [
// ... existing options ...
'strategies' => [
// ... compression, chunking ...
'encryption' => [
'enabled' => false,
'keys' => [],
'patterns' => [],
'encrypt_all' => false,
],
],
'circuit_breaker' => [
'enabled' => false,
'failure_threshold' => 5,
'recovery_timeout' => 30,
'success_threshold' => 3,
'shared' => false, // v1.12.0: share breaker state across workers via the cache
'shared_ttl' => 300, // v1.12.0: TTL for the shared state key
],
'rate_limiter' => [
'enabled' => true,
'window' => 60,
'max_attempts' => 10,
],
'jitter' => [
'enabled' => false,
'percentage' => 0.1,
],
'cost_aware' => [
'enabled' => true, // Toggle cost tracking on/off
'max_tracked_keys' => 1000, // Limit metadata memory
'metadata_ttl' => 86400, // Metadata persistence (1 day)
],
'deduplication' => [
'enabled' => true, // Write deduplication (Cache DNA)
],
'self_healing' => [
'enabled' => true, // Auto-evict corrupted entries
],
'swr' => [
'single_flight' => false, // v1.12.0: opt-in single-flight refresh (LockProvider stores)
],
'managed_keys' => [
'max_tracked' => 0, // v1.12.0: cap the in-memory managed-keys index (0 = unlimited)
],
'metadata_lock' => [
'enabled' => true, // Lock tag/dependency metadata when the store supports locks
'ttl' => 5,
'wait' => 1,
],
'model_invalidation' => [
'after_commit' => true, // Defer model invalidation until DB commit
],
'dashboard' => [
'enabled' => false,
'prefix' => 'smart-cache',
'middleware' => ['web'],
],
'warmers' => [
// 'products' => App\CacheWarmers\ProductCacheWarmer::class,
],
];
📚 API Reference
🔧 Basic Cache Operations
Retrieve an item from the cache with automatic restoration of optimized data.
$users = SmartCache::get('users', []);
$config = SmartCache::get('app_config', collect());
Store an item in the cache with automatic optimization (compression/chunking).
SmartCache::put('users', $users, 3600);
SmartCache::put('config', $config, now()->addHours(2));
Get an item from the cache, or execute the callback and store the result.
$users = SmartCache::remember('users', 3600, function() {
return User::with('profile')->get();
});
Get an item from the cache, or execute the callback and store the result only if the condition is met. The callback always runs on a cache miss, but the value is persisted only when $condition($value) returns true.
true to cache
$data = SmartCache::rememberIf('api_data', 3600,
fn() => Http::get('https://api.example.com/data')->json(),
fn($value) => !empty($value) && isset($value['status'])
);
Determine if an item exists in the cache.
if (SmartCache::has('users')) {
$users = SmartCache::get('users');
}
Remove an item from the cache, including any optimized chunks.
SmartCache::forget('users');
Get a SmartCache instance for a specific cache driver. Enabled SmartCache optimizations (compression, chunking, encryption, etc.) are preserved. The returned instance implements Illuminate\Contracts\Cache\Repository.
// Use Redis with all SmartCache optimizations
SmartCache::store('redis')->put('key', $value, 3600);
SmartCache::store('redis')->get('key');
// Use Memcached with optimizations
SmartCache::store('memcached')->remember('users', 3600, fn() => User::all());
// Chain operations on a specific store
$redisCache = SmartCache::store('redis');
$redisCache->put('users', $users, 3600);
$redisCache->put('products', $products, 3600);
// Works with Repository type hints
function cacheData(\Illuminate\Contracts\Cache\Repository $cache) {
$cache->put('key', 'value', 3600);
}
cacheData(SmartCache::store('redis')); // ✅ Works
Get direct access to Laravel's underlying cache repository. This bypasses all SmartCache optimizations.
// Direct access to Redis cache (no SmartCache optimizations)
SmartCache::repository('redis')->put('key', $value, 3600);
// Direct access to default cache store
SmartCache::repository()->get('key');
repository() only when you need to bypass SmartCache optimizations. For normal usage, prefer store() to maintain all optimization benefits.
🌊 SWR Patterns
Stale-While-Revalidate pattern. When a refresh is due, the callback runs synchronously before the stale value is returned.
$apiData = SmartCache::swr('github_repos', function() {
return Http::get('https://api.github.com/user/repos')->json();
}, 300, 900); // 5min fresh, 15min stale
Extended stale serving - allows serving stale data for extended periods.
$siteConfig = SmartCache::stale('site_config', function() {
return Config::fromDatabase();
}, 3600, 86400); // 1hour fresh, 24hour stale
Refresh-ahead pattern - proactively refreshes cache before expiration.
$analytics = SmartCache::refreshAhead('daily_analytics', function() {
return Analytics::generateReport();
}, 1800, 300); // 30min TTL, 5min refresh window
Dispatch a queued job that regenerates and stores a value. The callback must be serializable; Closures are rejected before dispatch.
"Class@method" string, or callable array
SmartCache::refreshAsync(
'dashboard_stats',
RefreshDashboardStats::class,
900,
'cache-refresh'
);
Queue-based background SWR — returns stale data immediately and dispatches a queued job to refresh the cache asynchronously.
"Class@method" string, or callable array; Closures are rejected
$data = SmartCache::asyncSwr(
'dashboard_stats',
RefreshDashboardStats::class,
300,
900,
'cache-refresh'
); // 5min fresh, 15min stale, queued on 'cache-refresh'
🔗 Cache Invalidation & Dependencies
Tag cache entries for organized invalidation.
SmartCache::tags(['users', 'profiles'])->put('user_1', $user1, 3600);
SmartCache::tags('products')->put('product_123', $product, 1800);
Flush all cache entries associated with given tags. Tag metadata is pruned lazily when tags are read, so expired entries do not build up indefinitely in rarely flushed tags.
SmartCache::flushTags(['users']); // Clear all user-related cache
Flush model cache tags explicitly after writes that bypass Eloquent events, such as query-builder updates, upserts, mass inserts, quiet saves, or raw SQL.
User::where('active', false)->delete();
User::flushCacheTags(['users']);
Add cache key dependency relationships.
SmartCache::dependsOn('user_posts', 'user_profile');
Clear cache entries matching wildcard or regex patterns.
$cleared = SmartCache::flushPatterns([
'user_*', // All user keys
'api_v2_*', // All API v2 cache
'/product_\d+/' // Regex: product_123, product_456
]);
📊 Performance Monitoring
Get comprehensive performance metrics about cache usage.
$metrics = SmartCache::getPerformanceMetrics();
/*
Returns:
[
'cache_efficiency' => [
'hit_ratio' => 0.943,
'miss_ratio' => 0.057,
'total_requests' => 1000
],
'optimization_impact' => [
'compression_savings' => 0.68,
'chunking_benefits' => 0.23,
'total_size_reduction' => 0.72
]
]
*/
Get automated performance analysis and recommendations.
$analysis = SmartCache::analyzePerformance();
/*
Returns:
[
'overall_health' => 'good', // good, warning, critical
'recommendations' => [
'Consider increasing cache TTL for frequently accessed data'
],
'score' => 85
]
*/
Execute cache management commands programmatically.
$status = SmartCache::executeCommand('status');
$result = SmartCache::executeCommand('clear', ['key' => 'specific_key']);
🔧 Extended API
Get a lock instance to prevent cache stampede and race conditions.
$lock = SmartCache::lock('expensive_operation', 10);
if ($lock->get()) {
$data = expensiveApiCall();
SmartCache::put('api_data', $data, 3600);
$lock->release();
}
Get a memoized cache instance that holds values in memory for the current request.
$memo = SmartCache::memo();
$users = $memo->remember('users', 3600, fn() => User::all());
$users = $memo->get('users'); // From memory — no cache round-trip
Retrieve multiple cache keys at once.
$values = SmartCache::many(['key1', 'key2', 'key3']);
Store multiple cache keys at once.
SmartCache::putMany([
'key1' => 'value1',
'key2' => 'value2',
], 3600);
Delete multiple cache keys at once.
SmartCache::deleteMultiple(['key1', 'key2', 'key3']);
Get compression statistics for optimized data.
$optimized = SmartCache::optimize($data);
$stats = SmartCache::getCompressionStats($optimized);
// Returns: level, original_size, compressed_size, ratio, savings_bytes, savings_percent
Get serialization statistics for optimized data.
$optimized = SmartCache::optimize($data);
$stats = SmartCache::getSerializationStats($optimized);
// Returns: method, original_size, serialized_size, ratio
✨ Resilience & Organization API
Set the active namespace for cache operations.
SmartCache::namespace('api_v2')->put('users', $users, 3600);
// Stores as 'api_v2:users'
Flush all cache entries in a namespace.
$count = SmartCache::flushNamespace('api_v2');
// Clears all keys prefixed with 'api_v2:'
Enable TTL jitter for subsequent operations.
SmartCache::withJitter(0.15)->put('key', $value, 3600);
// TTL will be 3060-3540 seconds (±15%)
Check if cache backend is available (circuit breaker).
if (SmartCache::isAvailable()) {
$data = SmartCache::get('key');
} else {
$data = $this->fallbackSource();
}
Execute cache operation with automatic fallback on failure.
$data = SmartCache::withFallback(
fn() => SmartCache::get('key'),
fn() => $this->database->get('key')
);
Rate-limit cache regeneration to prevent stampede.
$data = SmartCache::throttle('expensive_key', 10, 60, function() {
return expensiveOperation();
}); // Max 10 regenerations per 60 seconds
Remember with XFetch algorithm for probabilistic early refresh.
$data = SmartCache::rememberWithStampedeProtection(
'popular_key', 3600, fn() => expensiveQuery(), 1.0
);
Get circuit breaker statistics.
$stats = SmartCache::getCircuitBreakerStats();
// Returns: ['state' => 'closed', 'failures' => 0, 'successes' => 5]
🧠 Cost-Aware Caching API
Get value score metadata for a specific cache key.
$meta = SmartCache::cacheValue('analytics');
// ['cost_ms' => 800, 'access_count' => 47, 'size_bytes' => 4096, 'score' => 92.4, ...]
Get a full report of all tracked cache entries sorted by value score (highest first).
$report = SmartCache::getCacheValueReport();
// [
// ['key' => 'analytics', 'cost_ms' => 800, 'access_count' => 47, 'score' => 92.4],
// ['key' => 'users', 'cost_ms' => 5, 'access_count' => 120, 'score' => 14.1],
// ]
Suggest the lowest-value cache entries for eviction when you need to free space.
$evictable = SmartCache::suggestEvictions(5);
// Returns the 5 lowest-score keys — safest to remove first
foreach ($evictable as $entry) {
SmartCache::forget($entry['key']);
}
Get the underlying CostAwareCacheManager instance for advanced usage.
$manager = SmartCache::getCostAwareManager();
if ($manager) {
echo "Tracking " . $manager->trackedKeyCount() . " keys";
}
🛠️ Cache Management
Historical 1.x alias for clearManaged(). It clears tracked SmartCache keys only, not the entire PSR-16 cache pool. The behavior is preserved for backward compatibility.
SmartCache::clear(); // Clear all SmartCache managed keys
Explicitly clear cache entries tracked by SmartCache while preserving unrelated keys in the same store. Prefer this scope-explicit method in new application code. It is available on the facade and concrete class but intentionally omitted from the 1.x public contract to preserve compatibility with third-party implementations.
SmartCache::clearManaged(); // Preserve unrelated Laravel cache keys
Flush the entire cache store (all keys, not just SmartCache-managed ones). Equivalent to Laravel's Cache::flush().
SmartCache::flush(); // Flush ALL cache keys (entire store)
Get the raw (unrestored) value from cache, bypassing optimization strategies. Useful for diagnostics and debugging.
$raw = SmartCache::getRaw('users');
// Returns the raw stored value with compression/chunking markers intact
Get all keys currently managed by SmartCache.
$keys = SmartCache::getManagedKeys();
foreach ($keys as $key) {
echo "Managed key: {$key}\n";
}
🔧 Helper Functions
Global helper function for SmartCache operations.
// Get SmartCache instance
$cache = smart_cache();
// Get value
$users = smart_cache('users');
// Set value
smart_cache('users', $users, 3600);
💻 CLI Commands
Run a read-only audit of SmartCache managed keys, chunk health, large unoptimized values, orphan chunks, and cost-aware eviction suggestions.
cache.default
table for humans or json for CI/reporting
# Human-readable audit
php artisan smart-cache:audit
# JSON report for CI or dashboards
php artisan smart-cache:audit --driver=redis --format=json --limit=50
Benchmark raw Laravel cache operations against SmartCache optimization using controlled payload profiles for medium values, API JSON, large arrays, and sparse arrays.
all, medium, api-json, large-array, or sparse-array
# Benchmark the default cache store
php artisan smart-cache:bench
# Benchmark Redis with more samples
php artisan smart-cache:bench --driver=redis --iterations=10
# Save a JSON report
php artisan smart-cache:bench --format=json --output=storage/smart-cache-bench.json
docs/benchmark-report-redis.json was generated locally with PHP 8.4, Laravel 13, and Redis. Each profile includes a goal and result summary, so compression is judged by byte reduction while chunking is judged by safe splitting and key preservation. Use it as a reproducible example, not a universal performance promise.
Display comprehensive information about SmartCache usage, configuration, and performance.
# Basic status overview
php artisan smart-cache:status
# Detailed analysis with recommendations
php artisan smart-cache:status --force
Clear SmartCache managed items. Optionally specify a key to clear only that item.
# Clear all SmartCache managed keys
php artisan smart-cache:clear
# Clear specific key
php artisan smart-cache:clear user_profile_123
# Force clear any cache key
php artisan smart-cache:clear --force
Pre-populate cache with frequently accessed data using registered warmers.
# Warm all registered cache warmers
php artisan smart-cache:warm
# Warm specific warmers only
php artisan smart-cache:warm --warmer=products --warmer=categories
# List available warmers
php artisan smart-cache:warm --list
Clean up orphaned chunks from expired or deleted chunked cache entries.
# Run cleanup manually
php artisan smart-cache:cleanup-chunks
# Schedule in Kernel.php
$schedule->command('smart-cache:cleanup-chunks')->daily();
🧪 Testing & Validation
Package Test Suite
SmartCache ships with automated coverage for the Laravel-compatible API surface, optimization strategies, SWR patterns, invalidation, memoization, dashboard command execution, and large-data recovery behavior.
composer test
# 486 tests, 1,976 assertions
vendor/bin/phpunit tests/Unit/Strategies/ChunkingStrategyTest.php --testdox
vendor/bin/phpunit tests/Unit/SmartCacheTest.php --filter "missing_chunk|self_healing" --testdox
remember() to regenerate a clean value.
Test Controller
The controller below exercises core SmartCache features — basic operations, compression, chunking, SWR patterns, invalidation, and monitoring — and returns structured JSON results.
Controller Implementation
namespace App\Http\Controllers\Api\V1;
use App\Http\Controllers\Controller;
use Composer\InstalledVersions;
use SmartCache\Facades\SmartCache;
class TestSmartCacheDirectlyController extends Controller
{
public function testSmartCacheFeatures()
{
$packageInfo = [
'version' => InstalledVersions::getPrettyVersion('iazaran/smart-cache') ?? 'unknown',
'features_tested' => 8,
];
$tests = [];
try {
// Test 1: Basic Operations
$tests['basic_operations'] = $this->testBasicOperations();
// Test 2: Compression (2000+ records)
$tests['compression_test'] = $this->testCompressionFeature();
// Test 3: Chunking (5000+ items)
$tests['chunking_test'] = $this->testChunkingFeature();
// Test 4: Helper Functions
$tests['helper_function'] = $this->testHelperFunctions();
// Test 5: SWR Patterns
$tests['swr_patterns'] = $this->testSWRPatterns();
// Test 6: Cache Invalidation & Tags
$tests['cache_invalidation'] = $this->testCacheInvalidation();
// Test 7: Performance Monitoring
$tests['performance_monitoring'] = $this->testPerformanceMonitoring();
// Test 8: Advanced Features (remember, locking)
$tests['advanced_features'] = $this->testAdvancedFeatures();
return response()->json([
'message' => 'SmartCache feature tests completed',
'package_info' => $packageInfo,
'tests' => $tests,
'overall_status' => 'success'
]);
} catch (\Exception $e) {
return response()->json([
'message' => 'SmartCache test failed',
'error' => $e->getMessage()
], 500);
}
}
private function testBasicOperations()
{
$testData = ['data' => 'test_value', 'number' => 123, 'array' => range(1, 10)];
// Test facade operations
SmartCache::put('test_facade', $testData, 300);
$facadeResult = SmartCache::get('test_facade');
// Test helper operations
smart_cache(['test_helper' => $testData], 300);
$helperResult = smart_cache('test_helper');
return [
'status' => 'success',
'facade_results' => [
'data_integrity' => $testData === $facadeResult,
'has_key' => SmartCache::has('test_facade')
],
'helper_results' => [
'data_integrity' => $testData === $helperResult,
'has_key' => SmartCache::has('test_helper')
],
'both_methods_working' => ($testData === $facadeResult && $testData === $helperResult)
];
}
// ... Additional test methods for compression, chunking, SWR patterns, etc.
// See complete implementation in the GitHub Gist
}
How to Use the Test Controller
1. Installation
Add this controller to your Laravel application:
php artisan make:controller Api/V1/TestSmartCacheDirectlyController
Route::get('/test-smart-cache', [TestSmartCacheDirectlyController::class, 'testSmartCacheFeatures']);
2. Running the Tests
Access the endpoint to run comprehensive tests:
# Via HTTP request
curl http://your-app.com/api/test-smart-cache
# Or via browser
http://your-app.com/api/test-smart-cache
3. Understanding the Results
The test controller returns a comprehensive JSON response with detailed results:
{
"message": "SmartCache feature tests completed",
"package_info": {
"version": "1.13.0",
"features_tested": 8
},
"tests": {
"basic_operations": {
"status": "success",
"both_methods_working": true,
"facade_results": { "data_integrity": true, "has_key": true },
"helper_results": { "data_integrity": true, "has_key": true }
},
"compression_test": {
"status": "success",
"original_count": 2000,
"estimated_size_kb": 2207.29,
"both_methods_successful": true
},
"chunking_test": {
"status": "success",
"original_count": 5000,
"data_matches": true
},
"performance_monitoring": {
"status": "success",
"monitoring_working": true
}
// ... additional test results
},
"overall_status": "success"
}
What Each Test Validates
🔧 Basic Operations
Tests put/get/has/forget operations with both facade and helper functions, ensuring data integrity and method compatibility.
📦 Compression Testing
Validates automatic compression with 2000+ records, measuring size reduction and performance impact.
🧩 Chunking Validation
Tests chunking strategy with 5000+ items, verifying data integrity and retrieval performance.
🌊 SWR Patterns
Validates Stale-While-Revalidate patterns, write-through, write-behind, and cache-aside strategies.
🔗 Cache Invalidation
Tests tag-based invalidation, pattern matching, and dependency tracking features.
📊 Performance Monitoring
Validates metrics collection, performance analysis, and automated recommendations.
Full controller source: GitHub Gist
🔄 Migration Guide
From Laravel Cache
SmartCache implements Laravel's Repository contract, so ordinary operations can be migrated with an import change. Start with the large or operationally sensitive keys that benefit, then review clearing and rollback behavior explicitly:
composer require iazaran/smart-cache
// Before
use Illuminate\Support\Facades\Cache;
// After
use SmartCache\Facades\SmartCache;
// Before
Cache::put('key', $value, 3600);
$value = Cache::get('key');
// SmartCache uses familiar operations and applies its configured optimizations
SmartCache::put('key', $value, 3600);
$value = SmartCache::get('key');
clear(). Use clearManaged() to preserve unrelated keys or flush() to clear the entire underlying cache store.
Gradual Migration
You can migrate gradually by using both facades:
use Illuminate\Support\Facades\Cache;
use SmartCache\Facades\SmartCache;
// Keep existing code unchanged
Cache::put('old_key', $value, 3600);
// Use SmartCache for optimized caching
SmartCache::put('optimized_key', $value, 3600);
SmartCache::swr('api_data', $callback, 300, 900);
Testing Migration
Test your migration with these commands:
# Test basic functionality
php artisan smart-cache:status
# Test with your application
php artisan tinker
>>> SmartCache::put('test', 'Hello World', 60);
>>> SmartCache::get('test');
=> "Hello World"
Rollback Plan
Before removing SmartCache, expire or clear values written through it and let native Laravel cache repopulate them. SmartCache may store compressed, chunked, serialized, or encrypted wrappers that Laravel's native cache does not decode.
# Change back to Laravel Cache imports in your code
# Clear SmartCache-managed entries when it is safe for the application
php artisan smart-cache:clear
# Then remove the package after native cache paths are deployed
composer remove iazaran/smart-cache
flush() unless clearing every application's key is intentional.