Merge pull request #8558 from Seldaek/error-reporting
Improve error reporting of solver issuespull/8566/head
commit
65dfb26c77
|
@ -89,7 +89,7 @@ class BaseDependencyCommand extends BaseCommand
|
||||||
);
|
);
|
||||||
|
|
||||||
// Find packages that are or provide the requested package first
|
// 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)) {
|
if (empty($packages)) {
|
||||||
throw new \InvalidArgumentException(sprintf('Could not find package "%s" in your project', $needle));
|
throw new \InvalidArgumentException(sprintf('Could not find package "%s" in your project', $needle));
|
||||||
}
|
}
|
||||||
|
|
|
@ -31,7 +31,6 @@ class PoolBuilder
|
||||||
private $stabilityFlags;
|
private $stabilityFlags;
|
||||||
private $rootAliases;
|
private $rootAliases;
|
||||||
private $rootReferences;
|
private $rootReferences;
|
||||||
private $rootRequires;
|
|
||||||
|
|
||||||
private $aliasMap = array();
|
private $aliasMap = array();
|
||||||
private $nameConstraints = array();
|
private $nameConstraints = array();
|
||||||
|
@ -39,13 +38,12 @@ class PoolBuilder
|
||||||
private $packages = array();
|
private $packages = array();
|
||||||
private $unacceptableFixedPackages = 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->acceptableStabilities = $acceptableStabilities;
|
||||||
$this->stabilityFlags = $stabilityFlags;
|
$this->stabilityFlags = $stabilityFlags;
|
||||||
$this->rootAliases = $rootAliases;
|
$this->rootAliases = $rootAliases;
|
||||||
$this->rootReferences = $rootReferences;
|
$this->rootReferences = $rootReferences;
|
||||||
$this->rootRequires = $rootRequires;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public function buildPool(array $repositories, Request $request)
|
public function buildPool(array $repositories, Request $request)
|
||||||
|
|
|
@ -13,6 +13,8 @@
|
||||||
namespace Composer\DependencyResolver;
|
namespace Composer\DependencyResolver;
|
||||||
|
|
||||||
use Composer\Package\CompletePackageInterface;
|
use Composer\Package\CompletePackageInterface;
|
||||||
|
use Composer\Repository\RepositorySet;
|
||||||
|
use Composer\Semver\Constraint\Constraint;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Represents a problem detected while solving dependencies
|
* Represents a problem detected while solving dependencies
|
||||||
|
@ -35,13 +37,6 @@ class Problem
|
||||||
|
|
||||||
protected $section = 0;
|
protected $section = 0;
|
||||||
|
|
||||||
protected $pool;
|
|
||||||
|
|
||||||
public function __construct(Pool $pool)
|
|
||||||
{
|
|
||||||
$this->pool = $pool;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Add a rule as a reason
|
* Add a rule as a reason
|
||||||
*
|
*
|
||||||
|
@ -68,7 +63,7 @@ class Problem
|
||||||
* @param array $installedMap A map of all present packages
|
* @param array $installedMap A map of all present packages
|
||||||
* @return string
|
* @return string
|
||||||
*/
|
*/
|
||||||
public function getPrettyString(array $installedMap = array(), array $learnedPool = array())
|
public function getPrettyString(RepositorySet $repositorySet, Request $request, Pool $pool, array $installedMap = array(), array $learnedPool = array())
|
||||||
{
|
{
|
||||||
// TODO doesn't this entirely defeat the purpose of the problem sections? what's the point of sections?
|
// 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));
|
$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.");
|
throw new \LogicException("Single reason problems must contain a request rule.");
|
||||||
}
|
}
|
||||||
|
|
||||||
$request = $rule->getReasonData();
|
$reasonData = $rule->getReasonData();
|
||||||
$packageName = $request['packageName'];
|
$packageName = $reasonData['packageName'];
|
||||||
$constraint = $request['constraint'];
|
$constraint = $reasonData['constraint'];
|
||||||
|
|
||||||
if (isset($constraint)) {
|
if (isset($constraint)) {
|
||||||
$packages = $this->pool->whatProvides($packageName, $constraint);
|
$packages = $pool->whatProvides($packageName, $constraint);
|
||||||
} else {
|
} else {
|
||||||
$packages = array();
|
$packages = array();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (empty($packages)) {
|
if (empty($packages)) {
|
||||||
// handle php/hhvm
|
return "\n ".implode(self::getMissingPackageReason($repositorySet, $request, $pool, $packageName, $constraint));
|
||||||
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.';
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
$messages = array();
|
$messages = array();
|
||||||
|
|
||||||
foreach ($reasons as $rule) {
|
foreach ($reasons as $rule) {
|
||||||
$messages[] = $rule->getPrettyString($this->pool, $installedMap, $learnedPool);
|
$messages[] = $rule->getPrettyString($repositorySet, $request, $pool, $installedMap, $learnedPool);
|
||||||
}
|
}
|
||||||
|
|
||||||
return "\n - ".implode("\n - ", $messages);
|
return "\n - ".implode("\n - ", $messages);
|
||||||
|
@ -193,7 +122,150 @@ class Problem
|
||||||
$this->section++;
|
$this->section++;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function getPackageList($packages)
|
/**
|
||||||
|
* @internal
|
||||||
|
*/
|
||||||
|
public static function getMissingPackageReason(RepositorySet $repositorySet, Request $request, Pool $pool, $packageName, $constraint = null)
|
||||||
|
{
|
||||||
|
// 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.');
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($providers = $repositorySet->getProviders($packageName)) {
|
||||||
|
$maxProviders = 20;
|
||||||
|
$providersStr = implode(array_map(function ($p) {
|
||||||
|
return " - ${p['name']} ".substr($p['description'], 0, 100)."\n";
|
||||||
|
}, count($providers) > $maxProviders+1 ? array_slice($providers, 0, $maxProviders) : $providers));
|
||||||
|
if (count($providers) > $maxProviders+1) {
|
||||||
|
$providersStr .= ' ... and '.(count($providers)-$maxProviders).' more.'."\n";
|
||||||
|
}
|
||||||
|
return array("- Root composer.json requires $packageName".self::constraintToText($constraint).", it ", "could not be found in any version, but the following packages provide it: \n".$providersStr." Consider requiring one of these to satisfy the $packageName requirement.");
|
||||||
|
}
|
||||||
|
|
||||||
|
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();
|
$prepared = array();
|
||||||
foreach ($packages as $package) {
|
foreach ($packages as $package) {
|
||||||
|
@ -207,13 +279,27 @@ class Problem
|
||||||
return implode(', ', $prepared);
|
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
|
* Turns a constraint into text usable in a sentence describing a request
|
||||||
*
|
*
|
||||||
* @param \Composer\Semver\Constraint\ConstraintInterface $constraint
|
* @param \Composer\Semver\Constraint\ConstraintInterface $constraint
|
||||||
* @return string
|
* @return string
|
||||||
*/
|
*/
|
||||||
protected function constraintToText($constraint)
|
protected static function constraintToText($constraint)
|
||||||
{
|
{
|
||||||
return $constraint ? ' '.$constraint->getPrettyString() : '';
|
return $constraint ? ' '.$constraint->getPrettyString() : '';
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,6 +15,7 @@ namespace Composer\DependencyResolver;
|
||||||
use Composer\Package\CompletePackage;
|
use Composer\Package\CompletePackage;
|
||||||
use Composer\Package\Link;
|
use Composer\Package\Link;
|
||||||
use Composer\Package\PackageInterface;
|
use Composer\Package\PackageInterface;
|
||||||
|
use Composer\Repository\RepositorySet;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author Nils Adermann <naderman@naderman.de>
|
* @author Nils Adermann <naderman@naderman.de>
|
||||||
|
@ -122,7 +123,7 @@ abstract class Rule
|
||||||
|
|
||||||
abstract public function isAssertion();
|
abstract public function isAssertion();
|
||||||
|
|
||||||
public function getPrettyString(Pool $pool, array $installedMap = array(), array $learnedPool = array())
|
public function getPrettyString(RepositorySet $repositorySet, Request $request, Pool $pool, array $installedMap = array(), array $learnedPool = array())
|
||||||
{
|
{
|
||||||
$literals = $this->getLiterals();
|
$literals = $this->getLiterals();
|
||||||
|
|
||||||
|
@ -161,7 +162,7 @@ abstract class Rule
|
||||||
$package1 = $pool->literalToPackage($literals[0]);
|
$package1 = $pool->literalToPackage($literals[0]);
|
||||||
$package2 = $pool->literalToPackage($literals[1]);
|
$package2 = $pool->literalToPackage($literals[1]);
|
||||||
|
|
||||||
return $package1->getPrettyString().' conflicts with '.$this->formatPackagesUnique($pool, array($package2)).'.';
|
return $package2->getPrettyString().' conflicts with '.$package1->getPrettyString().'.';
|
||||||
|
|
||||||
case self::RULE_PACKAGE_REQUIRES:
|
case self::RULE_PACKAGE_REQUIRES:
|
||||||
$sourceLiteral = array_shift($literals);
|
$sourceLiteral = array_shift($literals);
|
||||||
|
@ -178,85 +179,103 @@ abstract class Rule
|
||||||
} else {
|
} else {
|
||||||
$targetName = $this->reasonData->getTarget();
|
$targetName = $this->reasonData->getTarget();
|
||||||
|
|
||||||
if ($targetName === 'php' || $targetName === 'php-64bit' || $targetName === 'hhvm') {
|
$reason = Problem::getMissingPackageReason($repositorySet, $request, $pool, $targetName, $this->reasonData->getConstraint());
|
||||||
// handle php/hhvm
|
|
||||||
if (defined('HHVM_VERSION')) {
|
|
||||||
return $text . ' -> your HHVM version does not satisfy that requirement.';
|
|
||||||
}
|
|
||||||
|
|
||||||
$packages = $pool->whatProvides($targetName);
|
return $text . ' -> ' . $reason[1];
|
||||||
$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;
|
return $text;
|
||||||
|
|
||||||
case self::RULE_PACKAGE_OBSOLETES:
|
case self::RULE_PACKAGE_OBSOLETES:
|
||||||
|
if (count($literals) === 2 && $literals[0] < 0 && $literals[1] < 0) {
|
||||||
|
$package1 = $pool->literalToPackage($literals[0]);
|
||||||
|
$package2 = $pool->literalToPackage($literals[1]);
|
||||||
|
|
||||||
|
$replaces1 = $this->getReplacedNames($package1);
|
||||||
|
$replaces2 = $this->getReplacedNames($package2);
|
||||||
|
|
||||||
|
$reason = null;
|
||||||
|
if ($conflictingNames = array_values(array_intersect($replaces1, $replaces2))) {
|
||||||
|
$reason = 'They both replace '.(count($conflictingNames) > 1 ? '['.implode(', ', $conflictingNames).']' : $conflictingNames[0]).' and can thus not coexist.';
|
||||||
|
} elseif (in_array($package1->getName(), $replaces2, true)) {
|
||||||
|
$reason = $package2->getName().' replaces '.$package1->getName().' and can thus not coexist with it.';
|
||||||
|
} elseif (in_array($package2->getName(), $replaces1, true)) {
|
||||||
|
$reason = $package1->getName().' replaces '.$package2->getName().' and can thus not coexist with it.';
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($reason) {
|
||||||
|
if (isset($installedMap[$package1->id]) && !isset($installedMap[$package2->id])) {
|
||||||
|
// swap vars so the if below passes
|
||||||
|
$tmp = $package2;
|
||||||
|
$package2 = $package1;
|
||||||
|
$package1 = $tmp;
|
||||||
|
}
|
||||||
|
if (!isset($installedMap[$package1->id]) && isset($installedMap[$package2->id])) {
|
||||||
|
return $package1->getPrettyString().' can not be installed as that would require removing '.$package2->getPrettyString().'. '.$reason;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!isset($installedMap[$package1->id]) && !isset($installedMap[$package2->id])) {
|
||||||
|
return 'Only one of these can be installed: '.$package1->getPrettyString().', '.$package2->getPrettyString().'. '.$reason;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return 'Only one of these can be installed: '.$package1->getPrettyString().', '.$package2->getPrettyString().'.';
|
||||||
|
}
|
||||||
|
|
||||||
return $ruleText;
|
return $ruleText;
|
||||||
case self::RULE_INSTALLED_PACKAGE_OBSOLETES:
|
case self::RULE_INSTALLED_PACKAGE_OBSOLETES:
|
||||||
return $ruleText;
|
return $ruleText;
|
||||||
case self::RULE_PACKAGE_SAME_NAME:
|
case self::RULE_PACKAGE_SAME_NAME:
|
||||||
return 'Same name, can only install one of: ' . $this->formatPackagesUnique($pool, $literals) . '.';
|
$replacedNames = null;
|
||||||
|
$packageNames = array();
|
||||||
|
foreach ($literals as $literal) {
|
||||||
|
$package = $pool->literalToPackage($literal);
|
||||||
|
$pkgReplaces = $this->getReplacedNames($package);
|
||||||
|
if ($pkgReplaces) {
|
||||||
|
if ($replacedNames === null) {
|
||||||
|
$replacedNames = $this->getReplacedNames($package);
|
||||||
|
} else {
|
||||||
|
$replacedNames = array_intersect($replacedNames, $this->getReplacedNames($package));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$packageNames[$package->getName()] = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($replacedNames) {
|
||||||
|
$replacedNames = array_values(array_intersect(array_keys($packageNames), $replacedNames));
|
||||||
|
}
|
||||||
|
if ($replacedNames && count($packageNames) > 1) {
|
||||||
|
$replacer = null;
|
||||||
|
foreach ($literals as $literal) {
|
||||||
|
$package = $pool->literalToPackage($literal);
|
||||||
|
if (array_intersect($replacedNames, $this->getReplacedNames($package))) {
|
||||||
|
$replacer = $package;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$replacedNames = count($replacedNames) > 1 ? '['.implode(', ', $replacedNames).']' : $replacedNames[0];
|
||||||
|
|
||||||
|
if ($replacer) {
|
||||||
|
return 'Only one of these can be installed: ' . $this->formatPackagesUnique($pool, $literals) . '. '.$replacer->getName().' replaces '.$replacedNames.' and can thus not coexist with it.';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return 'You can only install one version of a package, so only one of these can be installed: ' . $this->formatPackagesUnique($pool, $literals) . '.';
|
||||||
case self::RULE_PACKAGE_IMPLICIT_OBSOLETES:
|
case self::RULE_PACKAGE_IMPLICIT_OBSOLETES:
|
||||||
return $ruleText;
|
return $ruleText;
|
||||||
case self::RULE_LEARNED:
|
case self::RULE_LEARNED:
|
||||||
// TODO not sure this is a good idea, most of these rules should be listed in the problem anyway
|
|
||||||
$learnedString = '(learned rule, ';
|
|
||||||
if (isset($learnedPool[$this->reasonData])) {
|
if (isset($learnedPool[$this->reasonData])) {
|
||||||
|
$learnedString = ', learned rules:'."\n - ";
|
||||||
|
$reasons = array();
|
||||||
foreach ($learnedPool[$this->reasonData] as $learnedRule) {
|
foreach ($learnedPool[$this->reasonData] as $learnedRule) {
|
||||||
$learnedString .= $learnedRule->getPrettyString($pool, $installedMap, $learnedPool);
|
$reasons[] = $learnedRule->getPrettyString($repositorySet, $request, $pool, $installedMap, $learnedPool);
|
||||||
}
|
}
|
||||||
|
$learnedString .= implode("\n - ", array_unique($reasons));
|
||||||
} else {
|
} else {
|
||||||
$learnedString .= 'reasoning unavailable';
|
$learnedString = ' (reasoning unavailable)';
|
||||||
}
|
}
|
||||||
$learnedString .= ')';
|
|
||||||
|
|
||||||
return 'Conclusion: '.$ruleText.' '.$learnedString;
|
return 'Conclusion: '.$ruleText.$learnedString;
|
||||||
case self::RULE_PACKAGE_ALIAS:
|
case self::RULE_PACKAGE_ALIAS:
|
||||||
return $ruleText;
|
return $ruleText;
|
||||||
default:
|
default:
|
||||||
|
@ -272,20 +291,23 @@ abstract class Rule
|
||||||
*/
|
*/
|
||||||
protected function formatPackagesUnique($pool, array $packages)
|
protected function formatPackagesUnique($pool, array $packages)
|
||||||
{
|
{
|
||||||
// TODO this is essentially a duplicate of Problem: getPackageList, maintain in one place only?
|
|
||||||
|
|
||||||
$prepared = array();
|
$prepared = array();
|
||||||
foreach ($packages as $package) {
|
foreach ($packages as $index => $package) {
|
||||||
if (!is_object($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);
|
||||||
|
}
|
||||||
|
|
||||||
|
private function getReplacedNames(PackageInterface $package)
|
||||||
|
{
|
||||||
|
$names = array();
|
||||||
|
foreach ($package->getReplaces() as $link) {
|
||||||
|
$names[] = $link->getTarget();
|
||||||
|
}
|
||||||
|
|
||||||
|
return $names;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,6 +12,8 @@
|
||||||
|
|
||||||
namespace Composer\DependencyResolver;
|
namespace Composer\DependencyResolver;
|
||||||
|
|
||||||
|
use Composer\Repository\RepositorySet;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author Nils Adermann <naderman@naderman.de>
|
* @author Nils Adermann <naderman@naderman.de>
|
||||||
*/
|
*/
|
||||||
|
@ -155,13 +157,13 @@ class RuleSet implements \IteratorAggregate, \Countable
|
||||||
return array_keys($types);
|
return array_keys($types);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getPrettyString(Pool $pool = null)
|
public function getPrettyString(RepositorySet $repositorySet = null, Request $request = null, Pool $pool = null)
|
||||||
{
|
{
|
||||||
$string = "\n";
|
$string = "\n";
|
||||||
foreach ($this->rules as $type => $rules) {
|
foreach ($this->rules as $type => $rules) {
|
||||||
$string .= str_pad(self::$types[$type], 8, ' ') . ": ";
|
$string .= str_pad(self::$types[$type], 8, ' ') . ": ";
|
||||||
foreach ($rules as $rule) {
|
foreach ($rules as $rule) {
|
||||||
$string .= ($pool ? $rule->getPrettyString($pool) : $rule)."\n";
|
$string .= ($repositorySet && $request && $pool ? $rule->getPrettyString($repositorySet, $request, $pool) : $rule)."\n";
|
||||||
}
|
}
|
||||||
$string .= "\n\n";
|
$string .= "\n\n";
|
||||||
}
|
}
|
||||||
|
@ -171,6 +173,6 @@ class RuleSet implements \IteratorAggregate, \Countable
|
||||||
|
|
||||||
public function __toString()
|
public function __toString()
|
||||||
{
|
{
|
||||||
return $this->getPrettyString(null);
|
return $this->getPrettyString(null, null, null);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,9 +14,7 @@ namespace Composer\DependencyResolver;
|
||||||
|
|
||||||
use Composer\IO\IOInterface;
|
use Composer\IO\IOInterface;
|
||||||
use Composer\Package\PackageInterface;
|
use Composer\Package\PackageInterface;
|
||||||
use Composer\Repository\RepositoryInterface;
|
|
||||||
use Composer\Repository\PlatformRepository;
|
use Composer\Repository\PlatformRepository;
|
||||||
use Composer\Repository\RepositorySet;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author Nils Adermann <naderman@naderman.de>
|
* @author Nils Adermann <naderman@naderman.de>
|
||||||
|
@ -29,7 +27,7 @@ class Solver
|
||||||
/** @var PolicyInterface */
|
/** @var PolicyInterface */
|
||||||
protected $policy;
|
protected $policy;
|
||||||
/** @var Pool */
|
/** @var Pool */
|
||||||
protected $pool = null;
|
protected $pool;
|
||||||
|
|
||||||
/** @var RuleSet */
|
/** @var RuleSet */
|
||||||
protected $rules;
|
protected $rules;
|
||||||
|
@ -120,7 +118,7 @@ class Solver
|
||||||
$conflict = $this->decisions->decisionRule($literal);
|
$conflict = $this->decisions->decisionRule($literal);
|
||||||
|
|
||||||
if ($conflict && RuleSet::TYPE_PACKAGE === $conflict->getType()) {
|
if ($conflict && RuleSet::TYPE_PACKAGE === $conflict->getType()) {
|
||||||
$problem = new Problem($this->pool);
|
$problem = new Problem();
|
||||||
|
|
||||||
$problem->addRule($rule);
|
$problem->addRule($rule);
|
||||||
$problem->addRule($conflict);
|
$problem->addRule($conflict);
|
||||||
|
@ -130,7 +128,7 @@ class Solver
|
||||||
}
|
}
|
||||||
|
|
||||||
// conflict with another root require/fixed package
|
// conflict with another root require/fixed package
|
||||||
$problem = new Problem($this->pool);
|
$problem = new Problem();
|
||||||
$problem->addRule($rule);
|
$problem->addRule($rule);
|
||||||
$problem->addRule($conflict);
|
$problem->addRule($conflict);
|
||||||
|
|
||||||
|
@ -177,7 +175,7 @@ class Solver
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!$this->pool->whatProvides($packageName, $constraint)) {
|
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)));
|
$problem->addRule(new GenericRule(array(), Rule::RULE_ROOT_REQUIRE, array('packageName' => $packageName, 'constraint' => $constraint)));
|
||||||
$this->problems[] = $problem;
|
$this->problems[] = $problem;
|
||||||
}
|
}
|
||||||
|
@ -214,7 +212,7 @@ class Solver
|
||||||
$this->io->writeError(sprintf('Dependency resolution completed in %.3f seconds', microtime(true) - $before), true, IOInterface::VERBOSE);
|
$this->io->writeError(sprintf('Dependency resolution completed in %.3f seconds', microtime(true) - $before), true, IOInterface::VERBOSE);
|
||||||
|
|
||||||
if ($this->problems) {
|
if ($this->problems) {
|
||||||
throw new SolverProblemsException($this->problems, $request->getPresentMap(true), $this->learnedPool);
|
throw new SolverProblemsException($this->problems, $this->learnedPool);
|
||||||
}
|
}
|
||||||
|
|
||||||
return new LockTransaction($this->pool, $request->getPresentMap(), $request->getUnlockableMap(), $this->decisions);
|
return new LockTransaction($this->pool, $request->getPresentMap(), $request->getUnlockableMap(), $this->decisions);
|
||||||
|
@ -513,7 +511,7 @@ class Solver
|
||||||
*/
|
*/
|
||||||
private function analyzeUnsolvable(Rule $conflictRule)
|
private function analyzeUnsolvable(Rule $conflictRule)
|
||||||
{
|
{
|
||||||
$problem = new Problem($this->pool);
|
$problem = new Problem();
|
||||||
$problem->addRule($conflictRule);
|
$problem->addRule($conflictRule);
|
||||||
|
|
||||||
$this->analyzeUnsolvableRule($problem, $conflictRule);
|
$this->analyzeUnsolvableRule($problem, $conflictRule);
|
||||||
|
|
|
@ -13,6 +13,7 @@
|
||||||
namespace Composer\DependencyResolver;
|
namespace Composer\DependencyResolver;
|
||||||
|
|
||||||
use Composer\Util\IniHelper;
|
use Composer\Util\IniHelper;
|
||||||
|
use Composer\Repository\RepositorySet;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author Nils Adermann <naderman@naderman.de>
|
* @author Nils Adermann <naderman@naderman.de>
|
||||||
|
@ -20,24 +21,23 @@ use Composer\Util\IniHelper;
|
||||||
class SolverProblemsException extends \RuntimeException
|
class SolverProblemsException extends \RuntimeException
|
||||||
{
|
{
|
||||||
protected $problems;
|
protected $problems;
|
||||||
protected $installedMap;
|
|
||||||
protected $learnedPool;
|
protected $learnedPool;
|
||||||
|
|
||||||
public function __construct(array $problems, array $installedMap, array $learnedPool)
|
public function __construct(array $problems, array $learnedPool)
|
||||||
{
|
{
|
||||||
$this->problems = $problems;
|
$this->problems = $problems;
|
||||||
$this->installedMap = $installedMap;
|
|
||||||
$this->learnedPool = $learnedPool;
|
$this->learnedPool = $learnedPool;
|
||||||
|
|
||||||
parent::__construct($this->createMessage(), 2);
|
parent::__construct('Failed resolving dependencies with '.count($problems).' problems, call getPrettyString to get formatted details', 2);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function createMessage()
|
public function getPrettyString(RepositorySet $repositorySet, Request $request, Pool $pool)
|
||||||
{
|
{
|
||||||
|
$installedMap = $request->getPresentMap(true);
|
||||||
$text = "\n";
|
$text = "\n";
|
||||||
$hasExtensionProblems = false;
|
$hasExtensionProblems = false;
|
||||||
foreach ($this->problems as $i => $problem) {
|
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, $pool, $installedMap, $this->learnedPool)."\n";
|
||||||
|
|
||||||
if (!$hasExtensionProblems && $this->hasExtensionProblems($problem->getReasons())) {
|
if (!$hasExtensionProblems && $this->hasExtensionProblems($problem->getReasons())) {
|
||||||
$hasExtensionProblems = true;
|
$hasExtensionProblems = true;
|
||||||
|
|
|
@ -17,6 +17,7 @@ use Composer\IO\IOInterface;
|
||||||
use Composer\Package\PackageInterface;
|
use Composer\Package\PackageInterface;
|
||||||
use Composer\Util\Filesystem;
|
use Composer\Util\Filesystem;
|
||||||
use Composer\Util\Git as GitUtil;
|
use Composer\Util\Git as GitUtil;
|
||||||
|
use Composer\Util\Url;
|
||||||
use Composer\Util\Platform;
|
use Composer\Util\Platform;
|
||||||
use Composer\Util\ProcessExecutor;
|
use Composer\Util\ProcessExecutor;
|
||||||
use Composer\Cache;
|
use Composer\Cache;
|
||||||
|
@ -434,7 +435,7 @@ class GitDownloader extends VcsDownloader implements DvcsDownloaderInterface
|
||||||
$this->io->writeError(' <warning>'.$reference.' is gone (history was rewritten?)</warning>');
|
$this->io->writeError(' <warning>'.$reference.' is gone (history was rewritten?)</warning>');
|
||||||
}
|
}
|
||||||
|
|
||||||
throw new \RuntimeException(GitUtil::sanitizeUrl('Failed to execute ' . $command . "\n\n" . $this->process->getErrorOutput()));
|
throw new \RuntimeException(Url::sanitize('Failed to execute ' . $command . "\n\n" . $this->process->getErrorOutput()));
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function updateOriginUrl($path, $url)
|
protected function updateOriginUrl($path, $url)
|
||||||
|
|
|
@ -389,14 +389,14 @@ class Installer
|
||||||
$pool = $repositorySet->createPool($request);
|
$pool = $repositorySet->createPool($request);
|
||||||
|
|
||||||
// solve dependencies
|
// solve dependencies
|
||||||
$solver = new Solver($policy, $pool, $this->io);
|
$solver = new Solver($policy, $pool, $this->io, $repositorySet);
|
||||||
try {
|
try {
|
||||||
$lockTransaction = $solver->solve($request, $this->ignorePlatformReqs);
|
$lockTransaction = $solver->solve($request, $this->ignorePlatformReqs);
|
||||||
$ruleSetSize = $solver->getRuleSetSize();
|
$ruleSetSize = $solver->getRuleSetSize();
|
||||||
$solver = null;
|
$solver = null;
|
||||||
} catch (SolverProblemsException $e) {
|
} catch (SolverProblemsException $e) {
|
||||||
$this->io->writeError('<error>Your requirements could not be resolved to an installable set of packages.</error>', true, IOInterface::QUIET);
|
$this->io->writeError('<error>Your requirements could not be resolved to an installable set of packages.</error>', true, IOInterface::QUIET);
|
||||||
$this->io->writeError($e->getMessage());
|
$this->io->writeError($e->getPrettyString($repositorySet, $request, $pool));
|
||||||
if (!$this->devMode) {
|
if (!$this->devMode) {
|
||||||
$this->io->writeError('<warning>Running update with --no-dev does not mean require-dev is ignored, it just means the packages will not be installed. If dev requirements are blocking the update you have to resolve those problems.</warning>', true, IOInterface::QUIET);
|
$this->io->writeError('<warning>Running update with --no-dev does not mean require-dev is ignored, it just means the packages will not be installed. If dev requirements are blocking the update you have to resolve those problems.</warning>', true, IOInterface::QUIET);
|
||||||
}
|
}
|
||||||
|
@ -529,14 +529,14 @@ class Installer
|
||||||
$pool = $repositorySet->createPool($request);
|
$pool = $repositorySet->createPool($request);
|
||||||
|
|
||||||
//$this->eventDispatcher->dispatchInstallerEvent(InstallerEvents::PRE_DEPENDENCIES_SOLVING, false, $policy, $pool, $installedRepo, $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 {
|
try {
|
||||||
$nonDevLockTransaction = $solver->solve($request, $this->ignorePlatformReqs);
|
$nonDevLockTransaction = $solver->solve($request, $this->ignorePlatformReqs);
|
||||||
//$this->eventDispatcher->dispatchInstallerEvent(InstallerEvents::POST_DEPENDENCIES_SOLVING, false, $policy, $pool, $installedRepo, $request, $ops);
|
//$this->eventDispatcher->dispatchInstallerEvent(InstallerEvents::POST_DEPENDENCIES_SOLVING, false, $policy, $pool, $installedRepo, $request, $ops);
|
||||||
$solver = null;
|
$solver = null;
|
||||||
} catch (SolverProblemsException $e) {
|
} catch (SolverProblemsException $e) {
|
||||||
$this->io->writeError('<error>Unable to find a compatible set of packages based on your non-dev requirements alone.</error>', true, IOInterface::QUIET);
|
$this->io->writeError('<error>Unable to find a compatible set of packages based on your non-dev requirements alone.</error>', true, IOInterface::QUIET);
|
||||||
$this->io->writeError($e->getMessage());
|
$this->io->writeError($e->getPrettyString($repositorySet, $request, $pool));
|
||||||
|
|
||||||
return max(1, $e->getCode());
|
return max(1, $e->getCode());
|
||||||
}
|
}
|
||||||
|
@ -589,7 +589,7 @@ class Installer
|
||||||
$pool = $repositorySet->createPool($request);
|
$pool = $repositorySet->createPool($request);
|
||||||
|
|
||||||
// solve dependencies
|
// solve dependencies
|
||||||
$solver = new Solver($policy, $pool, $this->io);
|
$solver = new Solver($policy, $pool, $this->io, $repositorySet);
|
||||||
try {
|
try {
|
||||||
$lockTransaction = $solver->solve($request, $this->ignorePlatformReqs);
|
$lockTransaction = $solver->solve($request, $this->ignorePlatformReqs);
|
||||||
$solver = null;
|
$solver = null;
|
||||||
|
@ -602,7 +602,7 @@ class Installer
|
||||||
}
|
}
|
||||||
} catch (SolverProblemsException $e) {
|
} catch (SolverProblemsException $e) {
|
||||||
$this->io->writeError('<error>Your lock file does not contain a compatible set of packages. Please run composer update.</error>', true, IOInterface::QUIET);
|
$this->io->writeError('<error>Your lock file does not contain a compatible set of packages. Please run composer update.</error>', true, IOInterface::QUIET);
|
||||||
$this->io->writeError($e->getMessage());
|
$this->io->writeError($e->getPrettyString($repositorySet, $request, $pool));
|
||||||
|
|
||||||
return max(1, $e->getCode());
|
return max(1, $e->getCode());
|
||||||
}
|
}
|
||||||
|
@ -884,7 +884,7 @@ class Installer
|
||||||
$packageQueue = new \SplQueue;
|
$packageQueue = new \SplQueue;
|
||||||
$nameMatchesRequiredPackage = false;
|
$nameMatchesRequiredPackage = false;
|
||||||
|
|
||||||
$depPackages = $repositorySet->findPackages($packageName, null, false);
|
$depPackages = $repositorySet->findPackages($packageName, null, RepositorySet::ALLOW_PROVIDERS_REPLACERS);
|
||||||
$matchesByPattern = array();
|
$matchesByPattern = array();
|
||||||
|
|
||||||
// check if the name is a glob pattern that did not match directly
|
// 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
|
// add any installed package matching the whitelisted name/pattern
|
||||||
$whitelistPatternSearchRegexp = BasePackage::packageNameToRegexp($packageName, '^%s$');
|
$whitelistPatternSearchRegexp = BasePackage::packageNameToRegexp($packageName, '^%s$');
|
||||||
foreach ($lockRepo->search($whitelistPatternSearchRegexp) as $installedPackage) {
|
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
|
// add root requirements which match the whitelisted name/pattern
|
||||||
|
@ -933,7 +933,7 @@ class Installer
|
||||||
$requires = $package->getRequires();
|
$requires = $package->getRequires();
|
||||||
|
|
||||||
foreach ($requires as $require) {
|
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) {
|
foreach ($requirePackages as $requirePackage) {
|
||||||
if (isset($this->updateWhitelist[$requirePackage->getName()])) {
|
if (isset($this->updateWhitelist[$requirePackage->getName()])) {
|
||||||
|
|
|
@ -412,7 +412,7 @@ class PluginManager
|
||||||
*/
|
*/
|
||||||
private function lookupInstalledPackage(RepositorySet $repositorySet, Link $link)
|
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;
|
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}
|
* {@inheritDoc}
|
||||||
*/
|
*/
|
||||||
|
@ -57,7 +62,9 @@ class ArrayRepository extends BaseRepository
|
||||||
(!$packageMap[$package->getName()] || $packageMap[$package->getName()]->matches(new Constraint('==', $package->getVersion())))
|
(!$packageMap[$package->getName()] || $packageMap[$package->getName()]->matches(new Constraint('==', $package->getVersion())))
|
||||||
&& StabilityFilter::isPackageAcceptable($acceptableStabilities, $stabilityFlags, $package->getNames(), $package->getStability())
|
&& StabilityFilter::isPackageAcceptable($acceptableStabilities, $stabilityFlags, $package->getNames(), $package->getStability())
|
||||||
) {
|
) {
|
||||||
|
// add selected packages which match stability requirements
|
||||||
$result[spl_object_hash($package)] = $package;
|
$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())])) {
|
if ($package instanceof AliasPackage && !isset($result[spl_object_hash($package->getAliasOf())])) {
|
||||||
$result[spl_object_hash($package->getAliasOf())] = $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) {
|
foreach ($packages as $package) {
|
||||||
if ($package instanceof AliasPackage) {
|
if ($package instanceof AliasPackage) {
|
||||||
if (isset($result[spl_object_hash($package->getAliasOf())])) {
|
if (isset($result[spl_object_hash($package->getAliasOf())])) {
|
||||||
|
|
|
@ -43,6 +43,11 @@ class ArtifactRepository extends ArrayRepository implements ConfigurableReposito
|
||||||
$this->repoConfig = $repoConfig;
|
$this->repoConfig = $repoConfig;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function getRepoName()
|
||||||
|
{
|
||||||
|
return 'artifact repo ('.$this->lookup.')';
|
||||||
|
}
|
||||||
|
|
||||||
public function getRepoConfig()
|
public function getRepoConfig()
|
||||||
{
|
{
|
||||||
return $this->repoConfig;
|
return $this->repoConfig;
|
||||||
|
|
|
@ -34,6 +34,7 @@ use Composer\Semver\Constraint\Constraint;
|
||||||
use Composer\Semver\Constraint\EmptyConstraint;
|
use Composer\Semver\Constraint\EmptyConstraint;
|
||||||
use Composer\Util\Http\Response;
|
use Composer\Util\Http\Response;
|
||||||
use Composer\Util\MetadataMinifier;
|
use Composer\Util\MetadataMinifier;
|
||||||
|
use Composer\Util\Url;
|
||||||
use React\Promise\Util as PromiseUtil;
|
use React\Promise\Util as PromiseUtil;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -52,6 +53,8 @@ class ComposerRepository extends ArrayRepository implements ConfigurableReposito
|
||||||
protected $cache;
|
protected $cache;
|
||||||
protected $notifyUrl;
|
protected $notifyUrl;
|
||||||
protected $searchUrl;
|
protected $searchUrl;
|
||||||
|
/** @var string|null a URL containing %package% which can be queried to get providers of a given name */
|
||||||
|
protected $providersApiUrl;
|
||||||
protected $hasProviders = false;
|
protected $hasProviders = false;
|
||||||
protected $providersUrl;
|
protected $providersUrl;
|
||||||
protected $availablePackages;
|
protected $availablePackages;
|
||||||
|
@ -125,6 +128,11 @@ class ComposerRepository extends ArrayRepository implements ConfigurableReposito
|
||||||
$this->loop = new Loop($this->httpDownloader);
|
$this->loop = new Loop($this->httpDownloader);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function getRepoName()
|
||||||
|
{
|
||||||
|
return 'composer repo ('.Url::sanitize($this->url).')';
|
||||||
|
}
|
||||||
|
|
||||||
public function getRepoConfig()
|
public function getRepoConfig()
|
||||||
{
|
{
|
||||||
return $this->repoConfig;
|
return $this->repoConfig;
|
||||||
|
@ -411,6 +419,17 @@ class ComposerRepository extends ArrayRepository implements ConfigurableReposito
|
||||||
return parent::search($query, $mode);
|
return parent::search($query, $mode);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function getProviders($packageName)
|
||||||
|
{
|
||||||
|
if (!$this->providersApiUrl) {
|
||||||
|
return array();
|
||||||
|
}
|
||||||
|
|
||||||
|
$result = $this->httpDownloader->get(str_replace('%package%', $packageName, $this->providersApiUrl), $this->options)->decodeJson();
|
||||||
|
|
||||||
|
return $result['providers'];
|
||||||
|
}
|
||||||
|
|
||||||
private function getProviderNames()
|
private function getProviderNames()
|
||||||
{
|
{
|
||||||
$this->loadRootServerFile();
|
$this->loadRootServerFile();
|
||||||
|
@ -805,6 +824,10 @@ class ComposerRepository extends ArrayRepository implements ConfigurableReposito
|
||||||
$this->hasProviders = true;
|
$this->hasProviders = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!empty($data['providers-api'])) {
|
||||||
|
$this->providersApiUrl = $data['providers-api'];
|
||||||
|
}
|
||||||
|
|
||||||
return $this->rootData = $data;
|
return $this->rootData = $data;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -39,6 +39,11 @@ class CompositeRepository extends BaseRepository
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function getRepoName()
|
||||||
|
{
|
||||||
|
return 'composite repo ('.implode(', ', array_map(function ($repo) { return $repo->getRepoName(); }, $this->repositories)).')';
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns all the wrapped repositories
|
* Returns all the wrapped repositories
|
||||||
*
|
*
|
||||||
|
|
|
@ -21,4 +21,8 @@ namespace Composer\Repository;
|
||||||
*/
|
*/
|
||||||
class InstalledArrayRepository extends WritableArrayRepository implements InstalledRepositoryInterface
|
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
|
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
|
class LockArrayRepository extends ArrayRepository implements RepositoryInterface
|
||||||
{
|
{
|
||||||
|
public function getRepoName()
|
||||||
|
{
|
||||||
|
return 'lock '.parent::getRepoName();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -58,4 +58,9 @@ class PackageRepository extends ArrayRepository
|
||||||
$this->addPackage($package);
|
$this->addPackage($package);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function getRepoName()
|
||||||
|
{
|
||||||
|
return preg_replace('{^array }', 'package ', parent::getRepoName());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,6 +20,7 @@ use Composer\Package\Version\VersionGuesser;
|
||||||
use Composer\Package\Version\VersionParser;
|
use Composer\Package\Version\VersionParser;
|
||||||
use Composer\Util\Platform;
|
use Composer\Util\Platform;
|
||||||
use Composer\Util\ProcessExecutor;
|
use Composer\Util\ProcessExecutor;
|
||||||
|
use Composer\Util\Url;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This repository allows installing local packages that are not necessarily under their own VCS.
|
* This repository allows installing local packages that are not necessarily under their own VCS.
|
||||||
|
@ -111,6 +112,11 @@ class PathRepository extends ArrayRepository implements ConfigurableRepositoryIn
|
||||||
parent::__construct();
|
parent::__construct();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function getRepoName()
|
||||||
|
{
|
||||||
|
return 'path repo ('.Url::sanitize($this->repoConfig['url']).')';
|
||||||
|
}
|
||||||
|
|
||||||
public function getRepoConfig()
|
public function getRepoConfig()
|
||||||
{
|
{
|
||||||
return $this->repoConfig;
|
return $this->repoConfig;
|
||||||
|
|
|
@ -67,6 +67,11 @@ class PearRepository extends ArrayRepository implements ConfigurableRepositoryIn
|
||||||
$this->repoConfig = $repoConfig;
|
$this->repoConfig = $repoConfig;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function getRepoName()
|
||||||
|
{
|
||||||
|
return 'pear repo ('.$this->url.')';
|
||||||
|
}
|
||||||
|
|
||||||
public function getRepoConfig()
|
public function getRepoConfig()
|
||||||
{
|
{
|
||||||
return $this->repoConfig;
|
return $this->repoConfig;
|
||||||
|
|
|
@ -51,6 +51,11 @@ class PlatformRepository extends ArrayRepository
|
||||||
parent::__construct($packages);
|
parent::__construct($packages);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function getRepoName()
|
||||||
|
{
|
||||||
|
return 'platform repo';
|
||||||
|
}
|
||||||
|
|
||||||
protected function initialize()
|
protected function initialize()
|
||||||
{
|
{
|
||||||
parent::initialize();
|
parent::initialize();
|
||||||
|
@ -275,7 +280,7 @@ class PlatformRepository extends ArrayRepository
|
||||||
} else {
|
} else {
|
||||||
$actualText = 'actual: '.$package->getPrettyVersion();
|
$actualText = 'actual: '.$package->getPrettyVersion();
|
||||||
}
|
}
|
||||||
$overrider->setDescription($overrider->getDescription().' ('.$actualText.')');
|
$overrider->setDescription($overrider->getDescription().', '.$actualText);
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -288,7 +293,7 @@ class PlatformRepository extends ArrayRepository
|
||||||
} else {
|
} else {
|
||||||
$actualText = 'actual: '.$package->getPrettyVersion();
|
$actualText = 'actual: '.$package->getPrettyVersion();
|
||||||
}
|
}
|
||||||
$overrider->setDescription($overrider->getDescription().' ('.$actualText.')');
|
$overrider->setDescription($overrider->getDescription().', '.$actualText);
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
|
@ -83,4 +83,13 @@ interface RepositoryInterface extends \Countable
|
||||||
* @return array[] an array of array('name' => '...', 'description' => '...')
|
* @return array[] an array of array('name' => '...', 'description' => '...')
|
||||||
*/
|
*/
|
||||||
public function search($query, $mode = 0, $type = null);
|
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
|
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 */
|
/** @var array */
|
||||||
private $rootAliases;
|
private $rootAliases;
|
||||||
/** @var array */
|
/** @var array */
|
||||||
|
@ -39,10 +52,10 @@ class RepositorySet
|
||||||
|
|
||||||
private $acceptableStabilities;
|
private $acceptableStabilities;
|
||||||
private $stabilityFlags;
|
private $stabilityFlags;
|
||||||
protected $rootRequires;
|
private $rootRequires;
|
||||||
|
|
||||||
/** @var Pool */
|
/** @var bool */
|
||||||
private $pool;
|
private $locked = false;
|
||||||
|
|
||||||
public function __construct(array $rootAliases = array(), array $rootReferences = array(), $minimumStability = 'stable', array $stabilityFlags = array(), array $rootRequires = array())
|
public function __construct(array $rootAliases = array(), array $rootReferences = array(), $minimumStability = 'stable', array $stabilityFlags = array(), array $rootRequires = array())
|
||||||
{
|
{
|
||||||
|
@ -64,6 +77,11 @@ class RepositorySet
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function getRootRequires()
|
||||||
|
{
|
||||||
|
return $this->rootRequires;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Adds a repository to this repository set
|
* Adds a repository to this repository set
|
||||||
*
|
*
|
||||||
|
@ -74,7 +92,7 @@ class RepositorySet
|
||||||
*/
|
*/
|
||||||
public function addRepository(RepositoryInterface $repo)
|
public function addRepository(RepositoryInterface $repo)
|
||||||
{
|
{
|
||||||
if ($this->pool) {
|
if ($this->locked) {
|
||||||
throw new \RuntimeException("Pool has already been created from this repository set, it cannot be modified anymore.");
|
throw new \RuntimeException("Pool has already been created from this repository set, it cannot be modified anymore.");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -96,15 +114,32 @@ class RepositorySet
|
||||||
*
|
*
|
||||||
* @param string $name
|
* @param string $name
|
||||||
* @param ConstraintInterface|null $constraint
|
* @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 int $flags any of the ALLOW_* constants from this class to tweak what is returned
|
||||||
* @param bool $ignoreStability if set to true, packages are returned even though their stability does not match the required stability
|
|
||||||
* @return array
|
* @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();
|
$packages = array();
|
||||||
foreach ($this->repositories as $repository) {
|
if ($loadFromAllRepos) {
|
||||||
$packages[] = $repository->findPackages($name, $constraint) ?: array();
|
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();
|
$candidates = $packages ? call_user_func_array('array_merge', $packages) : array();
|
||||||
|
@ -123,6 +158,19 @@ class RepositorySet
|
||||||
return $candidates;
|
return $candidates;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function getProviders($packageName)
|
||||||
|
{
|
||||||
|
foreach ($this->repositories as $repository) {
|
||||||
|
if ($repository instanceof ComposerRepository) {
|
||||||
|
if ($providers = $repository->getProviders($packageName)) {
|
||||||
|
return $providers;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return array();
|
||||||
|
}
|
||||||
|
|
||||||
public function isPackageAcceptable($names, $stability)
|
public function isPackageAcceptable($names, $stability)
|
||||||
{
|
{
|
||||||
return StabilityFilter::isPackageAcceptable($this->acceptableStabilities, $this->stabilityFlags, $names, $stability);
|
return StabilityFilter::isPackageAcceptable($this->acceptableStabilities, $this->stabilityFlags, $names, $stability);
|
||||||
|
@ -135,7 +183,7 @@ class RepositorySet
|
||||||
*/
|
*/
|
||||||
public function createPool(Request $request)
|
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) {
|
foreach ($this->repositories as $repo) {
|
||||||
if ($repo instanceof InstalledRepositoryInterface) {
|
if ($repo instanceof InstalledRepositoryInterface) {
|
||||||
|
@ -143,7 +191,9 @@ class RepositorySet
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return $this->pool = $poolBuilder->buildPool($this->repositories, $request);
|
$this->locked = true;
|
||||||
|
|
||||||
|
return $poolBuilder->buildPool($this->repositories, $request);
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO unify this with above in some simpler version without "request"?
|
// TODO unify this with above in some simpler version without "request"?
|
||||||
|
@ -162,13 +212,4 @@ class RepositorySet
|
||||||
|
|
||||||
return $this->createPool($request);
|
return $this->createPool($request);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Access the pool object after it has been created, relevant for plugins which need to read info from the pool
|
|
||||||
* @return Pool
|
|
||||||
*/
|
|
||||||
public function getPool()
|
|
||||||
{
|
|
||||||
return $this->pool;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,4 +21,8 @@ namespace Composer\Repository;
|
||||||
*/
|
*/
|
||||||
class RootPackageRepository extends ArrayRepository
|
class RootPackageRepository extends ArrayRepository
|
||||||
{
|
{
|
||||||
|
public function getRepoName()
|
||||||
|
{
|
||||||
|
return 'root package repo';
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,6 +22,7 @@ use Composer\Package\Loader\LoaderInterface;
|
||||||
use Composer\EventDispatcher\EventDispatcher;
|
use Composer\EventDispatcher\EventDispatcher;
|
||||||
use Composer\Util\ProcessExecutor;
|
use Composer\Util\ProcessExecutor;
|
||||||
use Composer\Util\HttpDownloader;
|
use Composer\Util\HttpDownloader;
|
||||||
|
use Composer\Util\Url;
|
||||||
use Composer\IO\IOInterface;
|
use Composer\IO\IOInterface;
|
||||||
use Composer\Config;
|
use Composer\Config;
|
||||||
|
|
||||||
|
@ -79,6 +80,17 @@ class VcsRepository extends ArrayRepository implements ConfigurableRepositoryInt
|
||||||
$this->processExecutor = new ProcessExecutor($io);
|
$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.' '.Url::sanitize($this->url).')';
|
||||||
|
}
|
||||||
|
|
||||||
public function getRepoConfig()
|
public function getRepoConfig()
|
||||||
{
|
{
|
||||||
return $this->repoConfig;
|
return $this->repoConfig;
|
||||||
|
|
|
@ -255,15 +255,4 @@ class AuthHelper
|
||||||
|
|
||||||
return count($pathParts) >= 4 && $pathParts[3] == 'downloads';
|
return count($pathParts) >= 4 && $pathParts[3] == 'downloads';
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @param string $url
|
|
||||||
* @return string
|
|
||||||
*/
|
|
||||||
public function stripCredentialsFromUrl($url)
|
|
||||||
{
|
|
||||||
// GitHub repository rename result in redirect locations containing the access_token as GET parameter
|
|
||||||
// e.g. https://api.github.com/repositories/9999999999?access_token=github_token
|
|
||||||
return preg_replace('{([&?]access_token=)[^&]+}', '$1***', $url);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -362,27 +362,16 @@ class Git
|
||||||
return '(' . implode('|', array_map('preg_quote', $config->get('gitlab-domains'))) . ')';
|
return '(' . implode('|', array_map('preg_quote', $config->get('gitlab-domains'))) . ')';
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function sanitizeUrl($message)
|
|
||||||
{
|
|
||||||
return preg_replace_callback('{://(?P<user>[^@]+?):(?P<password>.+?)@}', function ($m) {
|
|
||||||
if (preg_match('{^[a-f0-9]{12,}$}', $m[1])) {
|
|
||||||
return '://***:***@';
|
|
||||||
}
|
|
||||||
|
|
||||||
return '://' . $m[1] . ':***@';
|
|
||||||
}, $message);
|
|
||||||
}
|
|
||||||
|
|
||||||
private function throwException($message, $url)
|
private function throwException($message, $url)
|
||||||
{
|
{
|
||||||
// git might delete a directory when it fails and php will not know
|
// git might delete a directory when it fails and php will not know
|
||||||
clearstatcache();
|
clearstatcache();
|
||||||
|
|
||||||
if (0 !== $this->process->execute('git --version', $ignoredOutput)) {
|
if (0 !== $this->process->execute('git --version', $ignoredOutput)) {
|
||||||
throw new \RuntimeException(self::sanitizeUrl('Failed to clone ' . $url . ', git was not found, check that it is installed and in your PATH env.' . "\n\n" . $this->process->getErrorOutput()));
|
throw new \RuntimeException(Url::sanitize('Failed to clone ' . $url . ', git was not found, check that it is installed and in your PATH env.' . "\n\n" . $this->process->getErrorOutput()));
|
||||||
}
|
}
|
||||||
|
|
||||||
throw new \RuntimeException(self::sanitizeUrl($message));
|
throw new \RuntimeException(Url::sanitize($message));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -72,23 +72,12 @@ class Hg
|
||||||
$this->throwException('Failed to clone ' . $url . ', ' . "\n\n" . $error, $url);
|
$this->throwException('Failed to clone ' . $url . ', ' . "\n\n" . $error, $url);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function sanitizeUrl($message)
|
|
||||||
{
|
|
||||||
return preg_replace_callback('{://(?P<user>[^@]+?):(?P<password>.+?)@}', function ($m) {
|
|
||||||
if (preg_match('{^[a-f0-9]{12,}$}', $m[1])) {
|
|
||||||
return '://***:***@';
|
|
||||||
}
|
|
||||||
|
|
||||||
return '://' . $m[1] . ':***@';
|
|
||||||
}, $message);
|
|
||||||
}
|
|
||||||
|
|
||||||
private function throwException($message, $url)
|
private function throwException($message, $url)
|
||||||
{
|
{
|
||||||
if (0 !== $this->process->execute('hg --version', $ignoredOutput)) {
|
if (0 !== $this->process->execute('hg --version', $ignoredOutput)) {
|
||||||
throw new \RuntimeException(self::sanitizeUrl('Failed to clone ' . $url . ', hg was not found, check that it is installed and in your PATH env.' . "\n\n" . $this->process->getErrorOutput()));
|
throw new \RuntimeException(Url::sanitize('Failed to clone ' . $url . ', hg was not found, check that it is installed and in your PATH env.' . "\n\n" . $this->process->getErrorOutput()));
|
||||||
}
|
}
|
||||||
|
|
||||||
throw new \RuntimeException(self::sanitizeUrl($message));
|
throw new \RuntimeException(Url::sanitize($message));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -195,7 +195,7 @@ class CurlDownloader
|
||||||
$usingProxy = !empty($options['http']['proxy']) ? ' using proxy ' . $options['http']['proxy'] : '';
|
$usingProxy = !empty($options['http']['proxy']) ? ' using proxy ' . $options['http']['proxy'] : '';
|
||||||
$ifModified = false !== strpos(strtolower(implode(',', $options['http']['header'])), 'if-modified-since:') ? ' if modified' : '';
|
$ifModified = false !== strpos(strtolower(implode(',', $options['http']['header'])), 'if-modified-since:') ? ' if modified' : '';
|
||||||
if ($attributes['redirects'] === 0) {
|
if ($attributes['redirects'] === 0) {
|
||||||
$this->io->writeError('Downloading ' . $this->authHelper->stripCredentialsFromUrl($url) . $usingProxy . $ifModified, true, IOInterface::DEBUG);
|
$this->io->writeError('Downloading ' . Url::sanitize($url) . $usingProxy . $ifModified, true, IOInterface::DEBUG);
|
||||||
}
|
}
|
||||||
|
|
||||||
$this->checkCurlResult(curl_multi_add_handle($this->multiHandle, $curlHandle));
|
$this->checkCurlResult(curl_multi_add_handle($this->multiHandle, $curlHandle));
|
||||||
|
@ -254,12 +254,12 @@ class CurlDownloader
|
||||||
$contents = stream_get_contents($job['bodyHandle']);
|
$contents = stream_get_contents($job['bodyHandle']);
|
||||||
}
|
}
|
||||||
$response = new Response(array('url' => $progress['url']), $statusCode, $headers, $contents);
|
$response = new Response(array('url' => $progress['url']), $statusCode, $headers, $contents);
|
||||||
$this->io->writeError('['.$statusCode.'] '.$this->authHelper->stripCredentialsFromUrl($progress['url']), true, IOInterface::DEBUG);
|
$this->io->writeError('['.$statusCode.'] '.Url::sanitize($progress['url']), true, IOInterface::DEBUG);
|
||||||
} else {
|
} else {
|
||||||
rewind($job['bodyHandle']);
|
rewind($job['bodyHandle']);
|
||||||
$contents = stream_get_contents($job['bodyHandle']);
|
$contents = stream_get_contents($job['bodyHandle']);
|
||||||
$response = new Response(array('url' => $progress['url']), $statusCode, $headers, $contents);
|
$response = new Response(array('url' => $progress['url']), $statusCode, $headers, $contents);
|
||||||
$this->io->writeError('['.$statusCode.'] '.$this->authHelper->stripCredentialsFromUrl($progress['url']), true, IOInterface::DEBUG);
|
$this->io->writeError('['.$statusCode.'] '.Url::sanitize($progress['url']), true, IOInterface::DEBUG);
|
||||||
}
|
}
|
||||||
fclose($job['bodyHandle']);
|
fclose($job['bodyHandle']);
|
||||||
|
|
||||||
|
@ -362,7 +362,7 @@ class CurlDownloader
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!empty($targetUrl)) {
|
if (!empty($targetUrl)) {
|
||||||
$this->io->writeError(sprintf('Following redirect (%u) %s', $job['attributes']['redirects'] + 1, $this->authHelper->stripCredentialsFromUrl($targetUrl)), true, IOInterface::DEBUG);
|
$this->io->writeError(sprintf('Following redirect (%u) %s', $job['attributes']['redirects'] + 1, Url::sanitize($targetUrl)), true, IOInterface::DEBUG);
|
||||||
|
|
||||||
return $targetUrl;
|
return $targetUrl;
|
||||||
}
|
}
|
||||||
|
|
|
@ -246,7 +246,7 @@ class RemoteFilesystem
|
||||||
|
|
||||||
$actualContextOptions = stream_context_get_options($ctx);
|
$actualContextOptions = stream_context_get_options($ctx);
|
||||||
$usingProxy = !empty($actualContextOptions['http']['proxy']) ? ' using proxy ' . $actualContextOptions['http']['proxy'] : '';
|
$usingProxy = !empty($actualContextOptions['http']['proxy']) ? ' using proxy ' . $actualContextOptions['http']['proxy'] : '';
|
||||||
$this->io->writeError((substr($origFileUrl, 0, 4) === 'http' ? 'Downloading ' : 'Reading ') . $this->authHelper->stripCredentialsFromUrl($origFileUrl) . $usingProxy, true, IOInterface::DEBUG);
|
$this->io->writeError((substr($origFileUrl, 0, 4) === 'http' ? 'Downloading ' : 'Reading ') . Url::sanitize($origFileUrl) . $usingProxy, true, IOInterface::DEBUG);
|
||||||
unset($origFileUrl, $actualContextOptions);
|
unset($origFileUrl, $actualContextOptions);
|
||||||
|
|
||||||
// Check for secure HTTP, but allow insecure Packagist calls to $hashed providers as file integrity is verified with sha256
|
// Check for secure HTTP, but allow insecure Packagist calls to $hashed providers as file integrity is verified with sha256
|
||||||
|
@ -704,7 +704,7 @@ class RemoteFilesystem
|
||||||
$this->redirects++;
|
$this->redirects++;
|
||||||
|
|
||||||
$this->io->writeError('', true, IOInterface::DEBUG);
|
$this->io->writeError('', true, IOInterface::DEBUG);
|
||||||
$this->io->writeError(sprintf('Following redirect (%u) %s', $this->redirects, $this->authHelper->stripCredentialsFromUrl($targetUrl)), true, IOInterface::DEBUG);
|
$this->io->writeError(sprintf('Following redirect (%u) %s', $this->redirects, Url::sanitize($targetUrl)), true, IOInterface::DEBUG);
|
||||||
|
|
||||||
$additionalOptions['redirects'] = $this->redirects;
|
$additionalOptions['redirects'] = $this->redirects;
|
||||||
|
|
||||||
|
|
|
@ -102,4 +102,21 @@ class Url
|
||||||
|
|
||||||
return $origin;
|
return $origin;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static function sanitize($url)
|
||||||
|
{
|
||||||
|
// GitHub repository rename result in redirect locations containing the access_token as GET parameter
|
||||||
|
// e.g. https://api.github.com/repositories/9999999999?access_token=github_token
|
||||||
|
$url = preg_replace('{([&?]access_token=)[^&]+}', '$1***', $url);
|
||||||
|
|
||||||
|
$url = preg_replace_callback('{://(?P<user>[^:/\s@]+):(?P<password>[^@\s/]+)@}i', function ($m) {
|
||||||
|
if (preg_match('{^[a-f0-9]{12,}$}', $m['user'])) {
|
||||||
|
return '://***:***@';
|
||||||
|
}
|
||||||
|
|
||||||
|
return '://'.$m['user'].':***@';
|
||||||
|
}, $url);
|
||||||
|
|
||||||
|
return $url;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -143,12 +143,15 @@ class RuleSetTest extends TestCase
|
||||||
$p = $this->getPackage('foo', '2.1'),
|
$p = $this->getPackage('foo', '2.1'),
|
||||||
));
|
));
|
||||||
|
|
||||||
|
$repositorySetMock = $this->getMockBuilder('Composer\Repository\RepositorySet')->disableOriginalConstructor()->getMock();
|
||||||
|
$requestMock = $this->getMockBuilder('Composer\DependencyResolver\Request')->disableOriginalConstructor()->getMock();
|
||||||
|
|
||||||
$ruleSet = new RuleSet;
|
$ruleSet = new RuleSet;
|
||||||
$literal = $p->getId();
|
$literal = $p->getId();
|
||||||
$rule = new GenericRule(array($literal), Rule::RULE_ROOT_REQUIRE, array('packageName' => 'foo/bar', 'constraint' => null));
|
$rule = new GenericRule(array($literal), Rule::RULE_ROOT_REQUIRE, array('packageName' => 'foo/bar', 'constraint' => null));
|
||||||
|
|
||||||
$ruleSet->add($rule, RuleSet::TYPE_REQUEST);
|
$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, $pool));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -99,8 +99,11 @@ class RuleTest extends TestCase
|
||||||
$p2 = $this->getPackage('baz', '1.1'),
|
$p2 = $this->getPackage('baz', '1.1'),
|
||||||
));
|
));
|
||||||
|
|
||||||
|
$repositorySetMock = $this->getMockBuilder('Composer\Repository\RepositorySet')->disableOriginalConstructor()->getMock();
|
||||||
|
$requestMock = $this->getMockBuilder('Composer\DependencyResolver\Request')->disableOriginalConstructor()->getMock();
|
||||||
|
|
||||||
$rule = new GenericRule(array($p1->getId(), -$p2->getId()), Rule::RULE_PACKAGE_REQUIRES, new Link('baz', 'foo'));
|
$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, $pool));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -34,6 +34,7 @@ class SolverTest extends TestCase
|
||||||
protected $request;
|
protected $request;
|
||||||
protected $policy;
|
protected $policy;
|
||||||
protected $solver;
|
protected $solver;
|
||||||
|
protected $pool;
|
||||||
|
|
||||||
public function setUp()
|
public function setUp()
|
||||||
{
|
{
|
||||||
|
@ -82,7 +83,7 @@ class SolverTest extends TestCase
|
||||||
$problems = $e->getProblems();
|
$problems = $e->getProblems();
|
||||||
$this->assertCount(1, $problems);
|
$this->assertCount(1, $problems);
|
||||||
$this->assertEquals(2, $e->getCode());
|
$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, $this->pool));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -651,9 +652,9 @@ class SolverTest extends TestCase
|
||||||
$msg = "\n";
|
$msg = "\n";
|
||||||
$msg .= " Problem 1\n";
|
$msg .= " Problem 1\n";
|
||||||
$msg .= " - Root composer.json requires a -> satisfiable by A[1.0].\n";
|
$msg .= " - Root composer.json requires a -> satisfiable by A[1.0].\n";
|
||||||
$msg .= " - B 1.0 conflicts with A[1.0].\n";
|
$msg .= " - A 1.0 conflicts with B 1.0.\n";
|
||||||
$msg .= " - Root composer.json requires b -> satisfiable by B[1.0].\n";
|
$msg .= " - Root composer.json requires b -> satisfiable by B[1.0].\n";
|
||||||
$this->assertEquals($msg, $e->getMessage());
|
$this->assertEquals($msg, $e->getPrettyString($this->repoSet, $this->request, $this->pool));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -682,14 +683,8 @@ class SolverTest extends TestCase
|
||||||
$msg = "\n";
|
$msg = "\n";
|
||||||
$msg .= " Problem 1\n";
|
$msg .= " Problem 1\n";
|
||||||
$msg .= " - Root composer.json requires a -> satisfiable by A[1.0].\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 .= " - A 1.0 requires b >= 2.0 -> found B[1.0] but it does not match your constraint.\n";
|
||||||
$msg .= "Potential causes:\n";
|
$this->assertEquals($msg, $e->getPrettyString($this->repoSet, $this->request, $this->pool));
|
||||||
$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.";
|
|
||||||
$this->assertEquals($msg, $e->getMessage());
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -731,10 +726,10 @@ class SolverTest extends TestCase
|
||||||
$msg .= " - C 1.0 requires d >= 1.0 -> satisfiable by D[1.0].\n";
|
$msg .= " - C 1.0 requires d >= 1.0 -> satisfiable by D[1.0].\n";
|
||||||
$msg .= " - D 1.0 requires b < 1.0 -> satisfiable by B[0.9].\n";
|
$msg .= " - D 1.0 requires b < 1.0 -> satisfiable by B[0.9].\n";
|
||||||
$msg .= " - B 1.0 requires c >= 1.0 -> satisfiable by C[1.0].\n";
|
$msg .= " - B 1.0 requires c >= 1.0 -> satisfiable by C[1.0].\n";
|
||||||
$msg .= " - Same name, can only install one of: B[0.9, 1.0].\n";
|
$msg .= " - You can only install one version of a package, so only one of these can be installed: B[0.9, 1.0].\n";
|
||||||
$msg .= " - A 1.0 requires b >= 1.0 -> satisfiable by B[1.0].\n";
|
$msg .= " - A 1.0 requires b >= 1.0 -> satisfiable by B[1.0].\n";
|
||||||
$msg .= " - Root composer.json requires a -> satisfiable by A[1.0].\n";
|
$msg .= " - Root composer.json requires a -> satisfiable by A[1.0].\n";
|
||||||
$this->assertEquals($msg, $e->getMessage());
|
$this->assertEquals($msg, $e->getPrettyString($this->repoSet, $this->request, $this->pool));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -895,7 +890,8 @@ class SolverTest extends TestCase
|
||||||
|
|
||||||
protected function createSolver()
|
protected function createSolver()
|
||||||
{
|
{
|
||||||
$this->solver = new Solver($this->policy, $this->repoSet->createPool($this->request), new NullIO());
|
$this->pool = $this->repoSet->createPool($this->request);
|
||||||
|
$this->solver = new Solver($this->policy, $this->pool, new NullIO());
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function checkSolverResult(array $expected)
|
protected function checkSolverResult(array $expected)
|
||||||
|
|
|
@ -26,7 +26,7 @@ Updating dependencies
|
||||||
Your requirements could not be resolved to an installable set of packages.
|
Your requirements could not be resolved to an installable set of packages.
|
||||||
|
|
||||||
Problem 1
|
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].
|
- 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].
|
- 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].
|
||||||
|
- conflicter/pkg 1.0.0 conflicts with victim/pkg 1.0.0.
|
||||||
|
- Root composer.json requires victim/pkg 1.0.0 -> satisfiable by victim/pkg[1.0.0].
|
||||||
|
|
||||||
|
--EXPECT--
|
||||||
|
|
|
@ -0,0 +1,40 @@
|
||||||
|
--TEST--
|
||||||
|
Test conflicts between a dependency's requirements and the root requirements
|
||||||
|
--COMPOSER--
|
||||||
|
{
|
||||||
|
"repositories": [
|
||||||
|
{
|
||||||
|
"type": "package",
|
||||||
|
"package": [
|
||||||
|
{ "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" }
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"require": {
|
||||||
|
"requirer/pkg": "1.*",
|
||||||
|
"dependency/pkg": "2.*"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
--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 requirer/pkg 1.* -> satisfiable by requirer/pkg[1.0.0].
|
||||||
|
- 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.*).
|
||||||
|
|
||||||
|
--EXPECT--
|
||||||
|
|
|
@ -37,7 +37,7 @@ Your requirements could not be resolved to an installable set of packages.
|
||||||
|
|
||||||
Problem 1
|
Problem 1
|
||||||
- Root composer.json requires a/a ~1.0 -> satisfiable by a/a[1.0.0].
|
- 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--
|
--EXPECT--
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
--TEST--
|
--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--
|
--COMPOSER--
|
||||||
{
|
{
|
||||||
"repositories": [
|
"repositories": [
|
||||||
|
@ -59,12 +59,4 @@ Updating dependencies
|
||||||
Your requirements could not be resolved to an installable set of packages.
|
Your requirements could not be resolved to an installable set of packages.
|
||||||
|
|
||||||
Problem 1
|
Problem 1
|
||||||
- The requested package b/unstable could not be found in any version, there may be a typo in the package name.
|
- 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.
|
||||||
|
|
||||||
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.
|
|
||||||
|
|
|
@ -0,0 +1,49 @@
|
||||||
|
--TEST--
|
||||||
|
Test that names provided by a dependent and root package cause a conflict only for replace
|
||||||
|
--COMPOSER--
|
||||||
|
{
|
||||||
|
"version": "1.2.3",
|
||||||
|
"repositories": [
|
||||||
|
{
|
||||||
|
"type": "package",
|
||||||
|
"package": [
|
||||||
|
{
|
||||||
|
"name": "provider/pkg",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"provide": { "root-provided/transitive-provided": "2.*", "root-replaced/transitive-provided": "2.*" },
|
||||||
|
"replace": { "root-provided/transitive-replaced": "2.*", "root-replaced/transitive-replaced": "2.*" }
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"require": {
|
||||||
|
"provider/pkg": "*"
|
||||||
|
},
|
||||||
|
"provide": {
|
||||||
|
"root-provided/transitive-replaced": "2.*",
|
||||||
|
"root-provided/transitive-provided": "2.*"
|
||||||
|
},
|
||||||
|
"replace": {
|
||||||
|
"root-replaced/transitive-replaced": "2.*",
|
||||||
|
"root-replaced/transitive-provided": "2.*"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
--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__ is present at version 1.2.3 and cannot be modified by Composer
|
||||||
|
- provider/pkg 1.0.0 can not be installed as that would require removing __root__ 1.2.3. They both replace root-replaced/transitive-replaced and can thus not coexist.
|
||||||
|
- Root composer.json requires provider/pkg * -> satisfiable by provider/pkg[1.0.0].
|
||||||
|
|
||||||
|
--EXPECT--
|
||||||
|
|
|
@ -0,0 +1,45 @@
|
||||||
|
--TEST--
|
||||||
|
Test that names provided by two dependents cause a conflict
|
||||||
|
--COMPOSER--
|
||||||
|
{
|
||||||
|
"repositories": [
|
||||||
|
{
|
||||||
|
"type": "package",
|
||||||
|
"package": [
|
||||||
|
{
|
||||||
|
"name": "provider/pkg",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"provide": { "third/pkg": "2.*" }
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "replacer/pkg",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"replace": { "third/pkg": "2.*" }
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"require": {
|
||||||
|
"provider/pkg": "*",
|
||||||
|
"replacer/pkg": "*"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
--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 provider/pkg * -> satisfiable by provider/pkg[1.0.0].
|
||||||
|
- Only one of these can be installed: replacer/pkg 1.0.0, provider/pkg 1.0.0.
|
||||||
|
- Root composer.json requires replacer/pkg * -> satisfiable by replacer/pkg[1.0.0].
|
||||||
|
|
||||||
|
--EXPECT--
|
||||||
|
|
|
@ -0,0 +1,54 @@
|
||||||
|
--TEST--
|
||||||
|
Test that a replacer can not be installed together with another version of the package it replaces
|
||||||
|
--COMPOSER--
|
||||||
|
{
|
||||||
|
"repositories": [
|
||||||
|
{
|
||||||
|
"type": "package",
|
||||||
|
"package": [
|
||||||
|
{"name": "replacer/pkg", "version": "2.0.0", "replace": { "regular/pkg": "self.version" }},
|
||||||
|
{"name": "replacer/pkg", "version": "2.0.1", "replace": { "regular/pkg": "self.version" }},
|
||||||
|
{"name": "replacer/pkg", "version": "2.0.2", "replace": { "regular/pkg": "self.version" }},
|
||||||
|
{"name": "replacer/pkg", "version": "2.0.3", "replace": { "regular/pkg": "self.version" }},
|
||||||
|
{"name": "regular/pkg", "version": "1.0.0"},
|
||||||
|
{"name": "regular/pkg", "version": "1.0.1"},
|
||||||
|
{"name": "regular/pkg", "version": "1.0.2"},
|
||||||
|
{"name": "regular/pkg", "version": "1.0.3"},
|
||||||
|
{"name": "regular/pkg", "version": "2.0.0"},
|
||||||
|
{"name": "regular/pkg", "version": "2.0.1"}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"require": {
|
||||||
|
"regular/pkg": "1.*",
|
||||||
|
"replacer/pkg": "2.*"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
--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
|
||||||
|
- Conclusion: don't install regular/pkg 1.0.3, learned rules:
|
||||||
|
- Root composer.json requires replacer/pkg 2.* -> satisfiable by replacer/pkg[2.0.0, 2.0.1, 2.0.2, 2.0.3].
|
||||||
|
- Only one of these can be installed: regular/pkg[1.0.3, 1.0.2, 1.0.1, 1.0.0], replacer/pkg[2.0.3, 2.0.2, 2.0.1, 2.0.0]. replacer/pkg replaces regular/pkg and can thus not coexist with it.
|
||||||
|
- Conclusion: don't install regular/pkg 1.0.2, learned rules:
|
||||||
|
- Root composer.json requires replacer/pkg 2.* -> satisfiable by replacer/pkg[2.0.0, 2.0.1, 2.0.2, 2.0.3].
|
||||||
|
- Only one of these can be installed: regular/pkg[1.0.3, 1.0.2, 1.0.1, 1.0.0], replacer/pkg[2.0.3, 2.0.2, 2.0.1, 2.0.0]. replacer/pkg replaces regular/pkg and can thus not coexist with it.
|
||||||
|
- Conclusion: don't install regular/pkg 1.0.1, learned rules:
|
||||||
|
- Root composer.json requires replacer/pkg 2.* -> satisfiable by replacer/pkg[2.0.0, 2.0.1, 2.0.2, 2.0.3].
|
||||||
|
- Only one of these can be installed: regular/pkg[1.0.3, 1.0.2, 1.0.1, 1.0.0], replacer/pkg[2.0.3, 2.0.2, 2.0.1, 2.0.0]. replacer/pkg replaces regular/pkg and can thus not coexist with it.
|
||||||
|
- Only one of these can be installed: regular/pkg[1.0.3, 1.0.2, 1.0.1, 1.0.0], replacer/pkg[2.0.3, 2.0.2, 2.0.1, 2.0.0]. replacer/pkg replaces regular/pkg and can thus not coexist with it.
|
||||||
|
- Root composer.json requires regular/pkg 1.* -> satisfiable by regular/pkg[1.0.0, 1.0.1, 1.0.2, 1.0.3].
|
||||||
|
- Root composer.json requires replacer/pkg 2.* -> satisfiable by replacer/pkg[2.0.0, 2.0.1, 2.0.2, 2.0.3].
|
||||||
|
|
||||||
|
--EXPECT--
|
||||||
|
|
|
@ -41,7 +41,7 @@ Your requirements could not be resolved to an installable set of packages.
|
||||||
|
|
||||||
Problem 1
|
Problem 1
|
||||||
- Root composer.json requires foo/standard 1.0.0 -> satisfiable by foo/standard[1.0.0].
|
- Root composer.json requires foo/standard 1.0.0 -> satisfiable by foo/standard[1.0.0].
|
||||||
- foo/standard 1.0.0 requires foo/does-not-exist 1.0.0 -> no matching package found.
|
- foo/standard 1.0.0 requires foo/does-not-exist 1.0.0 -> could not be found in any version, there may be a typo in the package name.
|
||||||
|
|
||||||
Potential causes:
|
Potential causes:
|
||||||
- A typo in the package name
|
- A typo in the package name
|
||||||
|
|
|
@ -28,15 +28,8 @@ Updating dependencies
|
||||||
Your requirements could not be resolved to an installable set of packages.
|
Your requirements could not be resolved to an installable set of packages.
|
||||||
|
|
||||||
Problem 1
|
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--
|
||||||
--EXPECT-EXIT-CODE--
|
--EXPECT-EXIT-CODE--
|
||||||
2
|
2
|
||||||
|
|
|
@ -6,34 +6,84 @@ Test the error output of solver problems.
|
||||||
{
|
{
|
||||||
"type": "package",
|
"type": "package",
|
||||||
"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": "2.0.0-alpha" },
|
||||||
{ "name": "unstable/package", "version": "1.0.0" },
|
{ "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": "2.0.0" },
|
||||||
{ "name": "dependency/pkg", "version": "1.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.1" },
|
||||||
{ "name": "stable-requiree-excluded/pkg", "version": "1.0.0" }
|
{ "name": "stable-requiree-excluded/pkg", "version": "1.0.0" }
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"require": {
|
"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.*",
|
"unstable/package": "2.*",
|
||||||
"bogus/pkg": "1.*",
|
"non-existent/pkg": "1.*",
|
||||||
"requirer/pkg": "1.*",
|
"requirer/pkg": "1.*",
|
||||||
"dependency/pkg": "2.*",
|
"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--
|
--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--
|
--LOCK--
|
||||||
{
|
{
|
||||||
"packages": [
|
"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": [],
|
"packages-dev": [],
|
||||||
"aliases": [],
|
"aliases": [],
|
||||||
|
@ -46,7 +96,7 @@ Test the error output of solver problems.
|
||||||
}
|
}
|
||||||
|
|
||||||
--RUN--
|
--RUN--
|
||||||
update unstable/package requirer/pkg dependency/pkg
|
update unstable/package requirer/pkg dependency/pkg conflict/requirer
|
||||||
|
|
||||||
--EXPECT-EXIT-CODE--
|
--EXPECT-EXIT-CODE--
|
||||||
2
|
2
|
||||||
|
@ -57,14 +107,44 @@ Updating dependencies
|
||||||
Your requirements could not be resolved to an installable set of packages.
|
Your requirements could not be resolved to an installable set of packages.
|
||||||
|
|
||||||
Problem 1
|
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
|
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
|
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
|
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].
|
- 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:
|
Potential causes:
|
||||||
- A typo in the package name
|
- 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
|
- 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.
|
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--
|
--EXPECT--
|
||||||
|
|
||||||
|
|
|
@ -323,6 +323,9 @@ class InstallerTest extends TestCase
|
||||||
$this->assertSame(rtrim($expect), implode("\n", $installationManager->getTrace()));
|
$this->assertSame(rtrim($expect), implode("\n", $installationManager->getTrace()));
|
||||||
|
|
||||||
if ($expectOutput) {
|
if ($expectOutput) {
|
||||||
|
$output = preg_replace('{^ - .*?\.ini$}m', '__inilist__', $output);
|
||||||
|
$output = preg_replace('{(__inilist__\r?\n)+}', "__inilist__\n", $output);
|
||||||
|
|
||||||
$this->assertStringMatchesFormat(rtrim($expectOutput), rtrim($output));
|
$this->assertStringMatchesFormat(rtrim($expectOutput), rtrim($output));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -58,4 +58,25 @@ class UrlTest extends TestCase
|
||||||
array('https://mygitlab.com/api/v3/projects/foo%2Fbar/repository/archive.tar.bz2?sha=abcd', 'https://mygitlab.com/api/v3/projects/foo%2Fbar/repository/archive.tar.bz2?sha=65', array('gitlab-domains' => array('mygitlab.com')), '65'),
|
array('https://mygitlab.com/api/v3/projects/foo%2Fbar/repository/archive.tar.bz2?sha=abcd', 'https://mygitlab.com/api/v3/projects/foo%2Fbar/repository/archive.tar.bz2?sha=65', array('gitlab-domains' => array('mygitlab.com')), '65'),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @dataProvider sanitizeProvider
|
||||||
|
*/
|
||||||
|
public function testSanitize($expected, $url)
|
||||||
|
{
|
||||||
|
$this->assertSame($expected, Url::sanitize($url));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function sanitizeProvider()
|
||||||
|
{
|
||||||
|
return array(
|
||||||
|
array('https://foo:***@example.org/', 'https://foo:bar@example.org/'),
|
||||||
|
array('https://foo@example.org/', 'https://foo@example.org/'),
|
||||||
|
array('https://example.org/', 'https://example.org/'),
|
||||||
|
array('http://***:***@example.org', 'http://10a8f08e8d7b7b9:foo@example.org'),
|
||||||
|
array('https://foo:***@example.org:123/', 'https://foo:bar@example.org:123/'),
|
||||||
|
array('https://example.org/foo/bar?access_token=***', 'https://example.org/foo/bar?access_token=abcdef'),
|
||||||
|
array('https://example.org/foo/bar?foo=bar&access_token=***', 'https://example.org/foo/bar?foo=bar&access_token=abcdef'),
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue