Improve error reporting of solver issues, refs #7779

Fixes #8525
Fixes #6513
main
Jordi Boggiano 4 years ago
parent f611c641db
commit 3fc7e10c5c
No known key found for this signature in database
GPG Key ID: 7BBD42C429EC80BC

@ -89,7 +89,7 @@ class BaseDependencyCommand extends BaseCommand
);
// Find packages that are or provide the requested package first
$packages = $repositorySet->findPackages(strtolower($needle), null, false);
$packages = $repositorySet->findPackages(strtolower($needle), null, RepositorySet::ALLOW_PROVIDERS_REPLACERS);
if (empty($packages)) {
throw new \InvalidArgumentException(sprintf('Could not find package "%s" in your project', $needle));
}

@ -31,7 +31,6 @@ class PoolBuilder
private $stabilityFlags;
private $rootAliases;
private $rootReferences;
private $rootRequires;
private $aliasMap = array();
private $nameConstraints = array();
@ -39,13 +38,12 @@ class PoolBuilder
private $packages = array();
private $unacceptableFixedPackages = array();
public function __construct(array $acceptableStabilities, array $stabilityFlags, array $rootAliases, array $rootReferences, array $rootRequires = array())
public function __construct(array $acceptableStabilities, array $stabilityFlags, array $rootAliases, array $rootReferences)
{
$this->acceptableStabilities = $acceptableStabilities;
$this->stabilityFlags = $stabilityFlags;
$this->rootAliases = $rootAliases;
$this->rootReferences = $rootReferences;
$this->rootRequires = $rootRequires;
}
public function buildPool(array $repositories, Request $request)

@ -13,6 +13,8 @@
namespace Composer\DependencyResolver;
use Composer\Package\CompletePackageInterface;
use Composer\Repository\RepositorySet;
use Composer\Semver\Constraint\Constraint;
/**
* Represents a problem detected while solving dependencies
@ -35,13 +37,6 @@ class Problem
protected $section = 0;
protected $pool;
public function __construct(Pool $pool)
{
$this->pool = $pool;
}
/**
* Add a rule as a reason
*
@ -68,7 +63,7 @@ class Problem
* @param array $installedMap A map of all present packages
* @return string
*/
public function getPrettyString(array $installedMap = array(), array $learnedPool = array())
public function getPrettyString(RepositorySet $repositorySet, Request $request, array $installedMap = array(), array $learnedPool = array())
{
// TODO doesn't this entirely defeat the purpose of the problem sections? what's the point of sections?
$reasons = call_user_func_array('array_merge', array_reverse($this->reasons));
@ -81,91 +76,25 @@ class Problem
throw new \LogicException("Single reason problems must contain a request rule.");
}
$request = $rule->getReasonData();
$packageName = $request['packageName'];
$constraint = $request['constraint'];
$reasonData = $rule->getReasonData();
$packageName = $reasonData['packageName'];
$constraint = $reasonData['constraint'];
if (isset($constraint)) {
$packages = $this->pool->whatProvides($packageName, $constraint);
$packages = $repositorySet->getPool()->whatProvides($packageName, $constraint);
} else {
$packages = array();
}
if (empty($packages)) {
// handle php/hhvm
if ($packageName === 'php' || $packageName === 'php-64bit' || $packageName === 'hhvm') {
$version = phpversion();
$available = $this->pool->whatProvides($packageName);
if (count($available)) {
$firstAvailable = reset($available);
$version = $firstAvailable->getPrettyVersion();
$extra = $firstAvailable->getExtra();
if ($firstAvailable instanceof CompletePackageInterface && isset($extra['config.platform']) && $extra['config.platform'] === true) {
$version .= '; ' . $firstAvailable->getDescription();
}
}
$msg = "\n - This package requires ".$packageName.$this->constraintToText($constraint).' but ';
if (defined('HHVM_VERSION') || (count($available) && $packageName === 'hhvm')) {
return $msg . 'your HHVM version does not satisfy that requirement.';
}
if ($packageName === 'hhvm') {
return $msg . 'you are running this with PHP and not HHVM.';
}
return $msg . 'your PHP version ('. $version .') does not satisfy that requirement.';
}
// handle php extensions
if (0 === stripos($packageName, 'ext-')) {
if (false !== strpos($packageName, ' ')) {
return "\n - The requested PHP extension ".$packageName.' should be required as '.str_replace(' ', '-', $packageName).'.';
}
$ext = substr($packageName, 4);
$error = extension_loaded($ext) ? 'has the wrong version ('.(phpversion($ext) ?: '0').') installed' : 'is missing from your system';
return "\n - The requested PHP extension ".$packageName.$this->constraintToText($constraint).' '.$error.'. Install or enable PHP\'s '.$ext.' extension.';
}
// handle linked libs
if (0 === stripos($packageName, 'lib-')) {
if (strtolower($packageName) === 'lib-icu') {
$error = extension_loaded('intl') ? 'has the wrong version installed, try upgrading the intl extension.' : 'is missing from your system, make sure the intl extension is loaded.';
return "\n - The requested linked library ".$packageName.$this->constraintToText($constraint).' '.$error;
}
return "\n - The requested linked library ".$packageName.$this->constraintToText($constraint).' has the wrong version installed or is missing from your system, make sure to load the extension providing it.';
}
if (!preg_match('{^[A-Za-z0-9_./-]+$}', $packageName)) {
$illegalChars = preg_replace('{[A-Za-z0-9_./-]+}', '', $packageName);
return "\n - The requested package ".$packageName.' could not be found, it looks like its name is invalid, "'.$illegalChars.'" is not allowed in package names.';
}
// TODO: The pool doesn't know about these anymore, it has to ask the RepositorySet
/*if ($providers = $this->pool->whatProvides($packageName, $constraint, true, true)) {
return "\n - The requested package ".$packageName.$this->constraintToText($constraint).' is satisfiable by '.$this->getPackageList($providers).' but these conflict with your requirements or minimum-stability.';
}*/
// TODO: The pool doesn't know about these anymore, it has to ask the RepositorySet
/*if ($providers = $this->pool->whatProvides($packageName, null, true, true)) {
return "\n - The requested package ".$packageName.$this->constraintToText($constraint).' exists as '.$this->getPackageList($providers).' but these are rejected by your constraint.';
}*/
return "\n - The requested package ".$packageName.' could not be found in any version, there may be a typo in the package name.';
return "\n ".implode(self::getMissingPackageReason($repositorySet, $request, $packageName, $constraint));
}
}
$messages = array();
foreach ($reasons as $rule) {
$messages[] = $rule->getPrettyString($this->pool, $installedMap, $learnedPool);
$messages[] = $rule->getPrettyString($repositorySet, $request, $installedMap, $learnedPool);
}
return "\n - ".implode("\n - ", $messages);
@ -193,7 +122,141 @@ class Problem
$this->section++;
}
protected function getPackageList($packages)
/**
* @internal
*/
public static function getMissingPackageReason(RepositorySet $repositorySet, Request $request, $packageName, $constraint = null)
{
$pool = $repositorySet->getPool();
// handle php/hhvm
if ($packageName === 'php' || $packageName === 'php-64bit' || $packageName === 'hhvm') {
$version = phpversion();
$available = $pool->whatProvides($packageName);
if (count($available)) {
$firstAvailable = reset($available);
$version = $firstAvailable->getPrettyVersion();
$extra = $firstAvailable->getExtra();
if ($firstAvailable instanceof CompletePackageInterface && isset($extra['config.platform']) && $extra['config.platform'] === true) {
$version .= '; ' . str_replace('Package ', '', $firstAvailable->getDescription());
}
}
$msg = "- Root composer.json requires ".$packageName.self::constraintToText($constraint).' but ';
if (defined('HHVM_VERSION') || (count($available) && $packageName === 'hhvm')) {
return array($msg, 'your HHVM version does not satisfy that requirement.');
}
if ($packageName === 'hhvm') {
return array($msg, 'you are running this with PHP and not HHVM.');
}
return array($msg, 'your '.$packageName.' version ('. $version .') does not satisfy that requirement.');
}
// handle php extensions
if (0 === stripos($packageName, 'ext-')) {
if (false !== strpos($packageName, ' ')) {
return array('- ', "PHP extension ".$packageName.' should be required as '.str_replace(' ', '-', $packageName).'.');
}
$ext = substr($packageName, 4);
$error = extension_loaded($ext) ? 'it has the wrong version ('.(phpversion($ext) ?: '0').') installed' : 'it is missing from your system';
return array("- Root composer.json requires PHP extension ".$packageName.self::constraintToText($constraint).' but ', $error.'. Install or enable PHP\'s '.$ext.' extension.');
}
// handle linked libs
if (0 === stripos($packageName, 'lib-')) {
if (strtolower($packageName) === 'lib-icu') {
$error = extension_loaded('intl') ? 'it has the wrong version installed, try upgrading the intl extension.' : 'it is missing from your system, make sure the intl extension is loaded.';
return array("- Root composer.json requires linked library ".$packageName.self::constraintToText($constraint).' but ', $error);
}
return array("- Root composer.json requires linked library ".$packageName.self::constraintToText($constraint).' but ', 'it has the wrong version installed or is missing from your system, make sure to load the extension providing it.');
}
$fixedPackage = null;
foreach ($request->getFixedPackages() as $package) {
if ($package->getName() === $packageName) {
$fixedPackage = $package;
if ($pool->isUnacceptableFixedPackage($package)) {
return array("- ", $package->getPrettyName().' is fixed to '.$package->getPrettyVersion().' (lock file version) by a partial update but that version is rejected by your minimum-stability. Make sure you whitelist it for update.');
}
break;
}
}
// first check if the actual requested package is found in normal conditions
// if so it must mean it is rejected by another constraint than the one given here
if ($packages = $repositorySet->findPackages($packageName, $constraint)) {
$rootReqs = $repositorySet->getRootRequires();
if (isset($rootReqs[$packageName])) {
$filtered = array_filter($packages, function ($p) use ($rootReqs, $packageName) {
return $rootReqs[$packageName]->matches(new Constraint('==', $p->getVersion()));
});
if (0 === count($filtered)) {
return array("- Root composer.json requires $packageName".self::constraintToText($constraint) . ', ', 'found '.self::getPackageList($packages).' but '.(self::hasMultipleNames($packages) ? 'these conflict' : 'it conflicts').' with your root composer.json require ('.$rootReqs[$packageName]->getPrettyString().').');
}
}
if ($fixedPackage) {
$fixedConstraint = new Constraint('==', $fixedPackage->getVersion());
$filtered = array_filter($packages, function ($p) use ($fixedConstraint) {
return $fixedConstraint->matches(new Constraint('==', $p->getVersion()));
});
if (0 === count($filtered)) {
return array("- Root composer.json requires $packageName".self::constraintToText($constraint) . ', ', 'found '.self::getPackageList($packages).' but the package is fixed to '.$fixedPackage->getPrettyVersion().' (lock file version) by a partial update and that version does not match. Make sure you whitelist it for update.');
}
}
return array("- Root composer.json requires $packageName".self::constraintToText($constraint) . ', ', 'found '.self::getPackageList($packages).' but '.(self::hasMultipleNames($packages) ? 'these conflict' : 'it conflicts').' with another require.');
}
// check if the package is found when bypassing stability checks
if ($packages = $repositorySet->findPackages($packageName, $constraint, RepositorySet::ALLOW_UNACCEPTABLE_STABILITIES)) {
return array("- Root composer.json requires $packageName".self::constraintToText($constraint) . ', ', 'found '.self::getPackageList($packages).' but '.(self::hasMultipleNames($packages) ? 'these do' : 'it does').' not match your minimum-stability.');
}
// check if the package is found when bypassing the constraint check
if ($packages = $repositorySet->findPackages($packageName, null)) {
// we must first verify if a valid package would be found in a lower priority repository
if ($allReposPackages = $repositorySet->findPackages($packageName, $constraint, RepositorySet::ALLOW_SHADOWED_REPOSITORIES)) {
$higherRepoPackages = $repositorySet->findPackages($packageName, null);
$nextRepoPackages = array();
$nextRepo = null;
foreach ($allReposPackages as $package) {
if ($nextRepo === null || $nextRepo === $package->getRepository()) {
$nextRepoPackages[] = $package;
$nextRepo = $package->getRepository();
} else {
break;
}
}
return array("- Root composer.json requires $packageName".self::constraintToText($constraint) . ', it is ', 'satisfiable by '.self::getPackageList($nextRepoPackages).' from '.$nextRepo->getRepoName().' but '.self::getPackageList($higherRepoPackages).' from '.reset($higherRepoPackages)->getRepository()->getRepoName().' has higher repository priority. The packages with higher priority do not match your constraint and are therefore not installable.');
}
return array("- Root composer.json requires $packageName".self::constraintToText($constraint) . ', ', 'found '.self::getPackageList($packages).' but '.(self::hasMultipleNames($packages) ? 'these do' : 'it does').' not match your constraint.');
}
if (!preg_match('{^[A-Za-z0-9_./-]+$}', $packageName)) {
$illegalChars = preg_replace('{[A-Za-z0-9_./-]+}', '', $packageName);
return array("- Root composer.json requires $packageName, it ", 'could not be found, it looks like its name is invalid, "'.$illegalChars.'" is not allowed in package names.');
}
return array("- Root composer.json requires $packageName, it ", "could not be found in any version, there may be a typo in the package name.");
}
/**
* @internal
*/
public static function getPackageList(array $packages)
{
$prepared = array();
foreach ($packages as $package) {
@ -207,13 +270,27 @@ class Problem
return implode(', ', $prepared);
}
private static function hasMultipleNames(array $packages)
{
$name = null;
foreach ($packages as $package) {
if ($name === null || $name === $package->getName()) {
$name = $package->getName();
} else {
return true;
}
}
return false;
}
/**
* Turns a constraint into text usable in a sentence describing a request
*
* @param \Composer\Semver\Constraint\ConstraintInterface $constraint
* @return string
*/
protected function constraintToText($constraint)
protected static function constraintToText($constraint)
{
return $constraint ? ' '.$constraint->getPrettyString() : '';
}

@ -15,6 +15,7 @@ namespace Composer\DependencyResolver;
use Composer\Package\CompletePackage;
use Composer\Package\Link;
use Composer\Package\PackageInterface;
use Composer\Repository\RepositorySet;
/**
* @author Nils Adermann <naderman@naderman.de>
@ -122,8 +123,9 @@ abstract class Rule
abstract public function isAssertion();
public function getPrettyString(Pool $pool, array $installedMap = array(), array $learnedPool = array())
public function getPrettyString(RepositorySet $repositorySet, Request $request, array $installedMap = array(), array $learnedPool = array())
{
$pool = $repositorySet->getPool();
$literals = $this->getLiterals();
$ruleText = '';
@ -178,60 +180,9 @@ abstract class Rule
} else {
$targetName = $this->reasonData->getTarget();
if ($targetName === 'php' || $targetName === 'php-64bit' || $targetName === 'hhvm') {
// handle php/hhvm
if (defined('HHVM_VERSION')) {
return $text . ' -> your HHVM version does not satisfy that requirement.';
}
$reason = Problem::getMissingPackageReason($repositorySet, $request, $targetName, $this->reasonData->getConstraint());
$packages = $pool->whatProvides($targetName);
$package = count($packages) ? current($packages) : phpversion();
if ($targetName === 'hhvm') {
if ($package instanceof CompletePackage) {
return $text . ' -> your HHVM version ('.$package->getPrettyVersion().') does not satisfy that requirement.';
} else {
return $text . ' -> you are running this with PHP and not HHVM.';
}
}
if (!($package instanceof CompletePackage)) {
return $text . ' -> your PHP version ('.phpversion().') does not satisfy that requirement.';
}
$extra = $package->getExtra();
if (!empty($extra['config.platform'])) {
$text .= ' -> your PHP version ('.phpversion().') overridden by "config.platform.php" version ('.$package->getPrettyVersion().') does not satisfy that requirement.';
} else {
$text .= ' -> your PHP version ('.$package->getPrettyVersion().') does not satisfy that requirement.';
}
return $text;
}
if (0 === strpos($targetName, 'ext-')) {
// handle php extensions
$ext = substr($targetName, 4);
$error = extension_loaded($ext) ? 'has the wrong version ('.(phpversion($ext) ?: '0').') installed' : 'is missing from your system';
return $text . ' -> the requested PHP extension '.$ext.' '.$error.'.';
}
if (0 === strpos($targetName, 'lib-')) {
// handle linked libs
$lib = substr($targetName, 4);
return $text . ' -> the requested linked library '.$lib.' has the wrong version installed or is missing from your system, make sure to have the extension providing it.';
}
// TODO: The pool doesn't know about these anymore, it has to ask the RepositorySet
/*if ($providers = $pool->whatProvides($targetName, $this->reasonData->getConstraint(), true, true)) {
return $text . ' -> satisfiable by ' . $this->formatPackagesUnique($pool, $providers) .' but these conflict with your requirements or minimum-stability.';
}*/
return $text . ' -> no matching package found.';
return $text . ' -> ' . $reason[1];
}
return $text;
@ -249,7 +200,7 @@ abstract class Rule
$learnedString = '(learned rule, ';
if (isset($learnedPool[$this->reasonData])) {
foreach ($learnedPool[$this->reasonData] as $learnedRule) {
$learnedString .= $learnedRule->getPrettyString($pool, $installedMap, $learnedPool);
$learnedString .= $learnedRule->getPrettyString($repositorySet, $request, $installedMap, $learnedPool);
}
} else {
$learnedString .= 'reasoning unavailable';
@ -272,20 +223,13 @@ abstract class Rule
*/
protected function formatPackagesUnique($pool, array $packages)
{
// TODO this is essentially a duplicate of Problem: getPackageList, maintain in one place only?
$prepared = array();
foreach ($packages as $package) {
foreach ($packages as $index => $package) {
if (!is_object($package)) {
$package = $pool->literalToPackage($package);
$packages[$index] = $pool->literalToPackage($package);
}
$prepared[$package->getName()]['name'] = $package->getPrettyName();
$prepared[$package->getName()]['versions'][$package->getVersion()] = $package->getPrettyVersion();
}
foreach ($prepared as $name => $package) {
$prepared[$name] = $package['name'].'['.implode(', ', $package['versions']).']';
}
return implode(', ', $prepared);
return Problem::getPackageList($packages);
}
}

@ -12,6 +12,8 @@
namespace Composer\DependencyResolver;
use Composer\Repository\RepositorySet;
/**
* @author Nils Adermann <naderman@naderman.de>
*/
@ -155,13 +157,13 @@ class RuleSet implements \IteratorAggregate, \Countable
return array_keys($types);
}
public function getPrettyString(Pool $pool = null)
public function getPrettyString(RepositorySet $repositorySet = null, Request $request = null)
{
$string = "\n";
foreach ($this->rules as $type => $rules) {
$string .= str_pad(self::$types[$type], 8, ' ') . ": ";
foreach ($rules as $rule) {
$string .= ($pool ? $rule->getPrettyString($pool) : $rule)."\n";
$string .= ($repositorySet && $request ? $rule->getPrettyString($repositorySet, $request) : $rule)."\n";
}
$string .= "\n\n";
}
@ -171,6 +173,6 @@ class RuleSet implements \IteratorAggregate, \Countable
public function __toString()
{
return $this->getPrettyString(null);
return $this->getPrettyString(null, null);
}
}

