How does composer handle multiple versions of the same package?
This may (should) have been asked before somewhere but I can't seem to find an answer. If someone provides a link I can delete this post!:
Just trying to get my head around some of composer's (probably applies to other package managers too) functionality.
Basically I just want to know what composer does in the following scenarios:
1.
My main project has a dependency:
"guzzlehttp/guzzle": "5.0.*",
My external bundle has a dependency on
"guzzlehttp/guzzle": "5.0.*",
Does composer install guzzlehttp/guzzle one time because it knows it only needs it once?
2. Same scenario but in the future if someone updates the main project to use:
"guzzlehttp/guzzle": "6.0.*",
Will composer now install 2 versions of guzzle (5 and 6) (I presume this is what it should do), or will it take the highest version (i.e. 6)? Also if there are 2 versions will this cause any conflicts because namespaces might be the same?
Thanks
Solution 1:
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
Solution 2:
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.