Laravel cache optimizer

Make large cache payloads feel lightweight.

Automatic compression, driver-safe chunking, write deduplication, and self-healing recovery behind Laravel's familiar cache API.

Optimization for large Laravel cache payloads. SmartCache wraps Laravel's familiar cache API with automatic compression, driver-safe chunking, invalidation, recovery, and diagnostics. It implements 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

1 Install the package:
composer require iazaran/smart-cache
2 Start using it immediately:
use SmartCache\Facades\SmartCache;

// Familiar Laravel cache operations
SmartCache::put('users', $users, 3600);
$users = SmartCache::get('users');
SmartCache works out-of-the-box with sensible defaults. Data is automatically compressed and optimized when beneficial.

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-zlib for gzip compression. JSON is always enabled in PHP 8+.
Compatibility is not an upstream security guarantee. SmartCache tests Laravel 8–13, but enterprise deployments should use a currently supported PHP release and a Laravel version inside the official support window.

Installation Steps

1 Install via Composer:
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:

1 Publish the configuration file:
php artisan vendor:publish --tag=smart-cache-config
2 Configure in config/smart-cache.php:
 [
        '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);
Note: The 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.
Repository compatibility: SmartCache implements Laravel's 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

Typical improvements depend on your data shapes and access patterns. Compression is most effective on large text and repetitive structures; chunking benefits datasets that exceed driver size limits. Enable monitoring to measure the actual impact in your environment.

🌊 SWR Patterns

Execution model: 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');
How refresh execution works. 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

All advanced features are opt-in and backwards-compatible: Atomic Locks, Memoization, Events, Adaptive Compression, Lazy Loading, Smart Invalidation, and more.

🔒 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
    ]);
});
Note: Events are disabled by default for performance. Enable only when you need monitoring.

🔢 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']);
Eloquent event blind spots. Model auto-invalidation depends on Eloquent events. It does not run for 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 compression for 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_limit in php.ini or enable lazy_loading via config to avoid server crashes.
  • Provider Not Found: Laravel aggressively caches service providers and aliases. Always run php artisan optimize:clear after 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 needing ide-helper generated files.

✨ Extended Features

All extended features are opt-in and backwards-compatible: Write Deduplication, Self-Healing Cache, Conditional Caching, Cost-Aware Caching, Encryption at Rest, Namespacing, TTL Jitter, Circuit Breaker, Rate Limiting, Cache Warming, and more.

🔐 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);
Why Jitter? When many cache entries expire at the same time, they all try to regenerate simultaneously (thundering herd). Jitter spreads out expirations to prevent this.

🔌 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.

How it works: Each cache entry receives a score calculated as 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
Useful for production monitoring, capacity planning, and intelligent eviction policies — know exactly which keys matter and which waste space.

🧬 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
How it works: A non-cryptographic 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()
);
Unlike wrapping 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
],
Enterprise use case: In high-availability environments, a single corrupted cache entry can cascade into repeated exceptions. Self-healing ensures the application degrades gracefully, evicts the bad entry, and recovers automatically.
v1.12.0 hardening. 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_tracked for high-cardinality workloads after considering the effect on managed-only bulk clearing.
  • Run php artisan smart-cache:audit before 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

Core public methods, parameters, return types, and operationally important behavior with examples.

🔧 Basic Cache Operations

SmartCache::get()
SmartCache::get(string $key, mixed $default = null): mixed

Retrieve an item from the cache with automatic restoration of optimized data.

$key (string) - The cache key
$default (mixed) - Default value if key doesn't exist
Returns: mixed - The cached value or default
Examples:
$users = SmartCache::get('users', []);
$config = SmartCache::get('app_config', collect());
SmartCache::put()
SmartCache::put(string $key, mixed $value, DateTimeInterface|DateInterval|int|null $ttl = null): bool

Store an item in the cache with automatic optimization (compression/chunking).

$key (string) - The cache key
$value (mixed) - The value to store
$ttl (DateTimeInterface|DateInterval|int|null) - Time to live
Returns: bool - True on success
Examples:
SmartCache::put('users', $users, 3600);
SmartCache::put('config', $config, now()->addHours(2));
SmartCache::remember()
SmartCache::remember(string $key, DateTimeInterface|DateInterval|int|null $ttl, Closure $callback): mixed

Get an item from the cache, or execute the callback and store the result.

