You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

222 lines
8.4 KiB
PHTML

<?php
/*
* This file is part of Composer.
*
* (c) Nils Adermann <naderman@naderman.de>
* Jordi Boggiano <j.boggiano@seld.be>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Composer\DependencyResolver;
6 years ago
use Composer\Package\CompletePackageInterface;
/**
* Represents a problem detected while solving dependencies
*
* @author Nils Adermann <naderman@naderman.de>
*/
class Problem
{
/**
* A map containing the id of each rule part of this problem as a key
* @var array
*/
protected $reasonSeen;
/**
* A set of reasons for the problem, each is a rule or a root require and a rule
* @var array
*/
protected $reasons = array();
protected $section = 0;
protected $pool;
public function __construct(Pool $pool)
{
$this->pool = $pool;
}
/**
* Add a rule as a reason
*
* @param Rule $rule A rule which is a reason for this problem
*/
public function addRule(Rule $rule)
{
$this->addReason(spl_object_hash($rule), $rule);
}
/**
* Retrieve all reasons for this problem
*
* @return array The problem's reasons
*/
public function getReasons()
{
return $this->reasons;
}
/**
* A human readable textual representation of the problem's reasons
*
* @param array $installedMap A map of all present packages
* @return string
*/
public function getPrettyString(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));
if (count($reasons) === 1) {
reset($reasons);
$rule = current($reasons);
if (!in_array($rule->getReason(), array(Rule::RULE_ROOT_REQUIRE, Rule::RULE_FIXED), true)) {
throw new \LogicException("Single reason problems must contain a request rule.");
}
$request = $rule->getReasonData();
$packageName = $request['packageName'];
$constraint = $request['constraint'];
if (isset($constraint)) {
$packages = $this->pool->whatProvides($packageName, $constraint);
} else {
$packages = array();
}
if ($request && 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.';
8 years ago
}
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();
foreach ($reasons as $rule) {
$messages[] = $rule->getPrettyString($this->pool, $installedMap, $learnedPool);
}
return "\n - ".implode("\n - ", $messages);
}
/**
* Store a reason descriptor but ignore duplicates
*
* @param string $id A canonical identifier for the reason
* @param Rule $reason The reason descriptor
*/
protected function addReason($id, Rule $reason)
{
// TODO: if a rule is part of a problem description in two sections, isn't this going to remove a message
// that is important to understand the issue?
if (!isset($this->reasonSeen[$id])) {
$this->reasonSeen[$id] = true;
$this->reasons[$this->section][] = $reason;
}
}
public function nextSection()
{
$this->section++;
}
protected function getPackageList($packages)
{
$prepared = array();
foreach ($packages as $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);
}
/**
* Turns a constraint into text usable in a sentence describing a request
*
* @param \Composer\Semver\Constraint\ConstraintInterface $constraint
* @return string
*/
protected function constraintToText($constraint)
{
return $constraint ? ' '.$constraint->getPrettyString() : '';
}
}