@ -29,7 +29,9 @@ class Solver
/** @var PolicyInterface */
protected $policy;
/** @var Pool */
protected $pool = null;
protected $pool;
/** @var RepositorySet */
protected $repositorySet;
/** @var RuleSet */
protected $rules;
@ -65,11 +67,12 @@ class Solver
* @param Pool $pool
* @param IOInterface $io
*/
public function __construct(PolicyInterface $policy, Pool $pool, IOInterface $io)
public function __construct(PolicyInterface $policy, Pool $pool, IOInterface $io, RepositorySet $repositorySet)
{
$this->io = $io;
$this->policy = $policy;
$this->pool = $pool;
$this->repositorySet = $repositorySet;
}
/**
@ -85,6 +88,11 @@ class Solver
return $this->pool;
}
public function getRepositorySet()
{
return $this->repositorySet;
}
// aka solver_makeruledecisions
private function makeAssertionRuleDecisions()
@ -120,7 +128,7 @@ class Solver
$conflict = $this->decisions->decisionRule($literal);
if ($conflict && RuleSet::TYPE_PACKAGE === $conflict->getType()) {
$problem = new Problem($this->pool);
$problem = new Problem();
$problem->addRule($rule);
$problem->addRule($conflict);
@ -130,7 +138,7 @@ class Solver
}
// conflict with another root require/fixed package
$problem = new Problem($this->pool);
$problem = new Problem();
$problem->addRule($rule);
$problem->addRule($conflict);
@ -177,7 +185,7 @@ class Solver
}
if (!$this->pool->whatProvides($packageName, $constraint)) {
$problem = new Problem($this->pool);
$problem = new Problem();
$problem->addRule(new GenericRule(array(), Rule::RULE_ROOT_REQUIRE, array('packageName' => $packageName, 'constraint' => $constraint)));
$this->problems[] = $problem;
}
@ -214,7 +222,7 @@ class Solver
$this->io->writeError(sprintf('Dependency resolution completed in %.3f seconds', microtime(true) - $before), true, IOInterface::VERBOSE);
if ($this->problems) {
throw new SolverProblemsException($this->problems, $request->getPresentMap(true), $this->learnedPool);
throw new SolverProblemsException($this->problems, $this->repositorySet, $request, $this->learnedPool);
}
return new LockTransaction($this->pool, $request->getPresentMap(), $request->getUnlockableMap(), $this->decisions);
@ -513,7 +521,7 @@ class Solver
*/
private function analyzeUnsolvable(Rule $conflictRule)
{
$problem = new Problem($this->pool);
$problem = new Problem();
$problem->addRule($conflictRule);
$this->analyzeUnsolvableRule($problem, $conflictRule);

@ -13,6 +13,7 @@
namespace Composer\DependencyResolver;
use Composer\Util\IniHelper;
use Composer\Repository\RepositorySet;
/**
* @author Nils Adermann <naderman@naderman.de>
@ -23,21 +24,21 @@ class SolverProblemsException extends \RuntimeException
protected $installedMap;
protected $learnedPool;
public function __construct(array $problems, array $installedMap, array $learnedPool)
public function __construct(array $problems, RepositorySet $repositorySet, Request $request, array $learnedPool)
{
$this->problems = $problems;
$this->installedMap = $installedMap;
$this->installedMap = $request->getPresentMap(true);
$this->learnedPool = $learnedPool;
parent::__construct($this->createMessage(), 2);
parent::__construct($this->createMessage($repositorySet, $request), 2);
}
protected function createMessage()
protected function createMessage(RepositorySet $repositorySet, Request $request)
{
$text = "\n";
$hasExtensionProblems = false;
foreach ($this->problems as $i => $problem) {
$text .= " Problem ".($i + 1).$problem->getPrettyString($this->installedMap, $this->learnedPool)."\n";
$text .= " Problem ".($i + 1).$problem->getPrettyString($repositorySet, $request, $this->installedMap, $this->learnedPool)."\n";
if (!$hasExtensionProblems && $this->hasExtensionProblems($problem->getReasons())) {
$hasExtensionProblems = true;

@ -389,7 +389,7 @@ class Installer
$pool = $repositorySet->createPool($request);
// solve dependencies
$solver = new Solver($policy, $pool, $this->io);
$solver = new Solver($policy, $pool, $this->io, $repositorySet);
try {
$lockTransaction = $solver->solve($request, $this->ignorePlatformReqs);
$ruleSetSize = $solver->getRuleSetSize();
@ -529,7 +529,7 @@ class Installer
$pool = $repositorySet->createPool($request);
//$this->eventDispatcher->dispatchInstallerEvent(InstallerEvents::PRE_DEPENDENCIES_SOLVING, false, $policy, $pool, $installedRepo, $request);
$solver = new Solver($policy, $pool, $this->io);
$solver = new Solver($policy, $pool, $this->io, $repositorySet);
try {
$nonDevLockTransaction = $solver->solve($request, $this->ignorePlatformReqs);
//$this->eventDispatcher->dispatchInstallerEvent(InstallerEvents::POST_DEPENDENCIES_SOLVING, false, $policy, $pool, $installedRepo, $request, $ops);
@ -589,7 +589,7 @@ class Installer
$pool = $repositorySet->createPool($request);
// solve dependencies
$solver = new Solver($policy, $pool, $this->io);
$solver = new Solver($policy, $pool, $this->io, $repositorySet);
try {
$lockTransaction = $solver->solve($request, $this->ignorePlatformReqs);
$solver = null;
@ -884,7 +884,7 @@ class Installer
$packageQueue = new \SplQueue;
$nameMatchesRequiredPackage = false;
$depPackages = $repositorySet->findPackages($packageName, null, false);
$depPackages = $repositorySet->findPackages($packageName, null, RepositorySet::ALLOW_PROVIDERS_REPLACERS);
$matchesByPattern = array();
// check if the name is a glob pattern that did not match directly
@ -892,7 +892,7 @@ class Installer
// add any installed package matching the whitelisted name/pattern
$whitelistPatternSearchRegexp = BasePackage::packageNameToRegexp($packageName, '^%s$');
foreach ($lockRepo->search($whitelistPatternSearchRegexp) as $installedPackage) {
$matchesByPattern[] = $repositorySet->findPackages($installedPackage['name'], null, false);
$matchesByPattern[] = $repositorySet->findPackages($installedPackage['name'], null, RepositorySet::ALLOW_PROVIDERS_REPLACERS);
}
// add root requirements which match the whitelisted name/pattern
@ -933,7 +933,7 @@ class Installer
$requires = $package->getRequires();
foreach ($requires as $require) {
$requirePackages = $repositorySet->findPackages($require->getTarget(), null, false);
$requirePackages = $repositorySet->findPackages($require->getTarget(), null, RepositorySet::ALLOW_PROVIDERS_REPLACERS);
foreach ($requirePackages as $requirePackage) {
if (isset($this->updateWhitelist[$requirePackage->getName()])) {

@ -412,7 +412,7 @@ class PluginManager
*/
private function lookupInstalledPackage(RepositorySet $repositorySet, Link $link)
{
$packages = $repositorySet->findPackages($link->getTarget(), $link->getConstraint(), false);
$packages = $repositorySet->findPackages($link->getTarget(), $link->getConstraint(), RepositorySet::ALLOW_PROVIDERS_REPLACERS | RepositorySet::ALLOW_SHADOWED_REPOSITORIES);
return !empty($packages) ? $packages[0] : null;
}

@ -42,6 +42,11 @@ class ArrayRepository extends BaseRepository
}
}
public function getRepoName()
{
return 'array repo (defining '.count($this->packages).' package'.(count($this->packages) > 1 ? 's' : '').')';
}
/**
* {@inheritDoc}
*/
@ -57,7 +62,9 @@ class ArrayRepository extends BaseRepository
(!$packageMap[$package->getName()] || $packageMap[$package->getName()]->matches(new Constraint('==', $package->getVersion())))
&& StabilityFilter::isPackageAcceptable($acceptableStabilities, $stabilityFlags, $package->getNames(), $package->getStability())
) {
// add selected packages which match stability requirements
$result[spl_object_hash($package)] = $package;
// add the aliased package for packages where the alias matches
if ($package instanceof AliasPackage && !isset($result[spl_object_hash($package->getAliasOf())])) {
$result[spl_object_hash($package->getAliasOf())] = $package->getAliasOf();
}
@ -67,6 +74,7 @@ class ArrayRepository extends BaseRepository
}
}
// add aliases of packages that were selected, even if the aliases did not match
foreach ($packages as $package) {
if ($package instanceof AliasPackage) {
if (isset($result[spl_object_hash($package->getAliasOf())])) {

@ -43,6 +43,11 @@ class ArtifactRepository extends ArrayRepository implements ConfigurableReposito
$this->repoConfig = $repoConfig;
}
public function getRepoName()
{
return 'platform repo ('.$this->lookup.')';
}
public function getRepoConfig()
{
return $this->repoConfig;

@ -125,6 +125,11 @@ class ComposerRepository extends ArrayRepository implements ConfigurableReposito
$this->loop = new Loop($this->httpDownloader);
}
public function getRepoName()
{
return 'composer repo ('.$this->url.')';
}
public function getRepoConfig()
{
return $this->repoConfig;

@ -39,6 +39,11 @@ class CompositeRepository extends BaseRepository
}
}
public function getRepoName()
{
return 'composite repo ('.count($this->repositories).' repos)';
}
/**
* Returns all the wrapped repositories
*

@ -21,4 +21,8 @@ namespace Composer\Repository;
*/
class InstalledArrayRepository extends WritableArrayRepository implements InstalledRepositoryInterface
{
public function getRepoName()
{
return 'installed '.parent::getRepoName();
}
}

@ -19,4 +19,8 @@ namespace Composer\Repository;
*/
class InstalledFilesystemRepository extends FilesystemRepository implements InstalledRepositoryInterface
{
public function getRepoName()
{
return 'installed '.parent::getRepoName();
}
}

@ -21,5 +21,9 @@ namespace Composer\Repository;
*/
class LockArrayRepository extends ArrayRepository implements RepositoryInterface
{
public function getRepoName()
{
return 'lock '.parent::getRepoName();
}
}

@ -58,4 +58,9 @@ class PackageRepository extends ArrayRepository
$this->addPackage($package);
}
}
public function getRepoName()
{
return preg_replace('{^array }', 'package ', parent::getRepoName());
}
}

