Browse Source

Add --unused flag to remove command, fixes #8283

Jordi Boggiano 5 years ago
parent
commit
11765ddfea
1 changed files with 48 additions and 0 deletions
  1. 48 0
      src/Composer/Command/RemoveCommand.php

+ 48 - 0
src/Composer/Command/RemoveCommand.php

@@ -48,6 +48,7 @@ class RemoveCommand extends BaseCommand
                 new InputOption('update-with-all-dependencies', null, InputOption::VALUE_NONE, 'Allows all inherited dependencies to be updated, including those that are root requirements.'),
                 new InputOption('with-all-dependencies', null, InputOption::VALUE_NONE, 'Alias for --update-with-all-dependencies'),
                 new InputOption('no-update-with-dependencies', null, InputOption::VALUE_NONE, 'Does not allow inherited dependencies to be updated with explicit dependencies.'),
+                new InputOption('unused', null, InputOption::VALUE_NONE, 'Remove all packages which are locked but not required by any other package.'),
                 new InputOption('ignore-platform-reqs', null, InputOption::VALUE_NONE, 'Ignore platform requirements (php & ext- packages).'),
                 new InputOption('optimize-autoloader', 'o', InputOption::VALUE_NONE, 'Optimize autoloader during autoloader dump'),
                 new InputOption('classmap-authoritative', 'a', InputOption::VALUE_NONE, 'Autoload classes from the classmap only. Implicitly enables `--optimize-autoloader`.'),
@@ -66,6 +67,53 @@ EOT
         ;
     }
 
+    protected function interact(InputInterface $input, OutputInterface $output)
+    {
+        if ($input->getOption('unused')) {
+            $composer = $this->getComposer();
+            $locker = $composer->getLocker();
+            if (!$locker->isLocked()) {
+                throw new \UnexpectedValueException('A valid composer.lock file is required to run this command with --unused');
+            }
+
+            $lockedPackages = $locker->getLockedRepository()->getPackages();
+
+            $required = array();
+            foreach (array_merge($composer->getPackage()->getRequires(), $composer->getPackage()->getDevRequires()) as $link) {
+                $required[$link->getTarget()] = true;
+            }
+
+            do {
+                $found = false;
+                foreach ($lockedPackages as $index => $package) {
+                    foreach ($package->getNames() as $name) {
+                        if (isset($required[$name])) {
+                            foreach ($package->getRequires() as $link) {
+                                $required[$link->getTarget()] = true;
+                            }
+                            $found = true;
+                            unset($lockedPackages[$index]);
+                            break;
+                        }
+                    }
+                }
+            } while ($found);
+
+            $unused = array();
+            foreach ($lockedPackages as $package) {
+                $unused[] = $package->getName();
+            }
+            $input->setArgument('packages', array_merge($input->getArgument('packages'), $unused));
+
+            if (!$input->getArgument('packages')) {
+                $this->getIO()->writeError('<info>No unused packages to remove</info>');
+                $this->setCode(function () {
+                    return 0;
+                });
+            }
+        }
+    }
+
     protected function execute(InputInterface $input, OutputInterface $output)
     {
         $packages = $input->getArgument('packages');