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

Breadcrumb

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

class BaseDependencyCommand

Base implementation for commands mapping dependency relationships.

@author Niels Keurentjes <niels.keurentjes@omines.com>

Hierarchy

  • class \Symfony\Component\Console\Command\Command
    • class \Composer\Command\BaseCommand extends \Symfony\Component\Console\Command\Command
      • class \Composer\Command\BaseDependencyCommand extends \Composer\Command\BaseCommand

Expanded class hierarchy of BaseDependencyCommand

File

vendor/composer/composer/src/Composer/Command/BaseDependencyCommand.php, line 42

Namespace

Composer\Command
View source
abstract class BaseDependencyCommand extends BaseCommand {
    protected const ARGUMENT_PACKAGE = 'package';
    protected const ARGUMENT_CONSTRAINT = 'version';
    protected const OPTION_RECURSIVE = 'recursive';
    protected const OPTION_TREE = 'tree';
    
    /** @var string[] */
    protected $colors;
    
    /**
     * Execute the command.
     *
     * @param  bool            $inverted Whether to invert matching process (why-not vs why behaviour)
     * @return int             Exit code of the operation.
     */
    protected function doExecute(InputInterface $input, OutputInterface $output, bool $inverted = false) : int {
        // Emit command event on startup
        $composer = $this->requireComposer();
        $commandEvent = new CommandEvent(PluginEvents::COMMAND, $this->getName(), $input, $output);
        $composer->getEventDispatcher()
            ->dispatch($commandEvent->getName(), $commandEvent);
        $repos = [];
        $repos[] = new RootPackageRepository(clone $composer->getPackage());
        if ($input->getOption('locked')) {
            $locker = $composer->getLocker();
            if (!$locker->isLocked()) {
                throw new \UnexpectedValueException('A valid composer.lock file is required to run this command with --locked');
            }
            $repos[] = $locker->getLockedRepository(true);
            $repos[] = new PlatformRepository([], $locker->getPlatformOverrides());
        }
        else {
            $localRepo = $composer->getRepositoryManager()
                ->getLocalRepository();
            $rootPkg = $composer->getPackage();
            if (count($localRepo->getPackages()) === 0 && (count($rootPkg->getRequires()) > 0 || count($rootPkg->getDevRequires()) > 0)) {
                $output->writeln('<warning>No dependencies installed. Try running composer install or update, or use --locked.</warning>');
                return 1;
            }
            $repos[] = $localRepo;
            $platformOverrides = $composer->getConfig()
                ->get('platform') ?: [];
            $repos[] = new PlatformRepository([], $platformOverrides);
        }
        $installedRepo = new InstalledRepository($repos);
        // Parse package name and constraint
        $needle = $input->getArgument(self::ARGUMENT_PACKAGE);
        $textConstraint = $input->hasArgument(self::ARGUMENT_CONSTRAINT) ? $input->getArgument(self::ARGUMENT_CONSTRAINT) : '*';
        // Find packages that are or provide the requested package first
        $packages = $installedRepo->findPackagesWithReplacersAndProviders($needle);
        if (empty($packages)) {
            throw new \InvalidArgumentException(sprintf('Could not find package "%s" in your project', $needle));
        }
        // If the version we ask for is not installed then we need to locate it in remote repos and add it.
        // This is needed for why-not to resolve conflicts from an uninstalled version against installed packages.
        $matchedPackage = $installedRepo->findPackage($needle, $textConstraint);
        if (!$matchedPackage) {
            $defaultRepos = new CompositeRepository(RepositoryFactory::defaultRepos($this->getIO(), $composer->getConfig(), $composer->getRepositoryManager()));
            if ($match = $defaultRepos->findPackage($needle, $textConstraint)) {
                $installedRepo->addRepository(new InstalledArrayRepository([
                    clone $match,
                ]));
            }
            elseif (PlatformRepository::isPlatformPackage($needle)) {
                $parser = new VersionParser();
                $constraint = $parser->parseConstraints($textConstraint);
                if ($constraint->getLowerBound() !== Bound::zero()) {
                    $tempPlatformPkg = new Package($needle, $constraint->getLowerBound()
                        ->getVersion(), $constraint->getLowerBound()
                        ->getVersion());
                    $installedRepo->addRepository(new InstalledArrayRepository([
                        $tempPlatformPkg,
                    ]));
                }
            }
            else {
                $this->getIO()
                    ->writeError('<error>Package "' . $needle . '" could not be found with constraint "' . $textConstraint . '", results below will most likely be incomplete.</error>');
            }
        }
        elseif (PlatformRepository::isPlatformPackage($needle)) {
            $extraNotice = '';
            if (($matchedPackage->getExtra()['config.platform'] ?? false) === true) {
                $extraNotice = ' (version provided by config.platform)';
            }
            $this->getIO()
                ->writeError('<info>Package "' . $needle . ' ' . $textConstraint . '" found in version "' . $matchedPackage->getPrettyVersion() . '"' . $extraNotice . '.</info>');
        }
        // Include replaced packages for inverted lookups as they are then the actual starting point to consider
        $needles = [
            $needle,
        ];
        if ($inverted) {
            foreach ($packages as $package) {
                $needles = array_merge($needles, array_map(static function (Link $link) : string {
                    return $link->getTarget();
                }, $package->getReplaces()));
            }
        }
        // Parse constraint if one was supplied
        if ('*' !== $textConstraint) {
            $versionParser = new VersionParser();
            $constraint = $versionParser->parseConstraints($textConstraint);
        }
        else {
            $constraint = null;
        }
        // Parse rendering options
        $renderTree = $input->getOption(self::OPTION_TREE);
        $recursive = $renderTree || $input->getOption(self::OPTION_RECURSIVE);
        $return = $inverted ? 1 : 0;
        // Resolve dependencies
        $results = $installedRepo->getDependents($needles, $constraint, $inverted, $recursive);
        if (empty($results)) {
            $extra = null !== $constraint ? sprintf(' in versions %smatching %s', $inverted ? 'not ' : '', $textConstraint) : '';
            $this->getIO()
                ->writeError(sprintf('<info>There is no installed package depending on "%s"%s</info>', $needle, $extra));
            $return = $inverted ? 0 : 1;
        }
        elseif ($renderTree) {
            $this->initStyles($output);
            $root = $packages[0];
            $this->getIO()
                ->write(sprintf('<info>%s</info> %s %s', $root->getPrettyName(), $root->getPrettyVersion(), $root instanceof CompletePackageInterface ? $root->getDescription() : ''));
            $this->printTree($results);
        }
        else {
            $this->printTable($output, $results);
        }
        if ($inverted && $input->hasArgument(self::ARGUMENT_CONSTRAINT) && !PlatformRepository::isPlatformPackage($needle)) {
            $composerCommand = 'update';
            foreach ($composer->getPackage()
                ->getRequires() as $rootRequirement) {
                if ($rootRequirement->getTarget() === $needle) {
                    $composerCommand = 'require';
                    break;
                }
            }
            foreach ($composer->getPackage()
                ->getDevRequires() as $rootRequirement) {
                if ($rootRequirement->getTarget() === $needle) {
                    $composerCommand = 'require --dev';
                    break;
                }
            }
            $this->getIO()
                ->writeError('Not finding what you were looking for? Try calling `composer ' . $composerCommand . ' "' . $needle . ':' . $textConstraint . '" --dry-run` to get another view on the problem.');
        }
        return $return;
    }
    
    /**
     * Assembles and prints a bottom-up table of the dependencies.
     *
     * @param array{PackageInterface, Link, array<mixed>|false}[] $results
     */
    protected function printTable(OutputInterface $output, array $results) : void {
        $table = [];
        $doubles = [];
        do {
            $queue = [];
            $rows = [];
            foreach ($results as $result) {
                
                /**
                 * @var PackageInterface $package
                 * @var Link             $link
                 */
                [
                    $package,
                    $link,
                    $children,
                ] = $result;
                $unique = (string) $link;
                if (isset($doubles[$unique])) {
                    continue;
                }
                $doubles[$unique] = true;
                $version = $package->getPrettyVersion() === RootPackage::DEFAULT_PRETTY_VERSION ? '-' : $package->getPrettyVersion();
                $packageUrl = PackageInfo::getViewSourceOrHomepageUrl($package);
                $nameWithLink = $packageUrl !== null ? '<href=' . OutputFormatter::escape($packageUrl) . '>' . $package->getPrettyName() . '</>' : $package->getPrettyName();
                $rows[] = [
                    $nameWithLink,
                    $version,
                    $link->getDescription(),
                    sprintf('%s (%s)', $link->getTarget(), $link->getPrettyConstraint()),
                ];
                if (is_array($children)) {
                    $queue = array_merge($queue, $children);
                }
            }
            $results = $queue;
            $table = array_merge($rows, $table);
        } while (\count($results) > 0);
        $this->renderTable($table, $output);
    }
    
    /**
     * Init styles for tree
     */
    protected function initStyles(OutputInterface $output) : void {
        $this->colors = [
            'green',
            'yellow',
            'cyan',
            'magenta',
            'blue',
        ];
        foreach ($this->colors as $color) {
            $style = new OutputFormatterStyle($color);
            $output->getFormatter()
                ->setStyle($color, $style);
        }
    }
    
    /**
     * Recursively prints a tree of the selected results.
     *
     * @param array{PackageInterface, Link, array<mixed>|false}[] $results Results to be printed at this level.
     * @param string  $prefix  Prefix of the current tree level.
     * @param int     $level   Current level of recursion.
     */
    protected function printTree(array $results, string $prefix = '', int $level = 1) : void {
        $count = count($results);
        $idx = 0;
        foreach ($results as $result) {
            [
                $package,
                $link,
                $children,
            ] = $result;
            $color = $this->colors[$level % count($this->colors)];
            $prevColor = $this->colors[($level - 1) % count($this->colors)];
            $isLast = ++$idx === $count;
            $versionText = $package->getPrettyVersion() === RootPackage::DEFAULT_PRETTY_VERSION ? '' : $package->getPrettyVersion();
            $packageUrl = PackageInfo::getViewSourceOrHomepageUrl($package);
            $nameWithLink = $packageUrl !== null ? '<href=' . OutputFormatter::escape($packageUrl) . '>' . $package->getPrettyName() . '</>' : $package->getPrettyName();
            $packageText = rtrim(sprintf('<%s>%s</%1$s> %s', $color, $nameWithLink, $versionText));
            $linkText = sprintf('%s <%s>%s</%2$s> %s', $link->getDescription(), $prevColor, $link->getTarget(), $link->getPrettyConstraint());
            $circularWarn = $children === false ? '(circular dependency aborted here)' : '';
            $this->writeTreeLine(rtrim(sprintf("%s%s%s (%s) %s", $prefix, $isLast ? '└──' : '├──', $packageText, $linkText, $circularWarn)));
            if (is_array($children)) {
                $this->printTree($children, $prefix . ($isLast ? '   ' : '│  '), $level + 1);
            }
        }
    }
    private function writeTreeLine(string $line) : void {
        $io = $this->getIO();
        if (!$io->isDecorated()) {
            $line = str_replace([
                '└',
                '├',
                '──',
                '│',
            ], [
                '`-',
                '|-',
                '-',
                '|',
            ], $line);
        }
        $io->write($line);
    }

}

Members