$key (string) - The cache key
$ttl (DateTimeInterface|DateInterval|int|null) - Time to live
$callback (Closure) - Function to generate fresh data
Returns: mixed - Cached or fresh data
Example:
$users = SmartCache::remember('users', 3600, function() {
    return User::with('profile')->get();
});
SmartCache::rememberIf()
SmartCache::rememberIf(string $key, mixed $ttl, Closure $callback, callable $condition): mixed

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.

$key (string) - The cache key
$ttl (DateTimeInterface|DateInterval|int|null) - Time to live
$callback (Closure) - Function to generate fresh data
$condition (callable) - Receives the callback result; return true to cache
Returns: mixed - Cached or fresh data (always returns the value, even when not cached)
Example:
$data = SmartCache::rememberIf('api_data', 3600,
    fn() => Http::get('https://api.example.com/data')->json(),
    fn($value) => !empty($value) && isset($value['status'])
);
SmartCache::has()
SmartCache::has(string $key): bool

Determine if an item exists in the cache.

$key (string) - The cache key
Returns: bool - True if exists
Example:
if (SmartCache::has('users')) {
    $users = SmartCache::get('users');
}
SmartCache::forget()
SmartCache::forget(string $key): bool

Remove an item from the cache, including any optimized chunks.

$key (string) - The cache key
Returns: bool - True on success
Example:
SmartCache::forget('users');
SmartCache::store()
SmartCache::store(string|null $name = null): static

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.

$name (string|null) - The cache store name (redis, file, memcached, etc.)
Returns: static - SmartCache instance configured for the specified store (also implements Repository)
Examples:
// 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
SmartCache::repository()
SmartCache::repository(string|null $name = null): \Illuminate\Contracts\Cache\Repository

Get direct access to Laravel's underlying cache repository. This bypasses all SmartCache optimizations.

$name (string|null) - The cache store name (redis, file, memcached, etc.)
Returns: Repository - Laravel's native cache repository
Examples:
// 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');
Note: Use repository() only when you need to bypass SmartCache optimizations. For normal usage, prefer store() to maintain all optimization benefits.

🌊 SWR Patterns

SmartCache::swr()
SmartCache::swr(string $key, Closure $callback, int $ttl = 3600, int $staleTtl = 7200): mixed

Stale-While-Revalidate pattern. When a refresh is due, the callback runs synchronously before the stale value is returned.

$key (string) - The cache key
$callback (Closure) - Function to generate fresh data
$ttl (int) - Fresh data TTL in seconds
$staleTtl (int) - Stale data TTL in seconds
Returns: mixed - Fresh or stale data
Example:
$apiData = SmartCache::swr('github_repos', function() {
    return Http::get('https://api.github.com/user/repos')->json();
}, 300, 900); // 5min fresh, 15min stale
SmartCache::stale()
SmartCache::stale(string $key, Closure $callback, int $ttl = 1800, int $staleTtl = 86400): mixed

Extended stale serving - allows serving stale data for extended periods.

$key (string) - The cache key
$callback (Closure) - Function to generate fresh data
$ttl (int) - Fresh data TTL in seconds
$staleTtl (int) - Stale data TTL in seconds
Returns: mixed - Fresh or stale data
Example:
$siteConfig = SmartCache::stale('site_config', function() {
    return Config::fromDatabase();
}, 3600, 86400); // 1hour fresh, 24hour stale
SmartCache::refreshAhead()
SmartCache::refreshAhead(string $key, Closure $callback, int $ttl = 3600, int $refreshWindow = 600): mixed

Refresh-ahead pattern - proactively refreshes cache before expiration.

$key (string) - The cache key
$callback (Closure) - Function to generate fresh data
$ttl (int) - Cache TTL in seconds
$refreshWindow (int) - Refresh window in seconds
Returns: mixed - Cached or fresh data
Example:
$analytics = SmartCache::refreshAhead('daily_analytics', function() {
    return Analytics::generateReport();
}, 1800, 300); // 30min TTL, 5min refresh window
SmartCache::refreshAsync()
SmartCache::refreshAsync(string $key, callable|string $callback, ?int $ttl = null, ?string $queue = null): void

Dispatch a queued job that regenerates and stores a value. The callback must be serializable; Closures are rejected before dispatch.

