Skip to content

Navigation Menu

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

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

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

Open
wants to merge 15 commits into
base: 7.3
Choose a base branch
Loading
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions 2 src/Symfony/Component/Console/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,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 29 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)) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why the isset?

Suggested change
if (isset($inputDefinition)) {
if ($inputDefinition) {

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

$inputDefinition is defined in a try..catch, but should be outside.

Copy link
Author

@flkasper flkasper Aug 26, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As @GromNaN has already explained, $inputDefinition is defined within a try...catch:

try {
    $inputDefinition = $this->getDefinition();
    $input->bind($inputDefinition);
} catch (ExceptionInterface $e) {
    if (!$this->ignoreValidationErrors) {
        throw $e;
    }
}

Since getDefinition can also throw an exception (if the parent constructor call is missing), moving it outside the try...catch makes little sense.

Therefore, isset must be used to check whether $inputDefinition is defined.

$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 ($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 2 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
12 changes: 10 additions & 2 deletions 12 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 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 20 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 28 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']);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would not unset this. The json is easier to use if we don't have conditional keys in it.

}

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") : '')
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

shouldn't we have a line Is deprecated: no instead ?

.'* Is negatable: '.($option->isNegatable() ? 'yes' : 'no')."\n"
.'* Default: `'.str_replace("\n", '', var_export($option->getDefault(), true)).'`'
);
Expand All @@ -81,19 +82,19 @@ 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) {
if ($inputOptions = $this->removeHiddenOptions($definition->getOptions(), $options)) {
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 +129,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));
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
31 changes: 18 additions & 13 deletions 31 src/Symfony/Component/Console/Descriptor/TextDescriptor.php
Original file line number Diff line number Diff line change
Expand Up @@ -73,41 +73,46 @@ protected function describeInputOption(InputOption $option, array $options = [])

$spacingWidth = $totalWidth - Helper::width($synopsis);

$this->writeText(\sprintf(' <info>%s</info> %s%s%s%s',
$synopsis = \sprintf('<%1$s>%2$s</%1$s>', $option->isDeprecated() ? 'fg=gray;' : 'info', $synopsis);

$description = trim(($option->isDeprecated() ? '[deprecated] ' : '').$option->getDescription());

$this->writeText(\sprintf(' %s %s%s%s%s',
$synopsis,
str_repeat(' ', $spacingWidth),
// + 4 = 2 spaces before <info>, 2 spaces after </info>
preg_replace('/\s*[\r\n]\s*/', "\n".str_repeat(' ', $totalWidth + 4), $option->getDescription()),
preg_replace('/\s*[\r\n]\s*/', "\n".str_repeat(' ', $totalWidth + 4), $description),
$default,
$option->isArray() ? '<comment> (multiple values allowed)</comment>' : ''
), $options);
}

protected function describeInputDefinition(InputDefinition $definition, array $options = []): void
{
$totalWidth = $this->calculateTotalWidthForOptions($definition->getOptions());
foreach ($definition->getArguments() as $argument) {
$inputArguments = $definition->getArguments();
$inputOptions = $this->removeHiddenOptions($definition->getOptions(), $options);
$totalWidth = $this->calculateTotalWidthForOptions($inputOptions);
foreach ($inputArguments as $argument) {
$totalWidth = max($totalWidth, Helper::width($argument->getName()));
}

if ($definition->getArguments()) {
if ($inputArguments) {
$this->writeText('<comment>Arguments:</comment>', $options);
$this->writeText("\n");
foreach ($definition->getArguments() as $argument) {
foreach ($inputArguments as $argument) {
$this->describeInputArgument($argument, array_merge($options, ['total_width' => $totalWidth]));
$this->writeText("\n");
}
}

if ($definition->getArguments() && $definition->getOptions()) {
$this->writeText("\n");
}

if ($definition->getOptions()) {
if ($inputOptions) {
if ($inputArguments) {
$this->writeText("\n");
}
$laterOptions = [];

$this->writeText('<comment>Options:</comment>', $options);
foreach ($definition->getOptions() as $option) {
foreach ($inputOptions as $option) {
if (\strlen($option->getShortcut() ?? '') > 1) {
$laterOptions[] = $option;
continue;
Expand Down Expand Up @@ -141,7 +146,7 @@ protected function describeCommand(Command $command, array $options = []): void
$this->writeText("\n");

$definition = $command->getDefinition();
if ($definition->getOptions() || $definition->getArguments()) {
if ($this->removeHiddenOptions($definition->getOptions(), $options) || $definition->getArguments()) {
$this->writeText("\n");
$this->describeInputDefinition($definition, $options);
$this->writeText("\n");
Expand Down
Loading
Loading
Morty Proxy This is a proxified and sanitized view of the page, visit original site.