@ -111,6 +111,11 @@ class PathRepository extends ArrayRepository implements ConfigurableRepositoryIn
parent::__construct();
}
public function getRepoName()
{
return 'path repo ('.$this->repoConfig['url'].')';
}
public function getRepoConfig()
{
return $this->repoConfig;

@ -67,6 +67,11 @@ class PearRepository extends ArrayRepository implements ConfigurableRepositoryIn
$this->repoConfig = $repoConfig;
}
public function getRepoName()
{
return 'pear repo ('.$this->url.')';
}
public function getRepoConfig()
{
return $this->repoConfig;

@ -51,6 +51,11 @@ class PlatformRepository extends ArrayRepository
parent::__construct($packages);
}
public function getRepoName()
{
return 'platform repo';
}
protected function initialize()
{
parent::initialize();
@ -275,7 +280,7 @@ class PlatformRepository extends ArrayRepository
} else {
$actualText = 'actual: '.$package->getPrettyVersion();
}
$overrider->setDescription($overrider->getDescription().' ('.$actualText.')');
$overrider->setDescription($overrider->getDescription().', '.$actualText);
return;
}
@ -288,7 +293,7 @@ class PlatformRepository extends ArrayRepository
} else {
$actualText = 'actual: '.$package->getPrettyVersion();
}
$overrider->setDescription($overrider->getDescription().' ('.$actualText.')');
$overrider->setDescription($overrider->getDescription().', '.$actualText);
return;
}

