diff --git a/src/CliProgram/Validation/TCommandWithValidation.php b/src/CliProgram/Validation/TCommandWithValidation.php index 00893df..152ff89 100644 --- a/src/CliProgram/Validation/TCommandWithValidation.php +++ b/src/CliProgram/Validation/TCommandWithValidation.php @@ -5,13 +5,14 @@ namespace jrosset\CliProgram\Validation; use Closure; use jrosset\CliProgram\Validation\Validators\InvalidValueException; use jrosset\CliProgram\Validation\Validators\IValidator; +use ReflectionFunction; use Stringable; +use Symfony\Component\Console\Command\Command; 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()} @@ -25,16 +26,58 @@ trait TCommandWithValidation { * @var array The validators of each {@see InputOption} */ private array $optionsValidator = []; + /** + * @var Closure|null The real code ({@see Command::$code} is used to perform input validation before execution) to execute when running this command. + *

If set, it overrides the code defined in the execute() method. + */ + private ?Closure $realCode = null; /** * @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); + protected function initialize (InputInterface $input, OutputInterface $output): void { + parent::setCode( + function (InputInterface $input, OutputInterface $output): int { + $this->validate($input, $output); + + return $this->realCode !== null + ? ($this->realCode)($input, $output) + : $this->execute($input, $output); + } + ); + parent::initialize($input, $output); + } + /** + * @inheritDoc + */ + public function setCode (callable $code): static { + if ($code instanceof Closure) { + /** @noinspection PhpUnhandledExceptionInspection */ + $codeReflection = new ReflectionFunction($code); + if ($codeReflection->getClosureThis() === null) { + set_error_handler( + static function () { + } + ); + try { + if ($rebindCode = Closure::bind($code, $this)) { + $code = $rebindCode; + } + } + finally { + restore_error_handler(); + } + } + } + else { + $code = $code(...); + } + + // {@see Command::$code} is used to perform input validation before execution, so set {@see self::$realCode} instead + $this->realCode = $code; + return $this; } + /** * Validate the command input * diff --git a/src/CliProgram/Validation/Validators/BasedValidator.php b/src/CliProgram/Validation/Validators/BasedValidator.php index 8dcbe9d..c7a5fe6 100644 --- a/src/CliProgram/Validation/Validators/BasedValidator.php +++ b/src/CliProgram/Validation/Validators/BasedValidator.php @@ -11,14 +11,14 @@ abstract class BasedValidator implements IValidator { /** * @var TValidator The internal validator */ - private $internalValidator; + private IValidator $internalValidator; /** * Create a validator * * @param TValidator $internalValidator The internal validator */ - public function __construct ($internalValidator) { + public function __construct (IValidator $internalValidator) { $this->internalValidator = $internalValidator; } /** @@ -26,7 +26,7 @@ abstract class BasedValidator implements IValidator { * * @return TValidator The internal validator */ - protected function getInternalValidator () { + protected function getInternalValidator (): IValidator { return $this->internalValidator; } diff --git a/src/CliProgram/Validation/Validators/TInternalValueValidator.php b/src/CliProgram/Validation/Validators/TInternalValueValidator.php index 307ae95..111be89 100644 --- a/src/CliProgram/Validation/Validators/TInternalValueValidator.php +++ b/src/CliProgram/Validation/Validators/TInternalValueValidator.php @@ -13,7 +13,7 @@ namespace jrosset\CliProgram\Validation\Validators; */ trait TInternalValueValidator { /** - * @var TValue|null The current value, after + * @var TValue|null The current value, after it's validation */ private $value = null;