-
-
Notifications
You must be signed in to change notification settings - Fork 4.7k
Description
First, this issue is not entirely trivial to reproduce, because everyone has different PHP versions and extensions installed. To reproduce the steps and behaviors below, please ensure that you run php -n $(which composer)
instead of plainly composer
, or that with composer show --platform | grep mongo
, nothing shows up :)
If you're on Ubuntu or something, php -n
will also prevent loading ext-phar
, so nothing will work. In that case, just make sure you have no ext-mongo
, and run composer
directly instead of php -n $(which composer)
.
Let's say your code needs ext-mongo
(via Doctrine ODM, for instance), but that's not available on PHP 7, so you want to use https://github.com/alcaeus/mongo-php-adapter, which in its composer.json
says "provide": { "ext-mongo": "1.6.12" }
, and which is a wrapper around ext-mongodb
to provide an interface that's compatible with the old ext-mongo
.
Start with a basic composer.json
, to mock the existence of ext-mongodb
(again, you do not want ext-mongo
installed when following along, so always run with php -n
later):
$ composer init -n
$ composer config platform.php "7.0.4"
$ composer config platform.ext-mhash "7.0.4"
$ composer config platform.ext-mongodb "1.1.3"
I will use the composer-provide
branch of https://github.com/alcaeus/mongo-php-adapter explicitly because the master branch has changed between using provide
and replace
a few times recently. There is also a composer-replace
branch that uses replace
instead of provide
for ext-mongo
.
Remember, php -n
will prevent ini loads, so if you have ext-mongo locally, it should not get loaded:
$ php -n $(which composer) require alcaeus/mongo-php-adapter:dev-composer-provide
./composer.json has been updated
Loading composer repositories with package information
Updating dependencies (including require-dev)
- Installing mongodb/mongodb (1.0.1)
Loading from cache
- Installing alcaeus/mongo-php-adapter (dev-composer-provide 698d301)
Cloning 698d3012c306af299491aa166dc9aa29a4bce5b8
Writing lock file
Generating autoload files
Now there is a package that provides "ext-mongo" installed, and that info is in the lock file. Which works as expected if you now require that extension:
$ php -n $(which composer) require ext-mongo:*
./composer.json has been updated
Loading composer repositories with package information
Updating dependencies (including require-dev)
Nothing to install or update
Writing lock file
Generating autoload files
A subsequent composer update
also works. Great, so let's try a case that fails - having the requirements already in place, but no lock file:
$ rm -rf composer.lock vendor
$ php -n $(which composer) update
Loading composer repositories with package information
Updating dependencies (including require-dev)
Your requirements could not be resolved to an installable set of packages.
Problem 1
- The requested PHP extension ext-mongo * is missing from your system. Install or enable PHP's mongo extension.
Weird, right? The same happens if you require both in one go obviously:
$ php -n $(which composer) remove ext-mongo
$ php -n $(which composer) remove alcaeus/mongo-php-adapter
$ php -n $(which composer) require alcaeus/mongo-php-adapter:dev-composer-provide ext-mongo:*
./composer.json has been updated
Loading composer repositories with package information
Updating dependencies (including require-dev)
Your requirements could not be resolved to an installable set of packages.
Problem 1
- The requested PHP extension ext-mongo * is missing from your system. Install or enable PHP's mongo extension.
Installation failed, reverting ./composer.json to its original content.
You could of course also manually put both packages into composer.json
and run composer update
, same effect (that's actually what happened when we removed lock file and vendor dir earlier and ran an update).
For the steps above, instead of requiring ext-mongo
, you can also use doctrine/mongodb
, which in turn requires ext-mongo
. Same behavior.
There is a workaround: if you put the "alcaeus/mongo-php-adapter" package into a "package" repo inside composer.json, things work. Take this composer.json
:
{
"repositories": [
{
"type": "vcs",
"url": "https://github.com/alcaeus/mongo-php-adapter"
}
],
"require": {
"alcaeus/mongo-php-adapter": "dev-composer-provide",
"doctrine/mongodb": "^1.2"
},
"config": {
"platform": {
"php": "7.0.4",
"ext-mhash": "7.0.4",
"ext-mongodb": "1.1.3"
}
}
}
That works:
$ rm -rf composer.lock vendor
$ php -n $(which composer) update
Loading composer repositories with package information
Updating dependencies (including require-dev)
…
This pointed to a problem with information from Packagist, and indeed, it's due to behavior in (and data available to) ComposerRepository
.
There is no information from Packagist on provided platform packages such as ext-mongo
, so without a lock file, the info that one of the other required packages provides ext-mongo
is apparently not there, and that's why things fail.
Here is the fix:
diff --git a/src/Composer/Repository/ComposerRepository.php b/src/Composer/Repository/ComposerRepository.php
index 5bd4e8a..91d6d58 100644
--- a/src/Composer/Repository/ComposerRepository.php
+++ b/src/Composer/Repository/ComposerRepository.php
@@ -371,7 +371,9 @@ public function whatProvides(Pool $pool, $name)
// override provider with its alias so it can be expanded in the if block above
$this->providersByUid[$version['uid']] = $package;
} else {
- $this->providers[$name][$version['uid']] = $package;
+ foreach($package->getNames() as $nameName) {
+ $this->providers[$nameName][$version['uid']] = $package;
+ }
$this->providersByUid[$version['uid']] = $package;
}
However, that feels like a band-aid treating a symptom of the real underlying issue, which is that Packagist does not have a list of provider packages for platform packages:
$ ls ~/.composer/cache/repo/https---packagist.org/provider-ext*
ls: .composer/cache/repo/https---packagist.org/provider-ext*: No such file or directory