Simplify suggester output when updating, refactor suggest command to reuse SuggestedPackagesReporter and make smarter defaults, fixes #6267
parent
5d8dc48bd4
commit
44d1e15294
|
@ -106,7 +106,6 @@ resolution.
|
||||||
* **--no-scripts:** Skips execution of scripts defined in `composer.json`.
|
* **--no-scripts:** Skips execution of scripts defined in `composer.json`.
|
||||||
* **--no-progress:** Removes the progress display that can mess with some
|
* **--no-progress:** Removes the progress display that can mess with some
|
||||||
terminals or scripts which don't handle backspace characters.
|
terminals or scripts which don't handle backspace characters.
|
||||||
* **--no-suggest:** Skips suggested packages in the output.
|
|
||||||
* **--optimize-autoloader (-o):** Convert PSR-0/4 autoloading to classmap to get a faster
|
* **--optimize-autoloader (-o):** Convert PSR-0/4 autoloading to classmap to get a faster
|
||||||
autoloader. This is recommended especially for production, but can take
|
autoloader. This is recommended especially for production, but can take
|
||||||
a bit of time to run so it is currently not done by default.
|
a bit of time to run so it is currently not done by default.
|
||||||
|
@ -156,7 +155,6 @@ php composer.phar update "vendor/*"
|
||||||
* **--no-scripts:** Skips execution of scripts defined in `composer.json`.
|
* **--no-scripts:** Skips execution of scripts defined in `composer.json`.
|
||||||
* **--no-progress:** Removes the progress display that can mess with some
|
* **--no-progress:** Removes the progress display that can mess with some
|
||||||
terminals or scripts which don't handle backspace characters.
|
terminals or scripts which don't handle backspace characters.
|
||||||
* **--no-suggest:** Skips suggested packages in the output.
|
|
||||||
* **--with-dependencies:** Add also dependencies of whitelisted packages to the whitelist, except those that are root requirements.
|
* **--with-dependencies:** Add also dependencies of whitelisted packages to the whitelist, except those that are root requirements.
|
||||||
* **--with-all-dependencies:** Add also all dependencies of whitelisted packages to the whitelist, including those that are root requirements.
|
* **--with-all-dependencies:** Add also all dependencies of whitelisted packages to the whitelist, including those that are root requirements.
|
||||||
* **--optimize-autoloader (-o):** Convert PSR-0/4 autoloading to classmap to get a faster
|
* **--optimize-autoloader (-o):** Convert PSR-0/4 autoloading to classmap to get a faster
|
||||||
|
@ -203,7 +201,6 @@ If you do not specify a package, composer will prompt you to search for a packag
|
||||||
* **--prefer-dist:** Install packages from `dist` when available.
|
* **--prefer-dist:** Install packages from `dist` when available.
|
||||||
* **--no-progress:** Removes the progress display that can mess with some
|
* **--no-progress:** Removes the progress display that can mess with some
|
||||||
terminals or scripts which don't handle backspace characters.
|
terminals or scripts which don't handle backspace characters.
|
||||||
* **--no-suggest:** Skips suggested packages in the output.
|
|
||||||
* **--no-update:** Disables the automatic update of the dependencies.
|
* **--no-update:** Disables the automatic update of the dependencies.
|
||||||
* **--no-scripts:** Skips execution of scripts defined in `composer.json`.
|
* **--no-scripts:** Skips execution of scripts defined in `composer.json`.
|
||||||
* **--update-no-dev:** Run the dependency update with the `--no-dev` option.
|
* **--update-no-dev:** Run the dependency update with the `--no-dev` option.
|
||||||
|
@ -410,16 +407,16 @@ Lists all packages suggested by currently installed set of packages. You can
|
||||||
optionally pass one or multiple package names in the format of `vendor/package`
|
optionally pass one or multiple package names in the format of `vendor/package`
|
||||||
to limit output to suggestions made by those packages only.
|
to limit output to suggestions made by those packages only.
|
||||||
|
|
||||||
Use the `--by-package` or `--by-suggestion` flags to group the output by
|
Use the `--by-package` (default) or `--by-suggestion` flags to group the output by
|
||||||
the package offering the suggestions or the suggested packages respectively.
|
the package offering the suggestions or the suggested packages respectively.
|
||||||
|
|
||||||
Use the `--verbose (-v)` flag to display the suggesting package and the suggestion reason.
|
If you only want a list of suggested package names, use `--list`.
|
||||||
This implies `--by-package --by-suggestion`, showing both lists.
|
|
||||||
|
|
||||||
### Options
|
### Options
|
||||||
|
|
||||||
* **--by-package:** Groups output by suggesting package.
|
* **--by-package:** Groups output by suggesting package (default).
|
||||||
* **--by-suggestion:** Groups output by suggested package.
|
* **--by-suggestion:** Groups output by suggested package.
|
||||||
|
* **--list:** Show only list of suggested package names.
|
||||||
* **--no-dev:** Excludes suggestions from `require-dev` packages.
|
* **--no-dev:** Excludes suggestions from `require-dev` packages.
|
||||||
|
|
||||||
## depends (why)
|
## depends (why)
|
||||||
|
|
|
@ -44,7 +44,6 @@ class InstallCommand extends BaseCommand
|
||||||
new InputOption('no-autoloader', null, InputOption::VALUE_NONE, 'Skips autoloader generation'),
|
new InputOption('no-autoloader', null, InputOption::VALUE_NONE, 'Skips autoloader generation'),
|
||||||
new InputOption('no-scripts', null, InputOption::VALUE_NONE, 'Skips the execution of all scripts defined in composer.json file.'),
|
new InputOption('no-scripts', null, InputOption::VALUE_NONE, 'Skips the execution of all scripts defined in composer.json file.'),
|
||||||
new InputOption('no-progress', null, InputOption::VALUE_NONE, 'Do not output download progress.'),
|
new InputOption('no-progress', null, InputOption::VALUE_NONE, 'Do not output download progress.'),
|
||||||
new InputOption('no-suggest', null, InputOption::VALUE_NONE, 'Do not show package suggestions.'),
|
|
||||||
new InputOption('verbose', 'v|vv|vvv', InputOption::VALUE_NONE, 'Shows more details including new commits pulled in when updating packages.'),
|
new InputOption('verbose', 'v|vv|vvv', InputOption::VALUE_NONE, 'Shows more details including new commits pulled in when updating packages.'),
|
||||||
new InputOption('optimize-autoloader', 'o', InputOption::VALUE_NONE, 'Optimize autoloader during autoloader dump'),
|
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`.'),
|
new InputOption('classmap-authoritative', 'a', InputOption::VALUE_NONE, 'Autoload classes from the classmap only. Implicitly enables `--optimize-autoloader`.'),
|
||||||
|
@ -107,7 +106,6 @@ EOT
|
||||||
->setDevMode(!$input->getOption('no-dev'))
|
->setDevMode(!$input->getOption('no-dev'))
|
||||||
->setDumpAutoloader(!$input->getOption('no-autoloader'))
|
->setDumpAutoloader(!$input->getOption('no-autoloader'))
|
||||||
->setRunScripts(!$input->getOption('no-scripts'))
|
->setRunScripts(!$input->getOption('no-scripts'))
|
||||||
->setSkipSuggest($input->getOption('no-suggest'))
|
|
||||||
->setOptimizeAutoloader($optimize)
|
->setOptimizeAutoloader($optimize)
|
||||||
->setClassMapAuthoritative($authoritative)
|
->setClassMapAuthoritative($authoritative)
|
||||||
->setApcuAutoloader($apcu)
|
->setApcuAutoloader($apcu)
|
||||||
|
|
|
@ -55,7 +55,6 @@ class RequireCommand extends InitCommand
|
||||||
new InputOption('prefer-dist', null, InputOption::VALUE_NONE, 'Forces installation from package dist even for dev versions.'),
|
new InputOption('prefer-dist', null, InputOption::VALUE_NONE, 'Forces installation from package dist even for dev versions.'),
|
||||||
new InputOption('fixed', null, InputOption::VALUE_NONE, 'Write fixed version to the composer.json.'),
|
new InputOption('fixed', null, InputOption::VALUE_NONE, 'Write fixed version to the composer.json.'),
|
||||||
new InputOption('no-progress', null, InputOption::VALUE_NONE, 'Do not output download progress.'),
|
new InputOption('no-progress', null, InputOption::VALUE_NONE, 'Do not output download progress.'),
|
||||||
new InputOption('no-suggest', null, InputOption::VALUE_NONE, 'Do not show package suggestions.'),
|
|
||||||
new InputOption('no-update', null, InputOption::VALUE_NONE, 'Disables the automatic update of the dependencies.'),
|
new InputOption('no-update', null, InputOption::VALUE_NONE, 'Disables the automatic update of the dependencies.'),
|
||||||
new InputOption('no-scripts', null, InputOption::VALUE_NONE, 'Skips the execution of all scripts defined in composer.json file.'),
|
new InputOption('no-scripts', null, InputOption::VALUE_NONE, 'Skips the execution of all scripts defined in composer.json file.'),
|
||||||
new InputOption('update-no-dev', null, InputOption::VALUE_NONE, 'Run the dependency update with the --no-dev option.'),
|
new InputOption('update-no-dev', null, InputOption::VALUE_NONE, 'Run the dependency update with the --no-dev option.'),
|
||||||
|
@ -259,7 +258,6 @@ EOT
|
||||||
->setPreferDist($input->getOption('prefer-dist'))
|
->setPreferDist($input->getOption('prefer-dist'))
|
||||||
->setDevMode($updateDevMode)
|
->setDevMode($updateDevMode)
|
||||||
->setRunScripts(!$input->getOption('no-scripts'))
|
->setRunScripts(!$input->getOption('no-scripts'))
|
||||||
->setSkipSuggest($input->getOption('no-suggest'))
|
|
||||||
->setOptimizeAutoloader($optimize)
|
->setOptimizeAutoloader($optimize)
|
||||||
->setClassMapAuthoritative($authoritative)
|
->setClassMapAuthoritative($authoritative)
|
||||||
->setApcuAutoloader($apcu)
|
->setApcuAutoloader($apcu)
|
||||||
|
|
|
@ -13,6 +13,9 @@
|
||||||
namespace Composer\Command;
|
namespace Composer\Command;
|
||||||
|
|
||||||
use Composer\Repository\PlatformRepository;
|
use Composer\Repository\PlatformRepository;
|
||||||
|
use Composer\Repository\RootPackageRepository;
|
||||||
|
use Composer\Repository\CompositeRepository;
|
||||||
|
use Composer\Installer\SuggestedPackagesReporter;
|
||||||
use Symfony\Component\Console\Input\InputArgument;
|
use Symfony\Component\Console\Input\InputArgument;
|
||||||
use Symfony\Component\Console\Input\InputInterface;
|
use Symfony\Component\Console\Input\InputInterface;
|
||||||
use Symfony\Component\Console\Input\InputOption;
|
use Symfony\Component\Console\Input\InputOption;
|
||||||
|
@ -26,8 +29,9 @@ class SuggestsCommand extends BaseCommand
|
||||||
->setName('suggests')
|
->setName('suggests')
|
||||||
->setDescription('Shows package suggestions.')
|
->setDescription('Shows package suggestions.')
|
||||||
->setDefinition(array(
|
->setDefinition(array(
|
||||||
new InputOption('by-package', null, InputOption::VALUE_NONE, 'Groups output by suggesting package'),
|
new InputOption('by-package', null, InputOption::VALUE_NONE, 'Groups output by suggesting package (default)'),
|
||||||
new InputOption('by-suggestion', null, InputOption::VALUE_NONE, 'Groups output by suggested package'),
|
new InputOption('by-suggestion', null, InputOption::VALUE_NONE, 'Groups output by suggested package'),
|
||||||
|
new InputOption('list', null, InputOption::VALUE_NONE, 'Show only list of suggested package names'),
|
||||||
new InputOption('no-dev', null, InputOption::VALUE_NONE, 'Exclude suggestions from require-dev packages'),
|
new InputOption('no-dev', null, InputOption::VALUE_NONE, 'Exclude suggestions from require-dev packages'),
|
||||||
new InputArgument('packages', InputArgument::IS_ARRAY | InputArgument::OPTIONAL, 'Packages that you want to list suggestions from.'),
|
new InputArgument('packages', InputArgument::IS_ARRAY | InputArgument::OPTIONAL, 'Packages that you want to list suggestions from.'),
|
||||||
))
|
))
|
||||||
|
@ -36,8 +40,6 @@ class SuggestsCommand extends BaseCommand
|
||||||
|
|
||||||
The <info>%command.name%</info> command shows a sorted list of suggested packages.
|
The <info>%command.name%</info> command shows a sorted list of suggested packages.
|
||||||
|
|
||||||
Enabling <info>-v</info> implies <info>--by-package --by-suggestion</info>, showing both lists.
|
|
||||||
|
|
||||||
Read more at https://getcomposer.org/doc/03-cli.md#suggests
|
Read more at https://getcomposer.org/doc/03-cli.md#suggests
|
||||||
EOT
|
EOT
|
||||||
)
|
)
|
||||||
|
@ -49,108 +51,50 @@ EOT
|
||||||
*/
|
*/
|
||||||
protected function execute(InputInterface $input, OutputInterface $output)
|
protected function execute(InputInterface $input, OutputInterface $output)
|
||||||
{
|
{
|
||||||
$lock = $this->getComposer()->getLocker()->getLockData();
|
$composer = $this->getComposer();
|
||||||
|
|
||||||
if (empty($lock)) {
|
$installedRepos = array(
|
||||||
throw new \RuntimeException('Lockfile seems to be empty?');
|
new RootPackageRepository(array(clone $composer->getPackage())),
|
||||||
|
);
|
||||||
|
|
||||||
|
$locker = $composer->getLocker();
|
||||||
|
if ($locker->isLocked()) {
|
||||||
|
$installedRepos[] = new PlatformRepository(array(), $locker->getPlatformOverrides());
|
||||||
|
$installedRepos[] = $locker->getLockedRepository(!$input->getOption('no-dev'));
|
||||||
|
} else {
|
||||||
|
$installedRepos[] = new PlatformRepository(array(), $composer->getConfig()->get('platform') ?: array());
|
||||||
|
$installedRepos[] = $composer->getRepositoryManager()->getLocalRepository();
|
||||||
}
|
}
|
||||||
|
|
||||||
$packages = $lock['packages'];
|
$installedRepo = new CompositeRepository($installedRepos);
|
||||||
|
$reporter = new SuggestedPackagesReporter($this->getIO());
|
||||||
if (!$input->getOption('no-dev')) {
|
|
||||||
$packages += $lock['packages-dev'];
|
|
||||||
}
|
|
||||||
|
|
||||||
$filter = $input->getArgument('packages');
|
$filter = $input->getArgument('packages');
|
||||||
|
foreach ($installedRepo->getPackages() as $package) {
|
||||||
// First assemble lookup list of packages that are installed, replaced or provided
|
if (!empty($filter) && !in_array($package->getName(), $filter)) {
|
||||||
$installed = array();
|
|
||||||
foreach ($packages as $package) {
|
|
||||||
$installed[] = $package['name'];
|
|
||||||
|
|
||||||
if (!empty($package['provide'])) {
|
|
||||||
$installed = array_merge($installed, array_keys($package['provide']));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!empty($package['replace'])) {
|
|
||||||
$installed = array_merge($installed, array_keys($package['replace']));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Undub and sort the install list into a sorted lookup array
|
|
||||||
$installed = array_flip($installed);
|
|
||||||
ksort($installed);
|
|
||||||
|
|
||||||
// Init platform repo
|
|
||||||
$platform = new PlatformRepository(array(), $this->getComposer()->getConfig()->get('platform') ?: array());
|
|
||||||
|
|
||||||
// Next gather all suggestions that are not in that list
|
|
||||||
$suggesters = array();
|
|
||||||
$suggested = array();
|
|
||||||
foreach ($packages as $package) {
|
|
||||||
$packageName = $package['name'];
|
|
||||||
if ((!empty($filter) && !in_array($packageName, $filter)) || empty($package['suggest'])) {
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
foreach ($package['suggest'] as $suggestion => $reason) {
|
|
||||||
if (preg_match(PlatformRepository::PLATFORM_PACKAGE_REGEX, $suggestion) && null !== $platform->findPackage($suggestion, '*')) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if (!isset($installed[$suggestion])) {
|
|
||||||
$suggesters[$packageName][$suggestion] = $reason;
|
|
||||||
$suggested[$suggestion][$packageName] = $reason;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ksort($suggesters);
|
|
||||||
ksort($suggested);
|
|
||||||
|
|
||||||
// Determine output mode
|
$reporter->addSuggestionsFromPackage($package);
|
||||||
$mode = 0;
|
}
|
||||||
|
|
||||||
|
// Determine output mode, default is by-package
|
||||||
|
$mode = SuggestedPackagesReporter::MODE_BY_PACKAGE;
|
||||||
$io = $this->getIO();
|
$io = $this->getIO();
|
||||||
if ($input->getOption('by-package') || $io->isVerbose()) {
|
// if by-suggestion is given we override the default
|
||||||
$mode |= 1;
|
|
||||||
}
|
|
||||||
if ($input->getOption('by-suggestion')) {
|
if ($input->getOption('by-suggestion')) {
|
||||||
$mode |= 2;
|
$mode = SuggestedPackagesReporter::MODE_BY_SUGGESTION;
|
||||||
|
}
|
||||||
|
// unless by-package is also present then we enable both
|
||||||
|
if ($input->getOption('by-package')) {
|
||||||
|
$mode |= SuggestedPackagesReporter::MODE_BY_PACKAGE;
|
||||||
|
}
|
||||||
|
// list is exclusive and overrides everything else
|
||||||
|
if ($input->getOption('list')) {
|
||||||
|
$mode = SuggestedPackagesReporter::MODE_LIST;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Simple mode
|
$reporter->output($mode, $installedRepo);
|
||||||
if ($mode === 0) {
|
|
||||||
foreach (array_keys($suggested) as $suggestion) {
|
|
||||||
$io->write(sprintf('<info>%s</info>', $suggestion));
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Grouped by package
|
|
||||||
if ($mode & 1) {
|
|
||||||
foreach ($suggesters as $suggester => $suggestions) {
|
|
||||||
$io->write(sprintf('<comment>%s</comment> suggests:', $suggester));
|
|
||||||
|
|
||||||
foreach ($suggestions as $suggestion => $reason) {
|
|
||||||
$io->write(sprintf(' - <info>%s</info>: %s', $suggestion, $reason ?: '*'));
|
|
||||||
}
|
|
||||||
$io->write('');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Grouped by suggestion
|
|
||||||
if ($mode & 2) {
|
|
||||||
// Improve readability in full mode
|
|
||||||
if ($mode & 1) {
|
|
||||||
$io->write(str_repeat('-', 78));
|
|
||||||
}
|
|
||||||
foreach ($suggested as $suggestion => $suggesters) {
|
|
||||||
$io->write(sprintf('<comment>%s</comment> is suggested by:', $suggestion));
|
|
||||||
|
|
||||||
foreach ($suggesters as $suggester => $reason) {
|
|
||||||
$io->write(sprintf(' - <info>%s</info>: %s', $suggester, $reason ?: '*'));
|
|
||||||
}
|
|
||||||
$io->write('');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
|
@ -48,7 +48,6 @@ class UpdateCommand extends BaseCommand
|
||||||
new InputOption('no-autoloader', null, InputOption::VALUE_NONE, 'Skips autoloader generation'),
|
new InputOption('no-autoloader', null, InputOption::VALUE_NONE, 'Skips autoloader generation'),
|
||||||
new InputOption('no-scripts', null, InputOption::VALUE_NONE, 'Skips the execution of all scripts defined in composer.json file.'),
|
new InputOption('no-scripts', null, InputOption::VALUE_NONE, 'Skips the execution of all scripts defined in composer.json file.'),
|
||||||
new InputOption('no-progress', null, InputOption::VALUE_NONE, 'Do not output download progress.'),
|
new InputOption('no-progress', null, InputOption::VALUE_NONE, 'Do not output download progress.'),
|
||||||
new InputOption('no-suggest', null, InputOption::VALUE_NONE, 'Do not show package suggestions.'),
|
|
||||||
new InputOption('with-dependencies', null, InputOption::VALUE_NONE, 'Add also dependencies of whitelisted packages to the whitelist, except those defined in root package.'),
|
new InputOption('with-dependencies', null, InputOption::VALUE_NONE, 'Add also dependencies of whitelisted packages to the whitelist, except those defined in root package.'),
|
||||||
new InputOption('with-all-dependencies', null, InputOption::VALUE_NONE, 'Add also all dependencies of whitelisted packages to the whitelist, including those defined in root package.'),
|
new InputOption('with-all-dependencies', null, InputOption::VALUE_NONE, 'Add also all dependencies of whitelisted packages to the whitelist, including those defined in root package.'),
|
||||||
new InputOption('verbose', 'v|vv|vvv', InputOption::VALUE_NONE, 'Shows more details including new commits pulled in when updating packages.'),
|
new InputOption('verbose', 'v|vv|vvv', InputOption::VALUE_NONE, 'Shows more details including new commits pulled in when updating packages.'),
|
||||||
|
@ -154,7 +153,6 @@ EOT
|
||||||
->setDevMode(!$input->getOption('no-dev'))
|
->setDevMode(!$input->getOption('no-dev'))
|
||||||
->setDumpAutoloader(!$input->getOption('no-autoloader'))
|
->setDumpAutoloader(!$input->getOption('no-autoloader'))
|
||||||
->setRunScripts(!$input->getOption('no-scripts'))
|
->setRunScripts(!$input->getOption('no-scripts'))
|
||||||
->setSkipSuggest($input->getOption('no-suggest'))
|
|
||||||
->setOptimizeAutoloader($optimize)
|
->setOptimizeAutoloader($optimize)
|
||||||
->setClassMapAuthoritative($authoritative)
|
->setClassMapAuthoritative($authoritative)
|
||||||
->setApcuAutoloader($apcu)
|
->setApcuAutoloader($apcu)
|
||||||
|
|
|
@ -131,7 +131,6 @@ class Installer
|
||||||
protected $ignorePlatformReqs = false;
|
protected $ignorePlatformReqs = false;
|
||||||
protected $preferStable = false;
|
protected $preferStable = false;
|
||||||
protected $preferLowest = false;
|
protected $preferLowest = false;
|
||||||
protected $skipSuggest = false;
|
|
||||||
protected $writeLock;
|
protected $writeLock;
|
||||||
protected $executeOperations = true;
|
protected $executeOperations = true;
|
||||||
|
|
||||||
|
@ -257,9 +256,13 @@ class Installer
|
||||||
$this->installationManager->notifyInstalls($this->io);
|
$this->installationManager->notifyInstalls($this->io);
|
||||||
}
|
}
|
||||||
|
|
||||||
// output suggestions if we're in dev mode
|
if ($this->update) {
|
||||||
if ($this->update && $this->devMode && !$this->skipSuggest) {
|
$installedRepos = array(
|
||||||
$this->suggestedPackagesReporter->output($this->locker->getLockedRepository($this->devMode));
|
$this->locker->getLockedRepository($this->devMode),
|
||||||
|
$this->createPlatformRepo(false),
|
||||||
|
new RootPackageRepository(array(clone $this->package)),
|
||||||
|
);
|
||||||
|
$this->suggestedPackagesReporter->outputMinimalistic(new CompositeRepository($installedRepos));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Find abandoned packages and warn user
|
// Find abandoned packages and warn user
|
||||||
|
@ -1310,19 +1313,6 @@ class Installer
|
||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Should suggestions be skipped?
|
|
||||||
*
|
|
||||||
* @param bool $skipSuggest
|
|
||||||
* @return Installer
|
|
||||||
*/
|
|
||||||
public function setSkipSuggest($skipSuggest = true)
|
|
||||||
{
|
|
||||||
$this->skipSuggest = (bool) $skipSuggest;
|
|
||||||
|
|
||||||
return $this;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Disables plugins.
|
* Disables plugins.
|
||||||
*
|
*
|
||||||
|
|
|
@ -24,6 +24,10 @@ use Symfony\Component\Console\Formatter\OutputFormatter;
|
||||||
*/
|
*/
|
||||||
class SuggestedPackagesReporter
|
class SuggestedPackagesReporter
|
||||||
{
|
{
|
||||||
|
const MODE_LIST = 1;
|
||||||
|
const MODE_BY_PACKAGE = 2;
|
||||||
|
const MODE_BY_SUGGESTION = 4;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @var array
|
* @var array
|
||||||
*/
|
*/
|
||||||
|
@ -91,38 +95,105 @@ class SuggestedPackagesReporter
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Output suggested packages.
|
* Output suggested packages.
|
||||||
|
*
|
||||||
* Do not list the ones already installed if installed repository provided.
|
* Do not list the ones already installed if installed repository provided.
|
||||||
*
|
*
|
||||||
* @param RepositoryInterface $installedRepo Installed packages
|
* @param int $mode One of the MODE_* constants from this class
|
||||||
* @return SuggestedPackagesReporter
|
* @return SuggestedPackagesReporter
|
||||||
*/
|
*/
|
||||||
public function output(RepositoryInterface $lockedRepo = null)
|
public function output($mode, RepositoryInterface $installedRepo = null)
|
||||||
|
{
|
||||||
|
$suggestedPackages = $this->getFilteredSuggestions($installedRepo);
|
||||||
|
|
||||||
|
$suggesters = array();
|
||||||
|
$suggested = array();
|
||||||
|
foreach ($suggestedPackages as $suggestion) {
|
||||||
|
$suggesters[$suggestion['source']][$suggestion['target']] = $suggestion['reason'];
|
||||||
|
$suggested[$suggestion['target']][$suggestion['source']] = $suggestion['reason'];
|
||||||
|
}
|
||||||
|
ksort($suggesters);
|
||||||
|
ksort($suggested);
|
||||||
|
|
||||||
|
// Simple mode
|
||||||
|
if ($mode & self::MODE_LIST) {
|
||||||
|
foreach (array_keys($suggested) as $name) {
|
||||||
|
$this->io->write(sprintf('<info>%s</info>', $name));
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Grouped by package
|
||||||
|
if ($mode & self::MODE_BY_PACKAGE) {
|
||||||
|
foreach ($suggesters as $suggester => $suggestions) {
|
||||||
|
$this->io->write(sprintf('<comment>%s</comment> suggests:', $suggester));
|
||||||
|
|
||||||
|
foreach ($suggestions as $suggestion => $reason) {
|
||||||
|
$this->io->write(sprintf(' - <info>%s</info>' . ($reason ? ': %s' : ''), $suggestion, $this->escapeOutput($reason)));
|
||||||
|
}
|
||||||
|
$this->io->write('');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Grouped by suggestion
|
||||||
|
if ($mode & self::MODE_BY_SUGGESTION) {
|
||||||
|
// Improve readability in full mode
|
||||||
|
if ($mode & self::MODE_BY_PACKAGE) {
|
||||||
|
$this->io->write(str_repeat('-', 78));
|
||||||
|
}
|
||||||
|
foreach ($suggested as $suggestion => $suggesters) {
|
||||||
|
$this->io->write(sprintf('<comment>%s</comment> is suggested by:', $suggestion));
|
||||||
|
|
||||||
|
foreach ($suggesters as $suggester => $reason) {
|
||||||
|
$this->io->write(sprintf(' - <info>%s</info>' . ($reason ? ': %s' : ''), $suggester, $this->escapeOutput($reason)));
|
||||||
|
}
|
||||||
|
$this->io->write('');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Output number of new suggested packages and a hint to use suggest command.
|
||||||
|
**
|
||||||
|
* Do not list the ones already installed if installed repository provided.
|
||||||
|
*
|
||||||
|
* @return SuggestedPackagesReporter
|
||||||
|
*/
|
||||||
|
public function outputMinimalistic(RepositoryInterface $installedRepo = null)
|
||||||
|
{
|
||||||
|
$suggestedPackages = $this->getFilteredSuggestions($installedRepo);
|
||||||
|
if ($suggestedPackages) {
|
||||||
|
$this->io->writeError(count($suggestedPackages).' package suggestions were added by new dependencies, use <info>composer suggest</info> to see details.');
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function getFilteredSuggestions(RepositoryInterface $installedRepo = null)
|
||||||
{
|
{
|
||||||
$suggestedPackages = $this->getPackages();
|
$suggestedPackages = $this->getPackages();
|
||||||
$lockedPackages = array();
|
$installedNames = array();
|
||||||
if (null !== $lockedRepo && ! empty($suggestedPackages)) {
|
if (null !== $installedRepo && !empty($suggestedPackages)) {
|
||||||
foreach ($lockedRepo->getPackages() as $package) {
|
foreach ($installedRepo->getPackages() as $package) {
|
||||||
$lockedPackages = array_merge(
|
$installedNames = array_merge(
|
||||||
$lockedPackages,
|
$installedNames,
|
||||||
$package->getNames()
|
$package->getNames()
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$suggestions = array();
|
||||||
foreach ($suggestedPackages as $suggestion) {
|
foreach ($suggestedPackages as $suggestion) {
|
||||||
if (in_array($suggestion['target'], $lockedPackages)) {
|
if (in_array($suggestion['target'], $installedNames)) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
$this->io->writeError(sprintf(
|
$suggestions[] = $suggestion;
|
||||||
'%s suggests installing %s%s',
|
|
||||||
$suggestion['source'],
|
|
||||||
$this->escapeOutput($suggestion['target']),
|
|
||||||
$this->escapeOutput('' !== $suggestion['reason'] ? ' ('.$suggestion['reason'].')' : '')
|
|
||||||
));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return $this;
|
return $suggestions;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -0,0 +1,32 @@
|
||||||
|
--TEST--
|
||||||
|
Suggestions are displayed even in non-dev mode for new suggesters installed when updating the lock file
|
||||||
|
--COMPOSER--
|
||||||
|
{
|
||||||
|
"repositories": [
|
||||||
|
{
|
||||||
|
"type": "package",
|
||||||
|
"package": [
|
||||||
|
{ "name": "a/a", "version": "1.0.0", "suggest": { "b/b": "an obscure reason" } }
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"require": {
|
||||||
|
"a/a": "1.0.0"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
--RUN--
|
||||||
|
install --no-dev
|
||||||
|
--EXPECT-OUTPUT--
|
||||||
|
<warning>No lock file found. Updating dependencies instead of installing from lock file. Use composer update over composer install if you do not have a lock file.</warning>
|
||||||
|
Loading composer repositories with package information
|
||||||
|
Updating dependencies
|
||||||
|
Lock file operations: 1 install, 0 updates, 0 removals
|
||||||
|
- Locking a/a (1.0.0)
|
||||||
|
Writing lock file
|
||||||
|
Installing dependencies from lock file
|
||||||
|
Package operations: 1 install, 0 updates, 0 removals
|
||||||
|
1 package suggestions were added by new dependencies, use composer suggest to see details.
|
||||||
|
Generating autoload files
|
||||||
|
|
||||||
|
--EXPECT--
|
||||||
|
Installing a/a (1.0.0)
|
|
@ -1,5 +1,5 @@
|
||||||
--TEST--
|
--TEST--
|
||||||
Suggestions are not displayed in non-dev mode
|
Suggestions are not displayed for when not updating the lock file
|
||||||
--COMPOSER--
|
--COMPOSER--
|
||||||
{
|
{
|
||||||
"repositories": [
|
"repositories": [
|
||||||
|
@ -14,16 +14,25 @@ Suggestions are not displayed in non-dev mode
|
||||||
"a/a": "1.0.0"
|
"a/a": "1.0.0"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
--LOCK--
|
||||||
|
{
|
||||||
|
"packages": [
|
||||||
|
{ "name": "a/a", "version": "1.0.0", "suggest": { "b/b": "an obscure reason" } }
|
||||||
|
],
|
||||||
|
"packages-dev": [],
|
||||||
|
"aliases": [],
|
||||||
|
"minimum-stability": "dev",
|
||||||
|
"stability-flags": [],
|
||||||
|
"prefer-stable": false,
|
||||||
|
"prefer-lowest": false,
|
||||||
|
"platform": [],
|
||||||
|
"platform-dev": []
|
||||||
|
}
|
||||||
--RUN--
|
--RUN--
|
||||||
install --no-dev
|
install
|
||||||
--EXPECT-OUTPUT--
|
--EXPECT-OUTPUT--
|
||||||
<warning>No lock file found. Updating dependencies instead of installing from lock file. Use composer update over composer install if you do not have a lock file.</warning>
|
Installing dependencies from lock file (including require-dev)
|
||||||
Loading composer repositories with package information
|
Verifying lock file contents can be installed on current platform.
|
||||||
Updating dependencies
|
|
||||||
Lock file operations: 1 install, 0 updates, 0 removals
|
|
||||||
- Locking a/a (1.0.0)
|
|
||||||
Writing lock file
|
|
||||||
Installing dependencies from lock file
|
|
||||||
Package operations: 1 install, 0 updates, 0 removals
|
Package operations: 1 install, 0 updates, 0 removals
|
||||||
Generating autoload files
|
Generating autoload files
|
||||||
|
|
||||||
|
|
|
@ -25,7 +25,7 @@ Lock file operations: 1 install, 0 updates, 0 removals
|
||||||
Writing lock file
|
Writing lock file
|
||||||
Installing dependencies from lock file (including require-dev)
|
Installing dependencies from lock file (including require-dev)
|
||||||
Package operations: 1 install, 0 updates, 0 removals
|
Package operations: 1 install, 0 updates, 0 removals
|
||||||
a/a suggests installing b/b (an obscure reason)
|
1 package suggestions were added by new dependencies, use composer suggest to see details.
|
||||||
Generating autoload files
|
Generating autoload files
|
||||||
|
|
||||||
--EXPECT--
|
--EXPECT--
|
||||||
|
|
|
@ -33,14 +33,13 @@ class SuggestedPackagesReporterTest extends TestCase
|
||||||
/**
|
/**
|
||||||
* @covers ::__construct
|
* @covers ::__construct
|
||||||
*/
|
*/
|
||||||
public function testContrsuctor()
|
public function testConstructor()
|
||||||
{
|
{
|
||||||
$this->io->expects($this->once())
|
$this->io->expects($this->once())
|
||||||
->method('writeError');
|
->method('write');
|
||||||
|
|
||||||
$suggestedPackagesReporter = new SuggestedPackagesReporter($this->io);
|
$this->suggestedPackagesReporter->addPackage('a', 'b', 'c');
|
||||||
$suggestedPackagesReporter->addPackage('a', 'b', 'c');
|
$this->suggestedPackagesReporter->output(SuggestedPackagesReporter::MODE_LIST);
|
||||||
$suggestedPackagesReporter->output();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -135,25 +134,33 @@ class SuggestedPackagesReporterTest extends TestCase
|
||||||
{
|
{
|
||||||
$this->suggestedPackagesReporter->addPackage('a', 'b', 'c');
|
$this->suggestedPackagesReporter->addPackage('a', 'b', 'c');
|
||||||
|
|
||||||
$this->io->expects($this->once())
|
$this->io->expects($this->at(0))
|
||||||
->method('writeError')
|
->method('write')
|
||||||
->with('a suggests installing b (c)');
|
->with('<comment>a</comment> suggests:');
|
||||||
|
|
||||||
$this->suggestedPackagesReporter->output();
|
$this->io->expects($this->at(1))
|
||||||
|
->method('write')
|
||||||
|
->with(' - <info>b</info>: c');
|
||||||
|
|
||||||
|
$this->suggestedPackagesReporter->output(SuggestedPackagesReporter::MODE_BY_PACKAGE);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @covers ::output
|
* @covers ::output
|
||||||
*/
|
*/
|
||||||
public function testOutputWithNoSuggestedPackage()
|
public function testOutputWithNoSuggestionReason()
|
||||||
{
|
{
|
||||||
$this->suggestedPackagesReporter->addPackage('a', 'b', '');
|
$this->suggestedPackagesReporter->addPackage('a', 'b', '');
|
||||||
|
|
||||||
$this->io->expects($this->once())
|
$this->io->expects($this->at(0))
|
||||||
->method('writeError')
|
->method('write')
|
||||||
->with('a suggests installing b');
|
->with('<comment>a</comment> suggests:');
|
||||||
|
|
||||||
$this->suggestedPackagesReporter->output();
|
$this->io->expects($this->at(1))
|
||||||
|
->method('write')
|
||||||
|
->with(' - <info>b</info>');
|
||||||
|
|
||||||
|
$this->suggestedPackagesReporter->output(SuggestedPackagesReporter::MODE_BY_PACKAGE);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -165,14 +172,18 @@ class SuggestedPackagesReporterTest extends TestCase
|
||||||
$this->suggestedPackagesReporter->addPackage('source', 'target2', "<bg=green>Like us on Facebook</>");
|
$this->suggestedPackagesReporter->addPackage('source', 'target2', "<bg=green>Like us on Facebook</>");
|
||||||
|
|
||||||
$this->io->expects($this->at(0))
|
$this->io->expects($this->at(0))
|
||||||
->method('writeError')
|
->method('write')
|
||||||
->with("source suggests installing target1 ([1;37;42m Like us on Facebook [0m)");
|
->with('<comment>source</comment> suggests:');
|
||||||
|
|
||||||
$this->io->expects($this->at(1))
|
$this->io->expects($this->at(1))
|
||||||
->method('writeError')
|
->method('write')
|
||||||
->with('source suggests installing target2 (\\<bg=green>Like us on Facebook\\</>)');
|
->with(' - <info>target1</info>: [1;37;42m Like us on Facebook [0m');
|
||||||
|
|
||||||
$this->suggestedPackagesReporter->output();
|
$this->io->expects($this->at(2))
|
||||||
|
->method('write')
|
||||||
|
->with(' - <info>target2</info>: \\<bg=green>Like us on Facebook\\</>');
|
||||||
|
|
||||||
|
$this->suggestedPackagesReporter->output(SuggestedPackagesReporter::MODE_BY_PACKAGE);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -184,14 +195,26 @@ class SuggestedPackagesReporterTest extends TestCase
|
||||||
$this->suggestedPackagesReporter->addPackage('source package', 'target', 'because reasons');
|
$this->suggestedPackagesReporter->addPackage('source package', 'target', 'because reasons');
|
||||||
|
|
||||||
$this->io->expects($this->at(0))
|
$this->io->expects($this->at(0))
|
||||||
->method('writeError')
|
->method('write')
|
||||||
->with('a suggests installing b (c)');
|
->with('<comment>a</comment> suggests:');
|
||||||
|
|
||||||
$this->io->expects($this->at(1))
|
$this->io->expects($this->at(1))
|
||||||
->method('writeError')
|
->method('write')
|
||||||
->with('source package suggests installing target (because reasons)');
|
->with(' - <info>b</info>: c');
|
||||||
|
|
||||||
$this->suggestedPackagesReporter->output();
|
$this->io->expects($this->at(2))
|
||||||
|
->method('write')
|
||||||
|
->with('');
|
||||||
|
|
||||||
|
$this->io->expects($this->at(3))
|
||||||
|
->method('write')
|
||||||
|
->with('<comment>source package</comment> suggests:');
|
||||||
|
|
||||||
|
$this->io->expects($this->at(4))
|
||||||
|
->method('write')
|
||||||
|
->with(' - <info>target</info>: because reasons');
|
||||||
|
|
||||||
|
$this->suggestedPackagesReporter->output(SuggestedPackagesReporter::MODE_BY_PACKAGE);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -221,11 +244,15 @@ class SuggestedPackagesReporterTest extends TestCase
|
||||||
$this->suggestedPackagesReporter->addPackage('a', 'b', 'c');
|
$this->suggestedPackagesReporter->addPackage('a', 'b', 'c');
|
||||||
$this->suggestedPackagesReporter->addPackage('source package', 'target', 'because reasons');
|
$this->suggestedPackagesReporter->addPackage('source package', 'target', 'because reasons');
|
||||||
|
|
||||||
$this->io->expects($this->once())
|
$this->io->expects($this->at(0))
|
||||||
->method('writeError')
|
->method('write')
|
||||||
->with('source package suggests installing target (because reasons)');
|
->with('<comment>source package</comment> suggests:');
|
||||||
|
|
||||||
$this->suggestedPackagesReporter->output($repository);
|
$this->io->expects($this->at(1))
|
||||||
|
->method('write')
|
||||||
|
->with(' - <info>target</info>: because reasons');
|
||||||
|
|
||||||
|
$this->suggestedPackagesReporter->output(SuggestedPackagesReporter::MODE_BY_PACKAGE, $repository);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -237,7 +264,7 @@ class SuggestedPackagesReporterTest extends TestCase
|
||||||
$repository->expects($this->exactly(0))
|
$repository->expects($this->exactly(0))
|
||||||
->method('getPackages');
|
->method('getPackages');
|
||||||
|
|
||||||
$this->suggestedPackagesReporter->output($repository);
|
$this->suggestedPackagesReporter->output(SuggestedPackagesReporter::MODE_BY_PACKAGE, $repository);
|
||||||
}
|
}
|
||||||
|
|
||||||
private function getSuggestedPackageArray()
|
private function getSuggestedPackageArray()
|
||||||
|
|
Loading…
Reference in New Issue