From Commit to Production: How a PHP Fix Actually Reaches You

A security fix landed in PHP: 2026, Oct 21st. The announcement is summed up in one line: “2 CVE, fixed in PHP 8.5.12”. Then an impressive machinery, usually invisible, is set in motion, and pushes that fix to your servers. The fix itself has to travel a gigantic supply chain first, and that chain is longer and have more branches than most people expect. This is its story.

I observed this phenomenon in action during the publication of the 8.5.12 version of PHP, yesterday. I was thunderstruck by the number of paths a fix can take, from the source code to the machines running it, all over the world. The result looks less like a pipeline and more like a family tree. Each branch has its own agenda, its own people, and its own delays. And in the end, it delivers.

 

The PHP fix tree

Layer 0: Merged, not running anywhere yet

It starts with a commit. May be several commits, with a merge, but that’s technical jargon. In any cases, a bug is reported, fixes are proposed and evaluated, and finally, a patch is written, merged into master, the main branch of the source code repository at php-src. This is the job of the PHP security team.

Nobody runs master in production, though. The fix must be merged again into each current and maintained branch: 8.2, 8.3, 8.4 and 8.5 at the time of writing. PHP 8.6 gets its by default, and will be published next Nov. 19th. This is the first fork in the tree, and the most important one.

The branches that are past their end-of-life do not receive the fix. If you run PHP 8.1, the fix exists in the world, but it is not coming to you from this source. But you can always count on the unsung heroes of the community, so when you are using Ondrej Sury packages on Debian and Ubuntu, you can know that he ported the fix himself. On any other distribution, check your package manager.

Layer 1: The release managers and the tarball

Each branch has a release manager. Once the fix is in their branch, they tag a patch release, such as 8.5.12, or 8.3.36. Patch numbers, that is the last number of the three, only go up, so when your version changes, you know the fix is inside. You can even use version_compare() to do such comparison.

This is also where the fix becomes publicly available in raw form: a source tarball on php.net, plus pre-compiled Windows binaries. In theory anyone can start here. Nowadays, almost nobody compiles PHP by hand, so the real journey begins one layer down.

Layer 2: The packagers, where the fix multiplies

Every packager in the ecosystem now waits for that tarball, and each adds its own build and its own schedule:

  • Linux distributions: Debian, Ubuntu, Fedora, RHEL, etc.. They run their own review and build processes.
  • Independent packagers: Remi Collet for the Red Hat family, Ondřej Surý for Debian and Ubuntu, Shivammathur on OSX, etc. Usually the fastest channels.
  • Homebrew and MacPorts on macOS.
  • The official php Docker images, built from the php.net tarballs, not from distribution packages. Docker images are a siblings of the distributions, not a child of them.
  • Static builds: static-php-cli, FrankenPHP, staticPHP and friends.

Then comes the trap: backports. A stable distribution like Debian or RHEL does not move you to 8.3.36. It patches the version you already have and keeps its number. So php -v still reports 8.3.x, while the fix sits inside:

$ php -v
PHP 8.3.x (cli) ...

$ apt changelog php8.3-cli     # Debian and Ubuntu
$ rpm -q --changelog php       # RHEL family

Backports are made for the stability of distributions, so this is by design. In practice, it make checking your PHP version a misleading task. Audit a fleet by version number and the fix is invisible: present, but invisible.

Layer 3: Rebuilds of rebuilds

The packages and images from layer 2 feed a second generation of artifacts, each waiting for the previous one:

  • RHEL rebuilds: AlmaLinux, Rocky, Oracle Linux and Amazon Linux, which are build on RHEL’s source packages.
  • Vendor images: Bitnami and friends.
  • CI runner images: GitHub Actions and GitLab templates pin specific PHP images: it is now time to update them.
  • Framework images: Laravel Sail, the official WordPress images.
  • Control panels: cPanel, Plesk, Ploi.io and CloudLinux’s Alt-PHP. A huge amount of shared hosting flows through, with its own updating process.
  • One-click local stacks: Laragon, DDEV, Lando. Check for release date and published PHP versions, as version numbers for these projects are independant: XAMP, MAMP and WAMP have all stopped updating for a while, and won’t probably get this recent version.

Local developer tools such as Herd, Yerd and Lerd ship their own managed PHP binaries. They do get the fix, but the urgency is not as high as online systems. Developers rarely attack their own laptops.

Layer 4: Somebody else’s calendar

