Implemented Prohibits and Depends correctly now.
parent
692a3ed300
commit
75bb0d9b10
|
@ -0,0 +1,188 @@
|
|||
<?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 Composer\Semver\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';
|
||||
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
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) {
|
||||
$root = $packages[0];
|
||||
$this->getIO()->write(sprintf('<info>%s</info> %s %s', $root->getPrettyName(), $root->getPrettyVersion(), $root->getDescription()));
|
||||
$this->printTree($output, $results);
|
||||
} else {
|
||||
$this->printTable($output, $results);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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')->setRows($table)->render();
|
||||
}
|
||||
|
||||
/**
|
||||
* Recursively prints a tree of the selected results.
|
||||
*
|
||||
* @param OutputInterface $output
|
||||
* @param array $results
|
||||
* @param string $prefix
|
||||
*/
|
||||
protected function printTree(OutputInterface $output, $results, $prefix = '')
|
||||
{
|
||||
$count = count($results);
|
||||
$idx = 0;
|
||||
foreach($results as $key => $result) {
|
||||
/**
|
||||
* @var PackageInterface $package
|
||||
* @var Link $link
|
||||
*/
|
||||
list($package, $link, $children) = $result;
|
||||
$isLast = (++$idx == $count);
|
||||
$versionText = (strpos($package->getPrettyVersion(), 'No version set') === 0) ? '' : $package->getPrettyVersion();
|
||||
$packageText = rtrim(sprintf('%s %s', $package->getPrettyName(), $versionText));
|
||||
$linkText = implode(' ', array($link->getDescription(), $link->getTarget(), $link->getPrettyConstraint()));
|
||||
$output->write(sprintf("%s%s %s (%s)\n", $prefix, $isLast ? '`-' : '|-', $packageText, $linkText));
|
||||
$this->printTree($output, $children, $prefix . ($isLast ? ' ' : '| '));
|
||||
}
|
||||
}
|
||||
}
|
|
@ -12,39 +12,25 @@
|
|||
|
||||
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 Composer\Semver\VersionParser;
|
||||
use Symfony\Component\Console\Helper\Table;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Input\InputArgument;
|
||||
use Symfony\Component\Console\Input\InputOption;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
|
||||
/**
|
||||
* @author Niels Keurentjes <niels.keurentjes@omines.com>
|
||||
*/
|
||||
class DependsCommand extends BaseCommand
|
||||
class DependsCommand extends BaseDependencyCommand
|
||||
{
|
||||
/**
|
||||
* Configure command metadata.
|
||||
*/
|
||||
protected function configure()
|
||||
{
|
||||
parent::configure();
|
||||
|
||||
$this
|
||||
->setName('depends')
|
||||
->setAliases(array('why'))
|
||||
->setDescription('Shows which packages depend on the given package')
|
||||
->setDefinition(array(
|
||||
new InputArgument('package', InputArgument::REQUIRED, 'Package to inspect'),
|
||||
new InputOption('recursive', 'r', InputOption::VALUE_NONE, 'Recursively resolves up to the root package'),
|
||||
new InputOption('tree', 't', InputOption::VALUE_NONE, 'Prints the results as a nested tree'),
|
||||
new InputOption('match-constraint', 'm', InputOption::VALUE_REQUIRED, 'Filters the dependencies shown using this constraint', '*'),
|
||||
new InputOption('invert-match-constraint', 'i', InputOption::VALUE_NONE, 'Turns --match-constraint around into a blacklist instead of whitelist'),
|
||||
))
|
||||
->setDescription('Shows which packages cause the given package to be installed')
|
||||
->setHelp(<<<EOT
|
||||
Displays detailed information about where a package is referenced.
|
||||
|
||||
|
@ -55,120 +41,15 @@ EOT
|
|||
;
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute the function.
|
||||
*
|
||||
* @param InputInterface $input
|
||||
* @param OutputInterface $output
|
||||
* @return int|null
|
||||
*/
|
||||
protected function execute(InputInterface $input, OutputInterface $output)
|
||||
{
|
||||
// Emit command event on startup
|
||||
$composer = $this->getComposer();
|
||||
$commandEvent = new CommandEvent(PluginEvents::COMMAND, 'depends', $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);
|
||||
|
||||
// Find packages that are or provide the requested package first
|
||||
$needle = $input->getArgument('package');
|
||||
$packages = $pool->whatProvides($needle);
|
||||
if (empty($packages)) {
|
||||
throw new \InvalidArgumentException(sprintf('Could not find package "%s" in your project', $needle));
|
||||
}
|
||||
|
||||
// Parse options that are only relevant for the initial needle(s)
|
||||
if ('*' !== ($textConstraint = $input->getOption('match-constraint'))) {
|
||||
$versionParser = new VersionParser();
|
||||
$constraint = $versionParser->parseConstraints($textConstraint);
|
||||
} else {
|
||||
$constraint = null;
|
||||
}
|
||||
$matchInvert = $input->getOption('invert-match-constraint');
|
||||
|
||||
// Parse rendering options
|
||||
$renderTree = $input->getOption('tree');
|
||||
$recursive = $renderTree || $input->getOption('recursive');
|
||||
|
||||
// Resolve dependencies
|
||||
$results = $this->getDependents($needle, $repository->getPackages(), $constraint, $matchInvert, $recursive);
|
||||
if (empty($results)) {
|
||||
$extra = (null !== $constraint) ? sprintf(' in versions %smatching %s', $matchInvert ? 'not ' : '', $textConstraint) : '';
|
||||
$this->getIO()->writeError(sprintf('<info>There is no installed package depending on "%s"%s</info>',
|
||||
$needle, $extra));
|
||||
} elseif ($renderTree) {
|
||||
$root = $packages[0];
|
||||
$this->getIO()->write(sprintf('<info>%s</info> %s %s', $root->getPrettyName(), $root->getPrettyVersion(), $root->getDescription()));
|
||||
$this->printTree($output, $results);
|
||||
} else {
|
||||
$this->printTable($output, $results);
|
||||
}
|
||||
parent::doExecute($input, $output, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Assembles and prints a bottom-up table of the dependencies.
|
||||
*
|
||||
* @param OutputInterface $output
|
||||
* @param array $results
|
||||
*/
|
||||
private 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')->setRows($table)->render();
|
||||
}
|
||||
|
||||
/**
|
||||
* Recursively prints a tree of the selected results.
|
||||
*
|
||||
* @param OutputInterface $output
|
||||
* @param array $results
|
||||
* @param string $prefix
|
||||
*/
|
||||
public function printTree(OutputInterface $output, $results, $prefix = '')
|
||||
{
|
||||
$count = count($results);
|
||||
$idx = 0;
|
||||
foreach($results as $key => $result) {
|
||||
/**
|
||||
* @var PackageInterface $package
|
||||
* @var Link $link
|
||||
*/
|
||||
list($package, $link, $children) = $result;
|
||||
$isLast = (++$idx == $count);
|
||||
$versionText = (strpos($package->getPrettyVersion(), 'No version set') === 0) ? '' : $package->getPrettyVersion();
|
||||
$packageText = rtrim(sprintf('%s %s', $package->getPrettyName(), $versionText));
|
||||
$linkText = implode(' ', array($link->getDescription(), $link->getTarget(), $link->getPrettyConstraint()));
|
||||
$output->write(sprintf("%s%s %s (%s)\n", $prefix, $isLast ? '`-' : '|-', $packageText, $linkText));
|
||||
$this->printTree($output, $children, $prefix . ($isLast ? ' ' : '| '));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
|
@ -0,0 +1,55 @@
|
|||
<?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 Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
|
||||
/**
|
||||
* @author Niels Keurentjes <niels.keurentjes@omines.com>
|
||||
*/
|
||||
class ProhibitsCommand extends BaseDependencyCommand
|
||||
{
|
||||
/**
|
||||
* Configure command metadata.
|
||||
*/
|
||||
protected function configure()
|
||||
{
|
||||
parent::configure();
|
||||
|
||||
$this
|
||||
->setName('prohibits')
|
||||
->setAliases(array('why-not'))
|
||||
->setDescription('Shows which packages prevent the given package from being installed')
|
||||
->setHelp(<<<EOT
|
||||
Displays detailed information about why a package cannot be installed.
|
||||
|
||||
<info>php composer.phar prohibits composer/composer</info>
|
||||
|
||||
EOT
|
||||
)
|
||||
;
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute the function.
|
||||
*
|
||||
* @param InputInterface $input
|
||||
* @param OutputInterface $output
|
||||
* @return int|null
|
||||
*/
|
||||
protected function execute(InputInterface $input, OutputInterface $output)
|
||||
{
|
||||
parent::doExecute($input, $output, true);
|
||||
}
|
||||
}
|
|
@ -279,33 +279,35 @@ class Application extends BaseApplication
|
|||
}
|
||||
|
||||
/**
|
||||
* Initializes all the composer commands
|
||||
* Initializes all the composer commands.
|
||||
*/
|
||||
protected function getDefaultCommands()
|
||||
{
|
||||
$commands = parent::getDefaultCommands();
|
||||
$commands[] = new Command\AboutCommand();
|
||||
$commands[] = new Command\ConfigCommand();
|
||||
$commands[] = new Command\DependsCommand();
|
||||
$commands[] = new Command\InitCommand();
|
||||
$commands[] = new Command\InstallCommand();
|
||||
$commands[] = new Command\CreateProjectCommand();
|
||||
$commands[] = new Command\UpdateCommand();
|
||||
$commands[] = new Command\SearchCommand();
|
||||
$commands[] = new Command\ValidateCommand();
|
||||
$commands[] = new Command\ShowCommand();
|
||||
$commands[] = new Command\SuggestsCommand();
|
||||
$commands[] = new Command\RequireCommand();
|
||||
$commands[] = new Command\DumpAutoloadCommand();
|
||||
$commands[] = new Command\StatusCommand();
|
||||
$commands[] = new Command\ArchiveCommand();
|
||||
$commands[] = new Command\DiagnoseCommand();
|
||||
$commands[] = new Command\RunScriptCommand();
|
||||
$commands[] = new Command\LicensesCommand();
|
||||
$commands[] = new Command\GlobalCommand();
|
||||
$commands[] = new Command\ClearCacheCommand();
|
||||
$commands[] = new Command\RemoveCommand();
|
||||
$commands[] = new Command\HomeCommand();
|
||||
$commands = array_merge(parent::getDefaultCommands(), array(
|
||||
new Command\AboutCommand(),
|
||||
new Command\ConfigCommand(),
|
||||
new Command\DependsCommand(),
|
||||
new Command\ProhibitsCommand(),
|
||||
new Command\InitCommand(),
|
||||
new Command\InstallCommand(),
|
||||
new Command\CreateProjectCommand(),
|
||||
new Command\UpdateCommand(),
|
||||
new Command\SearchCommand(),
|
||||
new Command\ValidateCommand(),
|
||||
new Command\ShowCommand(),
|
||||
new Command\SuggestsCommand(),
|
||||
new Command\RequireCommand(),
|
||||
new Command\DumpAutoloadCommand(),
|
||||
new Command\StatusCommand(),
|
||||
new Command\ArchiveCommand(),
|
||||
new Command\DiagnoseCommand(),
|
||||
new Command\RunScriptCommand(),
|
||||
new Command\LicensesCommand(),
|
||||
new Command\GlobalCommand(),
|
||||
new Command\ClearCacheCommand(),
|
||||
new Command\RemoveCommand(),
|
||||
new Command\HomeCommand(),
|
||||
));
|
||||
|
||||
if ('phar:' === substr(__FILE__, 0, 5)) {
|
||||
$commands[] = new Command\SelfUpdateCommand();
|
||||
|
|
|
@ -34,15 +34,19 @@ abstract class BaseRepository implements RepositoryInterface
|
|||
* @param bool $recurse Whether to recursively expand the requirement tree up to the root package.
|
||||
* @return array An associative array of arrays as described above.
|
||||
*/
|
||||
private function getDependents($needle, $constraint = null, $invert = false, $recurse = true)
|
||||
public function getDependents($needle, $constraint = null, $invert = false, $recurse = true)
|
||||
{
|
||||
$needles = is_array($needle) ? $needle : array($needle);
|
||||
$results = array();
|
||||
|
||||
// Loop over all currently installed packages.
|
||||
foreach ($this->getPackages() as $package) {
|
||||
// Requirements and replaces are both considered valid reasons for a package to be installed
|
||||
$links = $package->getRequires() + $package->getReplaces();
|
||||
$links = $package->getRequires();
|
||||
|
||||
// Replacements are considered valid reasons for a package to be installed during forward resolution
|
||||
if (!$invert) {
|
||||
$links += $package->getReplaces();
|
||||
}
|
||||
|
||||
// Require-dev is only relevant for the root package
|
||||
if ($package instanceof RootPackageInterface) {
|
||||
|
|
Loading…
Reference in New Issue