Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Console] Add of hidden and deprecation option flags #54439

Open
wants to merge 12 commits into
base: 7.2
Choose a base branch
from
Open
2 changes: 2 additions & 0 deletions src/Symfony/Component/Console/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ CHANGELOG
---

* Add `ArgvInput::getRawTokens()`
* Add `InputOption::HIDDEN` flag to hide options
* Add `InputOption::DEPRECATED` flag to mark options as deprecated

7.0
---
Expand Down
29 changes: 28 additions & 1 deletion src/Symfony/Component/Console/Command/Command.php
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
use Symfony\Component\Console\Exception\ExceptionInterface;
use Symfony\Component\Console\Exception\InvalidArgumentException;
use Symfony\Component\Console\Exception\LogicException;
use Symfony\Component\Console\Helper\FormatterHelper;
use Symfony\Component\Console\Helper\HelperInterface;
use Symfony\Component\Console\Helper\HelperSet;
use Symfony\Component\Console\Input\InputArgument;
Expand Down Expand Up @@ -235,7 +236,8 @@ public function run(InputInterface $input, OutputInterface $output): int

// bind the input against the command specific arguments/options
try {
$input->bind($this->getDefinition());
$inputDefinition = $this->getDefinition();
$input->bind($inputDefinition);
} catch (ExceptionInterface $e) {
if (!$this->ignoreValidationErrors) {
throw $e;
Expand Down Expand Up @@ -273,6 +275,10 @@ public function run(InputInterface $input, OutputInterface $output): int

$input->validate();

if (isset($inputDefinition)) {
$this->writeDeprecationMessages($inputDefinition, $input, $output);
}

if ($this->code) {
$statusCode = ($this->code)($input, $output);
} else {
Expand Down Expand Up @@ -648,6 +654,27 @@ public function getHelper(string $name): HelperInterface
return $this->helperSet->get($name);
}

private function writeDeprecationMessages(InputDefinition $inputDefinition, InputInterface $input, OutputInterface $output): void
{
$deprecationMessages = [];
foreach ($inputDefinition->getOptions() as $inputOption) {
if ($inputOption->isDeprecated()) {
$optionNames = ['--' . $inputOption->getName()];
if (null !== $inputOption->getShortcut()) {
$optionNames[] = '-'.$inputOption->getShortcut();
}
if ($input->hasParameterOption($optionNames, true)) {
$deprecationMessages[] = sprintf('The option "%s" is deprecated.', implode('|', $optionNames));
}
}
}
if (!empty($deprecationMessages)) {
/** @var FormatterHelper $formatter */
$formatter = $this->getHelper('formatter');
$output->writeln($formatter->formatBlock($deprecationMessages, 'fg=black;bg=yellow', true));
}
}

/**
* Validates a command name.
*
Expand Down
2 changes: 2 additions & 0 deletions src/Symfony/Component/Console/Command/HelpCommand.php
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ protected function configure(): void
new InputArgument('command_name', InputArgument::OPTIONAL, 'The command name', 'help', fn () => array_keys((new ApplicationDescription($this->getApplication()))->getCommands())),
new InputOption('format', null, InputOption::VALUE_REQUIRED, 'The output format (txt, xml, json, or md)', 'txt', fn () => (new DescriptorHelper())->getFormats()),
new InputOption('raw', null, InputOption::VALUE_NONE, 'To output raw command help'),
new InputOption('show-hidden-options', null, InputOption::VALUE_NONE | InputOption::HIDDEN, 'Show hidden options'),
])
->setDescription('Display help for a command')
->setHelp(<<<'EOF'
Expand Down Expand Up @@ -67,6 +68,7 @@ protected function execute(InputInterface $input, OutputInterface $output): int
$helper->describe($output, $this->command, [
'format' => $input->getOption('format'),
'raw_text' => $input->getOption('raw'),
'show-hidden-options' => $input->getOption('show-hidden-options'),
]);

unset($this->command);
Expand Down
14 changes: 11 additions & 3 deletions src/Symfony/Component/Console/Completion/CompletionInput.php
Original file line number Diff line number Diff line change
Expand Up @@ -198,11 +198,19 @@ private function getOptionFromToken(string $optionToken): ?InputOption

if ('-' === ($optionToken[1] ?? ' ')) {
// long option name
return $this->definition->hasOption($optionName) ? $this->definition->getOption($optionName) : null;
if ($this->definition->hasOption($optionName) && !$this->definition->getOption($optionName)->isHidden()) {
return $this->definition->getOption($optionName);
}

return null;
}

// short option name
return $this->definition->hasShortcut($optionName[0]) ? $this->definition->getOptionForShortcut($optionName[0]) : null;
if ($this->definition->hasShortcut($optionName[0]) && !$this->definition->getOptionForShortcut($optionName[0])->isHidden()) {
return $this->definition->getOptionForShortcut($optionName[0]);
}

return null;
}

/**
Expand All @@ -226,7 +234,7 @@ private function isCursorFree(): bool
return $this->currentIndex >= $nrOfTokens;
}

public function __toString()
public function __toString(): string
flkasper marked this conversation as resolved.
Show resolved Hide resolved
{
$str = '';
foreach ($this->tokens as $i => $token) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,9 @@ public function suggestValues(array $values): static
*/
public function suggestOption(InputOption $option): static
{
$this->optionSuggestions[] = $option;
if (!$option->isHidden()) {
$this->optionSuggestions[] = $option;
}

return $this;
}
Expand Down
20 changes: 20 additions & 0 deletions src/Symfony/Component/Console/Descriptor/Descriptor.php
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,26 @@ public function describe(OutputInterface $output, object $object, array $options
};
}

/**
* Filter hidden options from list.
*
* @param array<string,InputOption> $inputOptions
*
* @return array<string,InputOption>
*/
protected function removeHiddenOptions(array $inputOptions, array $options = []): array
{
return array_filter($inputOptions, fn (InputOption $option) => !$this->skipHiddenOption($option, $options));
}

/**
* Should InputOption be skipped?
*/
protected function skipHiddenOption(InputOption $inputOption, array $options = []): bool
{
return $inputOption->isHidden() && !($options['show-hidden-options'] ?? false);
GromNaN marked this conversation as resolved.
Show resolved Hide resolved
}

protected function write(string $content, bool $decorated = false): void
{
$this->output->write($content, false, $decorated ? OutputInterface::OUTPUT_NORMAL : OutputInterface::OUTPUT_RAW);
Expand Down
28 changes: 20 additions & 8 deletions src/Symfony/Component/Console/Descriptor/JsonDescriptor.php
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,9 @@ protected function describeInputArgument(InputArgument $argument, array $options

protected function describeInputOption(InputOption $option, array $options = []): void
{
if ($this->skipHiddenOption($option, $options)) {
return;
}
$this->writeData($this->getInputOptionData($option), $options);
if ($option->isNegatable()) {
$this->writeData($this->getInputOptionData($option, true), $options);
Expand All @@ -41,12 +44,12 @@ protected function describeInputOption(InputOption $option, array $options = [])

protected function describeInputDefinition(InputDefinition $definition, array $options = []): void
{
$this->writeData($this->getInputDefinitionData($definition), $options);
$this->writeData($this->getInputDefinitionData($definition, $options), $options);
}

protected function describeCommand(Command $command, array $options = []): void
{
$this->writeData($this->getCommandData($command, $options['short'] ?? false), $options);
$this->writeData($this->getCommandData($command, $options), $options);
}

protected function describeApplication(Application $application, array $options = []): void
Expand All @@ -56,7 +59,7 @@ protected function describeApplication(Application $application, array $options
$commands = [];

foreach ($description->getCommands() as $command) {
$commands[] = $this->getCommandData($command, $options['short'] ?? false);
$commands[] = $this->getCommandData($command, $options);
}

$data = [];
Expand Down Expand Up @@ -101,12 +104,13 @@ private function getInputArgumentData(InputArgument $argument): array

private function getInputOptionData(InputOption $option, bool $negated = false): array
{
return $negated ? [
$data = $negated ? [
'name' => '--no-'.$option->getName(),
'shortcut' => '',
'accept_value' => false,
'is_value_required' => false,
'is_multiple' => false,
'is_deprecated' => $option->isDeprecated(),
'description' => 'Negate the "--'.$option->getName().'" option',
'default' => false,
] : [
Expand All @@ -115,20 +119,26 @@ private function getInputOptionData(InputOption $option, bool $negated = false):
'accept_value' => $option->acceptValue(),
'is_value_required' => $option->isValueRequired(),
'is_multiple' => $option->isArray(),
'is_deprecated' => $option->isDeprecated(),
'description' => preg_replace('/\s*[\r\n]\s*/', ' ', $option->getDescription()),
'default' => \INF === $option->getDefault() ? 'INF' : $option->getDefault(),
];
if (!$option->isDeprecated()) {
unset($data['is_deprecated']);
}

return $data;
}

private function getInputDefinitionData(InputDefinition $definition): array
private function getInputDefinitionData(InputDefinition $definition, array $options): array
{
$inputArguments = [];
foreach ($definition->getArguments() as $name => $argument) {
$inputArguments[$name] = $this->getInputArgumentData($argument);
}

$inputOptions = [];
foreach ($definition->getOptions() as $name => $option) {
foreach ($this->removeHiddenOptions($definition->getOptions(), $options) as $name => $option) {
$inputOptions[$name] = $this->getInputOptionData($option);
if ($option->isNegatable()) {
$inputOptions['no-'.$name] = $this->getInputOptionData($option, true);
Expand All @@ -138,8 +148,10 @@ private function getInputDefinitionData(InputDefinition $definition): array
return ['arguments' => $inputArguments, 'options' => $inputOptions];
}

private function getCommandData(Command $command, bool $short = false): array
private function getCommandData(Command $command, array $options = []): array
{
$short = $options['short'] ?? false;

$data = [
'name' => $command->getName(),
'description' => $command->getDescription(),
Expand All @@ -155,7 +167,7 @@ private function getCommandData(Command $command, bool $short = false): array
$data += [
'usage' => array_merge([$command->getSynopsis()], $command->getUsages(), $command->getAliases()),
'help' => $command->getProcessedHelp(),
'definition' => $this->getInputDefinitionData($command->getDefinition()),
'definition' => $this->getInputDefinitionData($command->getDefinition(), $options),
];
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@ protected function describeInputOption(InputOption $option, array $options = [])
.'* Accept value: '.($option->acceptValue() ? 'yes' : 'no')."\n"
.'* Is value required: '.($option->isValueRequired() ? 'yes' : 'no')."\n"
.'* Is multiple: '.($option->isArray() ? 'yes' : 'no')."\n"
.($option->isDeprecated() ? ('* Is deprecated: yes'."\n") : '')
.'* Is negatable: '.($option->isNegatable() ? 'yes' : 'no')."\n"
.'* Default: `'.str_replace("\n", '', var_export($option->getDefault(), true)).'`'
);
Expand All @@ -81,19 +82,20 @@ protected function describeInputDefinition(InputDefinition $definition, array $o
$this->write('### Arguments');
foreach ($definition->getArguments() as $argument) {
$this->write("\n\n");
$this->describeInputArgument($argument);
$this->describeInputArgument($argument, $options);
}
}

if (\count($definition->getOptions()) > 0) {
$inputOptions = $this->removeHiddenOptions($definition->getOptions(), $options);
if (!empty($inputOptions)) {
if ($showArguments) {
$this->write("\n\n");
}

$this->write('### Options');
foreach ($definition->getOptions() as $option) {
foreach ($inputOptions as $option) {
$this->write("\n\n");
$this->describeInputOption($option);
$this->describeInputOption($option, $options);
}
}
}
Expand Down Expand Up @@ -128,9 +130,9 @@ protected function describeCommand(Command $command, array $options = []): void
}

$definition = $command->getDefinition();
if ($definition->getOptions() || $definition->getArguments()) {
if ($this->removeHiddenOptions($definition->getOptions(), $options) || $definition->getArguments()) {
$this->write("\n\n");
$this->describeInputDefinition($definition);
$this->describeInputDefinition($definition, $options);
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,7 @@ protected function describeInputOption(InputOption $option, array $options = [])
.'- **Accept value**: '.($option->acceptValue() ? 'yes' : 'no')."\n"
.'- **Is value required**: '.($option->isValueRequired() ? 'yes' : 'no')."\n"
.'- **Is multiple**: '.($option->isArray() ? 'yes' : 'no')."\n"
.($option->isDeprecated() ? ('- **Is deprecated**: yes'."\n") : '')
.'- **Is negatable**: '.($option->isNegatable() ? 'yes' : 'no')."\n"
.'- **Default**: ``'.str_replace("\n", '', var_export($option->getDefault(), true)).'``'."\n"
);
Expand All @@ -95,18 +96,19 @@ protected function describeInputDefinition(InputDefinition $definition, array $o
$this->write("Arguments\n".str_repeat($this->subsubsectionChar, 9))."\n\n";
foreach ($definition->getArguments() as $argument) {
$this->write("\n\n");
$this->describeInputArgument($argument);
$this->describeInputArgument($argument, $options);
}
}

if ($nonDefaultOptions = $this->getNonDefaultOptions($definition)) {
$inputOptions = $this->removeHiddenOptions($this->getNonDefaultOptions($definition), $options);
if (!empty($inputOptions)) {
if ($showArguments) {
$this->write("\n\n");
}

$this->write("Options\n".str_repeat($this->subsubsectionChar, 7)."\n\n");
foreach ($nonDefaultOptions as $option) {
$this->describeInputOption($option);
foreach ($inputOptions as $option) {
$this->describeInputOption($option, $options);
$this->write("\n");
}
}
Expand Down Expand Up @@ -145,9 +147,9 @@ protected function describeCommand(Command $command, array $options = []): void
}

$definition = $command->getDefinition();
if ($definition->getOptions() || $definition->getArguments()) {
if ($this->removeHiddenOptions($definition->getOptions(), $options) || $definition->getArguments()) {
$this->write("\n\n");
$this->describeInputDefinition($definition);
$this->describeInputDefinition($definition, $options);
}
}

Expand Down