$callback (callable|string) - Invokable class, "Class@method" string, or callable array
Example:
SmartCache::refreshAsync(
    'dashboard_stats',
    RefreshDashboardStats::class,
    900,
    'cache-refresh'
);
SmartCache::asyncSwr()
SmartCache::asyncSwr(string $key, callable|string $callback, int $ttl = 3600, int $staleTtl = 7200, ?string $queue = null): mixed

Queue-based background SWR — returns stale data immediately and dispatches a queued job to refresh the cache asynchronously.

$key (string) - The cache key
$callback (callable|string) - Serializable invokable class, "Class@method" string, or callable array; Closures are rejected
$ttl (int) - Fresh TTL in seconds
$staleTtl (int) - Maximum stale time in seconds
$queue (?string) - Queue name for the background refresh job
Returns: mixed - Fresh or stale data
Example:
$data = SmartCache::asyncSwr(
    'dashboard_stats',
    RefreshDashboardStats::class,
    300,
    900,
    'cache-refresh'
); // 5min fresh, 15min stale, queued on 'cache-refresh'

🔗 Cache Invalidation & Dependencies

SmartCache::tags()
SmartCache::tags(string|array $tags): static

Tag cache entries for organized invalidation.

$tags (string|array) - Tag name(s)
Returns: static - Chainable instance
Example:
SmartCache::tags(['users', 'profiles'])->put('user_1', $user1, 3600);
SmartCache::tags('products')->put('product_123', $product, 1800);
SmartCache::flushTags()
SmartCache::flushTags(string|array $tags, string $source = 'manual'): bool

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.

$tags (string|array) - Tag name(s)
$source (string) - Optional source label exposed on the TagFlushed event
Returns: bool - True on success
Example:
SmartCache::flushTags(['users']); // Clear all user-related cache
Model::flushCacheTags()
User::flushCacheTags(string|array|null $tags = null): bool

Flush model cache tags explicitly after writes that bypass Eloquent events, such as query-builder updates, upserts, mass inserts, quiet saves, or raw SQL.

Example:
User::where('active', false)->delete();
User::flushCacheTags(['users']);
SmartCache::dependsOn()
SmartCache::dependsOn(string $key, string|array $dependencies): static

Add cache key dependency relationships.

$key (string) - The dependent cache key
$dependencies (string|array) - Parent key(s)
Returns: static - Chainable instance
Example:
SmartCache::dependsOn('user_posts', 'user_profile');
SmartCache::flushPatterns()
SmartCache::flushPatterns(array $patterns): int

Clear cache entries matching wildcard or regex patterns.

$patterns (array) - Array of patterns (wildcards or regex)
Returns: int - Number of keys cleared
Example:
$cleared = SmartCache::flushPatterns([
    'user_*',           // All user keys
    'api_v2_*',         // All API v2 cache
    '/product_\d+/'     // Regex: product_123, product_456
]);

📊 Performance Monitoring

SmartCache::getPerformanceMetrics()
SmartCache::getPerformanceMetrics(): array

Get comprehensive performance metrics about cache usage.

Returns: array - Performance metrics including hit ratios, timing, and optimization impact
Example:
$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
    ]
]
*/
SmartCache::analyzePerformance()
SmartCache::analyzePerformance(): array

Get automated performance analysis and recommendations.

Returns: array - Analysis with health status, recommendations, and score
Example:
$analysis = SmartCache::analyzePerformance();
/*
Returns:
[
    'overall_health' => 'good', // good, warning, critical
    'recommendations' => [
        'Consider increasing cache TTL for frequently accessed data'
    ],
    'score' => 85
]
*/
SmartCache::executeCommand()
SmartCache::executeCommand(string $command, array $parameters = []): array

Execute cache management commands programmatically.

$command (string) - Command to execute (status, clear, etc.)
$parameters (array) - Command parameters
Returns: array - Command result
Example:
$status = SmartCache::executeCommand('status');
$result = SmartCache::executeCommand('clear', ['key' => 'specific_key']);

🔧 Extended API

SmartCache::lock()
SmartCache::lock(string $name, int $seconds = 0, string|null $owner = null): \Illuminate\Contracts\Cache\Lock

Get a lock instance to prevent cache stampede and race conditions.

