DependsCommand.php 7.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174
  1. <?php
  2. /*
  3. * This file is part of Composer.
  4. *
  5. * (c) Nils Adermann <naderman@naderman.de>
  6. * Jordi Boggiano <j.boggiano@seld.be>
  7. *
  8. * For the full copyright and license information, please view the LICENSE
  9. * file that was distributed with this source code.
  10. */
  11. namespace Composer\Command;
  12. use Composer\DependencyResolver\Pool;
  13. use Composer\Package\Link;
  14. use Composer\Package\PackageInterface;
  15. use Composer\Repository\ArrayRepository;
  16. use Composer\Repository\CompositeRepository;
  17. use Composer\Repository\PlatformRepository;
  18. use Composer\Plugin\CommandEvent;
  19. use Composer\Plugin\PluginEvents;
  20. use Composer\Semver\VersionParser;
  21. use Symfony\Component\Console\Helper\Table;
  22. use Symfony\Component\Console\Input\InputInterface;
  23. use Symfony\Component\Console\Input\InputArgument;
  24. use Symfony\Component\Console\Input\InputOption;
  25. use Symfony\Component\Console\Output\OutputInterface;
  26. /**
  27. * @author Niels Keurentjes <niels.keurentjes@omines.com>
  28. */
  29. class DependsCommand extends BaseCommand
  30. {
  31. protected function configure()
  32. {
  33. $this
  34. ->setName('depends')
  35. ->setAliases(array('why'))
  36. ->setDescription('Shows which packages depend on the given package')
  37. ->setDefinition(array(
  38. new InputArgument('package', InputArgument::REQUIRED, 'Package to inspect'),
  39. new InputOption('recursive', 'r', InputOption::VALUE_NONE, 'Recursively resolves up to the root package'),
  40. new InputOption('tree', 't', InputOption::VALUE_NONE, 'Prints the results as a nested tree'),
  41. new InputOption('match-constraint', 'm', InputOption::VALUE_REQUIRED, 'Filters the dependencies shown using this constraint', '*'),
  42. new InputOption('invert-match-constraint', 'i', InputOption::VALUE_NONE, 'Turns --match-constraint around into a blacklist instead of whitelist'),
  43. ))
  44. ->setHelp(<<<EOT
  45. Displays detailed information about where a package is referenced.
  46. <info>php composer.phar depends composer/composer</info>
  47. EOT
  48. )
  49. ;
  50. }
  51. protected function execute(InputInterface $input, OutputInterface $output)
  52. {
  53. // Emit command event on startup
  54. $composer = $this->getComposer();
  55. $commandEvent = new CommandEvent(PluginEvents::COMMAND, 'depends', $input, $output);
  56. $composer->getEventDispatcher()->dispatch($commandEvent->getName(), $commandEvent);
  57. // Prepare repositories and set up a pool
  58. $platformOverrides = $composer->getConfig()->get('platform') ?: array();
  59. $repository = new CompositeRepository(array(
  60. new ArrayRepository(array($composer->getPackage())),
  61. $composer->getRepositoryManager()->getLocalRepository(),
  62. new PlatformRepository(array(), $platformOverrides),
  63. ));
  64. $pool = new Pool();
  65. $pool->addRepository($repository);
  66. // Find packages that are or provide the requested package first
  67. $needle = $input->getArgument('package');
  68. $packages = $pool->whatProvides($needle);
  69. if (empty($packages)) {
  70. throw new \InvalidArgumentException(sprintf('Could not find package "%s" in your project', $needle));
  71. }
  72. // Parse options that are only relevant for the initial needle(s)
  73. if ('*' !== ($textConstraint = $input->getOption('match-constraint'))) {
  74. $versionParser = new VersionParser();
  75. $constraint = $versionParser->parseConstraints($textConstraint);
  76. } else {
  77. $constraint = null;
  78. }
  79. $matchInvert = $input->getOption('invert-match-constraint');
  80. // Parse rendering options
  81. $renderTree = $input->getOption('tree');
  82. $recursive = $renderTree || $input->getOption('recursive');
  83. // Resolve dependencies
  84. $results = $this->getDependents($needle, $repository->getPackages(), $constraint, $matchInvert, $recursive);
  85. if (empty($results)) {
  86. $extra = (null !== $constraint) ? sprintf(' in versions %smatching %s', $matchInvert ? 'not ' : '', $textConstraint) : '';
  87. $this->getIO()->writeError(sprintf('<info>There is no installed package depending on "%s"%s</info>',
  88. $needle, $extra));
  89. } elseif ($renderTree) {
  90. $root = $packages[0];
  91. $this->getIO()->write(sprintf('<info>%s</info> %s %s', $root->getPrettyName(), $root->getPrettyVersion(), $root->getDescription()));
  92. $this->printTree($output, $results);
  93. } else {
  94. $this->printTable($output, $results);
  95. }
  96. }
  97. /**
  98. * Assembles and prints a bottom-up table of the dependencies.
  99. *
  100. * @param OutputInterface $output
  101. * @param array $results
  102. */
  103. private function printTable(OutputInterface $output, $results)
  104. {
  105. $table = array();
  106. $doubles = array();
  107. do {
  108. $queue = array();
  109. $rows = array();
  110. foreach($results as $result) {
  111. /**
  112. * @var PackageInterface $package
  113. * @var Link $link
  114. */
  115. list($package, $link, $children) = $result;
  116. $unique = (string)$link;
  117. if (isset($doubles[$unique])) {
  118. continue;
  119. }
  120. $doubles[$unique] = true;
  121. $version = (strpos($package->getPrettyVersion(), 'No version set') === 0) ? '-' : $package->getPrettyVersion();
  122. $rows[] = array($package->getPrettyName(), $version, $link->getDescription(), sprintf('%s (%s)', $link->getTarget(), $link->getPrettyConstraint()));
  123. $queue = array_merge($queue, $children);
  124. }
  125. $results = $queue;
  126. $table = array_merge($rows, $table);
  127. } while(!empty($results));
  128. // Render table
  129. $renderer = new Table($output);
  130. $renderer->setStyle('compact')->setRows($table)->render();
  131. }
  132. /**
  133. * Recursively prints a tree of the selected results.
  134. *
  135. * @param OutputInterface $output
  136. * @param array $results
  137. * @param string $prefix
  138. */
  139. public function printTree(OutputInterface $output, $results, $prefix = '')
  140. {
  141. $count = count($results);
  142. $idx = 0;
  143. foreach($results as $key => $result) {
  144. /**
  145. * @var PackageInterface $package
  146. * @var Link $link
  147. */
  148. list($package, $link, $children) = $result;
  149. $isLast = (++$idx == $count);
  150. $versionText = (strpos($package->getPrettyVersion(), 'No version set') === 0) ? '' : $package->getPrettyVersion();
  151. $packageText = rtrim(sprintf('%s %s', $package->getPrettyName(), $versionText));
  152. $linkText = implode(' ', array($link->getDescription(), $link->getTarget(), $link->getPrettyConstraint()));
  153. $output->write(sprintf("%s%s %s (%s)\n", $prefix, $isLast ? '`-' : '|-', $packageText, $linkText));
  154. $this->printTree($output, $children, $prefix . ($isLast ? ' ' : '| '));
  155. }
  156. }
  157. }