Simplify suggester output when updating, refactor suggest command to reuse SuggestedPackagesReporter and make smarter defaults, fixes #6267

main
Jordi Boggiano 4 years ago
parent 5d8dc48bd4
commit 44d1e15294
No known key found for this signature in database
GPG Key ID: 7BBD42C429EC80BC

@ -106,7 +106,6 @@ resolution.
* **--no-scripts:** Skips execution of scripts defined in `composer.json`.
* **--no-progress:** Removes the progress display that can mess with some
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
autoloader. This is recommended especially for production, but can take
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-progress:** Removes the progress display that can mess with some
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-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
@ -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.
* **--no-progress:** Removes the progress display that can mess with some
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-scripts:** Skips execution of scripts defined in `composer.json`.
* **--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`
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.
Use the `--verbose (-v)` flag to display the suggesting package and the suggestion reason.
This implies `--by-package --by-suggestion`, showing both lists.
If you only want a list of suggested package names, use `--list`.
### Options
* **--by-package:** Groups output by suggesting package.
* **--by-package:** Groups output by suggesting package (default).
* **--by-suggestion:** Groups output by suggested package.
* **--list:** Show only list of suggested package names.
* **--no-dev:** Excludes suggestions from `require-dev` packages.
## depends (why)

@ -44,7 +44,6 @@ class InstallCommand extends BaseCommand
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-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('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`.'),
@ -107,7 +106,6 @@ EOT
->setDevMode(!$input->getOption('no-dev'))
->setDumpAutoloader(!$input->getOption('no-autoloader'))
->setRunScripts(!$input->getOption('no-scripts'))
->setSkipSuggest($input->getOption('no-suggest'))
->setOptimizeAutoloader($optimize)
->setClassMapAuthoritative($authoritative)
->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('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-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-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.'),
@ -259,7 +258,6 @@ EOT
->setPreferDist($input->getOption('prefer-dist'))
->setDevMode($updateDevMode)
->setRunScripts(!$input->getOption('no-scripts'))
->setSkipSuggest($input->getOption('no-suggest'))
->setOptimizeAutoloader($optimize)
->setClassMapAuthoritative($authoritative)
->setApcuAutoloader($apcu)

@ -13,6 +13,9 @@
namespace Composer\Command;
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\InputInterface;
use Symfony\Component\Console\Input\InputOption;
@ -26,8 +29,9 @@ class SuggestsCommand extends BaseCommand
->setName('suggests')
->setDescription('Shows package suggestions.')
->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('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 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.
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
EOT
)
@ -49,108 +51,50 @@ EOT
*/
protected function execute(InputInterface $input, OutputInterface $output)
{
$lock = $this->getComposer()->getLocker()->getLockData();
if (empty($lock)) {
throw new \RuntimeException('Lockfile seems to be empty?');
$composer = $this->getComposer();
$installedRepos = array(
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'];
if (!$input->getOption('no-dev')) {
$packages += $lock['packages-dev'];
}
$installedRepo = new CompositeRepository($installedRepos);
$reporter = new SuggestedPackagesReporter($this->getIO());
$filter = $input->getArgument('packages');
// First assemble lookup list of packages that are installed, replaced or provided
$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'])) {
foreach ($installedRepo->getPackages() as $package) {
if (!empty($filter) && !in_array($package->getName(), $filter)) {
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;
}
}
$reporter->addSuggestionsFromPackage($package);
}
ksort($suggesters);
ksort($suggested);
// Determine output mode
$mode = 0;
// Determine output mode, default is by-package
$mode = SuggestedPackagesReporter::MODE_BY_PACKAGE;
$io = $this->getIO();
if ($input->getOption('by-package') || $io->isVerbose()) {
$mode |= 1;
}
// if by-suggestion is given we override the default
if ($input->getOption('by-suggestion')) {
$mode |= 2;
$mode = SuggestedPackagesReporter::MODE_BY_SUGGESTION;
}
// Simple mode
if ($mode === 0) {
foreach (array_keys($suggested) as $suggestion) {
$io->write(sprintf('<info>%s</info>', $suggestion));
}
return 0;
// unless by-package is also present then we enable both
if ($input->getOption('by-package')) {
$mode |= SuggestedPackagesReporter::MODE_BY_PACKAGE;
}
// 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('');
}
// list is exclusive and overrides everything else
if ($input->getOption('list')) {
$mode = SuggestedPackagesReporter::MODE_LIST;
}
// 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('');
}
}
$reporter->output($mode, $installedRepo);
return 0;
}

