How does composer handle multiple versions of the same package?
We had a situation today where we were using multiple libraries, and one used Guzzle v5 and the other Guzzle v6. Upgrading (or downgrading) was not a viable option, as it was third party code, so we had to be able to install both versions of Guzzle.
Here's what we did. This is a TOTAL FRACKING HACK, and I'd advise doing this only as an absolute last resort. It works, but updating your calling code to use just one version is a much better option.
The trick is that you need to re-namespace one of the two versions. In our case we decided to change v6 to GuzzleHttp6. Here's how to do that:
- Make sure your
composer.json
has v6 enabled:
"require": {
"guzzlehttp/guzzle": "^6.2"
// possible other stuff
},
composer install
to get Guzzle v6 all its dependencies installed.- Move the
/vendor/guzzlehttp
directory over to a new/vendor-static/guzzlehttp
directory. - Do a case-sensitive find & replace on the
/vendor-static
directory to replace GuzzleHttp with GuzzleHttp6. This effectively brings the Guzzle 6 code into a new namespace. - Now update your
composer.json
to include Guzzle's own dependencies manually, and then autoload the code in the/vendor-static
folder. Note you'll want to REMOVE the main guzzle require statement (or change it include guzzle 5);
"require": {
"guzzlehttp/guzzle": "~5",
"psr/http-message": "~1.0",
"ralouphie/getallheaders": "^2.0.5"
},
"autoload": {
"files": ["vendor-static/guzzlehttp/guzzle/src/functions_include.php",
"vendor-static/guzzlehttp/psr7/src/functions_include.php",
"vendor-static/guzzlehttp/promises/src/functions_include.php"],
"psr-4": {
"GuzzleHttp6\\": "vendor-static/guzzlehttp/guzzle/src/",
"GuzzleHttp6\\Psr7\\": "vendor-static/guzzlehttp/psr7/src/",
"GuzzleHttp6\\Promise\\": "vendor-static/guzzlehttp/promises/src/"
}
},
composer update
to remove the old Guzzle v6, and install Guzzle v5. This will also install thepsr/http-message
andralouphie/getallheaders
dependencies.You may need to do a
composer dump-autoload
to force the autoloader to add the new include paths. In theory this should happen oncomposer update
but I had to force it.Now update your calling code; instead of calling \GuzzleHttp, you'll call \GuzzleHttp6 .
And that's it. You should be able to run both concurrently. Note that whatever version of Guzzle v6 you've got in the /vendor-static
directory will be there forever-more, so you may want to update that from time-to-time.
To question 1
Yes Composer can only install one version of each extension/package.
To question 2
Because of answer 1: Composer would consider your main project and the external package as incompatible.
In this case you could
- stay with version 5 at your main project too.
- ask the external package owner to upgrade to version 6 too if it's compatible to.
- fork the external package and make it compatible to version 6 yourself