diff --git a/src/Composer/Command/UpdateCommand.php b/src/Composer/Command/UpdateCommand.php index b67ff16cf..919ecd8a5 100644 --- a/src/Composer/Command/UpdateCommand.php +++ b/src/Composer/Command/UpdateCommand.php @@ -12,13 +12,17 @@ namespace Composer\Command; +use Composer\Composer; use Composer\Installer; use Composer\Plugin\CommandEvent; use Composer\Plugin\PluginEvents; +use Symfony\Component\Console\Helper\Table; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Input\InputOption; use Symfony\Component\Console\Input\InputArgument; use Symfony\Component\Console\Output\OutputInterface; +use Symfony\Component\Console\Question\ConfirmationQuestion; +use Symfony\Component\Console\Question\Question; /** * @author Jordi Boggiano @@ -51,6 +55,7 @@ class UpdateCommand extends Command new InputOption('ignore-platform-reqs', null, InputOption::VALUE_NONE, 'Ignore platform requirements (php & ext- packages).'), new InputOption('prefer-stable', null, InputOption::VALUE_NONE, 'Prefer stable versions of dependencies.'), new InputOption('prefer-lowest', null, InputOption::VALUE_NONE, 'Prefer lowest versions of dependencies.'), + new InputOption('interactive', 'i', InputOption::VALUE_NONE, 'interactive interface with autocompletion that can help to select the packages to update.'), )) ->setHelp(<<update command reads the composer.json file from the @@ -86,6 +91,13 @@ EOT } $composer = $this->getComposer(true, $input->getOption('no-plugins')); + + $packages = $input->getArgument('packages'); + + if ($input->getOption('interactive')) { + $packages = $this->getPackagesInteractively($input, $output, $composer, $packages); + } + $composer->getDownloadManager()->setOutputProgress(!$input->getOption('no-progress')); $commandEvent = new CommandEvent(PluginEvents::COMMAND, 'update', $input, $output); @@ -129,7 +141,7 @@ EOT ->setOptimizeAutoloader($optimize) ->setClassMapAuthoritative($authoritative) ->setUpdate(true) - ->setUpdateWhitelist($input->getOption('lock') ? array('lock') : $input->getArgument('packages')) + ->setUpdateWhitelist($input->getOption('lock') ? array('lock') : $packages) ->setWhitelistDependencies($input->getOption('with-dependencies')) ->setIgnorePlatformRequirements($input->getOption('ignore-platform-reqs')) ->setPreferStable($input->getOption('prefer-stable')) @@ -142,4 +154,83 @@ EOT return $install->run(); } + + private function getPackagesInteractively(InputInterface $input, OutputInterface $output, Composer $composer, $packages) + { + if (!$input->isInteractive()) { + throw new \InvalidArgumentException('--interactive cannot be used in non-interactive terminals.'); + } + + $packagesMap = $composer->getRepositoryManager() + ->getLocalRepository()->getPackages(); + + $requiredPackageNames = array(); + foreach ( + array_merge( + $composer->getPackage()->getRequires(), + $composer->getPackage()->getDevRequires() + ) as $require) { + $requiredPackageNames[] = $require->getTarget(); + } + + $InstalledPackageNames = array(); + foreach ($packagesMap as $package) { + $InstalledPackageNames[] = $package->getPrettyName(); + } + + $autocompleterValues = array_unique( + array_merge($InstalledPackageNames, $requiredPackageNames) + ); + + $helper = $this->getHelper('question'); + $question = new Question( + 'Enter package name: ', + null + ); + + $packages = is_array($packages) ? $packages : array(); + $output->writeln('NB: Empty package ends submission.'); // I couldn't find any better for now! + + do { + $question->setAutocompleterValues($autocompleterValues); + $addedPackage = $helper->ask($input, $output, $question); + + if (!is_string($addedPackage)) { + break; + } + + if (!in_array($addedPackage, $packages)) { + $packages[] = $addedPackage; + $autocompleterValues = array_diff($autocompleterValues, array($addedPackage)); + } else { + $output->writeln(sprintf('The package "%s" was already added.', $package)); + } + } while (true); + + $packages = array_filter($packages); + if (!$packages) { + throw new \InvalidArgumentException('You must enter minimum one package.'); + } + + $table = new Table($output); + $table->setHeaders(array('Selected packages')); + foreach ((array)$packages as $package) { + $table->addRow(array($package)); + } + $table->render(); + + $continue = new ConfirmationQuestion( + sprintf( + 'Would you like to continue and update the above package%s [yes|no] ? (yes) ', + 1 === count($packages) ? '' : 's' + ), + true + ); + + if ($helper->ask($input, $output, $continue)) { + return $packages; + } + + throw new \RuntimeException('Installation aborted.'); + } }