The validators of each {@see InputArgument} */ private array $argumentsValidator = []; /** * @var array The validators of each {@see InputOption} */ private array $optionsValidator = []; /** * @inheritDoc * * @throws Throwable If an argument or option is not valid */ protected function interact (InputInterface $input, OutputInterface $output): void { parent::interact($input, $output); $this->validate($input, $output); } /** * Validate the command input * * @param InputInterface $input The input * @param OutputInterface $output The output * * @return void */ protected function validate (InputInterface $input, OutputInterface $output): void { $this->validateArguments($input, $output); $this->validateOptions($input, $output); } /** * Validate values of {@see InputArgument} * * @param InputInterface $input The input * @param OutputInterface $output The output * * @return void * * @noinspection PhpUnusedParameterInspection */ protected function validateArguments (InputInterface $input, OutputInterface $output): void { foreach ($this->argumentsValidator as $argumentName => $argumentValidator) { //region Check the argument still exists if (!$input->hasArgument($argumentName)) { continue; } //endregion //region Get the argument value $argumentValue = $input->getArgument($argumentName); //endregion //region Check the argument value is not if ($argumentValue === null) { // If the value is strictly Null (the default value), don't check it → skip continue; } //endregion //region Check the value is valid and replace it by the one extracted from the validator if ($this->getDefinition()->getArgument($argumentName)->isArray() && is_array($argumentValue)) { // This is an “array” argument → check and replace each value instead of the array itself foreach ($argumentValue as &$argumentValueEntry) { if (!$argumentValidator->validate($argumentValueEntry)) { throw new InvalidValueException(sprintf('The "%s" argument has not a valid value', $argumentName)); } $argumentValueEntry = $argumentValidator->getValue(); } $input->setArgument($argumentName, $argumentValue); } else { if (!$argumentValidator->validate($argumentValue)) { throw new InvalidValueException(sprintf('The "%s" argument has not a valid value', $argumentName)); } $input->setArgument($argumentName, $argumentValidator->getValue()); } //endregion } } /** * Validate values of {@see InputOption} * * @param InputInterface $input The input * @param OutputInterface $output The output * * @return void * * @noinspection PhpUnusedParameterInspection */ protected function validateOptions (InputInterface $input, OutputInterface $output): void { foreach ($this->optionsValidator as $optionName => $optionValidator) { //region Check the option still exists if (!$input->hasOption($optionName)) { continue; } //endregion //region Get and check the option value $optionValue = $input->getOption($optionName); //endregion //region Check the option value is NOT Null or False if ($optionValue === null || $optionValue === false) { // If the value is strictly Null (the default value or not provided) or False (the default value), don't check it → skip continue; } //endregion //region Check the value is valid and replace it by the one extracted from the validator if ($this->getDefinition()->getOption($optionName)->isArray() && is_array($optionValue)) { // This is an “array” argument → check and replace each value instead of the array itself foreach ($optionValue as &$optionValueEntry) { if (!$optionValidator->validate($optionValueEntry)) { throw new InvalidValueException(sprintf('The "--%s" option has not a valid value', $optionName)); } $optionValueEntry = $optionValidator->getValue(); } $input->setArgument($optionName, $optionValue); } else { if (!$optionValidator->validate($optionValue)) { throw new InvalidValueException(sprintf('The "--%s" option has not a valid value', $optionName)); } $input->setOption($optionName, $optionValidator->getValue()); } //endregion } } /** * Adds an argument * * @param string $name The argument name * @param int|null $mode The argument mode: InputArgument::REQUIRED or InputArgument::OPTIONAL * @param mixed $default The default value (for InputArgument::OPTIONAL mode only) * @param string $description The argument description * @param IValidator|null $validator The validator or Null if none * @param Closure|array|null $suggestedValues The function or list of suggested values when completing ; Null if none * * @return $this * * @throws InvalidArgumentException When argument mode is not valid */ public function addArgument ( string $name, int $mode = null, string $description = '', mixed $default = null, ?IValidator $validator = null, Closure|array|null $suggestedValues = null ): static { $default = static::treatStringableDefaultValue($default); if ($validator !== null) { $default = $validator->getValidDefault($default); } if ($suggestedValues !== null) { parent::addArgument($name, $mode, $description, $default, $suggestedValues); } else { parent::addArgument($name, $mode, $description, $default); } return $this->setArgumentValidator($name, $validator); } /** * Set the validator for an {@see InputArgument} * * @param string $name The {@see InputArgument} name (must exist) * @param IValidator|null $validator The validator ; Null to remove it * * @return $this * * @throws InvalidArgumentException If the {@see InputArgument} doesn't exist */ public function setArgumentValidator (string $name, ?IValidator $validator): static { if (!$this->getDefinition()->hasArgument($name)) { throw new InvalidArgumentException(sprintf('The "%s" argument does not exist', $name)); } if ($validator === null) { unset($this->argumentsValidator[$name]); } else { $this->argumentsValidator[$name] = $validator; } return $this; } /** * Get the validator for an {@see InputArgument} * * @param string $name The {@see InputArgument} name (must exist) * * @return IValidator|null The validator or Null if none */ public function getArgumentValidator (string $name): ?IValidator { if (!$this->getDefinition()->hasArgument($name)) { throw new InvalidArgumentException(sprintf('The "%s" argument does not exist', $name)); } return $this->argumentsValidator[$name] ?? null; } /** * Adds an option * * @param string $name The argument name * @param string|string[]|null $shortcut The shortcuts, can be null, a string of shortcuts delimited by | or an array of shortcuts * @param int|null $mode The option mode: One of the InputOption::VALUE_* constants * @param string $description The argument description * @param mixed $default The default value (must be null for InputOption::VALUE_NONE) * @param IValidator|null $validator The validator or Null if none ; ignored if **$mode** = {@see InputOption::VALUE_NONE} * @param Closure|array|null $suggestedValues The function or list of suggested values when completing ; Null if none * * @return $this * * @throws InvalidArgumentException When argument mode is not valid */ public function addOption ( string $name, string|array|null $shortcut = null, int $mode = null, string $description = '', mixed $default = null, ?IValidator $validator = null, Closure|array|null $suggestedValues = null ): static { $default = static::treatStringableDefaultValue($default); if ($validator !== null) { $default = $validator->getValidDefault($default); } if ($suggestedValues !== null) { parent::addOption($name, $shortcut, $mode, $description, $default, $suggestedValues); } else { parent::addOption($name, $shortcut, $mode, $description, $default); } return $this->setOptionValidator($name, $validator); } /** * Set the validator for an {@see InputOption} * * @param string $name The {@see InputOption} name (must exist) * @param IValidator|null $validator The validator ; Null to remove it * * @return $this * * @throws InvalidArgumentException If the {@see InputOption} doesn't exist */ public function setOptionValidator (string $name, ?IValidator $validator): static { if (!$this->getDefinition()->hasOption($name)) { throw new InvalidArgumentException(sprintf('The "--%s" option does not exist', $name)); } if ($validator === null) { unset($this->optionsValidator[$name]); } else { $this->optionsValidator[$name] = $validator; } return $this; } /** * Get the validator for an {@see InputOption} * * @param string $name The {@see InputOption} name (must exist) * * @return IValidator|null The validator or Null if none */ public function getOptionValidator (string $name): ?IValidator { if (!$this->getDefinition()->hasOption($name)) { throw new InvalidArgumentException(sprintf('The "--%s" option does not exist', $name)); } return $this->optionsValidator[$name] ?? null; } /** * If it is the case, transform a {@see Stringable} default value into a string * * @param mixed $default The default value * * @return mixed The default value */ protected static function treatStringableDefaultValue (mixed $default): mixed { if ($default instanceof Stringable) { return $default->__toString(); } return $default; } }