pablok/cache-stats-bundle
Monitor and inspect your application's cache with detailed statistics on hits, misses, writes, and evictions. This bundle adds a simple dashboard and profiling tools so you can spot performance bottlenecks, tune cache configuration, and keep your cache healthy in development and production.
pablok/cache-stats-bundle is a Symfony bundle, not a Laravel package — worth stating up front because it changes how you should consume it. It wraps the three PHP extension surfaces that silently degrade production apps: OPCache (opcache_get_status()), APCu (apcu_cache_info() / apcu_sma_info()), and the realpath cache (realpath_cache_size() / realpath_cache_get()).
There is no CacheStatsServiceProvider, no config/cache-stats.php, no publishable assets. composer require pablok/cache-stats-bundle in a Laravel app installs files and does nothing else, because Laravel only auto-discovers Illuminate\Support\ServiceProvider implementations.
So do this instead:
opcache_get_status() and note the flags the author passes (false vs true for the script list) — that's the one decision that matters for memory safety.// app/Support/CacheStats.php
class CacheStats
{
public function opcache(): ?array
{
if (! function_exists('opcache_get_status')) return null;
// false = skip the per-script list. Critical on large apps.
return opcache_get_status(false) ?: null;
}
public function apcu(): ?array
{
if (! function_exists('apcu_enabled') || ! apcu_enabled()) return null;
return [
'info' => apcu_cache_info(true),
'sma' => apcu_sma_info(),
];
}
public function realpath(): array
{
return [
'bytes' => realpath_cache_size(),
'limit' => ini_get('realpath_cache_size'),
'ttl' => ini_get('realpath_cache_ttl'),
];
}
}
Artisan command for humans. php artisan cache:stats printing hit rate, wasted memory, free memory, restart counters. This is the direct port of what the bundle's console output gives a Symfony dev.
$s = $this->stats->opcache();
$this->table(['Metric', 'Value'], [
['Enabled', $s['opcache_enabled'] ? 'yes' : 'no'],
['Hit rate', $s['opcache_statistics']['opcache_hit_rate'].'%'],
['Cached scripts', $s['opcache_statistics']['num_cached_scripts']],
['Wasted memory', $s['memory_usage']['wasted_memory']],
['OOM restarts', $s['opcache_statistics']['oom_restarts']],
['Manual restarts',$s['opcache_statistics']['manual_restarts']],
]);
Health endpoint for scrapers. Expose a narrow JSON blob from a route behind auth or an internal-only middleware, then scrape it with Prometheus/Datadog. Keep it to numbers — never return opcache_get_status(true) or realpath_cache_get() output publicly; the script list is a filesystem map of your server.
Laravel Pulse card. If you already run Pulse, a custom card reading CacheStats is the idiomatic Laravel answer to what the bundle does with Symfony's profiler/toolbar. Same data, same polling cadence, no new infrastructure.
Scheduled baseline + alert. Schedule::call(...)->everyFiveMinutes() writing hit rate to a log channel or metrics store. Alert on deltas, not absolutes: a restart resets hits/misses, and oom_restarts incrementing is the single most actionable signal (your opcache.memory_consumption is too small for your codebase).
Octane. Under Octane the worker is long-lived, so opcache_get_status() reflects the real FPM-equivalent state and is genuinely useful. But never call opcache_reset() from a request or a queued job on a live pool — it invalidates every worker's compiled scripts at once. If you must, do it in a deploy step, not in application code.
php artisan cache:stats shows nothing unless opcache.enable_cli=1 and apc.enable_cli=1. Most "the package is broken" reports are this. Your FPM numbers only come from an HTTP request.opcache_get_status() emits a warning and returns false under opcache.restrict_api, and APCu functions are undefined when the extension isn't loaded. function_exists() + apcu_enabled() checks are not optional.realpath_cache_get() is expensive on apps with tens of thousands of files — it materialises the whole map. Use only realpath_cache_size() in the hot path and compare it against realpath_cache_size ini (default 4096k is too low for a modern Laravel app with a fat vendor/).apc driver long ago. APCu is useful for local object caching and for config:cache-adjacent tricks, but it does not replace Redis for anything shared. Don't try to bridge it into Cache::store().apcu_sma_info()['avail_mem'] is free segment memory, apcu_cache_info()['mem_size'] is used-by-entries — they aren't complements, and expunges > 0 is your real "APCu is too small" alarm.can:viewCacheStats or restrict by IP; a public /cache-stats leaking absolute paths is a pentest finding.app/Support/ and you get the same insight with zero upgrade risk and full control over what you expose.DataCollectorInterface, the Laravel equivalents are Pulse cards, a Manager-style class bound as a singleton, and a CacheStats facade for test fakes. Bind the class in a service provider so Http::fake()-style test doubles work.How can I help you explore Laravel packages today?