You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
250 lines
9.4 KiB
PHP
250 lines
9.4 KiB
PHP
<?php
|
|
|
|
namespace jrosset\CliProgram\Validation;
|
|
|
|
use Closure;
|
|
use jrosset\CliProgram\Validation\Validators\InvalidValueException;
|
|
use jrosset\CliProgram\Validation\Validators\IValidator;
|
|
use Stringable;
|
|
use Symfony\Component\Console\Exception\InvalidArgumentException;
|
|
use Symfony\Component\Console\Input\InputArgument;
|
|
use Symfony\Component\Console\Input\InputInterface;
|
|
use Symfony\Component\Console\Input\InputOption;
|
|
use Symfony\Component\Console\Output\OutputInterface;
|
|
use Throwable;
|
|
|
|
/**
|
|
* Provide a value validation process ({@see IValidator}) to {@see self::addArgument()} and {@see self::addOption()}
|
|
*/
|
|
trait TCommandWithValidation {
|
|
/**
|
|
* @var array<string, IValidator> The validators of each {@see InputArgument}
|
|
*/
|
|
private array $argumentsValidator = [];
|
|
/**
|
|
* @var array<string, IValidator> 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) {
|
|
if ($input->hasArgument($argumentName)) {
|
|
if (!$argumentValidator->validate($input->getArgument($argumentName))) {
|
|
throw new InvalidValueException(sprintf('The "%s" argument has not a valid value', $argumentName));
|
|
}
|
|
$input->setArgument($argumentName, $argumentValidator->getValue());
|
|
}
|
|
}
|
|
}
|
|
/**
|
|
* 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) {
|
|
if ($input->hasOption($optionName)) {
|
|
if (!$optionValidator->validate($input->getOption($optionName))) {
|
|
throw new InvalidValueException(sprintf('The "--%s" option has not a valid value', $optionName));
|
|
}
|
|
$input->setOption($optionName, $optionValidator->getValue());
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 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;
|
|
}
|
|
} |