@ -83,4 +83,13 @@ interface RepositoryInterface extends \Countable
* @return array[] an array of array('name' => '...', 'description' => '...')
*/
public function search($query, $mode = 0, $type = null);
/**
* Returns a name representing this repository to the user
*
* This is best effort and definitely can not always be very precise
*
* @return string
*/
public function getRepoName();
}

@ -29,6 +29,19 @@ use Composer\Package\Version\StabilityFilter;
*/
class RepositorySet
{
/**
* Packages which replace/provide the given name might be returned as well even if they do not match the name exactly
*/
const ALLOW_PROVIDERS_REPLACERS = 1;
/**
* Packages are returned even though their stability does not match the required stability
*/
const ALLOW_UNACCEPTABLE_STABILITIES = 2;
/**
* Packages will be looked up in all repositories, even after they have been found in a higher prio one
*/
const ALLOW_SHADOWED_REPOSITORIES = 4;
/** @var array */
private $rootAliases;
/** @var array */
@ -39,7 +52,7 @@ class RepositorySet
private $acceptableStabilities;
private $stabilityFlags;
protected $rootRequires;
private $rootRequires;
/** @var Pool */
private $pool;
@ -64,6 +77,11 @@ class RepositorySet
}
}
public function getRootRequires()
{
return $this->rootRequires;
}
/**
* Adds a repository to this repository set
*
@ -96,15 +114,32 @@ class RepositorySet
*
* @param string $name
* @param ConstraintInterface|null $constraint
* @param bool $exactMatch if set to false, packages which replace/provide the given name might be returned as well even if they do not match the name exactly
* @param bool $ignoreStability if set to true, packages are returned even though their stability does not match the required stability
* @param int $flags any of the ALLOW_* constants from this class to tweak what is returned
* @return array
*/
public function findPackages($name, ConstraintInterface $constraint = null, $exactMatch = true, $ignoreStability = false)
public function findPackages($name, ConstraintInterface $constraint = null, $flags = 0)
{
$exactMatch = ($flags & self::ALLOW_PROVIDERS_REPLACERS) === 0;
$ignoreStability = ($flags & self::ALLOW_UNACCEPTABLE_STABILITIES) !== 0;
$loadFromAllRepos = ($flags & self::ALLOW_SHADOWED_REPOSITORIES) !== 0;
$packages = array();
foreach ($this->repositories as $repository) {
$packages[] = $repository->findPackages($name, $constraint) ?: array();
if ($loadFromAllRepos) {
foreach ($this->repositories as $repository) {
$packages[] = $repository->findPackages($name, $constraint) ?: array();
}
} else {
foreach ($this->repositories as $repository) {
$result = $repository->loadPackages(array($name => $constraint), $ignoreStability ? BasePackage::$stabilities : $this->acceptableStabilities, $ignoreStability ? array() : $this->stabilityFlags);
$packages[] = $result['packages'];
foreach ($result['namesFound'] as $nameFound) {
// avoid loading the same package again from other repositories once it has been found
if ($name === $nameFound) {
break 2;
}
}
}
}
$candidates = $packages ? call_user_func_array('array_merge', $packages) : array();
@ -135,7 +170,7 @@ class RepositorySet
*/
public function createPool(Request $request)
{
$poolBuilder = new PoolBuilder($this->acceptableStabilities, $this->stabilityFlags, $this->rootAliases, $this->rootReferences, $this->rootRequires);
$poolBuilder = new PoolBuilder($this->acceptableStabilities, $this->stabilityFlags, $this->rootAliases, $this->rootReferences);
foreach ($this->repositories as $repo) {
if ($repo instanceof InstalledRepositoryInterface) {

@ -21,4 +21,8 @@ namespace Composer\Repository;
*/
class RootPackageRepository extends ArrayRepository
{
public function getRepoName()
{
return 'root package repo';
}
}

@ -79,6 +79,17 @@ class VcsRepository extends ArrayRepository implements ConfigurableRepositoryInt
$this->processExecutor = new ProcessExecutor($io);
}
public function getRepoName()
{
$driverClass = get_class($this->getDriver());
$driverType = array_search($driverClass, $this->drivers);
if (!$driverType) {
$driverType = $driverClass;
}
return 'vcs repo ('.$driverType.' '.$this->url.')';
}
public function getRepoConfig()
{
return $this->repoConfig;

@ -143,12 +143,18 @@ class RuleSetTest extends TestCase
$p = $this->getPackage('foo', '2.1'),
));
$repositorySetMock = $this->getMockBuilder('Composer\Repository\RepositorySet')->disableOriginalConstructor()->getMock();
$repositorySetMock->expects($this->any())
->method('getPool')
->willReturn($pool);
$requestMock = $this->getMockBuilder('Composer\DependencyResolver\Request')->disableOriginalConstructor()->getMock();
$ruleSet = new RuleSet;
$literal = $p->getId();
$rule = new GenericRule(array($literal), Rule::RULE_ROOT_REQUIRE, array('packageName' => 'foo/bar', 'constraint' => null));
$ruleSet->add($rule, RuleSet::TYPE_REQUEST);
$this->assertContains('REQUEST : No package found to satisfy root composer.json require foo/bar', $ruleSet->getPrettyString($pool));
$this->assertContains('REQUEST : No package found to satisfy root composer.json require foo/bar', $ruleSet->getPrettyString($repositorySetMock, $requestMock));
}
}

@ -99,8 +99,14 @@ class RuleTest extends TestCase
$p2 = $this->getPackage('baz', '1.1'),
));
$repositorySetMock = $this->getMockBuilder('Composer\Repository\RepositorySet')->disableOriginalConstructor()->getMock();
$repositorySetMock->expects($this->any())
->method('getPool')
->willReturn($pool);
$requestMock = $this->getMockBuilder('Composer\DependencyResolver\Request')->disableOriginalConstructor()->getMock();
$rule = new GenericRule(array($p1->getId(), -$p2->getId()), Rule::RULE_PACKAGE_REQUIRES, new Link('baz', 'foo'));
$this->assertEquals('baz 1.1 relates to foo -> satisfiable by foo[2.1].', $rule->getPrettyString($pool));
$this->assertEquals('baz 1.1 relates to foo -> satisfiable by foo[2.1].', $rule->getPrettyString($repositorySetMock, $requestMock));
}
}

