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