$name (string) - Lock name
$seconds (int) - Lock timeout in seconds
$owner (string|null) - Lock owner identifier
Returns: Lock - Lock instance
Example:
$lock = SmartCache::lock('expensive_operation', 10);
if ($lock->get()) {
    $data = expensiveApiCall();
    SmartCache::put('api_data', $data, 3600);
    $lock->release();
}
SmartCache::memo()
SmartCache::memo(): \SmartCache\Drivers\MemoizedCacheDriver

Get a memoized cache instance that holds values in memory for the current request.

Returns: MemoizedCacheDriver - Memoized cache instance
Example:
$memo = SmartCache::memo();
$users = $memo->remember('users', 3600, fn() => User::all());
$users = $memo->get('users'); // From memory — no cache round-trip
SmartCache::many()
SmartCache::many(array $keys): array

Retrieve multiple cache keys at once.

$keys (array) - Array of cache keys
Returns: array - Associative array of key => value pairs
Example:
$values = SmartCache::many(['key1', 'key2', 'key3']);
SmartCache::putMany()
SmartCache::putMany(array $values, DateTimeInterface|DateInterval|int|null $ttl = null): bool

Store multiple cache keys at once.

$values (array) - Associative array of key => value pairs
$ttl (int|null) - Time to live in seconds
Returns: bool - True on success
Example:
SmartCache::putMany([
    'key1' => 'value1',
    'key2' => 'value2',
], 3600);
SmartCache::deleteMultiple()
SmartCache::deleteMultiple(array $keys): bool

Delete multiple cache keys at once.

$keys (array) - Array of cache keys
Returns: bool - True on success
Example:
SmartCache::deleteMultiple(['key1', 'key2', 'key3']);
SmartCache::getCompressionStats()
SmartCache::getCompressionStats(mixed $optimizedValue): array

Get compression statistics for optimized data.

$optimizedValue (mixed) - Optimized value from cache
Returns: array - Compression stats (level, sizes, ratio, savings)
Example:
$optimized = SmartCache::optimize($data);
$stats = SmartCache::getCompressionStats($optimized);
// Returns: level, original_size, compressed_size, ratio, savings_bytes, savings_percent
SmartCache::getSerializationStats()
SmartCache::getSerializationStats(mixed $optimizedValue): array

Get serialization statistics for optimized data.

$optimizedValue (mixed) - Optimized value from cache
Returns: array - Serialization stats (method, sizes, ratio)
Example:
$optimized = SmartCache::optimize($data);
$stats = SmartCache::getSerializationStats($optimized);
// Returns: method, original_size, serialized_size, ratio

✨ Resilience & Organization API

SmartCache::namespace()
SmartCache::namespace(string $namespace): static

Set the active namespace for cache operations.

$namespace (string) - Namespace prefix
Returns: static - Chainable instance
Example:
SmartCache::namespace('api_v2')->put('users', $users, 3600);
// Stores as 'api_v2:users'
SmartCache::flushNamespace()
SmartCache::flushNamespace(string $namespace): int

Flush all cache entries in a namespace.

$namespace (string) - Namespace to flush
Returns: int - Number of keys flushed
Example:
$count = SmartCache::flushNamespace('api_v2');
// Clears all keys prefixed with 'api_v2:'
SmartCache::withJitter()
SmartCache::withJitter(float $percentage = 0.1): static

Enable TTL jitter for subsequent operations.

$percentage (float) - Jitter percentage (0.1 = 10%)
Returns: static - Chainable instance
Example:
SmartCache::withJitter(0.15)->put('key', $value, 3600);
// TTL will be 3060-3540 seconds (±15%)
SmartCache::isAvailable()
SmartCache::isAvailable(): bool

Check if cache backend is available (circuit breaker).

Returns: bool - True if cache is available
Example:
if (SmartCache::isAvailable()) {
    $data = SmartCache::get('key');
} else {
    $data = $this->fallbackSource();
}
SmartCache::withFallback()
SmartCache::withFallback(callable $callback, mixed $fallback = null): mixed

Execute cache operation with automatic fallback on failure.

$callback (callable) - Primary cache operation
$fallback (mixed) - Fallback value or callable if cache fails (default: null)
Returns: mixed - Result from operation or fallback
Example:
$data = SmartCache::withFallback(
    fn() => SmartCache::get('key'),
    fn() => $this->database->get('key')
);
SmartCache::throttle()
SmartCache::throttle(string $key, int $maxAttempts, int $decaySeconds, callable $callback): mixed

