How to Develop Sole Package in PHP 8.1 and Downgrade to PHP 7.2

The PHP downgrades are a thing. Most beneficial for package developers who want to move forward to the latest PHP features but also want to keep easy access to the broad PHP community and legacy projects.

The downgrade of a tool is a no-brainer - we downgrade the whole tool including vendor, and we know it will run on PHP 7.2.

But how to achieve the same with the package with separated dependencies?

"You have an awesome package. But if people can't install it,
nobody will use it."

1. Developer in one Repository, Release in Another

Before we get into the downgrade of the package itself, we have to have the repository prepared for the downgraded release. We have 1 repository for developing code and another for its releases.

Do you know such an example from packages in your project?

With 2 different repositories, we can put a downgrade process in the middle with a single line in GitHub Action.

Do we have two repositories? Now comes the fun part.

2. What Required Dependencies do We Control?

With the classical downgrade, we can work with the newest dependencies possible because we also publish and downgrade the whole vendor. With a package, it's slightly different.

The package is still part of the dependency tree as we know it. We can only downgrade the /src of our package, and we still depend on packages in the "require" section of composer.json.

There are 2 challenges we have to deal with. What packages are part of our PHP 8.1 ecosystem? Let's look at an example of symplify/phpstan-rules that I wanted to release as a downgraded package. It has a fantastic set of PHPStan rules that come in handy on legacy projects.

What does the composer.json look like?

    "require": {
        "php": ">=8.1",
        "nikic/php-parser": "^4.14.0",
        "nette/utils": "^3.2",
        "phpstan/phpstan": "^1.8.1",
        "symplify/astral": "^11.0.9",
        "symplify/composer-json-manipulator": "^11.0.9",
        "symplify/package-builder": "^11.0.9",
        "symplify/smart-file-system": "^11.0.9"

We see we have 3 external packages we cannot influence. Let's forget those for now.

Getting Rid of Dependencies

Then we see 4 Symplify packages that we own. Those 4 Symplify packages require PHP 8.1 and will not allow installing our package on PHP 7.2. What can we do about it?

The 1st option seems obvious, yet we would also have to downgrade all the dependencies of those packages and all dependencies of those dependencies... suddenly we don't work with a single package, but with a matrix of 4 times n packages.

Maybe the easier option is to review the need for our own packages. If we require a package for 10 or so classes, we could inline them and solve the problem.

I explore the classes and see the need for 3 of these dependencies as a matter of 2 classes. Let's copy them so we can downgrade them. The last package, symplify/astral, was a heavy dependency, but in the end, I moved 5 classes to the src and made it work.

Now we have a single /src directory with code we own. The rest of the dependencies are external, and let's look at those.

3. What external Dependencies Allow the Target version?

This variable is out of our control, but a standard is to target PHP 7.2 nowadays as the minimum version. If not, you can always allow multiple package versions.

How about our specific packages?

    "require": {
        "php": "^7.2|^8.0",
        "nikic/php-parser": "^4.14.0",
        "nette/utils": "^3.2",
        "phpstan/phpstan": "^1.8.1"

It looks great - all our dependencies require PHP 7.2 at least, so this package is now installable on PHP 7.2.

4. Add the Downgrade Step

The last step we have to do is add the actual downgrade to the release step. We have our PHP 8.1 composer.json for development and the one above for PHP 7.2+. This process is the same for all the downgrades. Do you want to learn more about it? I wrote about it here.

And the result? We've already installed the package on the PHP 7.4 project, used it in CI, and discovered bugs with 110 new PHPStan rules. Life is great :)

Happy coding!

Do you learn from my contents or use open-souce packages like Rector every day?
Consider supporting it on GitHub Sponsors. I'd really appreciate it!