Skip to content

Commit 8d455db

Browse files
greg-1-andersonjderusse
authored andcommitted
[WIP] Implements #24314: Support binary / negatable options, e.g. --foo and --no-foo.
1 parent b3de641 commit 8d455db

File tree

11 files changed

+245
-52
lines changed

11 files changed

+245
-52
lines changed

src/Symfony/Component/Console/Application.php

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1033,8 +1033,7 @@ protected function getDefaultInputDefinition()
10331033
new InputOption('--quiet', '-q', InputOption::VALUE_NONE, 'Do not output any message'),
10341034
new InputOption('--verbose', '-v|vv|vvv', InputOption::VALUE_NONE, 'Increase the verbosity of messages: 1 for normal output, 2 for more verbose output and 3 for debug'),
10351035
new InputOption('--version', '-V', InputOption::VALUE_NONE, 'Display this application version'),
1036-
new InputOption('--ansi', '', InputOption::VALUE_NONE, 'Force ANSI output'),
1037-
new InputOption('--no-ansi', '', InputOption::VALUE_NONE, 'Disable ANSI output'),
1036+
new InputOption('--ansi', '', InputOption::VALUE_BINARY, 'Force ANSI output', null),
10381037
new InputOption('--no-interaction', '-n', InputOption::VALUE_NONE, 'Do not ask any interactive question'),
10391038
]);
10401039
}

src/Symfony/Component/Console/Descriptor/JsonDescriptor.php

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -119,6 +119,7 @@ private function getInputOptionData(InputOption $option): array
119119
'accept_value' => $option->acceptValue(),
120120
'is_value_required' => $option->isValueRequired(),
121121
'is_multiple' => $option->isArray(),
122+
'is_negatable' => $option->isNegatable(),
122123
'description' => preg_replace('/\s*[\r\n]\s*/', ' ', $option->getDescription()),
123124
'default' => \INF === $option->getDefault() ? 'INF' : $option->getDefault(),
124125
];
@@ -133,6 +134,9 @@ private function getInputDefinitionData(InputDefinition $definition): array
133134

134135
$inputOptions = [];
135136
foreach ($definition->getOptions() as $name => $option) {
137+
if ($option->isHidden()) {
138+
continue;
139+
}
136140
$inputOptions[$name] = $this->getInputOptionData($option);
137141
}
138142

src/Symfony/Component/Console/Descriptor/MarkdownDescriptor.php

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,8 @@ protected function describeInputArgument(InputArgument $argument, array $options
6868
*/
6969
protected function describeInputOption(InputOption $option, array $options = [])
7070
{
71-
$name = '--'.$option->getName();
71+
$negatable = $option->isNegatable() ? '[no-]' : '';
72+
$name = '--'.$negatable.$option->getName();
7273
if ($option->getShortcut()) {
7374
$name .= '|-'.str_replace('|', '|-', $option->getShortcut()).'';
7475
}
@@ -79,6 +80,7 @@ protected function describeInputOption(InputOption $option, array $options = [])
7980
.'* Accept value: '.($option->acceptValue() ? 'yes' : 'no')."\n"
8081
.'* Is value required: '.($option->isValueRequired() ? 'yes' : 'no')."\n"
8182
.'* Is multiple: '.($option->isArray() ? 'yes' : 'no')."\n"
83+
.'* Is negatable: '.($option->isNegatable() ? 'yes' : 'no')."\n"
8284
.'* Default: `'.str_replace("\n", '', var_export($option->getDefault(), true)).'`'
8385
);
8486
}
@@ -105,6 +107,9 @@ protected function describeInputDefinition(InputDefinition $definition, array $o
105107

106108
$this->write('### Options');
107109
foreach ($definition->getOptions() as $option) {
110+
if ($option->isHidden()) {
111+
continue;
112+
}
108113
$this->write("\n\n");
109114
if (null !== $describeInputOption = $this->describeInputOption($option)) {
110115
$this->write($describeInputOption);

src/Symfony/Component/Console/Descriptor/TextDescriptor.php

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -56,10 +56,12 @@ protected function describeInputArgument(InputArgument $argument, array $options
5656
*/
5757
protected function describeInputOption(InputOption $option, array $options = [])
5858
{
59+
$default = '';
5960
if ($option->acceptValue() && null !== $option->getDefault() && (!\is_array($option->getDefault()) || \count($option->getDefault()))) {
6061
$default = sprintf('<comment> [default: %s]</comment>', $this->formatDefaultValue($option->getDefault()));
61-
} else {
62-
$default = '';
62+
} elseif ($option->isNegatable() && (null !== $option->getDefault())) {
63+
$negative_default = $option->getDefault() ? '' : 'no-';
64+
$default = sprintf('<comment> [default: --%s%s]</comment>', $negative_default, $option->getName());
6365
}
6466

6567
$value = '';
@@ -72,9 +74,10 @@ protected function describeInputOption(InputOption $option, array $options = [])
7274
}
7375

7476
$totalWidth = isset($options['total_width']) ? $options['total_width'] : $this->calculateTotalWidthForOptions([$option]);
77+
$negatable = $option->isNegatable() ? '[no-]' : '';
7578
$synopsis = sprintf('%s%s',
7679
$option->getShortcut() ? sprintf('-%s, ', $option->getShortcut()) : ' ',
77-
sprintf('--%s%s', $option->getName(), $value)
80+
sprintf('--%s%s%s', $negatable, $option->getName(), $value)
7881
);
7982

8083
$spacingWidth = $totalWidth - Helper::strlen($synopsis);
@@ -117,6 +120,9 @@ protected function describeInputDefinition(InputDefinition $definition, array $o
117120

118121
$this->writeText('<comment>Options:</comment>', $options);
119122
foreach ($definition->getOptions() as $option) {
123+
if ($option->isHidden()) {
124+
continue;
125+
}
120126
if (\strlen($option->getShortcut()) > 1) {
121127
$laterOptions[] = $option;
122128
continue;

src/Symfony/Component/Console/Descriptor/XmlDescriptor.php

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,9 @@ public function getInputDefinitionDocument(InputDefinition $definition): \DOMDoc
3838

3939
$definitionXML->appendChild($optionsXML = $dom->createElement('options'));
4040
foreach ($definition->getOptions() as $option) {
41-
$this->appendDocument($optionsXML, $this->getInputOptionDocument($option));
41+
if (!$option->isHidden()) {
42+
$this->appendDocument($optionsXML, $this->getInputOptionDocument($option));
43+
}
4244
}
4345

4446
return $dom;
@@ -210,6 +212,7 @@ private function getInputOptionDocument(InputOption $option): \DOMDocument
210212
$objectXML->setAttribute('accept_value', $option->acceptValue() ? 1 : 0);
211213
$objectXML->setAttribute('is_value_required', $option->isValueRequired() ? 1 : 0);
212214
$objectXML->setAttribute('is_multiple', $option->isArray() ? 1 : 0);
215+
$objectXML->setAttribute('is_negatable', $option->isNegatable() ? 1 : 0);
213216
$objectXML->appendChild($descriptionXML = $dom->createElement('description'));
214217
$descriptionXML->appendChild($dom->createTextNode($option->getDescription()));
215218

src/Symfony/Component/Console/Input/ArgvInput.php

Lines changed: 3 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -208,11 +208,7 @@ private function addShortOption(string $shortcut, $value)
208208
*/
209209
private function addLongOption(string $name, $value)
210210
{
211-
if (!$this->definition->hasOption($name)) {
212-
throw new RuntimeException(sprintf('The "--%s" option does not exist.', $name));
213-
}
214-
215-
$option = $this->definition->getOption($name);
211+
$option = $this->getOptionDefinition($name);
216212

217213
if (null !== $value && !$option->acceptValue()) {
218214
throw new RuntimeException(sprintf('The "--%s" option does not accept a value.', $name));
@@ -229,15 +225,8 @@ private function addLongOption(string $name, $value)
229225
}
230226
}
231227

232-
if (null === $value) {
233-
if ($option->isValueRequired()) {
234-
throw new RuntimeException(sprintf('The "--%s" option requires a value.', $name));
235-
}
236-
237-
if (!$option->isArray() && !$option->isValueOptional()) {
238-
$value = true;
239-
}
240-
}
228+
$name = $option->effectiveName();
229+
$value = $option->checkValue($value);
241230

242231
if ($option->isArray()) {
243232
$this->options[$name][] = $value;

src/Symfony/Component/Console/Input/ArrayInput.php

Lines changed: 1 addition & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -164,23 +164,7 @@ private function addShortOption(string $shortcut, $value)
164164
*/
165165
private function addLongOption(string $name, $value)
166166
{
167-
if (!$this->definition->hasOption($name)) {
168-
throw new InvalidOptionException(sprintf('The "--%s" option does not exist.', $name));
169-
}
170-
171-
$option = $this->definition->getOption($name);
172-
173-
if (null === $value) {
174-
if ($option->isValueRequired()) {
175-
throw new InvalidOptionException(sprintf('The "--%s" option requires a value.', $name));
176-
}
177-
178-
if (!$option->isValueOptional()) {
179-
$value = true;
180-
}
181-
}
182-
183-
$this->options[$name] = $value;
167+
$this->setOption($name, $value);
184168
}
185169

186170
/**

src/Symfony/Component/Console/Input/Input.php

Lines changed: 20 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -146,23 +146,17 @@ public function getOptions()
146146
*/
147147
public function getOption(string $name)
148148
{
149-
if (!$this->definition->hasOption($name)) {
150-
throw new InvalidArgumentException(sprintf('The "%s" option does not exist.', $name));
151-
}
152-
153-
return \array_key_exists($name, $this->options) ? $this->options[$name] : $this->definition->getOption($name)->getDefault();
149+
$option = $this->getOptionDefinition($name);
150+
return \array_key_exists($name, $this->options) ? $this->options[$name] : $option->getDefault();
154151
}
155152

156153
/**
157154
* {@inheritdoc}
158155
*/
159156
public function setOption(string $name, $value)
160157
{
161-
if (!$this->definition->hasOption($name)) {
162-
throw new InvalidArgumentException(sprintf('The "%s" option does not exist.', $name));
163-
}
164-
165-
$this->options[$name] = $value;
158+
$option = $this->getOptionDefinition($name);
159+
$this->options[$option->effectiveName()] = $option->checkValue($value);
166160
}
167161

168162
/**
@@ -198,4 +192,20 @@ public function getStream()
198192
{
199193
return $this->stream;
200194
}
195+
196+
/**
197+
* Look up the option definition for the given option name.
198+
*
199+
* @param string $name
200+
*
201+
* @return InputOption
202+
*/
203+
protected function getOptionDefinition($name)
204+
{
205+
if (!$this->definition->hasOption($name)) {
206+
throw new RuntimeException(sprintf('The "--%s" option does not exist.', $name));
207+
}
208+
209+
return $this->definition->getOption($name);
210+
}
201211
}

src/Symfony/Component/Console/Input/InputDefinition.php

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -227,6 +227,19 @@ public function addOptions(array $options = [])
227227
* @throws LogicException When option given already exist
228228
*/
229229
public function addOption(InputOption $option)
230+
{
231+
$this->doAddOption($option);
232+
233+
if ($option->isNegatable()) {
234+
$negatedOption = new NegatedInputOption($option);
235+
$this->doAddOption($negatedOption);
236+
}
237+
}
238+
239+
/**
240+
* @throws LogicException When option given already exist
241+
*/
242+
private function doAddOption(InputOption $option)
230243
{
231244
if (isset($this->options[$option->getName()]) && !$option->equals($this->options[$option->getName()])) {
232245
throw new LogicException(sprintf('An option named "%s" already exists.', $option->getName()));
@@ -316,7 +329,7 @@ public function getOptionDefaults()
316329
{
317330
$values = [];
318331
foreach ($this->options as $option) {
319-
$values[$option->getName()] = $option->getDefault();
332+
$values[$option->effectiveName()] = $option->getDefault();
320333
}
321334

322335
return $values;
@@ -351,6 +364,9 @@ public function getSynopsis(bool $short = false)
351364
$elements[] = '[options]';
352365
} elseif (!$short) {
353366
foreach ($this->getOptions() as $option) {
367+
if ($option->isHidden()) {
368+
continue;
369+
}
354370
$value = '';
355371
if ($option->acceptValue()) {
356372
$value = sprintf(

src/Symfony/Component/Console/Input/InputOption.php

Lines changed: 68 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
namespace Symfony\Component\Console\Input;
1313

1414
use Symfony\Component\Console\Exception\InvalidArgumentException;
15+
use Symfony\Component\Console\Exception\InvalidOptionException;
1516
use Symfony\Component\Console\Exception\LogicException;
1617

1718
/**
@@ -25,6 +26,9 @@ class InputOption
2526
public const VALUE_REQUIRED = 2;
2627
public const VALUE_OPTIONAL = 4;
2728
public const VALUE_IS_ARRAY = 8;
29+
public const VALUE_NEGATABLE = 16;
30+
public const VALUE_HIDDEN = 32;
31+
public const VALUE_BINARY = (self::VALUE_NONE | self::VALUE_NEGATABLE);
2832

2933
private $name;
3034
private $shortcut;
@@ -70,7 +74,7 @@ public function __construct(string $name, $shortcut = null, int $mode = null, st
7074

7175
if (null === $mode) {
7276
$mode = self::VALUE_NONE;
73-
} elseif ($mode > 15 || $mode < 1) {
77+
} elseif ($mode >= (self::VALUE_HIDDEN << 1) || $mode < 1) {
7478
throw new InvalidArgumentException(sprintf('Option mode "%s" is not valid.', $mode));
7579
}
7680

@@ -106,6 +110,11 @@ public function getName()
106110
return $this->name;
107111
}
108112

113+
public function effectiveName()
114+
{
115+
return $this->getName();
116+
}
117+
109118
/**
110119
* Returns true if the option accepts a value.
111120
*
@@ -146,6 +155,39 @@ public function isArray()
146155
return self::VALUE_IS_ARRAY === (self::VALUE_IS_ARRAY & $this->mode);
147156
}
148157

158+
/**
159+
* Returns true if the option is negatable (option --foo can be forced
160+
* to 'false' via the --no-foo option).
161+
*
162+
* @return bool true if mode is self::VALUE_NEGATABLE, false otherwise
163+
*/
164+
public function isNegatable()
165+
{
166+
return self::VALUE_NEGATABLE === (self::VALUE_NEGATABLE & $this->mode);
167+
}
168+
169+
/**
170+
* Returns true if the option should not be shown in help (e.g. a negated
171+
* option).
172+
*
173+
* @return bool true if mode is self::VALUE_HIDDEN, false otherwise
174+
*/
175+
public function isHidden()
176+
{
177+
return self::VALUE_HIDDEN === (self::VALUE_HIDDEN & $this->mode);
178+
}
179+
180+
/**
181+
* Returns true if the option is binary (can be --foo or --no-foo, and
182+
* nothing else).
183+
*
184+
* @return bool true if negatable and does not have a value.
185+
*/
186+
public function isBinary()
187+
{
188+
return $this->isNegatable() && !$this->acceptValue();
189+
}
190+
149191
/**
150192
* Sets the default value.
151193
*
@@ -155,7 +197,7 @@ public function isArray()
155197
*/
156198
public function setDefault($default = null)
157199
{
158-
if (self::VALUE_NONE === (self::VALUE_NONE & $this->mode) && null !== $default) {
200+
if (self::VALUE_NONE === ((self::VALUE_NONE | self::VALUE_NEGATABLE) & $this->mode) && null !== $default) {
159201
throw new LogicException('Cannot set a default value when using InputOption::VALUE_NONE mode.');
160202
}
161203

@@ -167,7 +209,7 @@ public function setDefault($default = null)
167209
}
168210
}
169211

170-
$this->default = $this->acceptValue() ? $default : false;
212+
$this->default = ($this->acceptValue() || $this->isNegatable()) ? $default : false;
171213
}
172214

173215
/**
@@ -190,6 +232,27 @@ public function getDescription()
190232
return $this->description;
191233
}
192234

235+
/**
236+
* Checks the validity of a value, and alters it as necessary
237+
*
238+
* @param mixed $value
239+
*
240+
* @return @mixed
241+
*/
242+
public function checkValue($value)
243+
{
244+
if (null === $value) {
245+
if ($this->isValueRequired()) {
246+
throw new InvalidOptionException(sprintf('The "--%s" option requires a value.', $this->getName()));
247+
}
248+
249+
if (!$this->isValueOptional()) {
250+
return true;
251+
}
252+
}
253+
return $value;
254+
}
255+
193256
/**
194257
* Checks whether the given option equals this one.
195258
*
@@ -200,6 +263,8 @@ public function equals(self $option)
200263
return $option->getName() === $this->getName()
201264
&& $option->getShortcut() === $this->getShortcut()
202265
&& $option->getDefault() === $this->getDefault()
266+
&& $option->isHidden() === $this->isHidden()
267+
&& $option->isNegatable() === $this->isNegatable()
203268
&& $option->isArray() === $this->isArray()
204269
&& $option->isValueRequired() === $this->isValueRequired()
205270
&& $option->isValueOptional() === $this->isValueOptional()

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy