Solving the Chicken-and-Egg Problem of PHP Versioning

https://unsplash.com/photos/happy-corgi-petted-by-person-qXnPEF3UctM

Imagine a client calls you today about a project you finished three years ago. You open the code, eager to fix a quick bug, and suddenly—everything crashes. Your current environment is too new, the project is frozen in time, and you’re staring at a wall of errors. This is the “Client from the Grave” scenario, and it’s a nightmare every professional PHP developer has faced.

And somehow somebody just threw out a response, “I use Docker“? LOL…

When I mentioned this struggle online, the common refrain was: “Just use Docker.”

Don’t get me wrong—I use Docker and DevContainers every single day. But for a simple version switch, Docker can feel like using a sledgehammer to crack a nut. The overhead of managing containers, the complex setup for some developers, and the resource drain on a local machine are real frictions. Sometimes, you don’t want a virtualized environment; you just want the right version of PHP to run on your machine.

Yes, PHP rarely introduces breaking changes, but frameworks often limit the minimum and maximum PHP versions. Most of the time when we work on projects, once finished, the client rarely wants maintenance support or services. Clients often come back 3–4 years after the project is finished due to a need for improvements or bug fixes. In the gap between the project’s completion and when they contact us again, PHP, libraries, and frameworks evolve; upgrading those usually requires more work than addressing the reported bugs or requested new features.

The reality is that while PHP itself is stable, the ecosystem isn’t. Frameworks often impose strict version requirements. When a client returns after three years, you aren’t just fighting a bug; you’re fighting a version mismatch. Upgrading the environment often takes more effort than fixing the actual code.

I went on a quest to find a tool that worked like nvm or pyenv—something that just worked out of the box. But I hit a “chicken-and-egg” problem: phpbrew is written in PHP, meaning you need PHP to install the tool that manages PHP. Other options like mise are powerful, but often feel like overkill for a developer who just needs a specific binary. And while distro package managers exist, they usually only give you the latest version, leaving older projects stranded.

This is why I built phpv. I wanted to strip away the friction of managing third-party libraries. Instead of wrestling with complex build flags, you can now use phpv ext add to install and enable extensions effortlessly. To tackle the dreaded build failures on modern operating systems, I integrated support for the Zig compiler (--compiler zig) and a custom patch system to keep security and compatibility up to date without the manual headache.

One of the biggest hurdles in PHP versioning is the lack of simple, downloadable binaries. Unlike some languages, PHP source code relies heavily on GNU GCC and is notoriously difficult to statically link. If you tried to bake every possible extension into a single binary, you’d end up with a bloated package that’s impossible to customize later.

The solution? Dynamic linking. By keeping the binaries lean, we get a system that is both flexible and lightweight.

Take a look at my Fedora machine in the image below. Despite requiring a few system packages, the total footprint of the PHP installation is only 64 MB. That’s the power of dynamic linking—it provides the functionality we need without the bulk of a full portable distribution.

Portable PHP is possible, but each version is bulky, consumes more space, and increases build times. Additionally, adding any new extension, such as PECL, requires a complete rebuild of the source code. Yes, I’m aware of Pie, but my response is: Do you think every project is maintained well and kept up to date?

While “Portable PHP” exists, it often comes at a cost: bulky files, slower build times, and the requirement to rebuild the entire source code just to add a single PECL extension. In a professional environment where projects aren’t always perfectly maintained, you need a tool that adapts to the project, not the other way around.

Unlike NVM, PHP requires a distinct binary build process and integrates directly with servers like Apache and NGINX. This complexity is why installing PHP via PHPV or other version managers is often slow.

I built PHPV using Go because it is the right tool for the job. While shell scripts are great for simple tasks, they become complex and difficult to maintain as more features are added.

Unlike nvm, PHP has to integrate with servers like Apache and NGINX and handle a distinct binary build process. This is why version managers for PHP are traditionally slower and more complex. I chose to build phpv in Go because it strikes the perfect balance: it’s far more maintainable than a shell script, but allows for much faster iteration than Rust.

Some might ask, “Why not Rust?” Rust is incredible when execution speed is the absolute priority. But for a tool like this, development speed and rapid iteration are what matter most. Go was simply the right tool for the job.

Leave a comment

Your email address will not be published. Required fields are marked *