Composer conflict, how can we use it?

Today I found a very useful Composer schema package link option named conflict, which is useful when third-party code you rely on is too permissive for your needs.

At the time of writing, the conflict man page states

Lists packages that conflict with this version of this package. They will not be allowed to be installed together with your package.

Note that when specifying ranges like <1.0 >=1.1 in a conflict link, this will state a conflict with all versions that are less than 1.0 and equal or newer than 1.1 at the same time, which is probably not what you want. You probably want to go for <1.0 || >=1.1 in this case.

In order to explain how we can take advantage of this, we’ll use a concrete example.

The problem with a Guzzle dependency

At Madisoft we love open source and believe in its power, not only because we have third-party code on which our application is based upon (BTW, thanks to everyone who makes our daily job easier!), but also because we like to contribute and have a commitment to OSS.

One of the packages we use and help improve is BehatPageObjectExtension, an extension for Behat that incapsulates the PageObject pattern (we have written a blog post about PageObjectExtension, in Italian, here).

When looking at this pull request I’ve opened to add PHP 7.4 support to the library, you may notice a previous Travis CI build with failing test.

The reasons behind the issue are:

Pay attention to last point on the list: when working on OSS, it’s important to try to keep everyone’s code - that depends on your OSS - working (at least until a bump to a major version) so, in sight of this, it’s common to check if the lowest possible dependencies your code accepts will still make the tests pass.

Once we shifted the deps=low flag to PHP 7.2, we found the above issue. It was already there, but we hadn’t noticed as the flag was only on PHP 7.1, meaning that tests never ran against PHP 7.2 with lowest dependencies. As PHP 7.2 introduced a warning on certain count() usages, our code didn’t manage to pass the tests.

But if you look closely, you’ll probably notice that the warning was not raised by our code directly, but by third-party code we’re requiring with Composer:

Warning: count(): Parameter must be an array or an object that implements Countable in /home/travis/build/sensiolabs/BehatPageObjectExtension/vendor/guzzlehttp/guzzle/src/Handler/CurlFactory.php line 66

Without discussing if it’s right or wrong to keep a version of third party code that could break everthing (take a look at this PR), we were suddenly at a crossroads: drop the deps=low and give up on this kind of tests or dig in the Composer manual and look for something that could possibly help us: you guessed right, conflict is what we needed (thanks to jakzal!).

Conclusions

With conflict you can force the minimum version of a dependency you cannot control directly, avoiding headaches or third party code forks.

Hope this little story helped you to learn something new, personally I did!

Happy coding!

Bonus

In order to avoid accepting third-party code with well-known security issues you can take advantage of SecurityAdvisories by Roave, a library which uses conflict as shown in this article to block unsafe packages. Give it a look!

28