diff --git a/src/Symfony/Component/Console/Input/Input.php b/src/Symfony/Component/Console/Input/Input.php index 7b90713c865f..4dab6169d912 100644 --- a/src/Symfony/Component/Console/Input/Input.php +++ b/src/Symfony/Component/Console/Input/Input.php @@ -61,6 +61,7 @@ public function validate() { $definition = $this->definition; $givenArguments = $this->arguments; + $givenOptions = $this->options; $missingArguments = array_filter(array_keys($definition->getArguments()), function ($argument) use ($definition, $givenArguments) { return !\array_key_exists($argument, $givenArguments) && $definition->getArgument($argument)->isRequired(); @@ -69,6 +70,14 @@ public function validate() if (\count($missingArguments) > 0) { throw new RuntimeException(sprintf('Not enough arguments (missing: "%s").', implode(', ', $missingArguments))); } + + $missingOptions = array_filter(array_keys($definition->getOptions()), function ($option) use ($definition, $givenOptions) { + return !\array_key_exists($option, $givenOptions) && $definition->getOption($option)->isRequired(); + }); + + if (\count($missingOptions) > 0) { + throw new RuntimeException(sprintf('Missing options (missing: "%s").', implode(', ', $missingOptions))); + } } public function isInteractive(): bool diff --git a/src/Symfony/Component/Console/Input/InputOption.php b/src/Symfony/Component/Console/Input/InputOption.php index 452c9f7fdec3..c3002afdc533 100644 --- a/src/Symfony/Component/Console/Input/InputOption.php +++ b/src/Symfony/Component/Console/Input/InputOption.php @@ -50,6 +50,11 @@ class InputOption */ public const VALUE_NEGATABLE = 16; + /** + * The option aka "named argument" must be passed. + */ + public const REQUIRED = 32; + private string $name; private string|array|null $shortcut; private int $mode; @@ -94,7 +99,7 @@ public function __construct(string $name, string|array $shortcut = null, int $mo if (null === $mode) { $mode = self::VALUE_NONE; - } elseif ($mode >= (self::VALUE_NEGATABLE << 1) || $mode < 1) { + } elseif ($mode >= (self::REQUIRED << 1) || $mode < 1) { throw new InvalidArgumentException(sprintf('Option mode "%s" is not valid.', $mode)); } @@ -133,6 +138,16 @@ public function getName(): string return $this->name; } + /** + * Returns true if the option is required. + * + * @return bool true if parameter mode is self::REQUIRED, false otherwise + */ + public function isRequired(): bool + { + return self::REQUIRED === (self::REQUIRED & $this->mode); + } + /** * Returns true if the option accepts a value. * @@ -247,6 +262,7 @@ public function equals(self $option): bool && $option->isArray() === $this->isArray() && $option->isValueRequired() === $this->isValueRequired() && $option->isValueOptional() === $this->isValueOptional() + && $option->isRequired() === $this->isRequired() ; } } diff --git a/src/Symfony/Component/Console/Tests/Input/InputOptionTest.php b/src/Symfony/Component/Console/Tests/Input/InputOptionTest.php index 0b5271b324ae..561b67e4e979 100644 --- a/src/Symfony/Component/Console/Tests/Input/InputOptionTest.php +++ b/src/Symfony/Component/Console/Tests/Input/InputOptionTest.php @@ -67,26 +67,37 @@ public function testModes() $this->assertFalse($option->acceptValue(), '__construct() gives a "InputOption::VALUE_NONE" mode by default'); $this->assertFalse($option->isValueRequired(), '__construct() gives a "InputOption::VALUE_NONE" mode by default'); $this->assertFalse($option->isValueOptional(), '__construct() gives a "InputOption::VALUE_NONE" mode by default'); + $this->assertFalse($option->isRequired(), '__construct() can take "InputOption::VALUE_NONE" as its mode'); $option = new InputOption('foo', 'f', null); $this->assertFalse($option->acceptValue(), '__construct() can take "InputOption::VALUE_NONE" as its mode'); $this->assertFalse($option->isValueRequired(), '__construct() can take "InputOption::VALUE_NONE" as its mode'); $this->assertFalse($option->isValueOptional(), '__construct() can take "InputOption::VALUE_NONE" as its mode'); + $this->assertFalse($option->isRequired(), '__construct() can take "InputOption::VALUE_NONE" as its mode'); $option = new InputOption('foo', 'f', InputOption::VALUE_NONE); $this->assertFalse($option->acceptValue(), '__construct() can take "InputOption::VALUE_NONE" as its mode'); $this->assertFalse($option->isValueRequired(), '__construct() can take "InputOption::VALUE_NONE" as its mode'); $this->assertFalse($option->isValueOptional(), '__construct() can take "InputOption::VALUE_NONE" as its mode'); + $this->assertFalse($option->isRequired(), '__construct() can take "InputOption::VALUE_NONE" as its mode'); $option = new InputOption('foo', 'f', InputOption::VALUE_REQUIRED); $this->assertTrue($option->acceptValue(), '__construct() can take "InputOption::VALUE_REQUIRED" as its mode'); $this->assertTrue($option->isValueRequired(), '__construct() can take "InputOption::VALUE_REQUIRED" as its mode'); $this->assertFalse($option->isValueOptional(), '__construct() can take "InputOption::VALUE_REQUIRED" as its mode'); + $this->assertFalse($option->isRequired(), '__construct() can take "InputOption::VALUE_REQUIRED" as its mode'); $option = new InputOption('foo', 'f', InputOption::VALUE_OPTIONAL); $this->assertTrue($option->acceptValue(), '__construct() can take "InputOption::VALUE_OPTIONAL" as its mode'); $this->assertFalse($option->isValueRequired(), '__construct() can take "InputOption::VALUE_OPTIONAL" as its mode'); $this->assertTrue($option->isValueOptional(), '__construct() can take "InputOption::VALUE_OPTIONAL" as its mode'); + $this->assertFalse($option->isRequired(), '__construct() can take "InputOption::VALUE_OPTIONAL" as its mode'); + + $option = new InputOption('foo', 'f', InputOption::REQUIRED); + $this->assertFalse($option->acceptValue(), '__construct() can take "InputOption::REQUIRED" as its mode'); + $this->assertFalse($option->isValueRequired(), '__construct() can take "InputOption::REQUIRED" as its mode'); + $this->assertTrue($option->isValueOptional(), '__construct() can take "InputOption::REQUIRED" as its mode'); + $this->assertTrue($option->isRequired(), '__construct() can take "InputOption::REQUIRED" as its mode'); } public function testInvalidModes() @@ -145,6 +156,9 @@ public function testGetDefault() $option = new InputOption('foo', null, InputOption::VALUE_NONE); $this->assertFalse($option->getDefault(), '->getDefault() returns false if the option does not take a value'); + + $option = new InputOption('foo', null, InputOption::REQUIRED); + $this->assertFalse($option->getDefault(), '->getDefault() returns false if the option is required'); } public function testSetDefault()