@ -82,7 +82,7 @@ class SolverTest extends TestCase
$problems = $e->getProblems();
$this->assertCount(1, $problems);
$this->assertEquals(2, $e->getCode());
$this->assertEquals("\n - The requested package b could not be found in any version, there may be a typo in the package name.", $problems[0]->getPrettyString());
$this->assertEquals("\n - Root composer.json requires b, it could not be found in any version, there may be a typo in the package name.", $problems[0]->getPrettyString($this->repoSet, $this->request));
}
}
@ -682,13 +682,7 @@ class SolverTest extends TestCase
$msg = "\n";
$msg .= " Problem 1\n";
$msg .= " - Root composer.json requires a -> satisfiable by A[1.0].\n";
$msg .= " - A 1.0 requires b >= 2.0 -> no matching package found.\n\n";
$msg .= "Potential causes:\n";
$msg .= " - A typo in the package name\n";
$msg .= " - The package is not available in a stable-enough version according to your minimum-stability setting\n";
$msg .= " see <https://getcomposer.org/doc/04-schema.md#minimum-stability> for more details.\n";
$msg .= " - It's a private package and you forgot to add a custom repository to find it\n\n";
$msg .= "Read <https://getcomposer.org/doc/articles/troubleshooting.md> for further common problems.";
$msg .= " - A 1.0 requires b >= 2.0 -> found B[1.0] but it does not match your constraint.\n";
$this->assertEquals($msg, $e->getMessage());
}
}
@ -895,7 +889,7 @@ class SolverTest extends TestCase
protected function createSolver()
{
$this->solver = new Solver($this->policy, $this->repoSet->createPool($this->request), new NullIO());
$this->solver = new Solver($this->policy, $this->repoSet->createPool($this->request), new NullIO(), $this->repoSet);
}
protected function checkSolverResult(array $expected)