Title Sort descending Deprecated Modifiers Object type Summary Overriden Title Overrides
BaseCommand::$composer private property
BaseCommand::$io private property
BaseCommand::complete public function @inheritdoc Overrides Command::complete 1
BaseCommand::createComposerInstance protected function Calls {
BaseCommand::formatRequirements protected function
BaseCommand::getApplication public function Gets the application instance for this command. Overrides Command::getApplication
BaseCommand::getAuditFormat protected function @internal
BaseCommand::getComposer Deprecated public function
BaseCommand::getIO public function
BaseCommand::getPlatformRequirementFilter protected function
BaseCommand::getPreferredInstallOptions protected function Returns preferSource and preferDist values based on the configuration.
BaseCommand::getTerminalWidth protected function
BaseCommand::initialize protected function @inheritDoc Overrides Command::initialize 1
BaseCommand::isProxyCommand public function Whether or not this command is meant to call another command. 2
BaseCommand::normalizeRequirements protected function
BaseCommand::renderTable protected function
BaseCommand::requireComposer public function Retrieves the default Composer\Composer instance or throws
BaseCommand::resetComposer public function Removes the cached composer instance
BaseCommand::setComposer public function
BaseCommand::setIO public function
BaseCommand::tryComposer public function Retrieves the default Composer\Composer instance or null
BaseDependencyCommand::$colors protected property @var string[]
BaseDependencyCommand::ARGUMENT_CONSTRAINT protected constant
BaseDependencyCommand::ARGUMENT_PACKAGE protected constant
BaseDependencyCommand::doExecute protected function Execute the command.
BaseDependencyCommand::initStyles protected function Init styles for tree
BaseDependencyCommand::OPTION_RECURSIVE protected constant
BaseDependencyCommand::OPTION_TREE protected constant
BaseDependencyCommand::printTable protected function Assembles and prints a bottom-up table of the dependencies.
BaseDependencyCommand::printTree protected function Recursively prints a tree of the selected results.
BaseDependencyCommand::writeTreeLine private function
Command::$aliases private property 1
Command::$application private property
Command::$code private property
Command::$definition private property
Command::$description private property 1
Command::$fullDefinition private property
Command::$help private property
Command::$helperSet private property
Command::$hidden private property
Command::$ignoreValidationErrors private property 2
Command::$name private property
Command::$processTitle private property
Command::$synopsis private property
Command::$usages private property
Command::addArgument public function Adds an argument. 2
Command::addOption public function Adds an option. 2
Command::addUsage public function Add a command usage example, it&#039;ll be prefixed with the command name. 2
Command::configure protected function Configures the current command. 50
Command::execute protected function Executes the current command. 51
Command::FAILURE public constant
Command::getAliases public function Returns the aliases for the command.
Command::getDefaultDescription public static function
Command::getDefaultName public static function
Command::getDefinition public function Gets the InputDefinition attached to this Command. 2
Command::getDescription public function Returns the description for the command.
Command::getHelp public function Returns the help for the command. 2
Command::getHelper public function Gets a helper instance by name. 2
Command::getHelperSet public function Gets the helper set. 1
Command::getName public function Returns the command name.
Command::getNativeDefinition public function Gets the InputDefinition to be used to create representations of this Command. 2
Command::getProcessedHelp public function Returns the processed help for the command replacing the %command.name% and
%command.full_name% patterns with the real values dynamically.
2
Command::getSynopsis public function Returns the synopsis for the command. 2
Command::getUsages public function Returns alternative usages of the command. 2
Command::ignoreValidationErrors public function Ignores validation errors. 2
Command::interact protected function Interacts with the user. 5
Command::INVALID public constant
Command::isEnabled public function Checks whether the command is enabled or not in the current environment. 2
Command::isHidden public function
Command::mergeApplicationDefinition public function Merges the application definition with the command definition. 2
Command::run public function Runs the command. 4
Command::setAliases public function Sets the aliases for the command.
Command::setApplication public function 2
Command::setCode public function Sets the code to execute when running this command. 2
Command::setDefinition public function Sets an array of argument and option instances. 2
Command::setDescription public function Sets the description for the command.
Command::setHelp public function Sets the help for the command. 2
Command::setHelperSet public function 2
Command::setHidden public function
Command::setName public function Sets the name of the command.
Command::setProcessTitle public function Sets the process title of the command. 2
Command::SUCCESS public constant
Command::validateName private function Validates a command name.
Command::__construct public function 15

API Navigation

  • Drupal Core 11.1.x
  • Topics
  • Classes
  • Functions
  • Constants
  • Globals
  • Files
  • Namespaces
  • Deprecated
  • Services
RSS feed
Powered by Drupal