123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237 |
- <?php
- /*
- * This file is part of Composer.
- *
- * (c) Nils Adermann <naderman@naderman.de>
- * Jordi Boggiano <j.boggiano@seld.be>
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
- namespace Composer\Command;
- use Composer\DependencyResolver\Pool;
- use Composer\Package\Link;
- use Composer\Package\PackageInterface;
- use Composer\Repository\ArrayRepository;
- use Composer\Repository\CompositeRepository;
- use Composer\Repository\PlatformRepository;
- use Composer\Plugin\CommandEvent;
- use Composer\Plugin\PluginEvents;
- use Symfony\Component\Console\Formatter\OutputFormatterStyle;
- use Composer\Package\Version\VersionParser;
- use Symfony\Component\Console\Helper\Table;
- use Symfony\Component\Console\Input\InputArgument;
- use Symfony\Component\Console\Input\InputInterface;
- use Symfony\Component\Console\Input\InputOption;
- use Symfony\Component\Console\Output\OutputInterface;
- /**
- * Base implementation for commands mapping dependency relationships.
- *
- * @author Niels Keurentjes <niels.keurentjes@omines.com>
- */
- class BaseDependencyCommand extends BaseCommand
- {
- const ARGUMENT_PACKAGE = 'package';
- const ARGUMENT_CONSTRAINT = 'constraint';
- const OPTION_RECURSIVE = 'recursive';
- const OPTION_TREE = 'tree';
- protected $colors;
- /**
- * Set common options and arguments.
- */
- protected function configure()
- {
- $this->setDefinition(array(
- new InputArgument(self::ARGUMENT_PACKAGE, InputArgument::REQUIRED, 'Package to inspect'),
- new InputArgument(self::ARGUMENT_CONSTRAINT, InputArgument::OPTIONAL, 'Optional version constraint', '*'),
- new InputOption(self::OPTION_RECURSIVE, 'r', InputOption::VALUE_NONE, 'Recursively resolves up to the root package'),
- new InputOption(self::OPTION_TREE, 't', InputOption::VALUE_NONE, 'Prints the results as a nested tree'),
- ));
- }
- /**
- * Execute the command.
- *
- * @param InputInterface $input
- * @param OutputInterface $output
- * @param bool $inverted Whether to invert matching process (why-not vs why behaviour)
- * @return int|null Exit code of the operation.
- */
- protected function doExecute(InputInterface $input, OutputInterface $output, $inverted = false)
- {
- // Emit command event on startup
- $composer = $this->getComposer();
- $commandEvent = new CommandEvent(PluginEvents::COMMAND, $this->getName(), $input, $output);
- $composer->getEventDispatcher()->dispatch($commandEvent->getName(), $commandEvent);
- // Prepare repositories and set up a pool
- $platformOverrides = $composer->getConfig()->get('platform') ?: array();
- $repository = new CompositeRepository(array(
- new ArrayRepository(array($composer->getPackage())),
- $composer->getRepositoryManager()->getLocalRepository(),
- new PlatformRepository(array(), $platformOverrides),
- ));
- $pool = new Pool();
- $pool->addRepository($repository);
- // Parse package name and constraint
- list($needle, $textConstraint) = array_pad(
- explode(':', $input->getArgument(self::ARGUMENT_PACKAGE)),
- 2,
- $input->getArgument(self::ARGUMENT_CONSTRAINT)
- );
- // Find packages that are or provide the requested package first
- $packages = $pool->whatProvides($needle);
- if (empty($packages)) {
- throw new \InvalidArgumentException(sprintf('Could not find package "%s" in your project', $needle));
- }
- // Include replaced packages for inverted lookups as they are then the actual starting point to consider
- $needles = array($needle);
- if ($inverted) {
- foreach ($packages as $package) {
- $needles = array_merge($needles, array_map(function (Link $link) {
- 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);
- // Resolve dependencies
- $results = $repository->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));
- } elseif ($renderTree) {
- $this->initStyles($output);
- $root = $packages[0];
- $this->getIO()->write(sprintf('<info>%s</info> %s %s', $root->getPrettyName(), $root->getPrettyVersion(), $root->getDescription()));
- $this->printTree($results);
- } else {
- $this->printTable($output, $results);
- }
- return 0;
- }
- /**
- * Assembles and prints a bottom-up table of the dependencies.
- *
- * @param OutputInterface $output
- * @param array $results
- */
- protected function printTable(OutputInterface $output, $results)
- {
- $table = array();
- $doubles = array();
- do {
- $queue = array();
- $rows = array();
- foreach ($results as $result) {
- /**
- * @var PackageInterface $package
- * @var Link $link
- */
- list($package, $link, $children) = $result;
- $unique = (string) $link;
- if (isset($doubles[$unique])) {
- continue;
- }
- $doubles[$unique] = true;
- $version = (strpos($package->getPrettyVersion(), 'No version set') === 0) ? '-' : $package->getPrettyVersion();
- $rows[] = array($package->getPrettyName(), $version, $link->getDescription(), sprintf('%s (%s)', $link->getTarget(), $link->getPrettyConstraint()));
- $queue = array_merge($queue, $children);
- }
- $results = $queue;
- $table = array_merge($rows, $table);
- } while (!empty($results));
- // Render table
- $renderer = new Table($output);
- $renderer->setStyle('compact');
- $renderer->getStyle()->setVerticalBorderChar('');
- $renderer->getStyle()->setCellRowContentFormat('%s ');
- $renderer->setRows($table)->render();
- }
- /**
- * Init styles for tree
- *
- * @param OutputInterface $output
- */
- protected function initStyles(OutputInterface $output)
- {
- $this->colors = array(
- '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 $results
- * @param string $prefix
- */
- protected function printTree($results, $prefix = '', $level = 1)
- {
- $count = count($results);
- $idx = 0;
- foreach ($results as $key => $result) {
- /**
- * @var PackageInterface $package
- * @var Link $link
- */
- list($package, $link, $children) = $result;
- $color = $this->colors[$level % count($this->colors)];
- $prevColor = $this->colors[($level - 1) % count($this->colors)];
- $isLast = (++$idx == $count);
- $versionText = (strpos($package->getPrettyVersion(), 'No version set') === 0) ? '' : $package->getPrettyVersion();
- $packageText = rtrim(sprintf('<%s>%s</%1$s> %s', $color, $package->getPrettyName(), $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 ($children) {
- $this->printTree($children, $prefix . ($isLast ? ' ' : '│ '), $level + 1);
- }
- }
- }
- private function writeTreeLine($line)
- {
- $io = $this->getIO();
- if (!$io->isDecorated()) {
- $line = str_replace(array('└', '├', '──', '│'), array('`-', '|-', '-', '|'), $line);
- }
- $io->write($line);
- }
- }
|