PaaS and managed platforms wait for their base images, then schedule their own rollouts. That covers Heroku, digital ocean, Platform.sh, OVH, Fly.io, Tencent, Google CLoud, Azure App Service, etc, etc, etc. It also covers serverless through AWS Lambda and Bref layers. It covers managed hosts such as WP Engine, Pantheon, Acquia and Laravel Vapor.

Each of these is two intermediaries away from php.net, with a rollout calendar that is independant of your own, but related to many other customers. A customer of a managed WordPress host may wait some time after the fix is commited, but it will get the update without asking for it. For the platform, this is a process. For you, it is a risk you are renting.

Layer 5: Finally, your pipeline

The fix finally arrives at the threshold of where it was always headed: your build pipeline. You pull the new base image, rebuild your application image, run CI, and deploy.

And, by deploy, you have to understand that there are three destinations, not just one:

  1. Production, the one that matters most, that everyone sees.
  2. Preproduction, staging, testing. Second in importance, but usually updated before production, because the actual tests are run there. Paradoxically, they may run the old binary for longer, for fallback reasons, and they may be reachable from the internet. Too frequently, they hold copies of production data. Preprod is part of the attack surface, even when it is more confidential than public.
  3. Developer machines. Theses ones are low criticality: developers don’t attack their own machine. But they want to be up to date with the latest versions for their own testing. Upgrade when convenient.

That is a large tree, indeed

Take a momentot count the way the PHP supply chain unfolds. There is a new layer each time an actor must wait for the previous one to finish. The tree is up to six layers deep. Layer 1 takes days. Layer 2 takes days to weeks, because stability is deliberately slow. Layers 3 and 4 each add another cycle, and may become months. On October 21st, this was not the case.

This representation is also a bit simpler than reality. There are three caveats.

First, this organigram allows some skips. A company compiling from the tarball jumps straight to layer 5. Anyone writing FROM php:8.5in a Dockerfile skips layers 3 and 4. There are shortcuts, that are often taken in cases of high emergency.

Second, the cumulative delay is a daily scourge of the field. From the commit on master to a site on a managed host, the journey is measured in weeks. And on an EOL branch, it is never. Next time you wonder why it takes so long between the news and the update of your binaries: this is it.

Third, PHP engine rewrites are not part of this process. A PHP engine written in another language, such as a Rust or Go, does not carry a C bug fix and does not need for the fix. Indeed, it has its own bugs, which is a different story, even if the process is very similar.

Publishing a fix in emergency

On Oct. 21st, 2026, the story starts actually even before the publication of the fix itself.

It actually starts with an embargo: that is a moment in time where the fix is ready, but not made public yet. While a bit strange at first, it is made to give time to all these actors to get prepared. Between the commit of the fix, and the first installation, there are recompilations, then sending the checked binaries to the delivery network. Because these systems will face a tsumani of requests at the publication time, so they need to be prepared for the event.

Some editors are also provided with the fix early, so they can prepare to serve their own community. This also spreads the load, and make the same solution available to more users across the different layers.

Conclusion: a good day for the community

Behind every layer of this tree, there are people. On October 21st, 2026, a lot of them did something remarkable, and the least we can do is say so.

First, the finders. The advisories for CVE-2026-109618 and CVE-2026-109619 credit four reporters: lcfr-hax, sagitzadik-wiz, tomash-areion and amyb-oai. Finding a remote code execution in a language that runs a large part of the web is not a weekend hobby. It takes skill, patience, and the discipline to report it privately rather than posting it online. Thank you.

Second, the fixers. Four remediation developers are credited on the same advisories: arnaud-lb, iluuu1994, bukka and ndossche. Two bugs, two different root causes: one in how out-of-memory handling can leave request data half-initialized, the other in stale global state. Untangling that inside the engine, without breaking everything else, is the kind of work nobody notices until it is missing. bukka also published the advisory itself, the same day.

Third, everyone who carried it downstream. Four branches were patched at once: 8.2.35, 8.3.36, 8.4.27 and 8.5.12. The release managers tagged them, the independent packagers rebuilt them, and the Docker image maintainers followed the tarballs, all within hours rather than weeks. Security vendors published write-ups and free checks on the same day, and CISA issued its own alert. Each of these people and teams had a small part of the chain, and they all moved at once.

The embargo, the early warnings to the editors, the preloaded delivery networks: none of that is glamorous. It is quiet coordination, and it worked. A fix that could have sat in the truck for weeks reached the machines that needed it in a matter of hours.

So the next time you read “Fixed in PHP 8.5.12”, remember the four people who found the bug, the four who fixed it, and the many four who made sure it travelled. Make sure your server is on the route, and thank them while you’re at it.