Merge pull request #8085 from danepowell/issue-8065-2

Fixes #8065: Sort plugins deterministically before loading.
main
Jordi Boggiano 5 years ago committed by GitHub
commit 9d642fe9af
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -21,6 +21,7 @@ use Composer\Package\PackageInterface;
use Composer\Repository\InstalledRepositoryInterface; use Composer\Repository\InstalledRepositoryInterface;
use Composer\Util\Filesystem; use Composer\Util\Filesystem;
use Composer\Script\ScriptEvents; use Composer\Script\ScriptEvents;
use Composer\Util\PackageSorter;
/** /**
* @author Igor Wiedler <igor@wiedler.ch> * @author Igor Wiedler <igor@wiedler.ch>
@ -973,80 +974,21 @@ INITIALIZER;
{ {
$packages = array(); $packages = array();
$paths = array(); $paths = array();
$usageList = array();
foreach ($packageMap as $item) { foreach ($packageMap as $item) {
list($package, $path) = $item; list($package, $path) = $item;
$name = $package->getName(); $name = $package->getName();
$packages[$name] = $package; $packages[$name] = $package;
$paths[$name] = $path; $paths[$name] = $path;
foreach (array_merge($package->getRequires(), $package->getDevRequires()) as $link) {
$target = $link->getTarget();
$usageList[$target][] = $name;
}
}
$computing = array();
$computed = array();
$computeImportance = function ($name) use (&$computeImportance, &$computing, &$computed, $usageList) {
// reusing computed importance
if (isset($computed[$name])) {
return $computed[$name];
}
// canceling circular dependency
if (isset($computing[$name])) {
return 0;
}
$computing[$name] = true;
$weight = 0;
if (isset($usageList[$name])) {
foreach ($usageList[$name] as $user) {
$weight -= 1 - $computeImportance($user);
}
}
unset($computing[$name]);
$computed[$name] = $weight;
return $weight;
};
$weightList = array();
foreach ($packages as $name => $package) {
$weight = $computeImportance($name);
$weightList[$name] = $weight;
} }
$stable_sort = function (&$array) { $sortedPackages = PackageSorter::sortPackages($packages);
static $transform, $restore;
$i = 0;
if (!$transform) {
$transform = function (&$v, $k) use (&$i) {
$v = array($v, ++$i, $k, $v);
};
$restore = function (&$v, $k) {
$v = $v[3];
};
}
array_walk($array, $transform);
asort($array);
array_walk($array, $restore);
};
$stable_sort($weightList);
$sortedPackageMap = array(); $sortedPackageMap = array();
foreach (array_keys($weightList) as $name) { foreach ($sortedPackages as $package) {
$name = $package->getName();
$sortedPackageMap[] = array($packages[$name], $paths[$name]); $sortedPackageMap[] = array($packages[$name], $paths[$name]);
} }

@ -15,15 +15,16 @@ namespace Composer\Plugin;
use Composer\Composer; use Composer\Composer;
use Composer\EventDispatcher\EventSubscriberInterface; use Composer\EventDispatcher\EventSubscriberInterface;
use Composer\IO\IOInterface; use Composer\IO\IOInterface;
use Composer\Package\CompletePackage;
use Composer\Package\Package; use Composer\Package\Package;
use Composer\Package\Version\VersionParser; use Composer\Package\Version\VersionParser;
use Composer\Repository\RepositoryInterface; use Composer\Repository\RepositoryInterface;
use Composer\Package\AliasPackage;
use Composer\Package\PackageInterface; use Composer\Package\PackageInterface;
use Composer\Package\Link; use Composer\Package\Link;
use Composer\Semver\Constraint\Constraint; use Composer\Semver\Constraint\Constraint;
use Composer\DependencyResolver\Pool; use Composer\DependencyResolver\Pool;
use Composer\Plugin\Capability\Capability; use Composer\Plugin\Capability\Capability;
use Composer\Util\PackageSorter;
/** /**
* Plugin manager * Plugin manager
@ -253,8 +254,10 @@ class PluginManager
*/ */
private function loadRepository(RepositoryInterface $repo) private function loadRepository(RepositoryInterface $repo)
{ {
foreach ($repo->getPackages() as $package) { /** @var PackageInterface $package */ $packages = $repo->getPackages();
if ($package instanceof AliasPackage) { $sortedPackages = array_reverse(PackageSorter::sortPackages($packages));
foreach ($sortedPackages as $package) {
if (!($package instanceof CompletePackage)) {
continue; continue;
} }
if ('composer-plugin' === $package->getType()) { if ('composer-plugin' === $package->getType()) {

@ -0,0 +1,92 @@
<?php
namespace Composer\Util;
use Composer\Package\Link;
use Composer\Package\PackageInterface;
class PackageSorter
{
/**
* Sorts packages by dependency weight
*
* Packages of equal weight retain the original order
*
* @param array $packages
* @return array
*/
public static function sortPackages(array $packages) {
$usageList = array();
foreach ($packages as $package) { /** @var PackageInterface $package */
foreach (array_merge($package->getRequires(), $package->getDevRequires()) as $link) { /** @var Link $link */
$target = $link->getTarget();
$usageList[$target][] = $package->getName();
}
}
$computing = array();
$computed = array();
$computeImportance = function ($name) use (&$computeImportance, &$computing, &$computed, $usageList) {
// reusing computed importance
if (isset($computed[$name])) {
return $computed[$name];
}
// canceling circular dependency
if (isset($computing[$name])) {
return 0;
}
$computing[$name] = true;
$weight = 0;
if (isset($usageList[$name])) {
foreach ($usageList[$name] as $user) {
$weight -= 1 - $computeImportance($user);
}
}
unset($computing[$name]);
$computed[$name] = $weight;
return $weight;
};
$weightList = array();
foreach ($packages as $name => $package) {
$weight = $computeImportance($name);
$weightList[$name] = $weight;
}
$stable_sort = function (&$array) {
static $transform, $restore;
$i = 0;
if (!$transform) {
$transform = function (&$v, $k) use (&$i) {
$v = array($v, ++$i, $k, $v);
};
$restore = function (&$v) {
$v = $v[3];
};
}
array_walk($array, $transform);
asort($array);
array_walk($array, $restore);
};
$stable_sort($weightList);
$sortedPackages = array();
foreach (array_keys($weightList) as $name) {
$sortedPackages[] = $packages[$name];
}
return $sortedPackages;
}
}
Loading…
Cancel
Save