From a8ad2be59f79a84c1f69855775dfba35e2d73d67 Mon Sep 17 00:00:00 2001 From: Greg Anderson Date: Fri, 23 Feb 2018 17:37:28 -0800 Subject: [PATCH 1/2] [WIP] Implements #24314: Support binary / negatable options, e.g. --foo and --no-foo. --- src/Symfony/Component/Console/Application.php | 3 +- .../Console/Descriptor/JsonDescriptor.php | 1 + .../Console/Descriptor/MarkdownDescriptor.php | 4 ++- .../Console/Descriptor/TextDescriptor.php | 9 +++-- .../Console/Descriptor/XmlDescriptor.php | 1 + .../Component/Console/Input/ArgvInput.php | 33 +++++++++++++++++++ .../Component/Console/Input/InputOption.php | 31 +++++++++++++++-- 7 files changed, 73 insertions(+), 9 deletions(-) diff --git a/src/Symfony/Component/Console/Application.php b/src/Symfony/Component/Console/Application.php index 4020bd527fc67..d0db1403b562a 100644 --- a/src/Symfony/Component/Console/Application.php +++ b/src/Symfony/Component/Console/Application.php @@ -949,8 +949,7 @@ protected function getDefaultInputDefinition() new InputOption('--quiet', '-q', InputOption::VALUE_NONE, 'Do not output any message'), 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'), new InputOption('--version', '-V', InputOption::VALUE_NONE, 'Display this application version'), - new InputOption('--ansi', '', InputOption::VALUE_NONE, 'Force ANSI output'), - new InputOption('--no-ansi', '', InputOption::VALUE_NONE, 'Disable ANSI output'), + new InputOption('--ansi', '', InputOption::VALUE_BINARY, 'Force ANSI output', null), new InputOption('--no-interaction', '-n', InputOption::VALUE_NONE, 'Do not ask any interactive question'), )); } diff --git a/src/Symfony/Component/Console/Descriptor/JsonDescriptor.php b/src/Symfony/Component/Console/Descriptor/JsonDescriptor.php index 35c87c2207da6..539e95644caa8 100644 --- a/src/Symfony/Component/Console/Descriptor/JsonDescriptor.php +++ b/src/Symfony/Component/Console/Descriptor/JsonDescriptor.php @@ -125,6 +125,7 @@ private function getInputOptionData(InputOption $option) 'accept_value' => $option->acceptValue(), 'is_value_required' => $option->isValueRequired(), 'is_multiple' => $option->isArray(), + 'is_negatable' => $option->isNegatable(), 'description' => preg_replace('/\s*[\r\n]\s*/', ' ', $option->getDescription()), 'default' => INF === $option->getDefault() ? 'INF' : $option->getDefault(), ); diff --git a/src/Symfony/Component/Console/Descriptor/MarkdownDescriptor.php b/src/Symfony/Component/Console/Descriptor/MarkdownDescriptor.php index 106bff5114992..553a9da236a55 100644 --- a/src/Symfony/Component/Console/Descriptor/MarkdownDescriptor.php +++ b/src/Symfony/Component/Console/Descriptor/MarkdownDescriptor.php @@ -68,7 +68,8 @@ protected function describeInputArgument(InputArgument $argument, array $options */ protected function describeInputOption(InputOption $option, array $options = array()) { - $name = '--'.$option->getName(); + $negatable = $option->isNegatable() ? '[no-]' : ''; + $name = '--'.$negatable.$option->getName(); if ($option->getShortcut()) { $name .= '|-'.implode('|-', explode('|', $option->getShortcut())).''; } @@ -79,6 +80,7 @@ protected function describeInputOption(InputOption $option, array $options = arr .'* Accept value: '.($option->acceptValue() ? 'yes' : 'no')."\n" .'* Is value required: '.($option->isValueRequired() ? 'yes' : 'no')."\n" .'* Is multiple: '.($option->isArray() ? 'yes' : 'no')."\n" + .'* Is negatable: '.($option->isNegatable() ? 'yes' : 'no')."\n" .'* Default: `'.str_replace("\n", '', var_export($option->getDefault(), true)).'`' ); } diff --git a/src/Symfony/Component/Console/Descriptor/TextDescriptor.php b/src/Symfony/Component/Console/Descriptor/TextDescriptor.php index ac848184c0323..51305e651dcbf 100644 --- a/src/Symfony/Component/Console/Descriptor/TextDescriptor.php +++ b/src/Symfony/Component/Console/Descriptor/TextDescriptor.php @@ -56,10 +56,12 @@ protected function describeInputArgument(InputArgument $argument, array $options */ protected function describeInputOption(InputOption $option, array $options = array()) { + $default = ''; if ($option->acceptValue() && null !== $option->getDefault() && (!is_array($option->getDefault()) || count($option->getDefault()))) { $default = sprintf(' [default: %s]', $this->formatDefaultValue($option->getDefault())); - } else { - $default = ''; + } elseif ($option->isNegatable() && (null !== $option->getDefault())) { + $negative_default = $option->getDefault() ? '' : 'no-'; + $default = sprintf(' [default: --%s%s]', $negative_default, $option->getName()); } $value = ''; @@ -72,9 +74,10 @@ protected function describeInputOption(InputOption $option, array $options = arr } $totalWidth = isset($options['total_width']) ? $options['total_width'] : $this->calculateTotalWidthForOptions(array($option)); + $negatable = $option->isNegatable() ? '[no-]' : ''; $synopsis = sprintf('%s%s', $option->getShortcut() ? sprintf('-%s, ', $option->getShortcut()) : ' ', - sprintf('--%s%s', $option->getName(), $value) + sprintf('--%s%s%s', $negatable, $option->getName(), $value) ); $spacingWidth = $totalWidth - Helper::strlen($synopsis); diff --git a/src/Symfony/Component/Console/Descriptor/XmlDescriptor.php b/src/Symfony/Component/Console/Descriptor/XmlDescriptor.php index 0d239868c4c99..8f6e82afed867 100644 --- a/src/Symfony/Component/Console/Descriptor/XmlDescriptor.php +++ b/src/Symfony/Component/Console/Descriptor/XmlDescriptor.php @@ -225,6 +225,7 @@ private function getInputOptionDocument(InputOption $option): \DOMDocument $objectXML->setAttribute('accept_value', $option->acceptValue() ? 1 : 0); $objectXML->setAttribute('is_value_required', $option->isValueRequired() ? 1 : 0); $objectXML->setAttribute('is_multiple', $option->isArray() ? 1 : 0); + $objectXML->setAttribute('is_negatable', $option->isNegatable() ? 1 : 0); $objectXML->appendChild($descriptionXML = $dom->createElement('description')); $descriptionXML->appendChild($dom->createTextNode($option->getDescription())); diff --git a/src/Symfony/Component/Console/Input/ArgvInput.php b/src/Symfony/Component/Console/Input/ArgvInput.php index edeabdbf257bc..957fcb104af56 100644 --- a/src/Symfony/Component/Console/Input/ArgvInput.php +++ b/src/Symfony/Component/Console/Input/ArgvInput.php @@ -214,6 +214,9 @@ private function addShortOption($shortcut, $value) private function addLongOption($name, $value) { if (!$this->definition->hasOption($name)) { + if ($this->addNegativeBinaryLongOption($name, $value)) { + return; + } throw new RuntimeException(sprintf('The "--%s" option does not exist.', $name)); } @@ -251,6 +254,36 @@ private function addLongOption($name, $value) } } + /** + * Adds a long option value. + * + * @param string $name The long option key + * @param mixed $value The value for the option + * + * @return boolean if negative option was found and added + */ + private function addNegativeBinaryLongOption($name, $value) + { + // Negative binary options always start with 'no-'. + if ('no-' != substr($name, 0, 3)) { + return false; + } + $binary_option_name = substr($name, 3); + if (!$this->definition->hasOption($binary_option_name)) { + return false; + } + + $option = $this->definition->getOption($binary_option_name); + if (!$option->isNegatable()) { + return false; + } + if (null !== $value && !$option->acceptValue()) { + throw new RuntimeException(sprintf('The "--%s" option does not accept a value.', $name)); + } + $this->options[$binary_option_name] = false; + return true; + } + /** * {@inheritdoc} */ diff --git a/src/Symfony/Component/Console/Input/InputOption.php b/src/Symfony/Component/Console/Input/InputOption.php index e4c855cae1706..a1a5559dda25d 100644 --- a/src/Symfony/Component/Console/Input/InputOption.php +++ b/src/Symfony/Component/Console/Input/InputOption.php @@ -25,6 +25,8 @@ class InputOption const VALUE_REQUIRED = 2; const VALUE_OPTIONAL = 4; const VALUE_IS_ARRAY = 8; + const VALUE_NEGATABLE = 16; + const VALUE_BINARY = (self::VALUE_NONE | self::VALUE_NEGATABLE); private $name; private $shortcut; @@ -70,7 +72,7 @@ public function __construct(string $name, $shortcut = null, int $mode = null, st if (null === $mode) { $mode = self::VALUE_NONE; - } elseif ($mode > 15 || $mode < 1) { + } elseif ($mode > 31 || $mode < 1) { throw new InvalidArgumentException(sprintf('Option mode "%s" is not valid.', $mode)); } @@ -146,6 +148,28 @@ public function isArray() return self::VALUE_IS_ARRAY === (self::VALUE_IS_ARRAY & $this->mode); } + /** + * Returns true if the option is negatable (option --foo can be forced + * to 'false' via the --no-foo option). + * + * @return bool true if mode is self::VALUE_NEGATABLE, false otherwise + */ + public function isNegatable() + { + return self::VALUE_NEGATABLE === (self::VALUE_NEGATABLE & $this->mode); + } + + /** + * Returns true if the option is binary (can be --foo or --no-foo, and + * nothing else). + * + * @return bool true if negatable and does not have a value. + */ + public function isBinary() + { + return $this->isNegatable() && !$this->acceptValue(); + } + /** * Sets the default value. * @@ -155,7 +179,7 @@ public function isArray() */ public function setDefault($default = null) { - if (self::VALUE_NONE === (self::VALUE_NONE & $this->mode) && null !== $default) { + if (self::VALUE_NONE === ((self::VALUE_NONE | self::VALUE_NEGATABLE) & $this->mode) && null !== $default) { throw new LogicException('Cannot set a default value when using InputOption::VALUE_NONE mode.'); } @@ -167,7 +191,7 @@ public function setDefault($default = null) } } - $this->default = $this->acceptValue() ? $default : false; + $this->default = ($this->acceptValue() || $this->isNegatable()) ? $default : false; } /** @@ -200,6 +224,7 @@ public function equals(InputOption $option) return $option->getName() === $this->getName() && $option->getShortcut() === $this->getShortcut() && $option->getDefault() === $this->getDefault() + && $option->isBinary() === $this->isBinary() && $option->isArray() === $this->isArray() && $option->isValueRequired() === $this->isValueRequired() && $option->isValueOptional() === $this->isValueOptional() From d92855a6b7b2788f19944f30c58b10797d0252af Mon Sep 17 00:00:00 2001 From: Greg Anderson Date: Wed, 28 Feb 2018 20:48:07 -0800 Subject: [PATCH 2/2] Move some of the responsibilities of option handling from ArgvInput/ArrayInput classes to InputOption class. Make InputDefinition handle negative options by creating a NegatedInputOption. --- .../Console/Descriptor/JsonDescriptor.php | 3 + .../Console/Descriptor/MarkdownDescriptor.php | 3 + .../Console/Descriptor/TextDescriptor.php | 3 + .../Console/Descriptor/XmlDescriptor.php | 4 +- .../Component/Console/Input/ArgvInput.php | 50 +------- .../Component/Console/Input/ArrayInput.php | 18 +-- src/Symfony/Component/Console/Input/Input.php | 30 +++-- .../Console/Input/InputDefinition.php | 18 ++- .../Component/Console/Input/InputOption.php | 44 ++++++- .../Console/Input/NegatedInputOption.php | 112 ++++++++++++++++++ 10 files changed, 207 insertions(+), 78 deletions(-) create mode 100644 src/Symfony/Component/Console/Input/NegatedInputOption.php diff --git a/src/Symfony/Component/Console/Descriptor/JsonDescriptor.php b/src/Symfony/Component/Console/Descriptor/JsonDescriptor.php index 539e95644caa8..e82ef41ba448f 100644 --- a/src/Symfony/Component/Console/Descriptor/JsonDescriptor.php +++ b/src/Symfony/Component/Console/Descriptor/JsonDescriptor.php @@ -143,6 +143,9 @@ private function getInputDefinitionData(InputDefinition $definition) $inputOptions = array(); foreach ($definition->getOptions() as $name => $option) { + if ($option->isHidden()) { + continue; + } $inputOptions[$name] = $this->getInputOptionData($option); } diff --git a/src/Symfony/Component/Console/Descriptor/MarkdownDescriptor.php b/src/Symfony/Component/Console/Descriptor/MarkdownDescriptor.php index 553a9da236a55..6937eb0380f47 100644 --- a/src/Symfony/Component/Console/Descriptor/MarkdownDescriptor.php +++ b/src/Symfony/Component/Console/Descriptor/MarkdownDescriptor.php @@ -105,6 +105,9 @@ protected function describeInputDefinition(InputDefinition $definition, array $o $this->write('### Options'); foreach ($definition->getOptions() as $option) { + if ($option->isHidden()) { + continue; + } $this->write("\n\n"); $this->write($this->describeInputOption($option)); } diff --git a/src/Symfony/Component/Console/Descriptor/TextDescriptor.php b/src/Symfony/Component/Console/Descriptor/TextDescriptor.php index 51305e651dcbf..5120b3a2671f2 100644 --- a/src/Symfony/Component/Console/Descriptor/TextDescriptor.php +++ b/src/Symfony/Component/Console/Descriptor/TextDescriptor.php @@ -120,6 +120,9 @@ protected function describeInputDefinition(InputDefinition $definition, array $o $this->writeText('Options:', $options); foreach ($definition->getOptions() as $option) { + if ($option->isHidden()) { + continue; + } if (strlen($option->getShortcut()) > 1) { $laterOptions[] = $option; continue; diff --git a/src/Symfony/Component/Console/Descriptor/XmlDescriptor.php b/src/Symfony/Component/Console/Descriptor/XmlDescriptor.php index 8f6e82afed867..f193d622fc0c3 100644 --- a/src/Symfony/Component/Console/Descriptor/XmlDescriptor.php +++ b/src/Symfony/Component/Console/Descriptor/XmlDescriptor.php @@ -41,7 +41,9 @@ public function getInputDefinitionDocument(InputDefinition $definition) $definitionXML->appendChild($optionsXML = $dom->createElement('options')); foreach ($definition->getOptions() as $option) { - $this->appendDocument($optionsXML, $this->getInputOptionDocument($option)); + if (!$option->isHidden()) { + $this->appendDocument($optionsXML, $this->getInputOptionDocument($option)); + } } return $dom; diff --git a/src/Symfony/Component/Console/Input/ArgvInput.php b/src/Symfony/Component/Console/Input/ArgvInput.php index 957fcb104af56..7efcf27ba8926 100644 --- a/src/Symfony/Component/Console/Input/ArgvInput.php +++ b/src/Symfony/Component/Console/Input/ArgvInput.php @@ -213,14 +213,7 @@ private function addShortOption($shortcut, $value) */ private function addLongOption($name, $value) { - if (!$this->definition->hasOption($name)) { - if ($this->addNegativeBinaryLongOption($name, $value)) { - return; - } - throw new RuntimeException(sprintf('The "--%s" option does not exist.', $name)); - } - - $option = $this->definition->getOption($name); + $option = $this->getOptionDefinition($name); if (null !== $value && !$option->acceptValue()) { throw new RuntimeException(sprintf('The "--%s" option does not accept a value.', $name)); @@ -237,15 +230,8 @@ private function addLongOption($name, $value) } } - if (null === $value) { - if ($option->isValueRequired()) { - throw new RuntimeException(sprintf('The "--%s" option requires a value.', $name)); - } - - if (!$option->isArray() && !$option->isValueOptional()) { - $value = true; - } - } + $name = $option->effectiveName(); + $value = $option->checkValue($value); if ($option->isArray()) { $this->options[$name][] = $value; @@ -254,36 +240,6 @@ private function addLongOption($name, $value) } } - /** - * Adds a long option value. - * - * @param string $name The long option key - * @param mixed $value The value for the option - * - * @return boolean if negative option was found and added - */ - private function addNegativeBinaryLongOption($name, $value) - { - // Negative binary options always start with 'no-'. - if ('no-' != substr($name, 0, 3)) { - return false; - } - $binary_option_name = substr($name, 3); - if (!$this->definition->hasOption($binary_option_name)) { - return false; - } - - $option = $this->definition->getOption($binary_option_name); - if (!$option->isNegatable()) { - return false; - } - if (null !== $value && !$option->acceptValue()) { - throw new RuntimeException(sprintf('The "--%s" option does not accept a value.', $name)); - } - $this->options[$binary_option_name] = false; - return true; - } - /** * {@inheritdoc} */ diff --git a/src/Symfony/Component/Console/Input/ArrayInput.php b/src/Symfony/Component/Console/Input/ArrayInput.php index e6c28de978cb1..afff08dd4f358 100644 --- a/src/Symfony/Component/Console/Input/ArrayInput.php +++ b/src/Symfony/Component/Console/Input/ArrayInput.php @@ -168,23 +168,7 @@ private function addShortOption($shortcut, $value) */ private function addLongOption($name, $value) { - if (!$this->definition->hasOption($name)) { - throw new InvalidOptionException(sprintf('The "--%s" option does not exist.', $name)); - } - - $option = $this->definition->getOption($name); - - if (null === $value) { - if ($option->isValueRequired()) { - throw new InvalidOptionException(sprintf('The "--%s" option requires a value.', $name)); - } - - if (!$option->isValueOptional()) { - $value = true; - } - } - - $this->options[$name] = $value; + $this->setOption($name, $value); } /** diff --git a/src/Symfony/Component/Console/Input/Input.php b/src/Symfony/Component/Console/Input/Input.php index 41413252194f5..d8e6dea0ab914 100644 --- a/src/Symfony/Component/Console/Input/Input.php +++ b/src/Symfony/Component/Console/Input/Input.php @@ -146,11 +146,8 @@ public function getOptions() */ public function getOption($name) { - if (!$this->definition->hasOption($name)) { - throw new InvalidArgumentException(sprintf('The "%s" option does not exist.', $name)); - } - - return array_key_exists($name, $this->options) ? $this->options[$name] : $this->definition->getOption($name)->getDefault(); + $option = $this->getOptionDefinition($name); + return array_key_exists($name, $this->options) ? $this->options[$name] : $option->getDefault(); } /** @@ -158,11 +155,8 @@ public function getOption($name) */ public function setOption($name, $value) { - if (!$this->definition->hasOption($name)) { - throw new InvalidArgumentException(sprintf('The "%s" option does not exist.', $name)); - } - - $this->options[$name] = $value; + $option = $this->getOptionDefinition($name); + $this->options[$option->effectiveName()] = $option->checkValue($value); } /** @@ -200,4 +194,20 @@ public function getStream() { return $this->stream; } + + /** + * Look up the option definition for the given option name. + * + * @param string $name + * + * @return InputOption + */ + protected function getOptionDefinition($name) + { + if (!$this->definition->hasOption($name)) { + throw new RuntimeException(sprintf('The "--%s" option does not exist.', $name)); + } + + return $this->definition->getOption($name); + } } diff --git a/src/Symfony/Component/Console/Input/InputDefinition.php b/src/Symfony/Component/Console/Input/InputDefinition.php index d5b99ab3965e1..871ea5c0c0fde 100644 --- a/src/Symfony/Component/Console/Input/InputDefinition.php +++ b/src/Symfony/Component/Console/Input/InputDefinition.php @@ -227,6 +227,19 @@ public function addOptions($options = array()) * @throws LogicException When option given already exist */ public function addOption(InputOption $option) + { + $this->doAddOption($option); + + if ($option->isNegatable()) { + $negatedOption = new NegatedInputOption($option); + $this->doAddOption($negatedOption); + } + } + + /** + * @throws LogicException When option given already exist + */ + private function doAddOption(InputOption $option) { if (isset($this->options[$option->getName()]) && !$option->equals($this->options[$option->getName()])) { throw new LogicException(sprintf('An option named "%s" already exists.', $option->getName())); @@ -324,7 +337,7 @@ public function getOptionDefaults() { $values = array(); foreach ($this->options as $option) { - $values[$option->getName()] = $option->getDefault(); + $values[$option->effectiveName()] = $option->getDefault(); } return $values; @@ -363,6 +376,9 @@ public function getSynopsis($short = false) $elements[] = '[options]'; } elseif (!$short) { foreach ($this->getOptions() as $option) { + if ($option->isHidden()) { + continue; + } $value = ''; if ($option->acceptValue()) { $value = sprintf( diff --git a/src/Symfony/Component/Console/Input/InputOption.php b/src/Symfony/Component/Console/Input/InputOption.php index a1a5559dda25d..17e31d9c7e58a 100644 --- a/src/Symfony/Component/Console/Input/InputOption.php +++ b/src/Symfony/Component/Console/Input/InputOption.php @@ -12,6 +12,7 @@ namespace Symfony\Component\Console\Input; use Symfony\Component\Console\Exception\InvalidArgumentException; +use Symfony\Component\Console\Exception\InvalidOptionException; use Symfony\Component\Console\Exception\LogicException; /** @@ -26,6 +27,7 @@ class InputOption const VALUE_OPTIONAL = 4; const VALUE_IS_ARRAY = 8; const VALUE_NEGATABLE = 16; + const VALUE_HIDDEN = 32; const VALUE_BINARY = (self::VALUE_NONE | self::VALUE_NEGATABLE); private $name; @@ -72,7 +74,7 @@ public function __construct(string $name, $shortcut = null, int $mode = null, st if (null === $mode) { $mode = self::VALUE_NONE; - } elseif ($mode > 31 || $mode < 1) { + } elseif ($mode >= (self::VALUE_HIDDEN << 1) || $mode < 1) { throw new InvalidArgumentException(sprintf('Option mode "%s" is not valid.', $mode)); } @@ -108,6 +110,11 @@ public function getName() return $this->name; } + public function effectiveName() + { + return $this->getName(); + } + /** * Returns true if the option accepts a value. * @@ -159,6 +166,17 @@ public function isNegatable() return self::VALUE_NEGATABLE === (self::VALUE_NEGATABLE & $this->mode); } + /** + * Returns true if the option should not be shown in help (e.g. a negated + * option). + * + * @return bool true if mode is self::VALUE_HIDDEN, false otherwise + */ + public function isHidden() + { + return self::VALUE_HIDDEN === (self::VALUE_HIDDEN & $this->mode); + } + /** * Returns true if the option is binary (can be --foo or --no-foo, and * nothing else). @@ -214,6 +232,27 @@ public function getDescription() return $this->description; } + /** + * Checks the validity of a value, and alters it as necessary + * + * @param mixed $value + * + * @return @mixed + */ + public function checkValue($value) + { + if (null === $value) { + if ($this->isValueRequired()) { + throw new InvalidOptionException(sprintf('The "--%s" option requires a value.', $this->getName())); + } + + if (!$this->isValueOptional()) { + return true; + } + } + return $value; + } + /** * Checks whether the given option equals this one. * @@ -224,7 +263,8 @@ public function equals(InputOption $option) return $option->getName() === $this->getName() && $option->getShortcut() === $this->getShortcut() && $option->getDefault() === $this->getDefault() - && $option->isBinary() === $this->isBinary() + && $option->isHidden() === $this->isHidden() + && $option->isNegatable() === $this->isNegatable() && $option->isArray() === $this->isArray() && $option->isValueRequired() === $this->isValueRequired() && $option->isValueOptional() === $this->isValueOptional() diff --git a/src/Symfony/Component/Console/Input/NegatedInputOption.php b/src/Symfony/Component/Console/Input/NegatedInputOption.php new file mode 100644 index 0000000000000..07a4bb6d09482 --- /dev/null +++ b/src/Symfony/Component/Console/Input/NegatedInputOption.php @@ -0,0 +1,112 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Input; + +use Symfony\Component\Console\Exception\InvalidArgumentException; +use Symfony\Component\Console\Exception\LogicException; + +/** + * Represents a command line option. + * + * @author Fabien Potencier + */ +class NegatedInputOption extends InputOption +{ + private $primaryOption; + + const VALUE_NONE = 1; + const VALUE_REQUIRED = 2; + const VALUE_OPTIONAL = 4; + const VALUE_IS_ARRAY = 8; + const VALUE_NEGATABLE = 16; + const VALUE_HIDDEN = 32; + const VALUE_BINARY = (self::VALUE_NONE | self::VALUE_NEGATABLE); + + private $name; + private $shortcut; + private $mode; + private $default; + private $description; + + /** + * @param string $name The option name + * @param string|array $shortcut The shortcuts, can be null, a string of shortcuts delimited by | or an array of shortcuts + * @param int $mode The option mode: One of the VALUE_* constants + * @param string $description A description text + * @param mixed $default The default value (must be null for self::VALUE_NONE) + * + * @throws InvalidArgumentException If option mode is invalid or incompatible + */ + public function __construct(InputOption $primaryOption) + { + $this->primaryOption = $primaryOption; + + /* string $name, $shortcut = null, int $mode = null, string $description = '', $default = null */ + $name = 'no-' . $primaryOption->getName(); + $shortcut = null; + $mode = self::VALUE_HIDDEN; + $description = $primaryOption->getDescription(); + $default = $this->negate($primaryOption->getDefault()); + + parent::__construct($name, $shortcut, $mode, $description, $default); + } + + public function effectiveName() + { + return $this->primaryOption->getName(); + } + + /** + * Sets the default value. + * + * @param mixed $default The default value + * + * @throws LogicException When incorrect default value is given + */ + public function setDefault($default = null) + { + $this->primaryOption->setDefault($this->negate($default)); + } + + /** + * Returns the default value. + * + * @return mixed The default value + */ + public function getDefault() + { + return $this->negate($this->primaryOption->getDefault()); + } + + /** + * @inheritdoc + */ + public function checkValue($value) + { + return false; + } + + /** + * Negate a value if it is provided. + * + * @param mixed $value + * + * @return mixed + */ + private function negate($value) + { + if ($value === null) { + return $value; + } + return !$value; + } +}