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
- class \Composer\Command\BaseCommand extends \Symfony\Component\Console\Command\Command
Expanded class hierarchy of BaseDependencyCommand
File
-
vendor/
composer/ composer/ src/ Composer/ Command/ BaseDependencyCommand.php, line 42
Namespace
Composer\CommandView 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'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 |