timegryd/opcache-reset-bundle
A Symfony bundle for resetting PHP OPcache, exposing simple endpoints and console commands so you can clear cached bytecode after deployments without restarting PHP-FPM.
This is a Symfony bundle, not a Laravel package — the first thing to internalize. It wraps PHP's opcache_reset() in a console command so you can invalidate a server's opcode cache from your shell/deploy pipeline instead of bouncing PHP-FPM.
Minimal steps (Symfony app):
composer require --dev timegryd/opcache-reset-bundle
Then register it in config/bundles.php (the Symfony equivalent of bootstrap/providers.php):
return [
// ...
Timegryd\OpcacheResetBundle\OpcacheResetBundle::class => ['all' => true],
];
If the bundle exposes no autoconfigured command, or you only want it in prod/dev, scope the array key accordingly. Run bin/console list and look for a command in the opcache namespace; bin/console <command> --help will tell you whether it takes arguments (host, pool, etc.) or is a zero-arg "just reset it."
Where to look first: the bundle's Command class and DependencyInjection/Configuration.php. With a 0-star, 0-dependent package, treat the source as the documentation — it is almost certainly a single command class and a service definition, readable in five minutes.
First use case: a post-deploy step after composer install and cache warmup, so the newly written PHP files are actually the ones FPM executes:
php bin/console <opcache-reset-command> # on each app server, after deploy
For a Laravel developer, the mental model is php artisan optimize:clear plus opcache_reset() — the bundle only provides the second half.
Deploy-hook pattern (the main one). Wire it into your release script after code is in place and before you shift traffic:
composer install --no-dev --optimize-autoloader
php bin/console cache:clear --env=prod
php bin/console <opcache-reset-command>
Put it after cache:clear, otherwise the warmed cache files themselves aren't in the new opcode state.
Fan-out to a fleet. The command only resets the opcache of the process it runs in. For N app servers, loop over hosts from your deploy tool (Envoyer, Deployer, Ansible, Capistrano, a for host in ...; do ssh ...; done block). Keep the host list in one place alongside your other deploy targets.
Web-context reset pattern. Because of the SAPI split (see Gotchas), the more robust production pattern is an authenticated HTTP endpoint that calls opcache_reset() inside FPM, and a deploy step that curls it per host. If the bundle ships a controller/route for this, lock it down with a secret token or IP allowlist; if it doesn't, this is the extension point you'll write yourself:
// routes: POST /_ops/opcache-reset, token-guarded, prod-only
if (function_exists('opcache_reset')) {
opcache_reset();
}
Env-scoped registration. Register the bundle only for prod and dev in config/bundles.php so the command can't be invoked from a staging container that shares a host with production.
Composer script hook for local/dev ergonomics:
"scripts": {
"post-autoload-dump": ["@php bin/console <opcache-reset-command> || true"]
}
The || true matters — on a machine with opcache.enable_cli=0 or no opcache extension the command will fail and you don't want that to break composer install.
Laravel-side analogue. If you're actually on Laravel and landed here by mistake: there is no bridge. Use cachetool or an artisan command that hits a token-guarded route, and remember php artisan optimize:clear does not touch opcache.
The CLI/FPM SAPI split is the number one trap. opcache_reset() only affects the opcode cache of the SAPI process calling it. A console command resets the CLI opcode cache, which is usually empty. Your FPM workers keep serving stale bytecode. Verify with opcache_get_status() over HTTP before and after; if opcache_statistics.num_cached_scripts doesn't drop, the command did nothing useful for your web traffic. The reliable resets are: an HTTP endpoint in FPM, cachetool opcache:reset --fcgi=/run/php/php-fpm.sock, or kill -USR2 on the FPM master. Test this on a real server before trusting the deploy step.
opcache.restrict_api silently voids resets. If it's set, opcache_reset() returns false from any script outside the allowed path. Check php -i | grep opcache.restrict_api on the target host — an empty result is what you want.
opcache.validate_timestamps=0 (common in production) is precisely why you need this at all: code changes are invisible until reset. If it's 1 with a sane revalidate_freq, you may not need the package. Know which mode each environment runs.
Permissions. If the command must run as the FPM user to affect the right cache, sudo -u www-data php bin/console ... — and make sure the deploy user has that sudo grant, or you'll discover it at 2am.
Maturity risk is real. Zero stars, zero dependents, single-purpose scope. That's fine for a 50-line command, but pin the version in composer.json and read the source rather than assuming behavior. Check the composer.json PHP and symfony/console constraints against your runtime before adopting; an unmaintained bundle will eventually conflict with a Symfony major bump. Vendor the command into your own src/Command/ if the dependency becomes friction — it's cheaper than waiting for a fix.
Extension points are likely just the command service definition and (maybe) a config tree for host/endpoint settings. If you need multi-host or HTTP reset and the bundle doesn't offer it, subclass or copy the command; don't fork the bundle for a one-file change.
Debugging checklist when "it didn't work": (1) is the opcache extension loaded in that SAPI? (2) opcache.enable_cli value? (3) restrict_api set? (4) are you hitting the right host/pool? (5) did the command exit 0 but return false internally — add var_dump(opcache_reset()) locally or check the command's verbosity flag.
How can I help you explore Laravel packages today?