@ -26,7 +26,7 @@ Updating dependencies
Your requirements could not be resolved to an installable set of packages.
Problem 1
- c/c 1.0.0 requires x/x 1.0 -> no matching package found.
- c/c 1.0.0 requires x/x 1.0 -> could not be found in any version, there may be a typo in the package name.
- b/b 1.0.0 requires c/c 1.* -> satisfiable by c/c[1.0.0].
- Root composer.json requires b/b 1.* -> satisfiable by b/b[1.0.0].

@ -0,0 +1,38 @@
--TEST--
Test the error output of solver problems for conflicts between two dependents
--COMPOSER--
{
"repositories": [
{
"type": "package",
"package": [
{ "name": "conflicter/pkg", "version": "1.0.0", "conflict": { "victim/pkg": "1.0.0"} },
{ "name": "victim/pkg", "version": "1.0.0" }
]
}
],
"require": {
"conflicter/pkg": "1.0.0",
"victim/pkg": "1.0.0"
}
}
--RUN--
update
--EXPECT-EXIT-CODE--
2
--EXPECT-OUTPUT--
Loading composer repositories with package information
Updating dependencies
Your requirements could not be resolved to an installable set of packages.
Problem 1
- Root composer.json requires conflicter/pkg 1.0.0 -> satisfiable by conflicter/pkg[1.0.0].
- victim/pkg 1.0.0 conflicts with conflicter/pkg[1.0.0].
- Root composer.json requires victim/pkg 1.0.0 -> satisfiable by victim/pkg[1.0.0].
--EXPECT--

@ -37,7 +37,7 @@ Your requirements could not be resolved to an installable set of packages.
Problem 1
- Root composer.json requires a/a ~1.0 -> satisfiable by a/a[1.0.0].
- a/a 1.0.0 requires php 5.5 -> your PHP version (%s) overridden by "config.platform.php" version (5.3) does not satisfy that requirement.
- a/a 1.0.0 requires php 5.5 -> your php version (5.3; overridden via config.platform, actual: %s) does not satisfy that requirement.
--EXPECT--

