Skip to main content
Drupal API
User account menu
  • Log in

Breadcrumb

  1. Drupal Core 11.1.x
  2. ShowCommand.php

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\Command

Code

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;
}
RSS feed
Powered by Drupal