@ -48,7 +48,6 @@ class UpdateCommand extends BaseCommand
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-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-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.'),
@ -154,7 +153,6 @@ EOT
->setDevMode(!$input->getOption('no-dev'))
->setDumpAutoloader(!$input->getOption('no-autoloader'))
->setRunScripts(!$input->getOption('no-scripts'))
->setSkipSuggest($input->getOption('no-suggest'))
->setOptimizeAutoloader($optimize)
->setClassMapAuthoritative($authoritative)
->setApcuAutoloader($apcu)

@ -131,7 +131,6 @@ class Installer
protected $ignorePlatformReqs = false;
protected $preferStable = false;
protected $preferLowest = false;
protected $skipSuggest = false;
protected $writeLock;
protected $executeOperations = true;
@ -257,9 +256,13 @@ class Installer
$this->installationManager->notifyInstalls($this->io);
}
// output suggestions if we're in dev mode
if ($this->update && $this->devMode && !$this->skipSuggest) {
$this->suggestedPackagesReporter->output($this->locker->getLockedRepository($this->devMode));
if ($this->update) {
$installedRepos = array(
$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
@ -1310,19 +1313,6 @@ class Installer
return $this;
}
/**
* Should suggestions be skipped?
*
* @param bool $skipSuggest
* @return Installer
*/
public function setSkipSuggest($skipSuggest = true)
{
$this->skipSuggest = (bool) $skipSuggest;
return $this;
}
/**
* Disables plugins.
*

@ -24,6 +24,10 @@ use Symfony\Component\Console\Formatter\OutputFormatter;
*/
class SuggestedPackagesReporter
{
const MODE_LIST = 1;
const MODE_BY_PACKAGE = 2;
const MODE_BY_SUGGESTION = 4;
/**
* @var array
*/
@ -91,38 +95,105 @@ class SuggestedPackagesReporter
/**
* Output suggested packages.
*
* 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
*/
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();
$lockedPackages = array();
if (null !== $lockedRepo && ! empty($suggestedPackages)) {
foreach ($lockedRepo->getPackages() as $package) {
$lockedPackages = array_merge(
$lockedPackages,
$installedNames = array();
if (null !== $installedRepo && !empty($suggestedPackages)) {
foreach ($installedRepo->getPackages() as $package) {
$installedNames = array_merge(
$installedNames,
$package->getNames()
);
}
}
$suggestions = array();
foreach ($suggestedPackages as $suggestion) {
if (in_array($suggestion['target'], $lockedPackages)) {
if (in_array($suggestion['target'], $installedNames)) {
continue;
}
$this->io->writeError(sprintf(
'%s suggests installing %s%s',
$suggestion['source'],
$this->escapeOutput($suggestion['target']),
$this->escapeOutput('' !== $suggestion['reason'] ? ' ('.$suggestion['reason'].')' : '')
));
$suggestions[] = $suggestion;
}
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--
Suggestions are not displayed in non-dev mode
Suggestions are not displayed for when not updating the lock file
--COMPOSER--
{
"repositories": [
@ -14,16 +14,25 @@ Suggestions are not displayed in non-dev mode
"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--
install --no-dev
install
--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
Installing dependencies from lock file (including require-dev)
Verifying lock file contents can be installed on current platform.
Package operations: 1 install, 0 updates, 0 removals
Generating autoload files

@ -25,7 +25,7 @@ Lock file operations: 1 install, 0 updates, 0 removals
Writing lock file
Installing dependencies from lock file (including require-dev)
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
--EXPECT--

@ -33,14 +33,13 @@ class SuggestedPackagesReporterTest extends TestCase
/**
* @covers ::__construct
*/
public function testContrsuctor()
public function testConstructor()
{
$this->io->expects($this->once())
->method('writeError');
->method('write');
$suggestedPackagesReporter = new SuggestedPackagesReporter($this->io);
$suggestedPackagesReporter->addPackage('a', 'b', 'c');
$suggestedPackagesReporter->output();
$this->suggestedPackagesReporter->addPackage('a', 'b', 'c');
$this->suggestedPackagesReporter->output(SuggestedPackagesReporter::MODE_LIST);
}
/**
@ -135,25 +134,33 @@ class SuggestedPackagesReporterTest extends TestCase
{
$this->suggestedPackagesReporter->addPackage('a', 'b', 'c');
$this->io->expects($this->once())
->method('writeError')
->with('a suggests installing b (c)');
$this->io->expects($this->at(0))
->method('write')
->with('<comment>a</comment> suggests:');
$this->io->expects($this->at(1))
->method('write')
->with(' - <info>b</info>: c');
$this->suggestedPackagesReporter->output();
$this->suggestedPackagesReporter->output(SuggestedPackagesReporter::MODE_BY_PACKAGE);
}
/**
* @covers ::output
*/
public function testOutputWithNoSuggestedPackage()
public function testOutputWithNoSuggestionReason()
{
$this->suggestedPackagesReporter->addPackage('a', 'b', '');
$this->io->expects($this->once())
->method('writeError')
->with('a suggests installing b');
$this->io->expects($this->at(0))
->method('write')
->with('<comment>a</comment> suggests:');
$this->io->expects($this->at(1))
->method('write')
->with(' - <info>b</info>');
$this->suggestedPackagesReporter->output();
$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->io->expects($this->at(0))
->method('writeError')
->with("source suggests installing target1 ([1;37;42m Like us on Facebook [0m)");
->method('write')
->with('<comment>source</comment> suggests:');
$this->io->expects($this->at(1))
->method('writeError')
->with('source suggests installing target2 (\\<bg=green>Like us on Facebook\\</>)');
->method('write')
->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->io->expects($this->at(0))
->method('writeError')
->with('a suggests installing b (c)');
->method('write')
->with('<comment>a</comment> suggests:');
$this->io->expects($this->at(1))
->method('writeError')
->with('source package suggests installing target (because reasons)');
->method('write')
->with(' - <info>b</info>: c');
$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();
$this->suggestedPackagesReporter->output(SuggestedPackagesReporter::MODE_BY_PACKAGE);
}
/**
@ -221,11 +244,15 @@ class SuggestedPackagesReporterTest extends TestCase
$this->suggestedPackagesReporter->addPackage('a', 'b', 'c');
$this->suggestedPackagesReporter->addPackage('source package', 'target', 'because reasons');
$this->io->expects($this->once())
->method('writeError')
->with('source package suggests installing target (because reasons)');
$this->io->expects($this->at(0))
->method('write')
->with('<comment>source package</comment> suggests:');
$this->io->expects($this->at(1))
->method('write')
->with(' - <info>target</info>: because reasons');
$this->suggestedPackagesReporter->output($repository);
$this->suggestedPackagesReporter->output(SuggestedPackagesReporter::MODE_BY_PACKAGE, $repository);
}
/**
@ -237,7 +264,7 @@ class SuggestedPackagesReporterTest extends TestCase
$repository->expects($this->exactly(0))
->method('getPackages');
$this->suggestedPackagesReporter->output($repository);
$this->suggestedPackagesReporter->output(SuggestedPackagesReporter::MODE_BY_PACKAGE, $repository);
}
private function getSuggestedPackageArray()

Loading…
Cancel
Save