@ -1,5 +1,5 @@
--TEST--
Partial update from lock file should apply lock file and downgrade unstable packages even if not whitelisted
Partial update from lock file should apply lock file and if an unstable package is not allowed anymore by latest composer.json it should fail
--COMPOSER--
{
"repositories": [
@ -59,12 +59,4 @@ Updating dependencies
Your requirements could not be resolved to an installable set of packages.
Problem 1
- The requested package b/unstable could not be found in any version, there may be a typo in the package name.
Potential causes:
- A typo in the package name
- The package is not available in a stable-enough version according to your minimum-stability setting
see <https://getcomposer.org/doc/04-schema.md#minimum-stability> for more details.
- It's a private package and you forgot to add a custom repository to find it
Read <https://getcomposer.org/doc/articles/troubleshooting.md> for further common problems.
- b/unstable is fixed to 1.1.0-alpha (lock file version) by a partial update but that version is rejected by your minimum-stability. Make sure you whitelist it for update.

@ -28,15 +28,8 @@ Updating dependencies
Your requirements could not be resolved to an installable set of packages.
Problem 1
- The requested package foo/a could not be found in any version, there may be a typo in the package name.
- Root composer.json requires foo/a 2.*, it is satisfiable by foo/a[2.0.0] from package repo (defining 1 package) but foo/a[1.0.0] from package repo (defining 1 package) has higher repository priority. The packages with higher priority do not match your constraint and are therefore not installable.
Potential causes:
- A typo in the package name
- The package is not available in a stable-enough version according to your minimum-stability setting
see <https://getcomposer.org/doc/04-schema.md#minimum-stability> for more details.
- It's a private package and you forgot to add a custom repository to find it
Read <https://getcomposer.org/doc/articles/troubleshooting.md> for further common problems.
--EXPECT--
--EXPECT-EXIT-CODE--
2

@ -6,34 +6,84 @@ Test the error output of solver problems.
{
"type": "package",
"package": [
{ "name": "package/found", "version": "2.0.0", "require": {
"unstable/package2": "2.*"
} },
{ "name": "package/found2", "version": "2.0.0", "require": {
"invalid/💩package": "*"
} },
{ "name": "package/found3", "version": "2.0.0", "require": {
"unstable/package2": "2.*"
} },
{ "name": "package/found4", "version": "2.0.0", "require": {
"non-existent/pkg2": "1.*"
} },
{ "name": "package/found5", "version": "2.0.0", "require": {
"requirer/pkg": "1.*"
} },
{ "name": "package/found6", "version": "2.0.0", "require": {
"stable-requiree-excluded/pkg2": "1.0.1"
} },
{ "name": "package/found7", "version": "2.0.0", "require": {
"php-64bit": "1.0.1"
} },
{ "name": "conflict/requirer", "version": "2.0.0", "require": {
"conflict/dep": "1.0.0"
} },
{ "name": "conflict/requirer2", "version": "2.0.0", "require": {
"conflict/dep": "2.0.0"
} },
{ "name": "conflict/dep", "version": "1.0.0" },
{ "name": "conflict/dep", "version": "2.0.0" },
{ "name": "unstable/package", "version": "2.0.0-alpha" },
{ "name": "unstable/package", "version": "1.0.0" },
{ "name": "requirer/pkg", "version": "1.0.0", "require": {"dependency/pkg": "1.0.0" } },
{ "name": "unstable/package2", "version": "2.0.0-alpha" },
{ "name": "unstable/package2", "version": "1.0.0" },
{ "name": "requirer/pkg", "version": "1.0.0", "require": {
"dependency/pkg": "1.0.0",
"dependency/unstable-pkg": "1.0.0-dev"
} },
{ "name": "dependency/pkg", "version": "2.0.0" },
{ "name": "dependency/pkg", "version": "1.0.0" },
{ "name": "dependency/unstable-pkg", "version": "1.0.0-dev" },
{ "name": "stable-requiree-excluded/pkg", "version": "1.0.1" },
{ "name": "stable-requiree-excluded/pkg", "version": "1.0.0" }
]
}
],
"require": {
"package/found": "2.*",
"package/found2": "2.*",
"package/found3": "2.*",
"package/found4": "2.*",
"package/found5": "2.*",
"package/found6": "2.*",
"package/found7": "2.*",
"conflict/requirer": "2.*",
"conflict/requirer2": "2.*",
"unstable/package": "2.*",
"bogus/pkg": "1.*",
"non-existent/pkg": "1.*",
"requirer/pkg": "1.*",
"dependency/pkg": "2.*",
"stable-requiree-excluded/pkg": "1.0.1"
"stable-requiree-excluded/pkg": "1.0.1",
"lib-xml": "1002.*",
"lib-icu": "1001.*",
"ext-xml": "1002.*",
"php": "1"
}
}
--INSTALLED--
[
{ "name": "stable-requiree-excluded/pkg", "version": "1.0.0" }
{ "name": "stable-requiree-excluded/pkg", "version": "1.0.0" },
{ "name": "stable-requiree-excluded/pkg2", "version": "1.0.0" }
]
--LOCK--
{
"packages": [
{ "name": "stable-requiree-excluded/pkg", "version": "1.0.0" }
{ "name": "stable-requiree-excluded/pkg", "version": "1.0.0" },
{ "name": "stable-requiree-excluded/pkg2", "version": "1.0.0" }
],
"packages-dev": [],
"aliases": [],
@ -46,7 +96,7 @@ Test the error output of solver problems.
}
--RUN--
update unstable/package requirer/pkg dependency/pkg
update unstable/package requirer/pkg dependency/pkg conflict/requirer
--EXPECT-EXIT-CODE--
2
@ -57,14 +107,44 @@ Updating dependencies
Your requirements could not be resolved to an installable set of packages.
Problem 1
- The requested package unstable/package could not be found in any version, there may be a typo in the package name.
- Root composer.json requires unstable/package 2.*, found unstable/package[2.0.0-alpha] but it does not match your minimum-stability.
Problem 2
- The requested package bogus/pkg could not be found in any version, there may be a typo in the package name.
- Root composer.json requires non-existent/pkg, it could not be found in any version, there may be a typo in the package name.
Problem 3
- The requested package stable-requiree-excluded/pkg could not be found in any version, there may be a typo in the package name.
- Root composer.json requires stable-requiree-excluded/pkg 1.0.1, found stable-requiree-excluded/pkg[1.0.1] but the package is fixed to 1.0.0 (lock file version) by a partial update and that version does not match. Make sure you whitelist it for update.
Problem 4
- Root composer.json requires linked library lib-xml 1002.* but it has the wrong version installed or is missing from your system, make sure to load the extension providing it.
Problem 5
- Root composer.json requires linked library lib-icu 1001.* but it has the wrong version installed, try upgrading the intl extension.
Problem 6
- Root composer.json requires PHP extension ext-xml 1002.* but it has the wrong version (%s) installed. Install or enable PHP's xml extension.
Problem 7
- Root composer.json requires php 1 but your php version (%s) does not satisfy that requirement.
Problem 8
- Root composer.json requires package/found 2.* -> satisfiable by package/found[2.0.0].
- package/found 2.0.0 requires unstable/package2 2.* -> found unstable/package2[2.0.0-alpha] but it does not match your minimum-stability.
Problem 9
- Root composer.json requires package/found2 2.* -> satisfiable by package/found2[2.0.0].
- package/found2 2.0.0 requires invalid/💩package * -> could not be found, it looks like its name is invalid, "💩" is not allowed in package names.
Problem 10
- Root composer.json requires package/found3 2.* -> satisfiable by package/found3[2.0.0].
- package/found3 2.0.0 requires unstable/package2 2.* -> found unstable/package2[2.0.0-alpha] but it does not match your minimum-stability.
Problem 11
- Root composer.json requires package/found4 2.* -> satisfiable by package/found4[2.0.0].
- package/found4 2.0.0 requires non-existent/pkg2 1.* -> could not be found in any version, there may be a typo in the package name.
Problem 12
- Root composer.json requires package/found6 2.* -> satisfiable by package/found6[2.0.0].
- package/found6 2.0.0 requires stable-requiree-excluded/pkg2 1.0.1 -> found stable-requiree-excluded/pkg2[1.0.0] but it does not match your constraint.
Problem 13
- Root composer.json requires package/found7 2.* -> satisfiable by package/found7[2.0.0].
- package/found7 2.0.0 requires php-64bit 1.0.1 -> your php-64bit version (%s) does not satisfy that requirement.
Problem 14
- Root composer.json requires requirer/pkg 1.* -> satisfiable by requirer/pkg[1.0.0].
- requirer/pkg 1.0.0 requires dependency/pkg 1.0.0 -> no matching package found.
- requirer/pkg 1.0.0 requires dependency/pkg 1.0.0 -> found dependency/pkg[1.0.0] but it conflicts with your root composer.json require (2.*).
Problem 15
- requirer/pkg 1.0.0 requires dependency/pkg 1.0.0 -> found dependency/pkg[1.0.0] but it conflicts with your root composer.json require (2.*).
- package/found5 2.0.0 requires requirer/pkg 1.* -> satisfiable by requirer/pkg[1.0.0].
- Root composer.json requires package/found5 2.* -> satisfiable by package/found5[2.0.0].
Potential causes:
- A typo in the package name
@ -73,6 +153,9 @@ Potential causes:
- It's a private package and you forgot to add a custom repository to find it
Read <https://getcomposer.org/doc/articles/troubleshooting.md> for further common problems.
To enable extensions, verify that they are enabled in your .ini files:
__inilist__
You can also run `php --ini` inside terminal to see which files are used by PHP in CLI mode.
--EXPECT--

@ -323,6 +323,9 @@ class InstallerTest extends TestCase
$this->assertSame(rtrim($expect), implode("\n", $installationManager->getTrace()));
if ($expectOutput) {
$output = preg_replace('{^ - .*?\.ini$}m', '__inilist__', $output);
$output = preg_replace('{(__inilist__\r?\n)+}', "__inilist__\n", $output);
$this->assertStringMatchesFormat(rtrim($expectOutput), rtrim($output));
}
}

Loading…
Cancel
Save