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.1in a
conflictlink, 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.1in 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).
The reasons behind the issue are:
- Dropping support for PHP older versions (check the PHP supported versions here)
- Reflecting this change on .travis.yaml (a file that tells the CI platform how to create the environment to run your tests on)
- Shifting the
deps=lowflag to the lowest supported PHP version in order to tell Composer to use the
--prefer-lowestflag when installing the dependencies.
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
We found the related issue on the Guzzle GitHub repository and we noticed a fix had been released with version 6.3 of this library. Hurray, we can bump Guzzle version to 6.3 and overcome the problem! But looking at BehatPageObjectExtension’s
composer.json we’ve soon realized that Guzzle isn’t a direct dependency managed by us. “It’s not a big deal” I thought as Guzzle was required by Goutte and we require Goutte through its Mink driver,so I expected to find some tagged version of Goutte and the Mink Driver with this requirements bump. Sadly I found it wasn’t the case as Guzzle is required in Goutte with
^6.0 (so, basically every version between 6 and 7) and this includes the versions with the warning described above. Therefore in our case the warning was displayed as a “side effect” of
deps=low that requires indirectly Guzzle 6.0 as is the lowest dependency accepted.
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!).
Here are the details of the commit 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!
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!