Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Opcache Bundle Laravel Package

sixdays/opcache-bundle

Symfony bundle for inspecting and managing PHP OPcache from your app: view cache statistics, memory usage, hit rates, and cached scripts, then reset or invalidate OPcache when needed. Useful for debugging performance and deployments.

View on GitHub
Deep Wiki
Context7

Getting Started

This is a Symfony bundle — not a Laravel package. There is no service provider, no facade, no publishable config, and no extra.laravel block. For a Laravel developer, "getting started" means reading the ~50 lines that matter and deciding whether to copy or replace them; installing it into a Laravel app just drags in Symfony bundle wiring your container will never boot.

First 15 minutes

  1. Read the source, don't install it yet:

    git clone https://github.com/sixdayz/OpcacheBundle /tmp/opcache-bundle
    ls /tmp/opcache-bundle/Command/        # the only directory that matters
    

    The payload is a single Command subclass calling opcache_reset() (and sometimes opcache_invalidate() per file). Everything else is a *Bundle class, a DI extension, and a services.xml.

  2. Establish whether the whole exercise is even meaningful on your runtime:

    php -m | grep -i Zend\ OPcache
    php -i | grep -E 'opcache.enable|opcache.enable_cli|opcache.validate_timestamps|opcache.validate_permission|opcache.restrict_api|disable_functions'
    

    If validate_timestamps=1, PHP already stats files on every request and you have nothing to invalidate — stop here. If validate_timestamps=0 and enable_cli=0 (the common production combo), the command's opcache_reset() runs against an empty CLI segment and clears nothing that serves traffic. That is the fork in the road.

  3. First real use case: post-deploy bytecode invalidation on a long-lived FPM box behind a symlink swap. Not a container rollout (new container = fresh OPcache = moot), not a fleet without a fan-out runner, not a local dev environment.

  4. If step 2 says the CLI/FPM segments differ, skip the package entirely and use cachetool against the FPM socket — that's the only approach that actually reaches the workers:

    php vendor/bin/cachetool opcache:reset --fcgi=127.0.0.1:9000
    

Where to look first, in order: the command class → composer.json (check the PHP/Symfony constraints; they predate PHP 8 and Symfony 6, so execute() will be missing its : int return type) → the bundle's service definition (to see how the command is registered, which tells you what you'd have to reproduce natively).

Implementation Patterns

Pattern A — vendor the command, register natively (Laravel). This is the pragmatic play: you get identical behaviour with zero supply-chain surface, and you fix the PHP 8 typing in the process.

// app/Console/Commands/OpcacheReset.php
#[AsCommand(name: 'opcache:reset')]
class OpcacheReset extends Command
{
    protected $signature = 'opcache:reset {--validate : Warn instead of failing when the reset is a no-op}';
    protected $description = 'Flush OPcache shared memory';

    public function handle(): int
    {
        if (! function_exists('opcache_reset')) {
            $this->error('OPcache extension not loaded.');
            return self::FAILURE;
        }

        if (PHP_SAPI === 'cli' && ! ini_get('opcache.enable_cli')) {
            $this->warn('opcache.enable_cli is off — this will NOT reach FPM workers.');
            if (! $this->option('validate')) {
                return self::FAILURE;
            }
        }

        if (! opcache_reset()) {           // returns false on failure
            $this->error('opcache_reset() returned false — check restrict_api/disable_functions.');
            return self::FAILURE;
        }

        $this->info('OPcache reset.');
        return self::SUCCESS;
    }
}

Register it in Kernel::commands (Laravel 10) or rely on auto-discovery (Laravel 11+). Note the explicit SAPI !== 'cli' guard and the false return check — both are the bits the original omits.

Pattern B — cachetool wrapper (the correct answer when the requirement is real). If the goal is genuinely "invalidate FPM bytecode," do not call opcache_reset() locally at all. Shell out to cachetool or hit a protected internal HTTP endpoint that runs opcache_reset() inside an FPM worker.

Pattern C — HTTP endpoint for fleet fan-out. On N hosts, the deploy tool (Envoy, Deployer, Ansible) must call the reset N times; the bundle has no cluster awareness whatsoever. The cleanest shape is a route guarded by a secret that runs opcache_reset() in the web SAPI, so curling it from each node actually clears the segment that matters:

Route::post('/_ops/opcache-reset', fn () => tap(
    ['ok' => opcache_reset()],
    fn ($r) => abort_unless($r['ok'], 500)
))->middleware('ops.token');

Then the command becomes curl fan-out in the pipeline and the Symfony bundle is irrelevant.

Pattern D — deploy hook sequencing. Whatever mechanism you choose, ordering is fixed:

build → upload release dir → symlink swap → migrate → opcache reset → warm cache → smoke check

Reset after the symlink swap (so the new paths are what get compiled and the old ones aren't re-cached) and before you declare success. Warm with a handful of curl requests to the top 5 routes; a cold-cache stampede on the first real traffic burst is a self-inflicted latency incident.

Integration tip — gate it. Wrap the step in @if($env !== 'local') (Envoy) / when: APP_ENV == 'production' so dev machines with OPcache off don't emit noise, and so nobody resets production from a laptop by accident.

Integration tip — Artisan-first for Laravel. Laravel already handles compiled views/config/routes via php artisan optimize:clear and clear-compiled. If that's your target, the OPcache problem is a separate concern and this bundle adds nothing; run optimize:clear then an FPM reload, and skip both the bundle and the vendored command.

Gotchas and Tips

The headline trap: CLI resets the wrong shared memory segment. OPcache is per-SAPI. opcache_reset() from an Artisan/Console process touches the CLI segment, and if opcache.enable_cli=0 (default), there is no CLI segment at all. The command exits 0 and clears nothing. Your deploy goes green while the old bytecode keeps serving. Verify — don't assume:

# On the FPM box, mutate a file's contents, then hit the app:
# if the served response does NOT change, your reset path is a no-op.

opcache_reset() returns false, not an exception. Suppression via opcache.restrict_api (path prefix must match the script's directory), disable_functions=opcache_reset, or SHM permission errors all fail silently. Always check the return value and exit(1); the original command's log-and-continue style is exactly what produces false confidence.

Archived and stale. Zero dependents, no upstream fixes, no CVE path. Expect Composer to refuse the install against modern Symfony (execute(): int became required in Symfony 6; this code predates it). Any adoption is really a fork — record it as such.

Octane changes everything. With Laravel Octane, workers hold booted application code in process memory independently of OPcache. An opcache_reset() alone leaves stale code running. You need php artisan octane:reload (or a full worker restart) as well. Under Octane, OPcache invalidation is the less important half of the problem.

Cold-cache stampede. A reset invalidates compiled bytecode for the entire pool; the next wave of requests all recompile, spiking CPU and latency. Never trigger it on a cron or from an admin button during peak — batch it into deploys and warm immediately after.

Mid-flight swap races. Resetting while release directories are changing can surface "cannot redeclare" or stale-include fatals. Fixed sequence: swap → reset → warm.

Container/immutable-image deployments make this redundant. A rollout boots fresh containers with empty Opcache; the command is dead weight there. Its value is confined to long-lived VMs or FPM pools behind symlink-based deploys.

Fleet blindness. No cluster coordination, no partial-failure reporting. Twelve nodes means twelve invocations, and a failure on node 7 is invisible unless your deploy tool checks each exit code.

Extension point is the command class itself. There is no event, no plugin, no middleware hook worth using. If you want to customise behaviour (per-file opcache_invalidate() instead of a wholesale flush, or returning a status payload), you're editing the class — another argument for vendoring rather than depending.

Debugging a suspected stale-code incident: confirm the file on disk is the new one (grep a version constant), then confirm the served response differs, then check opcache_get_status()['scripts'] for the file's timestamp. That sequence distinguishes "deploy broken" from "cache not invalidated" from "reset hit the wrong SAPI" in about a minute — put it in the runbook.

Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
frankyso/ipaymu
3mdikram/worldmoneywords
snowsoft/config
itmosoft/laravel-subscriptions
larva/laravel-ai-qwen
diephp/sequences
users-au/socialite-provider
twidpay/twid-logger
mrnewport/laravel-plaid
mrnewport/laravel-repo
mrnewport/laravel-docsign
mrnewport/laravel-stow
mrnewport/laravel-flow
mrnewport/laravel-priceable
mrnewport/laravel-nestedterms
fredmeng/laravel-apidoc-generator
itstructure/laravel-multilingual-tools
pdustdar/elasticsearch
markupai/php-sdk
rlacerda83/lumem-microservice-shipment