Rate-limit cache regeneration to prevent stampede.

$key (string) - Cache key
$maxAttempts (int) - Max regenerations allowed
$decaySeconds (int) - Time window in seconds
$callback (callable) - Value generator
Returns: mixed - Cached or generated value
Example:
$data = SmartCache::throttle('expensive_key', 10, 60, function() {
    return expensiveOperation();
}); // Max 10 regenerations per 60 seconds
SmartCache::rememberWithStampedeProtection()
SmartCache::rememberWithStampedeProtection(string $key, int $ttl, callable $callback, float $beta = 1.0): mixed

Remember with XFetch algorithm for probabilistic early refresh.

$key (string) - Cache key
$ttl (int) - Time to live in seconds
$callback (callable) - Value generator
$beta (float) - Beta factor (higher = earlier refresh)
Returns: mixed - Cached or generated value
Example:
$data = SmartCache::rememberWithStampedeProtection(
    'popular_key', 3600, fn() => expensiveQuery(), 1.0
);
SmartCache::getCircuitBreakerStats()
SmartCache::getCircuitBreakerStats(): array

Get circuit breaker statistics.

Returns: array - Stats (state, failures, successes, last_failure_time)
Example:
$stats = SmartCache::getCircuitBreakerStats();
// Returns: ['state' => 'closed', 'failures' => 0, 'successes' => 5]

🧠 Cost-Aware Caching API

SmartCache::cacheValue()
SmartCache::cacheValue(string $key): ?array

Get value score metadata for a specific cache key.

$key (string) - Cache key to inspect
Returns: ?array - Metadata (cost_ms, access_count, size_bytes, score, last_accessed) or null
Example:
$meta = SmartCache::cacheValue('analytics');
// ['cost_ms' => 800, 'access_count' => 47, 'size_bytes' => 4096, 'score' => 92.4, ...]
SmartCache::getCacheValueReport()
SmartCache::getCacheValueReport(): array

Get a full report of all tracked cache entries sorted by value score (highest first).

Returns: array - List of all tracked keys with their metadata and scores
Example:
$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],
// ]
SmartCache::suggestEvictions()
SmartCache::suggestEvictions(int $count = 10): array

Suggest the lowest-value cache entries for eviction when you need to free space.

$count (int) - Number of eviction candidates to return (default: 10)
Returns: array - List of lowest-score keys with metadata
Example:
$evictable = SmartCache::suggestEvictions(5);
// Returns the 5 lowest-score keys — safest to remove first
foreach ($evictable as $entry) {
    SmartCache::forget($entry['key']);
}
SmartCache::getCostAwareManager()
SmartCache::getCostAwareManager(): ?\SmartCache\Services\CostAwareCacheManager

Get the underlying CostAwareCacheManager instance for advanced usage.

Returns: ?CostAwareCacheManager - Manager instance or null if disabled
Example:
$manager = SmartCache::getCostAwareManager();
if ($manager) {
    echo "Tracking " . $manager->trackedKeyCount() . " keys";
}

🛠️ Cache Management

SmartCache::clear()
SmartCache::clear(): bool

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.

Returns: bool - True on success
Example:
SmartCache::clear(); // Clear all SmartCache managed keys
SmartCache::clearManaged()
SmartCache::clearManaged(): bool

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.

Returns: bool - True when all tracked entries were cleared successfully
Example:
SmartCache::clearManaged(); // Preserve unrelated Laravel cache keys
SmartCache::flush()
SmartCache::flush(): bool

Flush the entire cache store (all keys, not just SmartCache-managed ones). Equivalent to Laravel's Cache::flush().

Returns: bool - True on success
Example:
SmartCache::flush(); // Flush ALL cache keys (entire store)
SmartCache::getRaw()
SmartCache::getRaw(string $key): mixed

Get the raw (unrestored) value from cache, bypassing optimization strategies. Useful for diagnostics and debugging.

$key (string) - Cache key
Returns: mixed - Raw cached value (may include optimization markers)
Example:
$raw = SmartCache::getRaw('users');
// Returns the raw stored value with compression/chunking markers intact
SmartCache::getManagedKeys()
SmartCache::getManagedKeys(): array

Get all keys currently managed by SmartCache.

