From da6efc9b0234c4e20194013588c9c7c9149a3876 Mon Sep 17 00:00:00 2001 From: Stephen Date: Sat, 22 Apr 2017 15:20:50 -0400 Subject: [PATCH 1/4] Move all plugins and their dependencies to the front --- src/Composer/Installer.php | 25 +++++++++++-------------- 1 file changed, 11 insertions(+), 14 deletions(-) diff --git a/src/Composer/Installer.php b/src/Composer/Installer.php index 57172530f..ce981d3cb 100644 --- a/src/Composer/Installer.php +++ b/src/Composer/Installer.php @@ -745,7 +745,8 @@ class Installer private function movePluginsToFront(array $operations) { $installerOps = array(); - foreach ($operations as $idx => $op) { + $installerRequires = array(); + foreach (array_reverse($operations, true) as $idx => $op) { if ($op instanceof InstallOperation) { $package = $op->getPackage(); } elseif ($op instanceof UpdateOperation) { @@ -754,19 +755,15 @@ class Installer continue; } - if ($package->getType() === 'composer-plugin' || $package->getType() === 'composer-installer') { - // ignore requirements to platform or composer-plugin-api - $requires = array_keys($package->getRequires()); - foreach ($requires as $index => $req) { - if ($req === 'composer-plugin-api' || preg_match(PlatformRepository::PLATFORM_PACKAGE_REGEX, $req)) { - unset($requires[$index]); - } - } - // if there are no other requirements, move the plugin to the top of the op list - if (!count($requires)) { - $installerOps[] = $op; - unset($operations[$idx]); - } + if ($package->getType() === 'composer-plugin' + || $package->getType() === 'composer-installer' + || in_array($package->getName(), $installerRequires) + ) { + // capture the requirements for this package so those packages will be moved up as well + $installerRequires = array_merge($installerRequires, array_keys($package->getRequires())); + // move the operation to the front + array_unshift($installerOps, $op); + unset($operations[$idx]); } } From 6c4800b8d27f4d775565b93cf85e419ed8ae27f1 Mon Sep 17 00:00:00 2001 From: Stephen Date: Sat, 22 Apr 2017 16:38:37 -0400 Subject: [PATCH 2/4] Update corresponding test for plugin moving --- .../Fixtures/installer/plugins-are-installed-first.test | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/Composer/Test/Fixtures/installer/plugins-are-installed-first.test b/tests/Composer/Test/Fixtures/installer/plugins-are-installed-first.test index ad34e9c02..e6c13f5db 100644 --- a/tests/Composer/Test/Fixtures/installer/plugins-are-installed-first.test +++ b/tests/Composer/Test/Fixtures/installer/plugins-are-installed-first.test @@ -1,5 +1,5 @@ --TEST-- -Composer installers are installed first if they have no meaningful requirements +Composer installers and their requirements are installed first --COMPOSER-- { "repositories": [ @@ -25,7 +25,7 @@ Composer installers are installed first if they have no meaningful requirements install --EXPECT-- Installing inst (1.0.0) -Installing inst-with-req (1.0.0) -Installing pkg (1.0.0) Installing pkg2 (1.0.0) Installing inst-with-req2 (1.0.0) +Installing inst-with-req (1.0.0) +Installing pkg (1.0.0) From 4cda7e0a44d608f5bb6071003790046594535549 Mon Sep 17 00:00:00 2001 From: Stephen Beemsterboer Date: Tue, 23 May 2017 19:17:18 -0400 Subject: [PATCH 3/4] Take into account a package's "provide" and "replace" fields A package can satisfy a require by its "provide" or "replace" fields, so this leverages the getNames() method which returns these alternate names as well. --- src/Composer/Installer.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Composer/Installer.php b/src/Composer/Installer.php index ce981d3cb..8f8bcc957 100644 --- a/src/Composer/Installer.php +++ b/src/Composer/Installer.php @@ -755,9 +755,9 @@ class Installer continue; } - if ($package->getType() === 'composer-plugin' + if ($package->getType() === 'composer-plugin' || $package->getType() === 'composer-installer' - || in_array($package->getName(), $installerRequires) + || count(array_intersect($package->getNames(), $installerRequires)) ) { // capture the requirements for this package so those packages will be moved up as well $installerRequires = array_merge($installerRequires, array_keys($package->getRequires())); From 06bb6c7530bf7523fcb6f80c9a23ff6728c10742 Mon Sep 17 00:00:00 2001 From: Stephen Beemsterboer Date: Tue, 23 May 2017 20:28:32 -0400 Subject: [PATCH 4/4] Move plugins with no dependencies to the front, followed by plugins with their dependencies --- src/Composer/Installer.php | 37 +++++++++++++------ .../plugins-are-installed-first.test | 2 +- 2 files changed, 27 insertions(+), 12 deletions(-) diff --git a/src/Composer/Installer.php b/src/Composer/Installer.php index 8f8bcc957..2161e368e 100644 --- a/src/Composer/Installer.php +++ b/src/Composer/Installer.php @@ -744,8 +744,10 @@ class Installer */ private function movePluginsToFront(array $operations) { - $installerOps = array(); - $installerRequires = array(); + $pluginsNoDeps = array(); + $pluginsWithDeps = array(); + $pluginRequires = array(); + foreach (array_reverse($operations, true) as $idx => $op) { if ($op instanceof InstallOperation) { $package = $op->getPackage(); @@ -755,19 +757,32 @@ class Installer continue; } - if ($package->getType() === 'composer-plugin' - || $package->getType() === 'composer-installer' - || count(array_intersect($package->getNames(), $installerRequires)) - ) { - // capture the requirements for this package so those packages will be moved up as well - $installerRequires = array_merge($installerRequires, array_keys($package->getRequires())); - // move the operation to the front - array_unshift($installerOps, $op); + // is this package a plugin? + $is_plugin = $package->getType() === 'composer-plugin' || $package->getType() === 'composer-installer'; + + // is this a plugin or a dependency of a plugin? + if ($is_plugin || count(array_intersect($package->getNames(), $pluginRequires))) { + // get the package's requires, but filter out any platform requirements or 'composer-plugin-api' + $requires = array_filter(array_keys($package->getRequires()), function($req) { + return $req !== 'composer-plugin-api' && !preg_match(PlatformRepository::PLATFORM_PACKAGE_REGEX, $req); + }); + + // is this a plugin with no meaningful dependencies? + if ($is_plugin && !count($requires)) { + // plugins with no dependencies go to the very front + array_unshift($pluginsNoDeps, $op); + } else { + // capture the requirements for this package so those packages will be moved up as well + $pluginRequires = array_merge($pluginRequires, $requires); + // move the operation to the front + array_unshift($pluginsWithDeps, $op); + } + unset($operations[$idx]); } } - return array_merge($installerOps, $operations); + return array_merge($pluginsNoDeps, $pluginsWithDeps, $operations); } /** diff --git a/tests/Composer/Test/Fixtures/installer/plugins-are-installed-first.test b/tests/Composer/Test/Fixtures/installer/plugins-are-installed-first.test index e6c13f5db..009eb576d 100644 --- a/tests/Composer/Test/Fixtures/installer/plugins-are-installed-first.test +++ b/tests/Composer/Test/Fixtures/installer/plugins-are-installed-first.test @@ -25,7 +25,7 @@ Composer installers and their requirements are installed first install --EXPECT-- Installing inst (1.0.0) +Installing inst-with-req (1.0.0) Installing pkg2 (1.0.0) Installing inst-with-req2 (1.0.0) -Installing inst-with-req (1.0.0) Installing pkg (1.0.0)