diff --git a/src/Composer/Cache.php b/src/Composer/Cache.php index 837406290..9e14ceaff 100644 --- a/src/Composer/Cache.php +++ b/src/Composer/Cache.php @@ -65,4 +65,12 @@ class Cache return sha1_file($this->root . $file); } } + + public function sha256($file) + { + $file = preg_replace('{[^a-z0-9.]}i', '-', $file); + if ($this->enabled && file_exists($this->root . $file)) { + return hash_file('sha256', $this->root . $file); + } + } } diff --git a/src/Composer/DependencyResolver/Decisions.php b/src/Composer/DependencyResolver/Decisions.php index 451cb5ff7..a9808e60e 100644 --- a/src/Composer/DependencyResolver/Decisions.php +++ b/src/Composer/DependencyResolver/Decisions.php @@ -29,12 +29,7 @@ class Decisions implements \Iterator, \Countable public function __construct($pool) { $this->pool = $pool; - - if (version_compare(PHP_VERSION, '5.3.4', '>=')) { - $this->decisionMap = new \SplFixedArray($this->pool->getMaxId() + 1); - } else { - $this->decisionMap = array_fill(0, $this->pool->getMaxId() + 1, 0); - } + $this->decisionMap = array(); } public function decide($literal, $level, $why) @@ -51,8 +46,8 @@ class Decisions implements \Iterator, \Countable $packageId = abs($literal); return ( - $literal > 0 && $this->decisionMap[$packageId] > 0 || - $literal < 0 && $this->decisionMap[$packageId] < 0 + $literal > 0 && isset($this->decisionMap[$packageId]) && $this->decisionMap[$packageId] > 0 || + $literal < 0 && isset($this->decisionMap[$packageId]) && $this->decisionMap[$packageId] < 0 ); } @@ -61,29 +56,36 @@ class Decisions implements \Iterator, \Countable $packageId = abs($literal); return ( - ($this->decisionMap[$packageId] > 0 && $literal < 0) || - ($this->decisionMap[$packageId] < 0 && $literal > 0) + (isset($this->decisionMap[$packageId]) && $this->decisionMap[$packageId] > 0 && $literal < 0) || + (isset($this->decisionMap[$packageId]) && $this->decisionMap[$packageId] < 0 && $literal > 0) ); } public function decided($literalOrPackageId) { - return $this->decisionMap[abs($literalOrPackageId)] != 0; + return !empty($this->decisionMap[abs($literalOrPackageId)]); } public function undecided($literalOrPackageId) { - return $this->decisionMap[abs($literalOrPackageId)] == 0; + return empty($this->decisionMap[abs($literalOrPackageId)]); } public function decidedInstall($literalOrPackageId) { - return $this->decisionMap[abs($literalOrPackageId)] > 0; + $packageId = abs($literalOrPackageId); + + return isset($this->decisionMap[$packageId]) && $this->decisionMap[$packageId] > 0; } public function decisionLevel($literalOrPackageId) { - return abs($this->decisionMap[abs($literalOrPackageId)]); + $packageId = abs($literalOrPackageId); + if (isset($this->decisionMap[$packageId])) { + return abs($this->decisionMap[$packageId]); + } + + return 0; } public function decisionRule($literalOrPackageId) @@ -179,7 +181,7 @@ class Decisions implements \Iterator, \Countable { $packageId = abs($literal); - $previousDecision = $this->decisionMap[$packageId]; + $previousDecision = isset($this->decisionMap[$packageId]) ? $this->decisionMap[$packageId] : null; if ($previousDecision != 0) { $literalString = $this->pool->literalToString($literal); $package = $this->pool->literalToPackage($literal); diff --git a/src/Composer/DependencyResolver/Pool.php b/src/Composer/DependencyResolver/Pool.php index 5ee39d9a9..2216d5215 100644 --- a/src/Composer/DependencyResolver/Pool.php +++ b/src/Composer/DependencyResolver/Pool.php @@ -20,6 +20,7 @@ use Composer\Package\LinkConstraint\LinkConstraintInterface; use Composer\Package\LinkConstraint\VersionConstraint; use Composer\Repository\RepositoryInterface; use Composer\Repository\CompositeRepository; +use Composer\Repository\ComposerRepository; use Composer\Repository\InstalledRepositoryInterface; use Composer\Repository\StreamableRepositoryInterface; use Composer\Repository\PlatformRepository; @@ -39,11 +40,14 @@ class Pool const MATCH_REPLACE = 3; protected $repositories = array(); + protected $composerRepos = array(); protected $packages = array(); protected $packageByName = array(); protected $acceptableStabilities; protected $stabilityFlags; protected $versionParser; + protected $providerCache = array(); + protected $id = 1; public function __construct($minimumStability = 'stable', array $stabilityFlags = array()) { @@ -72,18 +76,21 @@ class Pool $repos = array($repo); } - $id = count($this->packages) + 1; foreach ($repos as $repo) { $this->repositories[] = $repo; $exempt = $repo instanceof PlatformRepository || $repo instanceof InstalledRepositoryInterface; - if ($repo instanceof StreamableRepositoryInterface) { + + if ($repo instanceof ComposerRepository && $repo->hasProviders()) { + $this->composerRepos[] = $repo; + $repo->resetPackageIds(); + } elseif ($repo instanceof StreamableRepositoryInterface) { foreach ($repo->getMinimalPackages() as $package) { $name = $package['name']; $version = $package['version']; $stability = VersionParser::parseStability($version); if ($exempt || $this->isPackageAcceptable($name, $stability)) { - $package['id'] = $id++; + $package['id'] = $this->id++; $this->packages[] = $package; // collect names @@ -102,7 +109,7 @@ class Pool } foreach (array_keys($names) as $provided) { - $this->packageByName[$provided][] =& $this->packages[$id-2]; + $this->packageByName[$provided][] =& $this->packages[$this->id - 2]; } // handle root package aliases @@ -119,12 +126,12 @@ class Pool $alias['version'] = $rootAliasData['alias_normalized']; $alias['alias'] = $rootAliasData['alias']; $alias['alias_of'] = $package['id']; - $alias['id'] = $id++; + $alias['id'] = $this->id++; $alias['root_alias'] = true; $this->packages[] = $alias; foreach (array_keys($names) as $name) { - $this->packageByName[$name][] =& $this->packages[$id-2]; + $this->packageByName[$name][] =& $this->packages[$this->id - 2]; } } @@ -135,11 +142,11 @@ class Pool $alias['version'] = $package['alias_normalized']; $alias['alias'] = $package['alias']; $alias['alias_of'] = $package['id']; - $alias['id'] = $id++; + $alias['id'] = $this->id++; $this->packages[] = $alias; foreach (array_keys($names) as $name) { - $this->packageByName[$name][] =& $this->packages[$id-2]; + $this->packageByName[$name][] =& $this->packages[$this->id - 2]; } } } @@ -149,7 +156,7 @@ class Pool $name = $package->getName(); $stability = $package->getStability(); if ($exempt || $this->isPackageAcceptable($name, $stability)) { - $package->setId($id++); + $package->setId($this->id++); $this->packages[] = $package; foreach ($package->getNames() as $name) { @@ -163,7 +170,7 @@ class Pool $package->setPrettyAlias($alias['alias']); $package->getRepository()->addPackage($aliasPackage = new AliasPackage($package, $alias['alias_normalized'], $alias['alias'])); $aliasPackage->setRootPackageAlias(true); - $aliasPackage->setId($id++); + $aliasPackage->setId($this->id++); $this->packages[] = $aliasPackage; @@ -201,16 +208,6 @@ class Pool return $this->packages[$id - 1]; } - /** - * Retrieves the highest id assigned to a package in this pool - * - * @return int Highest package id - */ - public function getMaxId() - { - return count($this->packages); - } - /** * Searches all packages providing the given package name and match the constraint * @@ -221,11 +218,33 @@ class Pool */ public function whatProvides($name, LinkConstraintInterface $constraint = null) { - if (!isset($this->packageByName[$name])) { - return array(); + if (isset($this->providerCache[$name][(string) $constraint])) { + return $this->providerCache[$name][(string) $constraint]; } - $candidates = $this->packageByName[$name]; + return $this->providerCache[$name][(string) $constraint] = $this->computeWhatProvides($name, $constraint); + } + + /** + * @see whatProvides + */ + private function computeWhatProvides($name, $constraint) + { + $candidates = array(); + + foreach ($this->composerRepos as $repo) { + foreach ($repo->whatProvides($this, $name) as $candidate) { + $candidates[] = $candidate; + if ($candidate->getId() < 1) { + $candidate->setId($this->id++); + $this->packages[$this->id - 2] = $candidate; + } + } + } + + if (isset($this->packageByName[$name])) { + $candidates = array_merge($candidates, $this->packageByName[$name]); + } if (null === $constraint) { foreach ($candidates as $key => $candidate) { @@ -298,7 +317,7 @@ class Pool return $prefix.' '.$package->getPrettyString(); } - private function isPackageAcceptable($name, $stability) + public function isPackageAcceptable($name, $stability) { // allow if package matches the global stability requirement and has no exception if (!isset($this->stabilityFlags[$name]) && isset($this->acceptableStabilities[$stability])) { @@ -368,16 +387,12 @@ class Pool $replaces = $candidate->getReplaces(); } - foreach ($provides as $link) { - if ($link->getTarget() === $name && $constraint->matches($link->getConstraint())) { - return self::MATCH_PROVIDE; - } + if (isset($provides[$name]) && $constraint->matches($provides[$name]->getConstraint())) { + return self::MATCH_PROVIDE; } - foreach ($replaces as $link) { - if ($link->getTarget() === $name && $constraint->matches($link->getConstraint())) { - return self::MATCH_REPLACE; - } + if (isset($replaces[$name]) && $constraint->matches($replaces[$name]->getConstraint())) { + return self::MATCH_REPLACE; } return self::MATCH_NONE; diff --git a/src/Composer/DependencyResolver/Solver.php b/src/Composer/DependencyResolver/Solver.php index c0a09f16b..2ad3965fe 100644 --- a/src/Composer/DependencyResolver/Solver.php +++ b/src/Composer/DependencyResolver/Solver.php @@ -53,7 +53,8 @@ class Solver { $decisionStart = count($this->decisions) - 1; - for ($ruleIndex = 0; $ruleIndex < count($this->rules); $ruleIndex++) { + $rulesCount = count($this->rules); + for ($ruleIndex = 0; $ruleIndex < $rulesCount; $ruleIndex++) { $rule = $this->rules->ruleById($ruleIndex); if (!$rule->isAssertion() || $rule->isDisabled()) { diff --git a/src/Composer/Json/JsonFile.php b/src/Composer/Json/JsonFile.php index b089a8b32..00bded4d0 100755 --- a/src/Composer/Json/JsonFile.php +++ b/src/Composer/Json/JsonFile.php @@ -85,7 +85,7 @@ class JsonFile $json = file_get_contents($this->path); } } catch (TransportException $e) { - throw new \RuntimeException($e->getMessage()); + throw new \RuntimeException($e->getMessage(), 0, $e); } catch (\Exception $e) { throw new \RuntimeException('Could not read '.$this->path."\n\n".$e->getMessage()); } diff --git a/src/Composer/Package/Version/VersionParser.php b/src/Composer/Package/Version/VersionParser.php index ce23854cb..a12cf41f4 100644 --- a/src/Composer/Package/Version/VersionParser.php +++ b/src/Composer/Package/Version/VersionParser.php @@ -189,7 +189,7 @@ class VersionParser } else { $parsedConstraint = $this->parseConstraints($constraint); } - $res[] = new Link($source, $target, $parsedConstraint, $description, $constraint); + $res[strtolower($target)] = new Link($source, $target, $parsedConstraint, $description, $constraint); } return $res; diff --git a/src/Composer/Repository/ComposerRepository.php b/src/Composer/Repository/ComposerRepository.php index 49f0bf7a2..03d18c3ca 100644 --- a/src/Composer/Repository/ComposerRepository.php +++ b/src/Composer/Repository/ComposerRepository.php @@ -14,12 +14,15 @@ namespace Composer\Repository; use Composer\Package\Loader\ArrayLoader; use Composer\Package\PackageInterface; +use Composer\Package\AliasPackage; use Composer\Package\Version\VersionParser; +use Composer\DependencyResolver\Pool; use Composer\Json\JsonFile; use Composer\Cache; use Composer\Config; use Composer\IO\IOInterface; use Composer\Util\RemoteFilesystem; +use Composer\Downloader\TransportException; /** * @author Jordi Boggiano @@ -29,13 +32,19 @@ class ComposerRepository extends ArrayRepository implements NotifiableRepository protected $config; protected $options; protected $url; + protected $baseUrl; protected $io; protected $cache; protected $notifyUrl; + protected $hasProviders = false; + protected $providerListing; + protected $providers = array(); + protected $providersByUid = array(); protected $loader; private $rawData; private $minimalPackages; private $degradedMode = false; + private $rootData; public function __construct(array $repoConfig, IOInterface $io, Config $config) { @@ -60,6 +69,7 @@ class ComposerRepository extends ArrayRepository implements NotifiableRepository $this->config = $config; $this->options = $repoConfig['options']; $this->url = $repoConfig['url']; + $this->baseUrl = rtrim(preg_replace('{^(.*)(?:/packages.json)?(?:[?#].*)?$}', '$1', $this->url), '/'); $this->io = $io; $this->cache = new Cache($io, $config->get('home').'/cache/'.preg_replace('{[^a-z0-9.]}i', '-', $this->url)); $this->loader = new ArrayLoader(); @@ -182,6 +192,90 @@ class ComposerRepository extends ArrayRepository implements NotifiableRepository return $aliasPackage; } + public function hasProviders() + { + $this->loadRootServerFile(); + + return $this->hasProviders; + } + + public function resetPackageIds() + { + foreach ($this->providersByUid as $package) { + $package->setId(-1); + } + } + + public function whatProvides(Pool $pool, $name) + { + // skip platform packages + if ($name === 'php' || in_array(substr($name, 0, 4), array('ext-', 'lib-'), true) || $name === '__root__') { + return array(); + } + + if (isset($this->providers[$name])) { + return $this->providers[$name]; + } + + if (null === $this->providerListing) { + $this->loadProviderListings($this->loadRootServerFile()); + } + + $url = 'p/'.$name.'.json'; + + // package does not exist in this repo + if (!isset($this->providerListing[$url])) { + return array(); + } + + if ($this->cache->sha256($url) === $this->providerListing[$url]['sha256']) { + $packages = json_decode($this->cache->read($url), true); + } else { + $packages = $this->fetchFile($url, null, $this->providerListing[$url]['sha256']); + } + + $this->providers[$name] = array(); + foreach ($packages['packages'] as $versions) { + foreach ($versions as $version) { + // avoid loading the same objects twice + if (isset($this->providersByUid[$version['uid']])) { + // skip if already assigned + if (!isset($this->providers[$name][$version['uid']])) { + // expand alias in two packages + if ($this->providersByUid[$version['uid']] instanceof AliasPackage) { + $this->providers[$name][$version['uid']] = $this->providersByUid[$version['uid']]->getAliasOf(); + $this->providers[$name][$version['uid'].'-alias'] = $this->providersByUid[$version['uid']]; + } else { + $this->providers[$name][$version['uid']] = $this->providersByUid[$version['uid']]; + } + } + } else { + if (!$pool->isPackageAcceptable($version['name'], VersionParser::parseStability($version['version']))) { + continue; + } + + // load acceptable packages in the providers + $package = $this->createPackage($version, 'Composer\Package\Package'); + $package->setRepository($this); + + $this->providers[$name][$version['uid']] = $package; + $this->providersByUid[$version['uid']] = $package; + + if ($package->getAlias()) { + $alias = $this->createAliasPackage($package); + $alias->setRepository($this); + + $this->providers[$name][$version['uid'].'-alias'] = $alias; + // override provider with its alias so it can be expanded in the if block above + $this->providersByUid[$version['uid']] = $alias; + } + } + } + } + + return $this->providers[$name]; + } + /** * {@inheritDoc} */ @@ -196,8 +290,12 @@ class ComposerRepository extends ArrayRepository implements NotifiableRepository } } - protected function loadDataFromServer() + protected function loadRootServerFile() { + if (null !== $this->rootData) { + return $this->rootData; + } + if (!extension_loaded('openssl') && 'https' === substr($this->url, 0, 5)) { throw new \RuntimeException('You must enable the openssl extension in your php.ini to load information from '.$this->url); } @@ -220,9 +318,42 @@ class ComposerRepository extends ArrayRepository implements NotifiableRepository } } + if (!empty($data['providers']) || !empty($data['providers-includes'])) { + $this->hasProviders = true; + } + + return $this->rootData = $data; + } + + protected function loadDataFromServer() + { + $data = $this->loadRootServerFile(); + return $this->loadIncludes($data); } + protected function loadProviderListings($data) + { + if (isset($data['providers'])) { + if (!is_array($this->providerListing)) { + $this->providerListing = array(); + } + $this->providerListing = array_merge($this->providerListing, $data['providers']); + } + + if (isset($data['providers-includes'])) { + foreach ($data['providers-includes'] as $include => $metadata) { + if ($this->cache->sha256($include) === $metadata['sha256']) { + $includedData = json_decode($this->cache->read($include), true); + } else { + $includedData = $this->fetchFile($include, null, $metadata['sha256']); + } + + $this->loadProviderListings($includedData); + } + } + } + protected function loadIncludes($data) { $packages = array(); @@ -269,11 +400,11 @@ class ComposerRepository extends ArrayRepository implements NotifiableRepository } } - protected function fetchFile($filename, $cacheKey = null) + protected function fetchFile($filename, $cacheKey = null, $sha256 = null) { if (!$cacheKey) { $cacheKey = $filename; - $filename = $this->url.'/'.$filename; + $filename = $this->baseUrl.'/'.$filename; } $retries = 3; @@ -281,7 +412,11 @@ class ComposerRepository extends ArrayRepository implements NotifiableRepository try { $json = new JsonFile($filename, new RemoteFilesystem($this->io, $this->options)); $data = $json->read(); - $this->cache->write($cacheKey, json_encode($data)); + $encoded = json_encode($data); + if ($sha256 && $sha256 !== hash('sha256', $encoded)) { + throw new \UnexpectedValueException('The contents of '.$filename.' do not match its signature, this may be due to a temporary glitch or a man-in-the-middle attack, aborting for safety. Please try running Composer again.'); + } + $this->cache->write($cacheKey, $encoded); break; } catch (\Exception $e) { diff --git a/tests/Composer/Test/DependencyResolver/PoolTest.php b/tests/Composer/Test/DependencyResolver/PoolTest.php index 7c6618582..aa38fa31d 100644 --- a/tests/Composer/Test/DependencyResolver/PoolTest.php +++ b/tests/Composer/Test/DependencyResolver/PoolTest.php @@ -129,20 +129,4 @@ class PoolTest extends TestCase $this->assertEquals(array(), $pool->whatProvides('foo')); } - - public function testGetMaxId() - { - $pool = new Pool; - $repository = new ArrayRepository; - $firstPackage = $this->getPackage('foo', '1'); - $secondPackage = $this->getPackage('foo1', '1'); - - $this->assertEquals(0, $pool->getMaxId()); - - $repository->addPackage($firstPackage); - $repository->addPackage($secondPackage); - $pool->addRepository($repository); - - $this->assertEquals(2, $pool->getMaxId()); - } } diff --git a/tests/Composer/Test/DependencyResolver/SolverTest.php b/tests/Composer/Test/DependencyResolver/SolverTest.php index e43f98c6e..9ca29bcd2 100644 --- a/tests/Composer/Test/DependencyResolver/SolverTest.php +++ b/tests/Composer/Test/DependencyResolver/SolverTest.php @@ -19,6 +19,7 @@ use Composer\DependencyResolver\Solver; use Composer\DependencyResolver\SolverProblemsException; use Composer\Package\Link; use Composer\Test\TestCase; +use Composer\Package\LinkConstraint\MultiConstraint; class SolverTest extends TestCase { @@ -103,7 +104,7 @@ class SolverTest extends TestCase $this->repo->addPackage($packageB = $this->getPackage('B', '1.0')); $this->repo->addPackage($newPackageB = $this->getPackage('B', '1.1')); - $packageA->setRequires(array(new Link('A', 'B', $this->getVersionConstraint('<', '1.1'), 'requires'))); + $packageA->setRequires(array('b' => new Link('A', 'B', $this->getVersionConstraint('<', '1.1'), 'requires'))); $this->reposComplete(); @@ -124,9 +125,11 @@ class SolverTest extends TestCase $this->repo->addPackage($newPackageB13 = $this->getPackage('B', '1.3')); $packageA->setRequires(array( - new Link('A', 'B', $this->getVersionConstraint('<=', '1.3'), 'requires'), - new Link('A', 'B', $this->getVersionConstraint('<>', '1.3'), 'requires'), - new Link('A', 'B', $this->getVersionConstraint('!=', '1.2'), 'requires'), + 'b' => new Link('A', 'B', new MultiConstraint(array( + $this->getVersionConstraint('<=', '1.3'), + $this->getVersionConstraint('<>', '1.3'), + $this->getVersionConstraint('!=', '1.2'), + )), 'requires'), )); $this->reposComplete(); @@ -146,11 +149,11 @@ class SolverTest extends TestCase $this->repo->addPackage($packageC = $this->getPackage('C', '1.0')); $packageB->setRequires(array( - new Link('B', 'A', $this->getVersionConstraint('>=', '1.0'), 'requires'), - new Link('B', 'C', $this->getVersionConstraint('>=', '1.0'), 'requires'), + 'a' => new Link('B', 'A', $this->getVersionConstraint('>=', '1.0'), 'requires'), + 'c' => new Link('B', 'C', $this->getVersionConstraint('>=', '1.0'), 'requires'), )); $packageC->setRequires(array( - new Link('C', 'A', $this->getVersionConstraint('>=', '1.0'), 'requires'), + 'a' => new Link('C', 'A', $this->getVersionConstraint('>=', '1.0'), 'requires'), )); $this->reposComplete(); @@ -216,7 +219,7 @@ class SolverTest extends TestCase $this->repo->addPackage($newPackageB = $this->getPackage('B', '1.1')); $this->reposComplete(); - $packageA->setRequires(array(new Link('A', 'B', $this->getVersionConstraint('>=', '1.0.0.0'), 'requires'))); + $packageA->setRequires(array('b' => new Link('A', 'B', $this->getVersionConstraint('>=', '1.0.0.0'), 'requires'))); $this->request->install('A', $this->getVersionConstraint('=', '1.0.0.0')); $this->request->install('B', $this->getVersionConstraint('=', '1.1.0.0')); @@ -249,8 +252,8 @@ class SolverTest extends TestCase $this->repo->addPackage($newPackageA = $this->getPackage('A', '1.1')); $this->repo->addPackage($newPackageB = $this->getPackage('B', '1.1')); - $packageA->setRequires(array(new Link('A', 'B', null, 'requires'))); - $newPackageA->setRequires(array(new Link('A', 'B', null, 'requires'))); + $packageA->setRequires(array('b' => new Link('A', 'B', null, 'requires'))); + $newPackageA->setRequires(array('b' => new Link('A', 'B', null, 'requires'))); $this->reposComplete(); @@ -361,7 +364,7 @@ class SolverTest extends TestCase $this->repo->addPackage($newPackageB = $this->getPackage('B', '1.1')); $this->repo->addPackage($packageC = $this->getPackage('C', '1.1')); $this->repo->addPackage($this->getPackage('D', '1.0')); - $packageA->setRequires(array(new Link('A', 'B', $this->getVersionConstraint('<', '1.1'), 'requires'))); + $packageA->setRequires(array('b' => new Link('A', 'B', $this->getVersionConstraint('<', '1.1'), 'requires'))); $this->reposComplete(); @@ -384,8 +387,8 @@ class SolverTest extends TestCase $this->repo->addPackage($middlePackageB = $this->getPackage('B', '1.0')); $this->repo->addPackage($newPackageB = $this->getPackage('B', '1.1')); $this->repo->addPackage($oldPackageB = $this->getPackage('B', '0.9')); - $packageA->setRequires(array(new Link('A', 'B', $this->getVersionConstraint('<', '1.1'), 'requires'))); - $packageA->setConflicts(array(new Link('A', 'B', $this->getVersionConstraint('<', '1.0'), 'conflicts'))); + $packageA->setRequires(array('b' => new Link('A', 'B', $this->getVersionConstraint('<', '1.1'), 'requires'))); + $packageA->setConflicts(array('b' => new Link('A', 'B', $this->getVersionConstraint('<', '1.0'), 'conflicts'))); $this->reposComplete(); @@ -401,7 +404,7 @@ class SolverTest extends TestCase { $this->repoInstalled->addPackage($packageA = $this->getPackage('A', '1.0')); $this->repo->addPackage($packageB = $this->getPackage('B', '1.0')); - $packageB->setReplaces(array(new Link('B', 'A', null))); + $packageB->setReplaces(array('a' => new Link('B', 'A', null))); $this->reposComplete(); @@ -430,8 +433,8 @@ class SolverTest extends TestCase { $this->repo->addPackage($packageA = $this->getPackage('A', '1.0')); $this->repo->addPackage($packageQ = $this->getPackage('Q', '1.0')); - $packageA->setRequires(array(new Link('A', 'B', $this->getVersionConstraint('>=', '1.0'), 'requires'))); - $packageQ->setProvides(array(new Link('Q', 'B', $this->getVersionConstraint('=', '1.0'), 'provides'))); + $packageA->setRequires(array('b' => new Link('A', 'B', $this->getVersionConstraint('>=', '1.0'), 'requires'))); + $packageQ->setProvides(array('b' => new Link('Q', 'B', $this->getVersionConstraint('=', '1.0'), 'provides'))); $this->reposComplete(); @@ -448,8 +451,8 @@ class SolverTest extends TestCase $this->repo->addPackage($packageA = $this->getPackage('A', '1.0')); $this->repo->addPackage($packageQ = $this->getPackage('Q', '1.0')); $this->repo->addPackage($packageB = $this->getPackage('B', '1.0')); - $packageA->setRequires(array(new Link('A', 'B', $this->getVersionConstraint('>=', '1.0'), 'requires'))); - $packageQ->setReplaces(array(new Link('Q', 'B', $this->getVersionConstraint('>=', '1.0'), 'replaces'))); + $packageA->setRequires(array('b' => new Link('A', 'B', $this->getVersionConstraint('>=', '1.0'), 'requires'))); + $packageQ->setReplaces(array('b' => new Link('Q', 'B', $this->getVersionConstraint('>=', '1.0'), 'replaces'))); $this->reposComplete(); @@ -465,8 +468,8 @@ class SolverTest extends TestCase { $this->repo->addPackage($packageA = $this->getPackage('A', '1.0')); $this->repo->addPackage($packageQ = $this->getPackage('Q', '1.0')); - $packageA->setRequires(array(new Link('A', 'B', $this->getVersionConstraint('>=', '1.0'), 'requires'))); - $packageQ->setReplaces(array(new Link('Q', 'B', $this->getVersionConstraint('>=', '1.0'), 'replaces'))); + $packageA->setRequires(array('b' => new Link('A', 'B', $this->getVersionConstraint('>=', '1.0'), 'requires'))); + $packageQ->setReplaces(array('b' => new Link('Q', 'B', $this->getVersionConstraint('>=', '1.0'), 'replaces'))); $this->reposComplete(); @@ -483,8 +486,8 @@ class SolverTest extends TestCase $this->repo->addPackage($packageA = $this->getPackage('A', '1.0')); $this->repo->addPackage($packageQ = $this->getPackage('Q', '1.0')); $this->repo->addPackage($packageB = $this->getPackage('B', '1.0')); - $packageA->setRequires(array(new Link('A', 'B', $this->getVersionConstraint('>=', '1.0'), 'requires'))); - $packageQ->setReplaces(array(new Link('Q', 'B', $this->getVersionConstraint('>=', '1.0'), 'replaces'))); + $packageA->setRequires(array('b' => new Link('A', 'B', $this->getVersionConstraint('>=', '1.0'), 'requires'))); + $packageQ->setReplaces(array('b' => new Link('Q', 'B', $this->getVersionConstraint('>=', '1.0'), 'replaces'))); $this->reposComplete(); @@ -501,24 +504,28 @@ class SolverTest extends TestCase { $this->repo->addPackage($packageX = $this->getPackage('X', '1.0')); $packageX->setRequires(array( - new Link('X', 'A', $this->getVersionConstraint('>=', '2.0.0.0'), 'requires'), - new Link('X', 'B', $this->getVersionConstraint('>=', '2.0.0.0'), 'requires'))); + 'a' => new Link('X', 'A', $this->getVersionConstraint('>=', '2.0.0.0'), 'requires'), + 'b' => new Link('X', 'B', $this->getVersionConstraint('>=', '2.0.0.0'), 'requires') + )); $this->repo->addPackage($packageA = $this->getPackage('A', '2.0.0')); $this->repo->addPackage($newPackageA = $this->getPackage('A', '2.1.0')); $this->repo->addPackage($newPackageB = $this->getPackage('B', '2.1.0')); - $packageA->setRequires(array(new Link('A', 'B', $this->getVersionConstraint('>=', '2.0.0.0'), 'requires'))); + $packageA->setRequires(array('b' => new Link('A', 'B', $this->getVersionConstraint('>=', '2.0.0.0'), 'requires'))); // new package A depends on version of package B that does not exist // => new package A is not installable - $newPackageA->setRequires(array(new Link('A', 'B', $this->getVersionConstraint('>=', '2.2.0.0'), 'requires'))); + $newPackageA->setRequires(array('b' => new Link('A', 'B', $this->getVersionConstraint('>=', '2.2.0.0'), 'requires'))); // add a package S replacing both A and B, so that S and B or S and A cannot be simultaneously installed // but an alternative option for A and B both exists // this creates a more difficult so solve conflict $this->repo->addPackage($packageS = $this->getPackage('S', '2.0.0')); - $packageS->setReplaces(array(new Link('S', 'A', $this->getVersionConstraint('>=', '2.0.0.0'), 'replaces'), new Link('S', 'B', $this->getVersionConstraint('>=', '2.0.0.0'), 'replaces'))); + $packageS->setReplaces(array( + 'a' => new Link('S', 'A', $this->getVersionConstraint('>=', '2.0.0.0'), 'replaces'), + 'b' => new Link('S', 'B', $this->getVersionConstraint('>=', '2.0.0.0'), 'replaces') + )); $this->reposComplete(); @@ -536,8 +543,8 @@ class SolverTest extends TestCase $this->repo->addPackage($packageA = $this->getPackage('A', '1.0')); $this->repo->addPackage($packageB1 = $this->getPackage('B', '0.9')); $this->repo->addPackage($packageB2 = $this->getPackage('B', '1.1')); - $packageA->setRequires(array(new Link('A', 'B', $this->getVersionConstraint('>=', '1.0'), 'requires'))); - $packageB2->setRequires(array(new Link('B', 'A', $this->getVersionConstraint('>=', '1.0'), 'requires'))); + $packageA->setRequires(array('b' => new Link('A', 'B', $this->getVersionConstraint('>=', '1.0'), 'requires'))); + $packageB2->setRequires(array('a' => new Link('B', 'A', $this->getVersionConstraint('>=', '1.0'), 'requires'))); $this->reposComplete(); @@ -555,13 +562,13 @@ class SolverTest extends TestCase $this->repo->addPackage($packageB = $this->getPackage('B', '1.0')); $this->repo->addPackage($packageC = $this->getPackage('C', '1.0')); $this->repo->addPackage($packageD = $this->getPackage('D', '1.0')); - $packageA->setRequires(array(new Link('A', 'B', $this->getVersionConstraint('>=', '1.0'), 'requires'))); - $packageB->setRequires(array(new Link('B', 'Virtual', $this->getVersionConstraint('>=', '1.0'), 'requires'))); - $packageC->setProvides(array(new Link('C', 'Virtual', $this->getVersionConstraint('==', '1.0'), 'provides'))); - $packageD->setProvides(array(new Link('D', 'Virtual', $this->getVersionConstraint('==', '1.0'), 'provides'))); + $packageA->setRequires(array('b' => new Link('A', 'B', $this->getVersionConstraint('>=', '1.0'), 'requires'))); + $packageB->setRequires(array('virtual' => new Link('B', 'Virtual', $this->getVersionConstraint('>=', '1.0'), 'requires'))); + $packageC->setProvides(array('virtual' => new Link('C', 'Virtual', $this->getVersionConstraint('==', '1.0'), 'provides'))); + $packageD->setProvides(array('virtual' => new Link('D', 'Virtual', $this->getVersionConstraint('==', '1.0'), 'provides'))); - $packageC->setRequires(array(new Link('C', 'A', $this->getVersionConstraint('==', '1.0'), 'requires'))); - $packageD->setRequires(array(new Link('D', 'A', $this->getVersionConstraint('==', '1.0'), 'requires'))); + $packageC->setRequires(array('a' => new Link('C', 'A', $this->getVersionConstraint('==', '1.0'), 'requires'))); + $packageD->setRequires(array('a' => new Link('D', 'A', $this->getVersionConstraint('==', '1.0'), 'requires'))); $this->reposComplete(); @@ -586,18 +593,18 @@ class SolverTest extends TestCase $this->repo->addPackage($packageD2 = $this->getPackage('D', '1.1')); $packageA->setRequires(array( - new Link('A', 'B', $this->getVersionConstraint('>=', '1.0'), 'requires'), - new Link('A', 'C', $this->getVersionConstraint('>=', '1.0'), 'requires'), + 'b' => new Link('A', 'B', $this->getVersionConstraint('>=', '1.0'), 'requires'), + 'c' => new Link('A', 'C', $this->getVersionConstraint('>=', '1.0'), 'requires'), )); $packageD->setReplaces(array( - new Link('D', 'B', $this->getVersionConstraint('>=', '1.0'), 'replaces'), - new Link('D', 'C', $this->getVersionConstraint('>=', '1.0'), 'replaces'), + 'b' => new Link('D', 'B', $this->getVersionConstraint('>=', '1.0'), 'replaces'), + 'c' => new Link('D', 'C', $this->getVersionConstraint('>=', '1.0'), 'replaces'), )); $packageD2->setReplaces(array( - new Link('D', 'B', $this->getVersionConstraint('>=', '1.0'), 'replaces'), - new Link('D', 'C', $this->getVersionConstraint('>=', '1.0'), 'replaces'), + 'b' => new Link('D', 'B', $this->getVersionConstraint('>=', '1.0'), 'replaces'), + 'c' => new Link('D', 'C', $this->getVersionConstraint('>=', '1.0'), 'replaces'), )); $this->reposComplete(); @@ -621,19 +628,19 @@ class SolverTest extends TestCase $this->repo->addPackage($packageD = $this->getPackage('D', '2.0.9')); $packageC->setRequires(array( - new Link('C', 'A', $this->getVersionConstraint('>=', '2.0'), 'requires'), - new Link('C', 'D', $this->getVersionConstraint('>=', '2.0'), 'requires'), + 'a' => new Link('C', 'A', $this->getVersionConstraint('>=', '2.0'), 'requires'), + 'd' => new Link('C', 'D', $this->getVersionConstraint('>=', '2.0'), 'requires'), )); $packageD->setRequires(array( - new Link('D', 'A', $this->getVersionConstraint('>=', '2.1'), 'requires'), - new Link('D', 'B', $this->getVersionConstraint('>=', '2.0-dev'), 'requires'), + 'a' => new Link('D', 'A', $this->getVersionConstraint('>=', '2.1'), 'requires'), + 'b' => new Link('D', 'B', $this->getVersionConstraint('>=', '2.0-dev'), 'requires'), )); - $packageB1->setRequires(array(new Link('B', 'A', $this->getVersionConstraint('==', '2.1.0.0-dev'), 'requires'))); - $packageB2->setRequires(array(new Link('B', 'A', $this->getVersionConstraint('==', '2.1.0.0-dev'), 'requires'))); + $packageB1->setRequires(array('a' => new Link('B', 'A', $this->getVersionConstraint('==', '2.1.0.0-dev'), 'requires'))); + $packageB2->setRequires(array('a' => new Link('B', 'A', $this->getVersionConstraint('==', '2.1.0.0-dev'), 'requires'))); - $packageB2->setReplaces(array(new Link('B', 'D', $this->getVersionConstraint('==', '2.0.9.0'), 'replaces'))); + $packageB2->setReplaces(array('d' => new Link('B', 'D', $this->getVersionConstraint('==', '2.0.9.0'), 'replaces'))); $this->reposComplete(); @@ -650,7 +657,7 @@ class SolverTest extends TestCase $this->repo->addPackage($packageB = $this->getPackage('B', '1.0'));; $packageA->setConflicts(array( - new Link('A', 'B', $this->getVersionConstraint('>=', '1.0'), 'conflicts'), + 'b' => new Link('A', 'B', $this->getVersionConstraint('>=', '1.0'), 'conflicts'), )); $this->reposComplete(); @@ -680,7 +687,7 @@ class SolverTest extends TestCase $this->repo->addPackage($packageB = $this->getPackage('B', '1.0')); $packageA->setRequires(array( - new Link('A', 'B', $this->getVersionConstraint('>=', '2.0'), 'requires'), + 'b' => new Link('A', 'B', $this->getVersionConstraint('>=', '2.0'), 'requires'), )); $this->reposComplete(); @@ -717,16 +724,16 @@ class SolverTest extends TestCase $this->repo->addPackage($packageD = $this->getPackage('D', '1.0')); $packageA->setRequires(array( - new Link('A', 'B', $this->getVersionConstraint('>=', '1.0'), 'requires'), + 'b' => new Link('A', 'B', $this->getVersionConstraint('>=', '1.0'), 'requires'), )); $packageB->setRequires(array( - new Link('B', 'C', $this->getVersionConstraint('>=', '1.0'), 'requires'), + 'c' => new Link('B', 'C', $this->getVersionConstraint('>=', '1.0'), 'requires'), )); $packageC->setRequires(array( - new Link('C', 'D', $this->getVersionConstraint('>=', '1.0'), 'requires'), + 'd' => new Link('C', 'D', $this->getVersionConstraint('>=', '1.0'), 'requires'), )); $packageD->setRequires(array( - new Link('D', 'B', $this->getVersionConstraint('<', '1.0'), 'requires'), + 'b' => new Link('D', 'B', $this->getVersionConstraint('<', '1.0'), 'requires'), )); $this->reposComplete(); @@ -761,11 +768,11 @@ class SolverTest extends TestCase $this->repo->addPackage($packageTwigBridge = $this->getPackage('symfony/twig-bridge', '2.0')); $packageTwigBridge->setRequires(array( - new Link('symfony/twig-bridge', 'twig/twig', $this->getVersionConstraint('<', '2.0'), 'requires'), + 'twig/twig' => new Link('symfony/twig-bridge', 'twig/twig', $this->getVersionConstraint('<', '2.0'), 'requires'), )); $packageSymfony->setReplaces(array( - new Link('symfony/symfony', 'symfony/twig-bridge', $this->getVersionConstraint('==', '2.0'), 'replaces'), + 'symfony/twig-bridge' => new Link('symfony/symfony', 'symfony/twig-bridge', $this->getVersionConstraint('==', '2.0'), 'replaces'), )); $this->reposComplete(); @@ -786,10 +793,10 @@ class SolverTest extends TestCase $this->repo->addPackage($packageA2 = $this->getPackage('A', '2.0')); $packageA2->setRequires(array( - new Link('A', 'B', $this->getVersionConstraint('==', '2.0'), 'requires', '== 2.0'), + 'b' => new Link('A', 'B', $this->getVersionConstraint('==', '2.0'), 'requires', '== 2.0'), )); $packageB->setRequires(array( - new Link('B', 'A', $this->getVersionConstraint('>=', '2.0'), 'requires'), + 'a' => new Link('B', 'A', $this->getVersionConstraint('>=', '2.0'), 'requires'), )); $this->repo->addPackage($packageA2Alias = $this->getAliasPackage($packageA2, '1.1')); @@ -811,7 +818,7 @@ class SolverTest extends TestCase $this->repo->addPackage($packageB = $this->getPackage('B', '1.0')); $packageB->setRequires(array( - new Link('B', 'A', $this->getVersionConstraint('<', '2.0'), 'requires'), + 'a' => new Link('B', 'A', $this->getVersionConstraint('<', '2.0'), 'requires'), )); $this->repo->addPackage($packageAAlias = $this->getAliasPackage($packageA, '1.1')); diff --git a/tests/Composer/Test/Package/Loader/ArrayLoaderTest.php b/tests/Composer/Test/Package/Loader/ArrayLoaderTest.php index 508936d2d..ef1295ff8 100644 --- a/tests/Composer/Test/Package/Loader/ArrayLoaderTest.php +++ b/tests/Composer/Test/Package/Loader/ArrayLoaderTest.php @@ -34,7 +34,7 @@ class ArrayLoaderTest extends \PHPUnit_Framework_TestCase $package = $this->loader->load($config); $replaces = $package->getReplaces(); - $this->assertEquals('== 1.2.3.4', (string) $replaces[0]->getConstraint()); + $this->assertEquals('== 1.2.3.4', (string) $replaces['foo']->getConstraint()); } public function testTypeDefault()