Returns: array - List of managed cache keys
Example:
$keys = SmartCache::getManagedKeys();
foreach ($keys as $key) {
    echo "Managed key: {$key}\n";
}

🔧 Helper Functions

smart_cache()
smart_cache(string|array|null $key = null, mixed $value = null, DateTimeInterface|DateInterval|int|null $ttl = null): mixed

Global helper function for SmartCache operations.

$key (string|array|null) - Cache key or key-value array
$value (mixed) - Value to store
$ttl (DateTimeInterface|DateInterval|int|null) - Time to live
Returns: mixed - SmartCache instance or cached value
Examples:
// Get SmartCache instance
$cache = smart_cache();

// Get value
$users = smart_cache('users');

// Set value
smart_cache('users', $users, 3600);

💻 CLI Commands

smart-cache:audit
php artisan smart-cache:audit [--driver=] [--format=table|json] [--limit=20]

Run a read-only audit of SmartCache managed keys, chunk health, large unoptimized values, orphan chunks, and cost-aware eviction suggestions.

--driver (optional) - Cache store to audit, defaults to cache.default
--format (optional) - Use table for humans or json for CI/reporting
Examples:
# 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
smart-cache:bench
php artisan smart-cache:bench [--profile=all] [--driver=] [--iterations=3] [--format=table|json] [--output=]

Benchmark raw Laravel cache operations against SmartCache optimization using controlled payload profiles for medium values, API JSON, large arrays, and sparse arrays.

--profile (optional) - One of all, medium, api-json, large-array, or sparse-array
--output (optional) - Write the JSON benchmark report to a file
Examples:
# 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
The included 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.
smart-cache:status
php artisan smart-cache:status [--force]

Display comprehensive information about SmartCache usage, configuration, and performance.

--force (flag) - Include Laravel cache analysis and detailed diagnostics
Examples:
# Basic status overview
php artisan smart-cache:status

# Detailed analysis with recommendations
php artisan smart-cache:status --force
smart-cache:clear
php artisan smart-cache:clear [key] [--force]

Clear SmartCache managed items. Optionally specify a key to clear only that item.

key (optional) - Specific cache key to clear
--force (flag) - Force clear keys even if not managed by SmartCache
Examples:
# 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
smart-cache:warm
php artisan smart-cache:warm [--warmer=*] [--list]

Pre-populate cache with frequently accessed data using registered warmers.

--warmer (optional, repeatable) - Specific warmer(s) to run
--list (optional) - List available warmers
Examples:
# 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
smart-cache:cleanup-chunks
php artisan smart-cache:cleanup-chunks

Clean up orphaned chunks from expired or deleted chunked cache entries.

Examples:
# Run cleanup manually
php artisan smart-cache:cleanup-chunks

# Schedule in Kernel.php
$schedule->command('smart-cache:cleanup-chunks')->daily();
SmartCache implements Laravel's cache repository contract and applies enabled optimizations based on data size and type. Review the explicit managed-only versus whole-store clearing semantics when migrating.

🧪 Testing & Validation

A sample test controller you can drop into your application to validate SmartCache features end-to-end.

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
The suite includes key-preservation checks for chunked arrays and self-healing checks that confirm missing chunks are treated as cache misses, allowing 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
}
📋 Complete Implementation: The full controller code with all test methods, detailed validation logic, and comprehensive error handling is available in the GitHub Gist.

How to Use the Test Controller

1. Installation

Add this controller to your Laravel application:

1 Create the controller:
php artisan make:controller Api/V1/TestSmartCacheDirectlyController
2 Copy the implementation above into your controller
3 Add a route in your routes/api.php:
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"
}
📊 Complete Response: The full JSON response includes detailed metrics for all 20+ features, performance timings, and comprehensive validation results. View the complete response format in the GitHub Gist.

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.

Tip: Run this controller in staging before deploying to production. It validates data integrity and surfaces any configuration issues early.

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:

1 Install SmartCache:
composer require iazaran/smart-cache
2 Update your imports:
// Before
use Illuminate\Support\Facades\Cache;

// After
use SmartCache\Facades\SmartCache;
3 Update your code:
// 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');
Review bulk clearing: SmartCache 1.x keeps historical managed-only behavior for 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
Do not assume optimized entries are readable through Laravel's native cache. Plan cache repopulation and test rollback in staging. On a shared store, avoid flush() unless clearing every application's key is intentional.