Deduplicate link instances between versions of a given package
parent
e753bf08b1
commit
e8c6948770
|
@ -38,6 +38,70 @@ class ArrayLoader implements LoaderInterface
|
|||
}
|
||||
|
||||
public function load(array $config, $class = 'Composer\Package\CompletePackage')
|
||||
{
|
||||
$package = $this->createObject($config, $class);
|
||||
|
||||
foreach (Package\BasePackage::$supportedLinkTypes as $type => $opts) {
|
||||
if (isset($config[$type])) {
|
||||
$method = 'set'.ucfirst($opts['method']);
|
||||
$package->{$method}(
|
||||
$this->parseLinks(
|
||||
$package->getName(),
|
||||
$package->getPrettyVersion(),
|
||||
$opts['description'],
|
||||
$config[$type]
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
$package = $this->configureObject($package, $config);
|
||||
|
||||
return $package;
|
||||
}
|
||||
|
||||
public function loadPackages(array $versions, $class)
|
||||
{
|
||||
static $uniqKeys = array('version', 'version_normalized', 'source', 'dist', 'time');
|
||||
|
||||
$packages = array();
|
||||
$linkCache = array();
|
||||
|
||||
foreach ($versions as $version) {
|
||||
if (isset($version['versions'])) {
|
||||
$baseVersion = $version;
|
||||
foreach ($uniqKeys as $key) {
|
||||
unset($baseVersion[$key.'s']);
|
||||
}
|
||||
|
||||
foreach ($version['versions'] as $index => $dummy) {
|
||||
$unpackedVersion = $baseVersion;
|
||||
foreach ($uniqKeys as $key) {
|
||||
$unpackedVersion[$key] = $version[$key.'s'][$index];
|
||||
}
|
||||
|
||||
$package = $this->createObject($unpackedVersion, $class);
|
||||
|
||||
$this->configureCachedLinks($linkCache, $package, $unpackedVersion);
|
||||
$package = $this->configureObject($package, $unpackedVersion);
|
||||
|
||||
$packages[] = $package;
|
||||
}
|
||||
} else {
|
||||
$package = $this->createObject($version, $class);
|
||||
|
||||
$this->configureCachedLinks($linkCache, $package, $version);
|
||||
$package = $this->configureObject($package, $version);
|
||||
|
||||
$packages[] = $package;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return $packages;
|
||||
}
|
||||
|
||||
private function createObject(array $config, $class)
|
||||
{
|
||||
if (!isset($config['name'])) {
|
||||
throw new \UnexpectedValueException('Unknown package has no name defined ('.json_encode($config).').');
|
||||
|
@ -52,7 +116,12 @@ class ArrayLoader implements LoaderInterface
|
|||
} else {
|
||||
$version = $this->versionParser->normalize($config['version']);
|
||||
}
|
||||
$package = new $class($config['name'], $version, $config['version']);
|
||||
|
||||
return new $class($config['name'], $version, $config['version']);
|
||||
}
|
||||
|
||||
private function configureObject($package, array $config)
|
||||
{
|
||||
$package->setType(isset($config['type']) ? strtolower($config['type']) : 'library');
|
||||
|
||||
if (isset($config['target-dir'])) {
|
||||
|
@ -109,20 +178,6 @@ class ArrayLoader implements LoaderInterface
|
|||
}
|
||||
}
|
||||
|
||||
foreach (Package\BasePackage::$supportedLinkTypes as $type => $opts) {
|
||||
if (isset($config[$type])) {
|
||||
$method = 'set'.ucfirst($opts['method']);
|
||||
$package->{$method}(
|
||||
$this->parseLinks(
|
||||
$package->getName(),
|
||||
$package->getPrettyVersion(),
|
||||
$opts['description'],
|
||||
$config[$type]
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if (isset($config['suggest']) && is_array($config['suggest'])) {
|
||||
foreach ($config['suggest'] as $target => $reason) {
|
||||
if ('self.version' === trim($reason)) {
|
||||
|
@ -202,21 +257,50 @@ class ArrayLoader implements LoaderInterface
|
|||
}
|
||||
}
|
||||
|
||||
if ($aliasNormalized = $this->getBranchAlias($config)) {
|
||||
if ($package instanceof RootPackageInterface) {
|
||||
$package = new RootAliasPackage($package, $aliasNormalized, preg_replace('{(\.9{7})+}', '.x', $aliasNormalized));
|
||||
} else {
|
||||
$package = new AliasPackage($package, $aliasNormalized, preg_replace('{(\.9{7})+}', '.x', $aliasNormalized));
|
||||
}
|
||||
}
|
||||
|
||||
if ($this->loadOptions && isset($config['transport-options'])) {
|
||||
$package->setTransportOptions($config['transport-options']);
|
||||
}
|
||||
|
||||
if ($aliasNormalized = $this->getBranchAlias($config)) {
|
||||
if ($package instanceof RootPackageInterface) {
|
||||
return new RootAliasPackage($package, $aliasNormalized, preg_replace('{(\.9{7})+}', '.x', $aliasNormalized));
|
||||
}
|
||||
|
||||
return new AliasPackage($package, $aliasNormalized, preg_replace('{(\.9{7})+}', '.x', $aliasNormalized));
|
||||
}
|
||||
|
||||
return $package;
|
||||
}
|
||||
|
||||
private function configureCachedLinks(&$linkCache, $package, array $config)
|
||||
{
|
||||
$name = $package->getName();
|
||||
$prettyVersion = $package->getPrettyVersion();
|
||||
|
||||
foreach (Package\BasePackage::$supportedLinkTypes as $type => $opts) {
|
||||
if (isset($config[$type])) {
|
||||
$method = 'set'.ucfirst($opts['method']);
|
||||
|
||||
$links = array();
|
||||
foreach ($config[$type] as $prettyTarget => $constraint) {
|
||||
$target = strtolower($prettyTarget);
|
||||
if ($constraint === 'self.version') {
|
||||
$links[$target] = $this->createLink($name, $prettyVersion, $opts['description'], $target, $constraint);
|
||||
} else {
|
||||
if (!isset($linkCache[$name][$type][$target][$constraint])) {
|
||||
$linkCache[$name][$type][$target][$constraint] = array($target, $this->createLink($name, $prettyVersion, $opts['description'], $target, $constraint));
|
||||
}
|
||||
|
||||
list($target, $link) = $linkCache[$name][$type][$target][$constraint];
|
||||
$links[$target] = $link;
|
||||
}
|
||||
}
|
||||
|
||||
$package->{$method}($links);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $source source package name
|
||||
* @param string $sourceVersion source package version (pretty version ideally)
|
||||
|
@ -228,21 +312,26 @@ class ArrayLoader implements LoaderInterface
|
|||
{
|
||||
$res = array();
|
||||
foreach ($links as $target => $constraint) {
|
||||
if (!is_string($constraint)) {
|
||||
throw new \UnexpectedValueException('Link constraint in '.$source.' '.$description.' > '.$target.' should be a string, got '.gettype($constraint) . ' (' . var_export($constraint, true) . ')');
|
||||
}
|
||||
if ('self.version' === $constraint) {
|
||||
$parsedConstraint = $this->versionParser->parseConstraints($sourceVersion);
|
||||
} else {
|
||||
$parsedConstraint = $this->versionParser->parseConstraints($constraint);
|
||||
}
|
||||
|
||||
$res[strtolower($target)] = new Link($source, $target, $parsedConstraint, $description, $constraint);
|
||||
$res[strtolower($target)] = $this->createLink($source, $sourceVersion, $description, $target, $constraint);
|
||||
}
|
||||
|
||||
return $res;
|
||||
}
|
||||
|
||||
private function createLink($source, $sourceVersion, $description, $target, $prettyConstraint)
|
||||
{
|
||||
if (!is_string($prettyConstraint)) {
|
||||
throw new \UnexpectedValueException('Link constraint in '.$source.' '.$description.' > '.$target.' should be a string, got '.gettype($prettyConstraint) . ' (' . var_export($prettyConstraint, true) . ')');
|
||||
}
|
||||
if ('self.version' === $prettyConstraint) {
|
||||
$parsedConstraint = $this->versionParser->parseConstraints($sourceVersion);
|
||||
} else {
|
||||
$parsedConstraint = $this->versionParser->parseConstraints($prettyConstraint);
|
||||
}
|
||||
|
||||
return new Link($source, $target, $parsedConstraint, $description, $prettyConstraint);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves a branch alias (dev-master => 1.0.x-dev for example) if it exists
|
||||
*
|
||||
|
|
|
@ -61,7 +61,11 @@ class ComposerRepository extends ArrayRepository implements ConfigurableReposito
|
|||
private $rootData;
|
||||
private $hasPartialPackages;
|
||||
private $partialPackagesByName;
|
||||
private $versionParser;
|
||||
/**
|
||||
* TODO v3 should make this private once we can drop PHP 5.3 support
|
||||
* @private
|
||||
*/
|
||||
public $versionParser;
|
||||
|
||||
public function __construct(array $repoConfig, IOInterface $io, Config $config, HttpDownloader $httpDownloader, EventDispatcher $eventDispatcher = null)
|
||||
{
|
||||
|
@ -414,6 +418,7 @@ class ComposerRepository extends ArrayRepository implements ConfigurableReposito
|
|||
|
||||
$this->providers[$name] = array();
|
||||
foreach ($packages['packages'] as $versions) {
|
||||
$versionsToLoad = array();
|
||||
foreach ($versions as $version) {
|
||||
if (!$loadingPartialPackage && $this->hasPartialPackages && isset($this->partialPackagesByName[$version['name']])) {
|
||||
continue;
|
||||
|
@ -440,40 +445,44 @@ class ComposerRepository extends ArrayRepository implements ConfigurableReposito
|
|||
continue;
|
||||
}
|
||||
|
||||
// load acceptable packages in the providers
|
||||
$package = $this->createPackage($version, 'Composer\Package\CompletePackage');
|
||||
$package->setRepository($this);
|
||||
$versionsToLoad[] = $version;
|
||||
}
|
||||
}
|
||||
|
||||
if ($package instanceof AliasPackage) {
|
||||
$aliased = $package->getAliasOf();
|
||||
$aliased->setRepository($this);
|
||||
// load acceptable packages in the providers
|
||||
$loadedPackages = $this->createPackages($versionsToLoad, 'Composer\Package\CompletePackage');
|
||||
foreach ($loadedPackages as $package) {
|
||||
$package->setRepository($this);
|
||||
|
||||
$this->providers[$name][$version['uid']] = $aliased;
|
||||
$this->providers[$name][$version['uid'].'-alias'] = $package;
|
||||
if ($package instanceof AliasPackage) {
|
||||
$aliased = $package->getAliasOf();
|
||||
$aliased->setRepository($this);
|
||||
|
||||
// override provider with its alias so it can be expanded in the if block above
|
||||
$this->providersByUid[$version['uid']] = $package;
|
||||
} else {
|
||||
$this->providers[$name][$version['uid']] = $package;
|
||||
$this->providersByUid[$version['uid']] = $package;
|
||||
}
|
||||
$this->providers[$name][$version['uid']] = $aliased;
|
||||
$this->providers[$name][$version['uid'].'-alias'] = $package;
|
||||
|
||||
// handle root package aliases
|
||||
unset($rootAliasData);
|
||||
// override provider with its alias so it can be expanded in the if block above
|
||||
$this->providersByUid[$version['uid']] = $package;
|
||||
} else {
|
||||
$this->providers[$name][$version['uid']] = $package;
|
||||
$this->providersByUid[$version['uid']] = $package;
|
||||
}
|
||||
|
||||
if (isset($this->rootAliases[$package->getName()][$package->getVersion()])) {
|
||||
$rootAliasData = $this->rootAliases[$package->getName()][$package->getVersion()];
|
||||
} elseif ($package instanceof AliasPackage && isset($this->rootAliases[$package->getName()][$package->getAliasOf()->getVersion()])) {
|
||||
$rootAliasData = $this->rootAliases[$package->getName()][$package->getAliasOf()->getVersion()];
|
||||
}
|
||||
// handle root package aliases
|
||||
unset($rootAliasData);
|
||||
|
||||
if (isset($rootAliasData)) {
|
||||
$alias = $this->createAliasPackage($package, $rootAliasData['alias_normalized'], $rootAliasData['alias']);
|
||||
$alias->setRepository($this);
|
||||
if (isset($this->rootAliases[$package->getName()][$package->getVersion()])) {
|
||||
$rootAliasData = $this->rootAliases[$package->getName()][$package->getVersion()];
|
||||
} elseif ($package instanceof AliasPackage && isset($this->rootAliases[$package->getName()][$package->getAliasOf()->getVersion()])) {
|
||||
$rootAliasData = $this->rootAliases[$package->getName()][$package->getAliasOf()->getVersion()];
|
||||
}
|
||||
|
||||
$this->providers[$name][$version['uid'].'-root'] = $alias;
|
||||
$this->providersByUid[$version['uid'].'-root'] = $alias;
|
||||
}
|
||||
if (isset($rootAliasData)) {
|
||||
$alias = $this->createAliasPackage($package, $rootAliasData['alias_normalized'], $rootAliasData['alias']);
|
||||
$alias->setRepository($this);
|
||||
|
||||
$this->providers[$name][$version['uid'].'-root'] = $alias;
|
||||
$this->providersByUid[$version['uid'].'-root'] = $alias;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -501,8 +510,8 @@ class ComposerRepository extends ArrayRepository implements ConfigurableReposito
|
|||
|
||||
$repoData = $this->loadDataFromServer();
|
||||
|
||||
foreach ($repoData as $package) {
|
||||
$this->addPackage($this->createPackage($package, 'Composer\Package\CompletePackage'));
|
||||
foreach ($this->createPackages($repoData, 'Composer\Package\CompletePackage') as $package) {
|
||||
$this->addPackage($package);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -545,6 +554,8 @@ class ComposerRepository extends ArrayRepository implements ConfigurableReposito
|
|||
|
||||
$this->asyncFetchFile($url, $cacheKey, $lastModified)
|
||||
->then(function ($response) use (&$packages, $contents, $name, $constraint, $repo, $isPackageAcceptableCallable) {
|
||||
static $uniqKeys = array('version', 'version_normalized', 'source', 'dist', 'time');
|
||||
|
||||
if (true === $response) {
|
||||
$response = $contents;
|
||||
}
|
||||
|
@ -553,24 +564,37 @@ class ComposerRepository extends ArrayRepository implements ConfigurableReposito
|
|||
return;
|
||||
}
|
||||
|
||||
$uniqKeys = array('version', 'version_normalized', 'source', 'dist', 'time');
|
||||
$versionsToLoad = array();
|
||||
foreach ($response['packages'][$name] as $version) {
|
||||
if (isset($version['versions'])) {
|
||||
$baseVersion = $version;
|
||||
foreach ($uniqKeys as $key) {
|
||||
unset($baseVersion[$key.'s']);
|
||||
}
|
||||
|
||||
foreach ($version['versions'] as $index => $dummy) {
|
||||
$unpackedVersion = $baseVersion;
|
||||
foreach ($uniqKeys as $key) {
|
||||
$unpackedVersion[$key] = $version[$key.'s'][$index];
|
||||
if (isset($version['version_normalizeds'])) {
|
||||
foreach ($version['version_normalizeds'] as $index => $normalizedVersion) {
|
||||
if (!$repo->isVersionAcceptable($isPackageAcceptableCallable, $constraint, $name, $normalizedVersion)) {
|
||||
foreach ($uniqKeys as $key) {
|
||||
unset($version[$key.'s'][$index]);
|
||||
}
|
||||
}
|
||||
|
||||
$repo->createPackageIfAcceptable($packages, $isPackageAcceptableCallable, $unpackedVersion, $constraint);
|
||||
}
|
||||
if (count($version['version_normalizeds'])) {
|
||||
$versionsToLoad[] = $version;
|
||||
}
|
||||
} else {
|
||||
$repo->createPackageIfAcceptable($packages, $isPackageAcceptableCallable, $version, $constraint);
|
||||
if (!isset($version['version_normalized'])) {
|
||||
$version['version_normalized'] = $repo->versionParser->normalize($version['version']);
|
||||
}
|
||||
|
||||
if ($repo->isVersionAcceptable($isPackageAcceptableCallable, $constraint, $name, $version['version_normalized'])) {
|
||||
$versionsToLoad[] = $version;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$loadedPackages = $this->createPackages($versionsToLoad, 'Composer\Package\CompletePackage');
|
||||
foreach ($loadedPackages as $package) {
|
||||
$package->setRepository($this);
|
||||
|
||||
$packages[spl_object_hash($package)] = $package;
|
||||
if ($package instanceof AliasPackage && !isset($packages[spl_object_hash($package->getAliasOf())])) {
|
||||
$packages[spl_object_hash($package->getAliasOf())] = $package->getAliasOf();
|
||||
}
|
||||
}
|
||||
}, function ($e) {
|
||||
|
@ -592,27 +616,17 @@ class ComposerRepository extends ArrayRepository implements ConfigurableReposito
|
|||
*
|
||||
* @private
|
||||
*/
|
||||
public function createPackageIfAcceptable(&$packages, $isPackageAcceptableCallable, $version, $constraint)
|
||||
public function isVersionAcceptable($isPackageAcceptableCallable, $constraint, $name, $versionNormalized)
|
||||
{
|
||||
if (!call_user_func($isPackageAcceptableCallable, strtolower($version['name']), VersionParser::parseStability($version['version']))) {
|
||||
return;
|
||||
if (!call_user_func($isPackageAcceptableCallable, strtolower($name), VersionParser::parseStability($versionNormalized))) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (isset($version['version_normalized']) && $constraint && !$constraint->matches(new Constraint('==', $version['version_normalized']))) {
|
||||
return;
|
||||
if ($constraint && !$constraint->matches(new Constraint('==', $versionNormalized))) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// load acceptable packages in the providers
|
||||
$package = $this->createPackage($version, 'Composer\Package\CompletePackage');
|
||||
$package->setRepository($this);
|
||||
|
||||
// if there was no version_normalized, then we need to check now for the constraint
|
||||
if (!$constraint || isset($version['version_normalized']) || $constraint->matches(new Constraint('==', $package->getVersion()))) {
|
||||
$packages[spl_object_hash($package)] = $package;
|
||||
if ($package instanceof AliasPackage && !isset($packages[spl_object_hash($package->getAliasOf())])) {
|
||||
$packages[spl_object_hash($package->getAliasOf())] = $package->getAliasOf();
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
protected function loadRootServerFile()
|
||||
|
@ -775,23 +789,37 @@ class ComposerRepository extends ArrayRepository implements ConfigurableReposito
|
|||
return $packages;
|
||||
}
|
||||
|
||||
protected function createPackage(array $data, $class = 'Composer\Package\CompletePackage')
|
||||
/**
|
||||
* TODO v3 should make this private once we can drop PHP 5.3 support
|
||||
*
|
||||
* @private
|
||||
*/
|
||||
public function createPackages(array $packages, $class = 'Composer\Package\CompletePackage')
|
||||
{
|
||||
if (!$packages) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
if (!isset($data['notification-url'])) {
|
||||
$data['notification-url'] = $this->notifyUrl;
|
||||
foreach ($packages as &$data) {
|
||||
if (!isset($data['notification-url'])) {
|
||||
$data['notification-url'] = $this->notifyUrl;
|
||||
}
|
||||
}
|
||||
|
||||
$package = $this->loader->load($data, $class);
|
||||
if (isset($this->sourceMirrors[$package->getSourceType()])) {
|
||||
$package->setSourceMirrors($this->sourceMirrors[$package->getSourceType()]);
|
||||
}
|
||||
$package->setDistMirrors($this->distMirrors);
|
||||
$this->configurePackageTransportOptions($package);
|
||||
$packages = $this->loader->loadPackages($packages, $class);
|
||||
|
||||
return $package;
|
||||
foreach ($packages as $package) {
|
||||
if (isset($this->sourceMirrors[$package->getSourceType()])) {
|
||||
$package->setSourceMirrors($this->sourceMirrors[$package->getSourceType()]);
|
||||
}
|
||||
$package->setDistMirrors($this->distMirrors);
|
||||
$this->configurePackageTransportOptions($package);
|
||||
}
|
||||
|
||||
return $packages;
|
||||
} catch (\Exception $e) {
|
||||
throw new \RuntimeException('Could not load package '.(isset($data['name']) ? $data['name'] : json_encode($data)).' in '.$this->url.': ['.get_class($e).'] '.$e->getMessage(), 0, $e);
|
||||
throw new \RuntimeException('Could not load packages '.(isset($packages[0]['name']) ? $packages[0]['name'] : json_encode($packages)).' in '.$this->url.': ['.get_class($e).'] '.$e->getMessage(), 0, $e);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -32,7 +32,7 @@ class ComposerRepositoryTest extends TestCase
|
|||
);
|
||||
|
||||
$repository = $this->getMockBuilder('Composer\Repository\ComposerRepository')
|
||||
->setMethods(array('loadRootServerFile', 'createPackage'))
|
||||
->setMethods(array('loadRootServerFile', 'createPackages'))
|
||||
->setConstructorArgs(array(
|
||||
$repoConfig,
|
||||
new NullIO,
|
||||
|
@ -47,16 +47,17 @@ class ComposerRepositoryTest extends TestCase
|
|||
->method('loadRootServerFile')
|
||||
->will($this->returnValue($repoPackages));
|
||||
|
||||
$stubs = array();
|
||||
foreach ($expected as $at => $arg) {
|
||||
$stubPackage = $this->getPackage('stub/stub', '1.0.0');
|
||||
|
||||
$repository
|
||||
->expects($this->at($at + 2))
|
||||
->method('createPackage')
|
||||
->with($this->identicalTo($arg), $this->equalTo('Composer\Package\CompletePackage'))
|
||||
->will($this->returnValue($stubPackage));
|
||||
$stubs[] = $this->getPackage('stub/stub', '1.0.0');
|
||||
}
|
||||
|
||||
$repository
|
||||
->expects($this->at(2))
|
||||
->method('createPackages')
|
||||
->with($this->identicalTo($expected), $this->equalTo('Composer\Package\CompletePackage'))
|
||||
->will($this->returnValue($stubs));
|
||||
|
||||
// Triggers initialization
|
||||
$packages = $repository->getPackages();
|
||||
|
||||
|
|
Loading…
Reference in New Issue