Scope and Downgrade your PHP Tools for Everyone to Use

Yesterday, I came across a cool PHP tool. I wanted to try it, but the installation instructions were a bit tricky. The tool required a specific PHP version and a specific version of each dependency. It required Symfony 5.4+, but our project has Symfony 3.3. I was unable to use it.

Many PHP tools suffer from the same issue, so I thought I'd share a way to do it better.

This is a tiny part of composer.json that shows the point:

{
    "require": {
        "php": "^7.4|^8.0",
        "illuminate/container": "^8.0|^9.0|^10.0|^11.0",
        "symfony/console": "^5.4|^6.4|^7.0",
        "symfony/finder": "^5.4|^6.0|^7.0"
    }
}

The tool has to allow every version of every package so that people can install it. It doesn't bring any value to the package. You might suggest "use a PHAR" - that won't work, as it has the same PHP and version requirements. Just in a single "ZIP file".


What happens in the next 2 years?

 {
     "require": {
-        "php": "^7.4|^8.0",
+        "php": "^7.4|^8.0|^9.0",
-        "illuminate/container": "^8.0|^9.0|^10.0|^11.0",
+        "illuminate/container": "^8.0|^9.0|^10.0|^11.0|^12.0|^13.0",
-        "symfony/console": "^5.4|^6.4|^7.0",
+        "symfony/console": "^5.4|^6.4|^7.0|^8.0|^9.0",
-        "symfony/finder": "^5.4|^6.0|^7.0"
+        "symfony/finder": "^5.4|^6.0|^7.0|^8.0|^9.0"
     }
When we sweep it under the rug, it doesn't work.
The rug will eventually start to rise.

Limit for Tool Maintainer

If you decide to maintain your package this way, you put a lock on your utils code:

The same goes for packages you use. Once you use Symfony 5.4, 6, and 7 at the same time, you have to look for features that are available in Symfony 5.4 but also in Symfony 7.0. You cannot use new features, you cannot use deprecated and removed features either.

Soon, you support everything for everyone.

They joy of fresh coding is slowly going away.

It's like driving a Tesla car. But instead of an electricity-powered engine,
you'd have to use your legs and bike pedals.

Result? Abandoned Packages

Frustrations from struggling with legacy code are among the reasons developers leave their paid jobs. It's no surprise that these packages—once cool and useful tools—are now slowly abandoned and forgotten. Even merging PRs with diffs like above takes months. It takes a few more months to tag and release a new version.

It's a pity because these PHP tools are often handy and well-written. They help you improve code, monitor code quality, or make your code style consistent.


E.g. check this composer.json snippet from php-cs-fixer:

{
    "require": {
        // ...
        "symfony/console": "^5.4 || ^6.0 || ^7.0",
        "symfony/event-dispatcher": "^5.4 || ^6.0 || ^7.0",
        "symfony/filesystem": "^5.4 || ^6.0 || ^7.0",
        "symfony/finder": "^5.4 || ^6.0 || ^7.0",
        "symfony/options-resolver": "^5.4 || ^6.0 || ^7.0",
        // ...
    }
}

New major dependency version? = Unable to install

The php-cs-fixer uses roughly 20 dependencies. The moment a major version of any of those 20 dependencies is out, and your projects start to use it, you're unable to install the tool. That's 20 possible BC breaks your tools users can experience.


Where do these tools bring the most impact?

The irony is that the older the project, the more helpful these tools are. It's quite easy to have high-quality code on a PHP 8.2+ project. Running a standard coding tool is nice, but it does not elevate the codebase to another level.

On the other hand, if your project uses PHP 5.4, using a tool just to fix all spaces and indents will move it light-years ahead.

"If your old bike gets GPS and an electric power engine with battery,
you'll notice it more than your car's Bluetooth firmware upgrade."

The older the project is, the more value from such tooling gets.


What's the way out?

As maintainers, we want to:


As developers who want to use a tool, we want to:


In e-commerce, the solution is called omnichannel. Shops sell all kinds of goods; you can buy them and choose your way to deliver. Do you prefer the post office? It's there. Do you fancy DHL with a driver calling you? We got it covered. Do you want to pick it up in your country's standard boxes?


Does the size of post office boxes limit the goods e-commerce can deliver?

No.


The sold goods are completely separated from the means of delivery.


You'd be free to create and innovate, while users would be free to use your tool.


Separate Development and Delivery

This is not an idea in the air. We have used this approach for years for Easy Coding Standard, for Rector, and PHPStan uses it too. Many WordPress plugins are on board as well.


That's why these tools are so popular, because it's easy to install them on any kind of project.


In the software world, the process is called downgrade and scope.


You can read more about the technical process here:



Plug this process into your tool and make it fun to maintain and easy to install again:

 {
     "require": {
-       "php": "^7.4|^8.0",
+       "php": "^8.2",
-       "illuminate/container": "^8.0|^9.0|^10.0|^11.0",
+       "illuminate/container": "^11.0",
-       "symfony/console": "^5.4|^6.4|^7.0",
+       "symfony/console": "^7.1",
-       "symfony/finder": "^5.4|^6.0|^7.0"
+       "symfony/finder": "^7.1"
    }
}

Happy coding!




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