function ShowCommand::execute
Overrides Command::execute
File
-
vendor/
composer/ composer/ src/ Composer/ Command/ ShowCommand.php, line 137
Class
- ShowCommand
- @author Robert Schönthal <seroscho@googlemail.com> @author Jordi Boggiano <j.boggiano@seld.be> @author Jérémy Romey <jeremyFreeAgent> @author Mihai Plasoianu <mihai@plasoianu.de>
Namespace
Composer\CommandCode
protected function execute(InputInterface $input, OutputInterface $output) : int {
$this->versionParser = new VersionParser();
if ($input->getOption('tree')) {
$this->initStyles($output);
}
$composer = $this->tryComposer();
$io = $this->getIO();
if ($input->getOption('installed') && !$input->getOption('self')) {
$io->writeError('<warning>You are using the deprecated option "installed". Only installed packages are shown by default now. The --all option can be used to show all packages.</warning>');
}
if ($input->getOption('outdated')) {
$input->setOption('latest', true);
}
elseif (count($input->getOption('ignore')) > 0) {
$io->writeError('<warning>You are using the option "ignore" for action other than "outdated", it will be ignored.</warning>');
}
if ($input->getOption('direct') && ($input->getOption('all') || $input->getOption('available') || $input->getOption('platform'))) {
$io->writeError('The --direct (-D) option is not usable in combination with --all, --platform (-p) or --available (-a)');
return 1;
}
if ($input->getOption('tree') && ($input->getOption('all') || $input->getOption('available'))) {
$io->writeError('The --tree (-t) option is not usable in combination with --all or --available (-a)');
return 1;
}
if (count(array_filter([
$input->getOption('patch-only'),
$input->getOption('minor-only'),
$input->getOption('major-only'),
])) > 1) {
$io->writeError('Only one of --major-only, --minor-only or --patch-only can be used at once');
return 1;
}
if ($input->getOption('tree') && $input->getOption('latest')) {
$io->writeError('The --tree (-t) option is not usable in combination with --latest (-l)');
return 1;
}
if ($input->getOption('tree') && $input->getOption('path')) {
$io->writeError('The --tree (-t) option is not usable in combination with --path (-P)');
return 1;
}
$format = $input->getOption('format');
if (!in_array($format, [
'text',
'json',
])) {
$io->writeError(sprintf('Unsupported format "%s". See help for supported formats.', $format));
return 1;
}
$platformReqFilter = $this->getPlatformRequirementFilter($input);
// init repos
$platformOverrides = [];
if ($composer) {
$platformOverrides = $composer->getConfig()
->get('platform');
}
$platformRepo = new PlatformRepository([], $platformOverrides);
$lockedRepo = null;
if ($input->getOption('self') && !$input->getOption('installed') && !$input->getOption('locked')) {
$package = clone $this->requireComposer()
->getPackage();
if ($input->getOption('name-only')) {
$io->write($package->getName());
return 0;
}
if ($input->getArgument('package')) {
throw new \InvalidArgumentException('You cannot use --self together with a package name');
}
$repos = $installedRepo = new InstalledRepository([
new RootPackageRepository($package),
]);
}
elseif ($input->getOption('platform')) {
$repos = $installedRepo = new InstalledRepository([
$platformRepo,
]);
}
elseif ($input->getOption('available')) {
$installedRepo = new InstalledRepository([
$platformRepo,
]);
if ($composer) {
$repos = new CompositeRepository($composer->getRepositoryManager()
->getRepositories());
$installedRepo->addRepository($composer->getRepositoryManager()
->getLocalRepository());
}
else {
$defaultRepos = RepositoryFactory::defaultReposWithDefaultManager($io);
$repos = new CompositeRepository($defaultRepos);
$io->writeError('No composer.json found in the current directory, showing available packages from ' . implode(', ', array_keys($defaultRepos)));
}
}
elseif ($input->getOption('all') && $composer) {
$localRepo = $composer->getRepositoryManager()
->getLocalRepository();
$locker = $composer->getLocker();
if ($locker->isLocked()) {
$lockedRepo = $locker->getLockedRepository(true);
$installedRepo = new InstalledRepository([
$lockedRepo,
$localRepo,
$platformRepo,
]);
}
else {
$installedRepo = new InstalledRepository([
$localRepo,
$platformRepo,
]);
}
$repos = new CompositeRepository(array_merge([
new FilterRepository($installedRepo, [
'canonical' => false,
]),
], $composer->getRepositoryManager()
->getRepositories()));
}
elseif ($input->getOption('all')) {
$defaultRepos = RepositoryFactory::defaultReposWithDefaultManager($io);
$io->writeError('No composer.json found in the current directory, showing available packages from ' . implode(', ', array_keys($defaultRepos)));
$installedRepo = new InstalledRepository([
$platformRepo,
]);
$repos = new CompositeRepository(array_merge([
$installedRepo,
], $defaultRepos));
}
elseif ($input->getOption('locked')) {
if (!$composer || !$composer->getLocker()
->isLocked()) {
throw new \UnexpectedValueException('A valid composer.json and composer.lock files is required to run this command with --locked');
}
$locker = $composer->getLocker();
$lockedRepo = $locker->getLockedRepository(!$input->getOption('no-dev'));
if ($input->getOption('self')) {
$lockedRepo->addPackage(clone $composer->getPackage());
}
$repos = $installedRepo = new InstalledRepository([
$lockedRepo,
]);
}
else {
// --installed / default case
if (!$composer) {
$composer = $this->requireComposer();
}
$rootPkg = $composer->getPackage();
$rootRepo = new InstalledArrayRepository();
if ($input->getOption('self')) {
$rootRepo = new RootPackageRepository(clone $rootPkg);
}
if ($input->getOption('no-dev')) {
$packages = RepositoryUtils::filterRequiredPackages($composer->getRepositoryManager()
->getLocalRepository()
->getPackages(), $rootPkg);
$repos = $installedRepo = new InstalledRepository([
$rootRepo,
new InstalledArrayRepository(array_map(static function ($pkg) : PackageInterface {
return clone $pkg;
}, $packages)),
]);
}
else {
$repos = $installedRepo = new InstalledRepository([
$rootRepo,
$composer->getRepositoryManager()
->getLocalRepository(),
]);
}
if (!$installedRepo->getPackages()) {
$hasNonPlatformReqs = static function (array $reqs) : bool {
return (bool) array_filter(array_keys($reqs), function (string $name) {
return !PlatformRepository::isPlatformPackage($name);
});
};
if ($hasNonPlatformReqs($rootPkg->getRequires()) || $hasNonPlatformReqs($rootPkg->getDevRequires())) {
$io->writeError('<warning>No dependencies installed. Try running composer install or update.</warning>');
}
}
}
if ($composer) {
$commandEvent = new CommandEvent(PluginEvents::COMMAND, 'show', $input, $output);
$composer->getEventDispatcher()
->dispatch($commandEvent->getName(), $commandEvent);
}
if ($input->getOption('latest') && null === $composer) {
$io->writeError('No composer.json found in the current directory, disabling "latest" option');
$input->setOption('latest', false);
}
$packageFilter = $input->getArgument('package');
// show single package or single version
if (isset($package)) {
$versions = [
$package->getPrettyVersion() => $package->getVersion(),
];
}
elseif (null !== $packageFilter && !str_contains($packageFilter, '*')) {
[
$package,
$versions,
] = $this->getPackage($installedRepo, $repos, $packageFilter, $input->getArgument('version'));
if (isset($package) && $input->getOption('direct')) {
if (!in_array($package->getName(), $this->getRootRequires(), true)) {
throw new \InvalidArgumentException('Package "' . $package->getName() . '" is installed but not a direct dependent of the root package.');
}
}
if (!isset($package)) {
$options = $input->getOptions();
$hint = '';
if ($input->getOption('locked')) {
$hint .= ' in lock file';
}
if (isset($options['working-dir'])) {
$hint .= ' in ' . $options['working-dir'] . '/composer.json';
}
if (PlatformRepository::isPlatformPackage($packageFilter) && !$input->getOption('platform')) {
$hint .= ', try using --platform (-p) to show platform packages';
}
if (!$input->getOption('all') && !$input->getOption('available')) {
$hint .= ', try using --available (-a) to show all available packages';
}
throw new \InvalidArgumentException('Package "' . $packageFilter . '" not found' . $hint . '.');
}
}
if (isset($package)) {
assert(isset($versions));
$exitCode = 0;
if ($input->getOption('tree')) {
$arrayTree = $this->generatePackageTree($package, $installedRepo, $repos);
if ('json' === $format) {
$io->write(JsonFile::encode([
'installed' => [
$arrayTree,
],
]));
}
else {
$this->displayPackageTree([
$arrayTree,
]);
}
return $exitCode;
}
$latestPackage = null;
if ($input->getOption('latest')) {
$latestPackage = $this->findLatestPackage($package, $composer, $platformRepo, $input->getOption('major-only'), $input->getOption('minor-only'), $input->getOption('patch-only'), $platformReqFilter);
}
if ($input->getOption('outdated') && $input->getOption('strict') && null !== $latestPackage && $latestPackage->getFullPrettyVersion() !== $package->getFullPrettyVersion() && (!$latestPackage instanceof CompletePackageInterface || !$latestPackage->isAbandoned())) {
$exitCode = 1;
}
if ($input->getOption('path')) {
$io->write($package->getName(), false);
$path = $composer->getInstallationManager()
->getInstallPath($package);
if (is_string($path)) {
$io->write(' ' . strtok(realpath($path), "\r\n"));
}
else {
$io->write(' null');
}
return $exitCode;
}
if ('json' === $format) {
$this->printPackageInfoAsJson($package, $versions, $installedRepo, $latestPackage ?: null);
}
else {
$this->printPackageInfo($package, $versions, $installedRepo, $latestPackage ?: null);
}
return $exitCode;
}
// show tree view if requested
if ($input->getOption('tree')) {
$rootRequires = $this->getRootRequires();
$packages = $installedRepo->getPackages();
usort($packages, static function (BasePackage $a, BasePackage $b) : int {
return strcmp((string) $a, (string) $b);
});
$arrayTree = [];
foreach ($packages as $package) {
if (in_array($package->getName(), $rootRequires, true)) {
$arrayTree[] = $this->generatePackageTree($package, $installedRepo, $repos);
}
}
if ('json' === $format) {
$io->write(JsonFile::encode([
'installed' => $arrayTree,
]));
}
else {
$this->displayPackageTree($arrayTree);
}
return 0;
}
// list packages
/** @var array<string, array<string, string|CompletePackageInterface>> $packages */
$packages = [];
$packageFilterRegex = null;
if (null !== $packageFilter) {
$packageFilterRegex = '{^' . str_replace('\\*', '.*?', preg_quote($packageFilter)) . '$}i';
}
$packageListFilter = null;
if ($input->getOption('direct')) {
$packageListFilter = $this->getRootRequires();
}
if ($input->getOption('path') && null === $composer) {
$io->writeError('No composer.json found in the current directory, disabling "path" option');
$input->setOption('path', false);
}
foreach (RepositoryUtils::flattenRepositories($repos) as $repo) {
if ($repo === $platformRepo) {
$type = 'platform';
}
elseif ($lockedRepo !== null && $repo === $lockedRepo) {
$type = 'locked';
}
elseif ($repo === $installedRepo || in_array($repo, $installedRepo->getRepositories(), true)) {
$type = 'installed';
}
else {
$type = 'available';
}
if ($repo instanceof ComposerRepository) {
foreach ($repo->getPackageNames($packageFilter) as $name) {
$packages[$type][$name] = $name;
}
}
else {
foreach ($repo->getPackages() as $package) {
if (!isset($packages[$type][$package->getName()]) || !is_object($packages[$type][$package->getName()]) || version_compare($packages[$type][$package->getName()]
->getVersion(), $package->getVersion(), '<')) {
while ($package instanceof AliasPackage) {
$package = $package->getAliasOf();
}
if (!$packageFilterRegex || Preg::isMatch($packageFilterRegex, $package->getName())) {
if (null === $packageListFilter || in_array($package->getName(), $packageListFilter, true)) {
$packages[$type][$package->getName()] = $package;
}
}
}
}
if ($repo === $platformRepo) {
foreach ($platformRepo->getDisabledPackages() as $name => $package) {
$packages[$type][$name] = $package;
}
}
}
}
$showAllTypes = $input->getOption('all');
$showLatest = $input->getOption('latest');
$showMajorOnly = $input->getOption('major-only');
$showMinorOnly = $input->getOption('minor-only');
$showPatchOnly = $input->getOption('patch-only');
$ignoredPackagesRegex = BasePackage::packageNamesToRegexp(array_map('strtolower', $input->getOption('ignore')));
$indent = $showAllTypes ? ' ' : '';
/** @var PackageInterface[] $latestPackages */
$latestPackages = [];
$exitCode = 0;
$viewData = [];
$viewMetaData = [];
$writeVersion = false;
$writeDescription = false;
foreach ([
'platform' => true,
'locked' => true,
'available' => false,
'installed' => true,
] as $type => $showVersion) {
if (isset($packages[$type])) {
ksort($packages[$type]);
$nameLength = $versionLength = $latestLength = $releaseDateLength = 0;
if ($showLatest && $showVersion) {
foreach ($packages[$type] as $package) {
if (is_object($package) && !Preg::isMatch($ignoredPackagesRegex, $package->getPrettyName())) {
$latestPackage = $this->findLatestPackage($package, $composer, $platformRepo, $showMajorOnly, $showMinorOnly, $showPatchOnly, $platformReqFilter);
if ($latestPackage === null) {
continue;
}
$latestPackages[$package->getPrettyName()] = $latestPackage;
}
}
}
$writePath = !$input->getOption('name-only') && $input->getOption('path');
$writeVersion = !$input->getOption('name-only') && !$input->getOption('path') && $showVersion;
$writeLatest = $writeVersion && $showLatest;
$writeDescription = !$input->getOption('name-only') && !$input->getOption('path');
$writeReleaseDate = $writeLatest && ($input->getOption('sort-by-age') || $format === 'json');
$hasOutdatedPackages = false;
if ($input->getOption('sort-by-age')) {
usort($packages[$type], function ($a, $b) {
if (is_object($a) && is_object($b)) {
return $a->getReleaseDate() <=> $b->getReleaseDate();
}
return 0;
});
}
$viewData[$type] = [];
foreach ($packages[$type] as $package) {
$packageViewData = [];
if (is_object($package)) {
$latestPackage = null;
if ($showLatest && isset($latestPackages[$package->getPrettyName()])) {
$latestPackage = $latestPackages[$package->getPrettyName()];
}
// Determine if Composer is checking outdated dependencies and if current package should trigger non-default exit code
$packageIsUpToDate = $latestPackage && $latestPackage->getFullPrettyVersion() === $package->getFullPrettyVersion() && (!$latestPackage instanceof CompletePackageInterface || !$latestPackage->isAbandoned());
// When using --major-only, and no bigger version than current major is found then it is considered up to date
$packageIsUpToDate = $packageIsUpToDate || $latestPackage === null && $showMajorOnly;
$packageIsIgnored = Preg::isMatch($ignoredPackagesRegex, $package->getPrettyName());
if ($input->getOption('outdated') && ($packageIsUpToDate || $packageIsIgnored)) {
continue;
}
if ($input->getOption('outdated') || $input->getOption('strict')) {
$hasOutdatedPackages = true;
}
$packageViewData['name'] = $package->getPrettyName();
$packageViewData['direct-dependency'] = in_array($package->getName(), $this->getRootRequires(), true);
if ($format !== 'json' || true !== $input->getOption('name-only')) {
$packageViewData['homepage'] = $package instanceof CompletePackageInterface ? $package->getHomepage() : null;
$packageViewData['source'] = PackageInfo::getViewSourceUrl($package);
}
$nameLength = max($nameLength, strlen($packageViewData['name']));
if ($writeVersion) {
$packageViewData['version'] = $package->getFullPrettyVersion();
if ($format === 'text') {
$packageViewData['version'] = ltrim($packageViewData['version'], 'v');
}
$versionLength = max($versionLength, strlen($packageViewData['version']));
}
if ($writeReleaseDate) {
if ($package->getReleaseDate() !== null) {
$packageViewData['release-age'] = str_replace(' ago', ' old', $this->getRelativeTime($package->getReleaseDate()));
if (!str_contains($packageViewData['release-age'], ' old')) {
$packageViewData['release-age'] = 'from ' . $packageViewData['release-age'];
}
$releaseDateLength = max($releaseDateLength, strlen($packageViewData['release-age']));
$packageViewData['release-date'] = $package->getReleaseDate()
->format(DateTimeInterface::ATOM);
}
else {
$packageViewData['release-age'] = '';
$packageViewData['release-date'] = '';
}
}
if ($writeLatest && $latestPackage) {
$packageViewData['latest'] = $latestPackage->getFullPrettyVersion();
if ($format === 'text') {
$packageViewData['latest'] = ltrim($packageViewData['latest'], 'v');
}
$packageViewData['latest-status'] = $this->getUpdateStatus($latestPackage, $package);
$latestLength = max($latestLength, strlen($packageViewData['latest']));
if ($latestPackage->getReleaseDate() !== null) {
$packageViewData['latest-release-date'] = $latestPackage->getReleaseDate()
->format(DateTimeInterface::ATOM);
}
else {
$packageViewData['latest-release-date'] = '';
}
}
elseif ($writeLatest) {
$packageViewData['latest'] = '[none matched]';
$packageViewData['latest-status'] = 'up-to-date';
$latestLength = max($latestLength, strlen($packageViewData['latest']));
}
if ($writeDescription && $package instanceof CompletePackageInterface) {
$packageViewData['description'] = $package->getDescription();
}
if ($writePath) {
$path = $composer->getInstallationManager()
->getInstallPath($package);
if (is_string($path)) {
$packageViewData['path'] = strtok(realpath($path), "\r\n");
}
else {
$packageViewData['path'] = null;
}
}
$packageIsAbandoned = false;
if ($latestPackage instanceof CompletePackageInterface && $latestPackage->isAbandoned()) {
$replacementPackageName = $latestPackage->getReplacementPackage();
$replacement = $replacementPackageName !== null ? 'Use ' . $latestPackage->getReplacementPackage() . ' instead' : 'No replacement was suggested';
$packageWarning = sprintf('Package %s is abandoned, you should avoid using it. %s.', $package->getPrettyName(), $replacement);
$packageViewData['warning'] = $packageWarning;
$packageIsAbandoned = $replacementPackageName ?? true;
}
$packageViewData['abandoned'] = $packageIsAbandoned;
}
else {
$packageViewData['name'] = $package;
$nameLength = max($nameLength, strlen($package));
}
$viewData[$type][] = $packageViewData;
}
$viewMetaData[$type] = [
'nameLength' => $nameLength,
'versionLength' => $versionLength,
'latestLength' => $latestLength,
'releaseDateLength' => $releaseDateLength,
'writeLatest' => $writeLatest,
'writeReleaseDate' => $writeReleaseDate,
];
if ($input->getOption('strict') && $hasOutdatedPackages) {
$exitCode = 1;
break;
}
}
}
if ('json' === $format) {
$io->write(JsonFile::encode($viewData));
}
else {
if ($input->getOption('latest') && array_filter($viewData)) {
if (!$io->isDecorated()) {
$io->writeError('Legend:');
$io->writeError('! patch or minor release available - update recommended');
$io->writeError('~ major release available - update possible');
if (!$input->getOption('outdated')) {
$io->writeError('= up to date version');
}
}
else {
$io->writeError('<info>Color legend:</info>');
$io->writeError('- <highlight>patch or minor</highlight> release available - update recommended');
$io->writeError('- <comment>major</comment> release available - update possible');
if (!$input->getOption('outdated')) {
$io->writeError('- <info>up to date</info> version');
}
}
}
$width = $this->getTerminalWidth();
foreach ($viewData as $type => $packages) {
$nameLength = $viewMetaData[$type]['nameLength'];
$versionLength = $viewMetaData[$type]['versionLength'];
$latestLength = $viewMetaData[$type]['latestLength'];
$releaseDateLength = $viewMetaData[$type]['releaseDateLength'];
$writeLatest = $viewMetaData[$type]['writeLatest'];
$writeReleaseDate = $viewMetaData[$type]['writeReleaseDate'];
$versionFits = $nameLength + $versionLength + 3 <= $width;
$latestFits = $nameLength + $versionLength + $latestLength + 3 <= $width;
$releaseDateFits = $nameLength + $versionLength + $latestLength + $releaseDateLength + 3 <= $width;
$descriptionFits = $nameLength + $versionLength + $latestLength + $releaseDateLength + 24 <= $width;
if ($latestFits && !$io->isDecorated()) {
$latestLength += 2;
}
if ($showAllTypes) {
if ('available' === $type) {
$io->write('<comment>' . $type . '</comment>:');
}
else {
$io->write('<info>' . $type . '</info>:');
}
}
if ($writeLatest && !$input->getOption('direct')) {
$directDeps = [];
$transitiveDeps = [];
foreach ($packages as $pkg) {
if ($pkg['direct-dependency'] ?? false) {
$directDeps[] = $pkg;
}
else {
$transitiveDeps[] = $pkg;
}
}
$io->writeError('');
$io->writeError('<info>Direct dependencies required in composer.json:</>');
if (\count($directDeps) > 0) {
$this->printPackages($io, $directDeps, $indent, $writeVersion && $versionFits, $latestFits, $writeDescription && $descriptionFits, $width, $versionLength, $nameLength, $latestLength, $writeReleaseDate && $releaseDateFits, $releaseDateLength);
}
else {
$io->writeError('Everything up to date');
}
$io->writeError('');
$io->writeError('<info>Transitive dependencies not required in composer.json:</>');
if (\count($transitiveDeps) > 0) {
$this->printPackages($io, $transitiveDeps, $indent, $writeVersion && $versionFits, $latestFits, $writeDescription && $descriptionFits, $width, $versionLength, $nameLength, $latestLength, $writeReleaseDate && $releaseDateFits, $releaseDateLength);
}
else {
$io->writeError('Everything up to date');
}
}
else {
if ($writeLatest && \count($packages) === 0) {
$io->writeError('All your direct dependencies are up to date');
}
else {
$this->printPackages($io, $packages, $indent, $writeVersion && $versionFits, $writeLatest && $latestFits, $writeDescription && $descriptionFits, $width, $versionLength, $nameLength, $latestLength, $writeReleaseDate && $releaseDateFits, $releaseDateLength);
}
}
if ($showAllTypes) {
$io->write('');
}
}
}
return $exitCode;
}