elcobvg/laravel-opcache
A Laravel package for monitoring and managing PHP OPcache. It provides a status dashboard and Artisan commands to clear, reset, and optimize OPcache, helping keep cached PHP scripts fresh after deployments.
Prerequisites
opcache.enable=1). Confirm with:
php -r "var_dump(function_exists('opcache_compile_file'), opcache_get_status(false) !== false);"
composer.json constraints against your app before installing.Install and wire it up
composer require elcobvg/laravel-opcache
Laravel 5.5+ package discovery registers Elcobvg\OPcache\OPcacheServiceProvider. On older versions add it to config/app.php. Then register a cache store in config/cache.php:
'opcache' => [
'driver' => 'opcache',
'path' => storage_path('framework/cache/opcache'),
],
Make sure the directory exists and is writable by the web user. If no config is published, check vendor/elcobvg/laravel-opcache/config for the exact keys.
First use case
Use it as a dedicated, read-mostly store — not as your default driver. The classic fit is small, hot data on a single-server app: application settings, feature flags, translation lookups, computed navigation/permissions, third-party API responses.
Cache::store('opcache')->put('settings', $payload, 300);
Cache::store('opcache')->get('settings');
Sanity check it's actually caching in shared memory: opcache_get_status(false)['scripts'] should grow as you read entries.
Keep it as a secondary store. Leave CACHE_DRIVER=file/redis for sessions, queues, and anything that must survive a deploy or a node. Reference the opcache store explicitly:
Cache::store('opcache')->remember('nav.permissions.'.$user->id, 120, fn () => $user->permissions);
Cache-aside for hot reads. The driver's value is in remember/get on the read path. Don't use it for writes-heavy data — every put rewrites a file and calls opcache_invalidate, which is more expensive than a Redis SET.
Warm the cache via HTTP, not artisan. OPcache shared memory is per SAPI pool. Code run in CLI (php artisan, queue workers) uses a completely separate OPcache instance (and usually has OPcache compiled out for CLI). So a deploy-time warming command only helps if opcache.enable_cli=1, and even then it warms the CLI pool, not FPM's. Prime keys from an HTTP endpoint after deploy, or accept lazy population on first hit.
Version/handle your keys across deploys. With OPcache revalidation disabled (see below), an old cached value can outlive your release. Either flush the store on deploy or bake a build hash into keys:
$key = 'v'.config('app.build').'.settings';
Cache::store('opcache')->forget('settings'); // or ->flush() on release
Extend the driver when you need behavior changes (custom serialization, key namespacing, metrics):
Cache::extend('opcache2', fn ($app) => Cache::repository(
new MyStore($app['config']->get('cache.stores.opcache'))
));
Tags are not supported. If your code calls Cache::tags(...), this driver will throw. Keep tagged caches on Redis/memcached.
opcache.enable_cli=0 means your first test in php artisan tinker will look correct but show no speedup (or behave inconsistently). Test through the web SAPI.opcache.memory_consumption and opcache.max_accelerated_files budget as compiled Laravel/PHP files. A large cache will evict framework scripts from OPcache and slow your whole app down. Keep entries small, tune the limits, and watch opcache_get_status(false) for hits/evictions.opcache.validate_timestamps=0 for the speed claim to hold — but then correctness depends entirely on the driver calling opcache_invalidate($path, true) on every write and forget. Verify that in the source. If files are silently revalidated on every include, you're paying a stat per read and the Redis comparison gets much less impressive.opcache.restrict_api and opcache.blacklist_filename can break it. If the cache path falls under a blacklisted pattern, or restrict_api blocks the opcache calls, writes "succeed" but reads fetch stale/empty data. Check opcache_compile_file()'s return value when debugging.path? opcache_get_status()['scripts'] showing the entry files? opcache_invalidate returning true? PHP version compatible with the package's Store interface implementation (Laravel 9/10 changed cache contracts — expect to fork or extend if on a new version)?How can I help you explore Laravel packages today?