My composer.lock knows the exact commit hash of a left-pad-sized string helper I pulled in three years ago. It has no idea which build of PHP will execute it. On October 21 that gap stopped being a philosophical quirk: the PHP project shipped 8.5.12, 8.4.27, 8.3.36 and 8.2.35 to close CVE-2026-109618 and CVE-2026-109619, both scored 9.2 under CVSS v4.0, both reachable without a login. I think the lesson for those of us who write the applications is blunt. The interpreter is a dependency, and we should version it, ship it and roll it back with the same discipline we spend on our vendor directory.

Look at who was exposed. The advisory names PHP-FPM, Apache with mod_php, php-cgi, FrankenPHP and LiteSpeed, with mod_php singled out as the worst case because a hostile request gets to the vulnerable code before your front controller has even been loaded. Your Laravel middleware never runs. Your Symfony firewall never runs. The 40-line status.php that a monitoring vendor asked you to drop in the web root in 2019 is as good an entry point as your checkout flow. I won't walk through the internals here, the advisories on GitHub do that better than I could, but the short version is that one bug lives in how the engine bails out when memory runs dry mid-request, and the other in leftovers that a recycled worker should have wiped between two visitors.

That second one stings a little, because it pokes at the thing we tell every newcomer about PHP: each request starts from nothing. That promise was always about our userland variables, and it still holds there. Underneath, an FPM child is a process that might serve ten thousand requests before pm.max_requests retires it, and the engine is the only thing standing between visitor 4,812 and whatever visitor 4,811 left behind. We tune that setting for memory leaks in some image library. Few of us ever thought of it as a security knob, and honestly it still isn't one; only the patched binary is.

Now the counter-argument, and it's a strong one. Most of us run PHP from Debian, Ubuntu, RHEL or a host's control panel precisely so that someone else handles this. Distribution security teams backport fixes into packages whose version string looks frozen in time, which is the only reason PHP 8.1 and older boxes have any path at all now that upstream has stopped patching them. A platform team that owns the runtime fleet-wide can patch two hundred services in an afternoon, where two hundred app teams each bumping a Dockerfile would take two weeks and a Slack thread nobody reads. Centralising the runtime is a real security win, and I don't want to wave that away.

Where I still land on the other side is the restart. A package upgrade on disk does nothing for an FPM master that has been up since August; the old code stays mapped into memory until someone reloads the pool or bounces Apache. In the centralised model that step tends to fall into the crack between "ops installed the update" and "devs own the deploy", and the dashboard proudly shows the new package version while the running processes disagree. When the runtime ships inside your image, built from your repo, a patch is just a deploy: new container, old one drained, every worker fresh by construction. You also get a diff to review and a revert button if 8.4.27 trips over some extension you forgot you load.

Timing makes the argument harder to dodge. Amazon's threat intelligence team says its MadPot honeypot caught an attempted PHP2Shell exploit on October 17, four days ahead of the public release, and it saw more proof-of-concept traffic around disclosure from scattered infrastructure. Amazon couldn't say whether that early attempt would have worked against a real server. Still, if your runtime upgrade path needs a ticket, a change-advisory board and a maintenance window, four days is roughly the time it takes to get the meeting scheduled. The researchers credited for pushing these to an urgent fix, Calif, OpenAI and Wiz, will not be the last to find this kind of thing with tooling that keeps getting faster.

For the stretch before you can deploy, the project's own stopgaps are worth applying, as long as nobody mistakes them for the fix. Switch off session.upload_progress.enabled, and on 8.5 turn off fatal_error_backtraces, then actually restart the workers so the ini change takes. Keep request bodies small at the proxy. And resist the clever idea of capping process memory with RLIMIT_AS or RLIMIT_DATA, which the advisory explicitly warns can drive the engine straight into the failure path the patch removes. Cloudflare and AWS both put WAF rules in front of this, which buys time for traffic that goes through them and nothing for an origin IP someone can hit directly.

So here's what I'd like to hear from you, especially if you run more than a handful of services. Does the PHP version live in your repository, pinned in a Dockerfile or a composer platform constraint that a pull request can change, or does it belong to a platform or hosting team that patches it under you? And if it's the second, when the October 21 release landed, how long did it take before the processes actually serving your traffic were running the new code, measured in hours rather than in package-manager output?