diff --git a/src/Composer/DependencyResolver/Pool.php b/src/Composer/DependencyResolver/Pool.php index 5ee39d9a9..57e1b245c 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,13 @@ 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 $id = 1; public function __construct($minimumStability = 'stable', array $stabilityFlags = array()) { @@ -72,18 +75,20 @@ 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; + } 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 +107,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 +124,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 +140,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 +154,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 +168,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; @@ -208,7 +213,7 @@ class Pool */ public function getMaxId() { - return count($this->packages); + return $this->id - 1; } /** @@ -221,11 +226,25 @@ class Pool */ public function whatProvides($name, LinkConstraintInterface $constraint = null) { - if (!isset($this->packageByName[$name])) { + $candidates = array(); + + foreach ($this->composerRepos as $repo) { + foreach ($repo->whatProvides($name) as $candidate) { + $candidates[] = $candidate; + if ($candidate->getId() < 1) { + $candidate->setId($this->id++); + $this->packages[$candidate->getId()] = $candidate; + } + } + } + + if (!isset($this->packageByName[$name]) && !$candidates) { return array(); } - $candidates = $this->packageByName[$name]; + if (isset($this->packageByName[$name])) { + $candidates = array_merge($candidates, $this->packageByName[$name]); + } if (null === $constraint) { foreach ($candidates as $key => $candidate) { 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/Repository/ComposerRepository.php b/src/Composer/Repository/ComposerRepository.php index 49f0bf7a2..6fe23e9ae 100644 --- a/src/Composer/Repository/ComposerRepository.php +++ b/src/Composer/Repository/ComposerRepository.php @@ -20,6 +20,7 @@ use Composer\Cache; use Composer\Config; use Composer\IO\IOInterface; use Composer\Util\RemoteFilesystem; +use Composer\Downloader\TransportException; /** * @author Jordi Boggiano @@ -32,10 +33,14 @@ class ComposerRepository extends ArrayRepository implements NotifiableRepository protected $io; protected $cache; protected $notifyUrl; + protected $providersUrl; + 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) { @@ -182,6 +187,55 @@ class ComposerRepository extends ArrayRepository implements NotifiableRepository return $aliasPackage; } + public function hasProviders() + { + $this->loadRootServerFile(); + + return null !== $this->providersUrl; + } + + public function whatProvides($name) + { + if (isset($this->providers[$name])) { + return $this->providers[$name]; + } + + $url = str_replace('%package%', strtolower($name), $this->providersUrl); + + try { + $json = new JsonFile($url, new RemoteFilesystem($this->io)); + $packages = $json->read(); + } catch (\RuntimeException $e) { + if (!$e->getPrevious() instanceof TransportException || $e->getPrevious()->getCode() !== 404) { + throw $e; + } + } + + $this->providers[$name] = array(); + foreach ($packages['packages'] as $name => $versions) { + foreach ($versions as $version) { + if (isset($this->providersByUid[$version['uid']])) { + $this->providers[$name][] = $this->providersByUid[$version['uid']]; + } else { + $package = $this->createPackage($version, 'Composer\Package\Package'); + $package->setRepository($this); + + $this->providers[$name][] = $package; + $this->providersByUid[$version['uid']] = $package; + + if ($package->getAlias()) { + $alias = $this->createAliasPackage($package); + + $this->providers[$name][] = $alias; + $this->providersByUid[$version['uid']] = $alias; + } + } + } + } + + return $this->providers[$name]; + } + /** * {@inheritDoc} */ @@ -196,8 +250,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,6 +278,21 @@ class ComposerRepository extends ArrayRepository implements NotifiableRepository } } + if (!empty($data['providers'])) { + if ('/' === $data['providers'][0]) { + $this->providersUrl = preg_replace('{(https?://[^/]+).*}i', '$1' . $data['providers'], $this->url); + } else { + $this->providersUrl = $data['providers']; + } + } + + return $this->rootData = $data; + } + + protected function loadDataFromServer() + { + $data = $this->loadRootServerFile(); + return $this->loadIncludes($data); }