Compare commits

..

20 Commits
master ... 2.x

Author SHA1 Message Date
Julien Rosset 08282a7c01 Fix command with validation when using -n/--no-interaction option 6 months ago
Julien Rosset c4a81079cc OutputWithLogger : fix method for “error” output 6 months ago
Julien Rosset 7fe0eea59d OutputWithLogger : add method for “error” output 8 months ago
Julien Rosset fda925927b Revert "CommandCall : allow command class name as command name"
This reverts commit 2f19a89609.
9 months ago
Julien Rosset 2f19a89609 CommandCall : allow command class name as command name 9 months ago
Julien Rosset ca6db43fed CommandCall: fix command name when AutoPrix or AutoDiscovery 9 months ago
Julien Rosset 4ce251a8bc Add command requirements support 9 months ago
Julien Rosset 0a9a99f13c Add method for normalizing command name in TAutoDiscoveryApplication 11 months ago
Julien Rosset be47153bce Suppression alerte exécution dans AutoDiscoveryDirectory 2 years ago
Julien Rosset b4d3006ef9 Correction respect des sauts de lignes dans les loggers 2 years ago
Julien Rosset f4f5f8a943 CommandCall: add default value for argument list 2 years ago
Julien Rosset 9440e3edb6 Add CommandCall 2 years ago
Julien Rosset 38c1a583dc Replace jrosset/collections with voku/arrayy 2 years ago
Julien Rosset 56e05cdc2e Fix arguments/options validation when "IS_ARRAY" is true 2 years ago
Julien Rosset c151e8038f Correction dépendance composer 2 years ago
Julien Rosset 47c5d1be51 Add validator for directory and file 2 years ago
Julien Rosset de43de4ca0 Generalize application for any LoggerInterface 2 years ago
Julien Rosset 01a7d7ffc4 Add EmailValidator 2 years ago
Julien Rosset f83533fc69 Validator : do not check Null or False (default value / not provided) 2 years ago
Julien Rosset 0ef982f8b7 PHP 7.4 & PHP 8.0 → branche 2.x 2 years ago

@ -7,20 +7,20 @@
"config": { "config": {
"sort-packages": true "sort-packages": true
}, },
"extra": { "extra": {
"branch-alias": { "branch-alias": {
"dev-master": "3.x-dev" "dev-master": "2.x-dev"
} }
}, },
"minimum-stability": "stable", "minimum-stability": "stable",
"require": { "require": {
"php": "^8.1", "php": "^7.4 || ^8.0.0",
"jrosset/betterphptoken": "^1.0", "jrosset/betterphptoken": "^1.0",
"jrosset/extendedmonolog": "^2.0", "jrosset/extendedmonolog": "^1.1",
"psr/log": "^2.0", "psr/log": "^1.1",
"symfony/console": "^6.1", "symfony/console": "^5.4",
"symfony/event-dispatcher": "^6.1", "symfony/event-dispatcher": "^5.4",
"voku/arrayy": "^7.9" "voku/arrayy": "^7.9"
}, },
"autoload": { "autoload": {

@ -127,7 +127,7 @@ class AutoDiscoveryDirectory implements IAutoDiscoverySpot {
$classes[$fileInfo->getRealPath()] = $this->applyAutoPrefixOnCommand($class->newInstance()); $classes[$fileInfo->getRealPath()] = $this->applyAutoPrefixOnCommand($class->newInstance());
} }
catch (ReflectionException) { catch (ReflectionException $exception) {
continue; continue;
} }
} }

@ -30,7 +30,7 @@ trait TAutoDiscoveryApplication {
* *
* @return $this * @return $this
*/ */
public function addAutoDiscoveredCommands (): static { public function addAutoDiscoveredCommands (): self {
/** @var IAutoDiscoverySpot $autoDiscoverySpot */ /** @var IAutoDiscoverySpot $autoDiscoverySpot */
foreach ($this->getAutoDiscoverySpots() as $autoDiscoverySpot) { foreach ($this->getAutoDiscoverySpots() as $autoDiscoverySpot) {
foreach ($autoDiscoverySpot->getCommands() as $command) { foreach ($autoDiscoverySpot->getCommands() as $command) {

@ -18,13 +18,6 @@ class BaseCommand extends Command {
*/ */
public static function getDefaultName (): ?string { public static function getDefaultName (): ?string {
$reflectionClass = new ReflectionClass(static::class); $reflectionClass = new ReflectionClass(static::class);
if ($attribute = $reflectionClass->getAttributes(CliCommand::class)) {
/** @var CliCommand $cliCommandAttribute */
$cliCommandAttribute = $attribute[0]->newInstance();
if ($cliCommandAttribute->name !== null) {
return $cliCommandAttribute->name;
}
}
if (($name = parent::getDefaultName()) !== null) { if (($name = parent::getDefaultName()) !== null) {
return $name; return $name;
} }
@ -36,34 +29,18 @@ class BaseCommand extends Command {
* @return string[] The command default aliases * @return string[] The command default aliases
*/ */
public static function getDefaultAliases (): array { public static function getDefaultAliases (): array {
$reflectionClass = new ReflectionClass(static::class);
if ($attribute = $reflectionClass->getAttributes(CliCommand::class)) {
/** @var CliCommand $cliCommandAttribute */
$cliCommandAttribute = $attribute[0]->newInstance();
return $cliCommandAttribute->aliases;
}
return []; return [];
} }
/** /**
* @return string|null The command default description * @return string|null The command default description
*/ */
public static function getDefaultDescription (): ?string { public static function getDefaultDescription (): ?string {
if ($attribute = (new ReflectionClass(static::class))->getAttributes(CliCommand::class)) {
/** @var CliCommand $cliCommandAttribute */
$cliCommandAttribute = $attribute[0]->newInstance();
return $cliCommandAttribute->description;
}
return parent::getDefaultDescription(); return parent::getDefaultDescription();
} }
/** /**
* @return bool Is the command hidden from command list by default ? * @return bool Is the command hidden from command list by default ?
*/ */
public static function getDefaultHidden (): bool { public static function getDefaultHidden (): bool {
if ($attribute = (new ReflectionClass(static::class))->getAttributes(CliCommand::class)) {
/** @var CliCommand $cliCommandAttribute */
$cliCommandAttribute = $attribute[0]->newInstance();
return $cliCommandAttribute->hidden;
}
return false; return false;
} }
@ -75,7 +52,7 @@ class BaseCommand extends Command {
* @param string|null $description The command description ; Null if the default one (cf. {@see static::getDefaultDescription()}) * @param string|null $description The command description ; Null if the default one (cf. {@see static::getDefaultDescription()})
* @param bool|null $hidden Is the command hidden from command list ? Null if the default one (cf. {@see static::getDefaultHidden()}) * @param bool|null $hidden Is the command hidden from command list ? Null if the default one (cf. {@see static::getDefaultHidden()})
*/ */
public function __construct (?string $name = null, array|string|null $aliases = null, ?string $description = null, ?bool $hidden = null) { public function __construct (?string $name = null, $aliases = null, ?string $description = null, ?bool $hidden = null) {
parent::__construct($name); parent::__construct($name);
$this->setAliases(is_string($aliases) ? [$aliases] : $aliases ?? static::getDefaultAliases()); $this->setAliases(is_string($aliases) ? [$aliases] : $aliases ?? static::getDefaultAliases());
$this->setDescription($description ?? $this->getDescription()); $this->setDescription($description ?? $this->getDescription());
@ -94,7 +71,7 @@ class BaseCommand extends Command {
* *
* @throws Throwable If an error occurs * @throws Throwable If an error occurs
*/ */
public function runSubCommands (CommandCallsList|CommandCall $subcommandList, OutputInterface $output): int { public function runSubCommands ($subcommandList, OutputInterface $output): int {
if (!$subcommandList instanceof CommandCallsList) { if (!$subcommandList instanceof CommandCallsList) {
$subcommandList = new CommandCallsList([$subcommandList]); $subcommandList = new CommandCallsList([$subcommandList]);
} }

@ -1,47 +0,0 @@
<?php
namespace jrosset\CliProgram;
use Attribute;
use jrosset\CliProgram\AutoDiscovery\IAutoDiscoverySpot;
use Symfony\Component\Console\Attribute\AsCommand;
/**
* Attribute to tag command
*
* Identical to {@see AsCommand} but allow empty command name (i.e., for {@see IAutoDiscoverySpot})
*/
#[Attribute(Attribute::TARGET_CLASS)]
class CliCommand {
/**
* @var string|null The commande name
*/
public readonly ?string $name;
/**
* @var string|null The command description
*/
public readonly ?string $description;
/**
* @var string[] The command aliases
*/
public readonly array $aliases;
/**
* @var bool Is the command hidden from command list ?
*/
public readonly bool $hidden;
/**
* Initialization
*
* @param string|null $name The commande name
* @param string|null $description The command description
* @param string[] $aliases The command aliases
* @param bool $hidden Is the command hidden from command list ?
*/
public function __construct (?string $name = null, ?string $description = null, array $aliases = [], bool $hidden = false) {
$this->name = $name;
$this->description = $description;
$this->aliases = $aliases;
$this->hidden = $hidden;
}
}

@ -33,7 +33,7 @@ abstract class CliHelper {
*/ */
public static final function getCommandNameFromClass (Application $application, string $commandClass): ?string { public static final function getCommandNameFromClass (Application $application, string $commandClass): ?string {
foreach ($application->all() as $possibleCommand) { foreach ($application->all() as $possibleCommand) {
if ($possibleCommand::class == $commandClass) { if (get_class($possibleCommand) == $commandClass) {
return $possibleCommand->getName(); return $possibleCommand->getName();
} }
} }

@ -1,67 +0,0 @@
<?php
namespace jrosset\CliProgram\CommandCall;
use Traversable;
/**
* A list of arguments for a command call
*/
class CommandArgumentList {
/**
* @var array<string, mixed> The arguments
*/
protected array $arguments;
/**
* Initialization
*
* @param Traversable|null $arguments The arguments
*/
public function __construct (?Traversable $arguments = null) {
$this->arguments = [];
if ($arguments !== null) {
foreach ($arguments as $name => $value) {
$this->arguments[$name] = $value;
}
}
}
/**
* The arguments
*
* @return array<string, mixed> The arguments
*/
public function getArguments (): array {
return $this->arguments;
}
/**
* Add an argument to the list
*
* Replace it if existing.
*
* @param string $name The argument name
* @param mixed $value The argument value
*
* @return $this
*/
public function addArgument (string $name, mixed $value): static {
$this->arguments[$name] = $value;
return $this;
}
/**
* Add an option to the list
*
* Replace it if existing.
*
* @param string $name The option name (with or without the leading dashes)
* @param mixed $value The option value
*
* @return $this
*/
public function addOption (string $name, mixed $value = true): static {
$this->arguments[(str_starts_with($name, '-') ? '' : '--') . $name] = $value;
return $this;
}
}

@ -20,17 +20,17 @@ class CommandCall {
*/ */
private string $commandName; private string $commandName;
/** /**
* @var CommandArgumentList The command arguments * @var Arrayy<string, mixed> The command arguments
*/ */
private CommandArgumentList $commandArguments; private Arrayy $commandArguments;
/** /**
* @param string|Command $commandName The new command name * @param string|Command $commandName The new command name
* @param CommandArgumentList|Arrayy<string, mixed>|null $commandArguments The command new arguments * @param null|Arrayy<string, mixed> $commandArguments The command new arguments
*/ */
public function __construct (string|Command $commandName, CommandArgumentList|Arrayy|null $commandArguments = null) { public function __construct ($commandName, ?Arrayy $commandArguments = null) {
$this->setCommandName($commandName); $this->setCommandName($commandName);
$this->setCommandArguments($commandArguments ?? new CommandArgumentList()); $this->setCommandArguments($commandArguments ?? new Arrayy());
} }
/** /**
@ -40,12 +40,9 @@ class CommandCall {
*/ */
public function generateInput (): InputInterface { public function generateInput (): InputInterface {
return new ArrayInput( return new ArrayInput(
array_merge( (clone $this->getCommandArguments())
[ ->prepend($this->getCommandName(), 'command')
'command' => $this->getCommandName(), ->toArray()
],
$this->getCommandArguments()->getArguments()
)
); );
} }
/** /**
@ -77,9 +74,9 @@ class CommandCall {
* *
* @return $this * @return $this
*/ */
public function setCommandName (string|Command $commandName): self { public function setCommandName ($commandName): self {
$this->commandName = $commandName instanceof Command $this->commandName = $commandName instanceof Command
? CliHelper::getCommandNameFromClass($commandName->getApplication(), $commandName::class) ? CliHelper::getCommandNameFromClass($commandName->getApplication(), get_class($commandName))
: $commandName; : $commandName;
return $this; return $this;
} }
@ -89,18 +86,18 @@ class CommandCall {
* *
* @return Arrayy<string, mixed> The command arguments * @return Arrayy<string, mixed> The command arguments
*/ */
public function getCommandArguments (): CommandArgumentList { public function getCommandArguments (): Arrayy {
return $this->commandArguments; return $this->commandArguments;
} }
/** /**
* Set the command arguments * Set the command arguments
* *
* @param CommandArgumentList|Arrayy $commandArguments The command new arguments * @param Arrayy<string, mixed> $commandArguments The command new arguments
* *
* @return $this * @return $this
*/ */
public function setCommandArguments (CommandArgumentList|Arrayy $commandArguments): self { public function setCommandArguments (Arrayy $commandArguments): self {
$this->commandArguments = $commandArguments instanceof CommandArgumentList ? $commandArguments : new CommandArgumentList($commandArguments); $this->commandArguments = $commandArguments;
return $this; return $this;
} }
} }

@ -3,7 +3,6 @@
namespace jrosset\CliProgram\Monolog; namespace jrosset\CliProgram\Monolog;
use jrosset\CliProgram\Output\OutputWithLogger; use jrosset\CliProgram\Output\OutputWithLogger;
use Monolog\Level;
use Monolog\Logger; use Monolog\Logger;
use Psr\Log\LoggerInterface; use Psr\Log\LoggerInterface;
use Symfony\Component\Console\Output\OutputInterface; use Symfony\Component\Console\Output\OutputInterface;
@ -17,24 +16,24 @@ class ConsoleOutputWithMonolog extends OutputWithLogger {
/** /**
* @inheritDoc * @inheritDoc
*/ */
protected function getLoggerLevelFromVerbosity (int $verbosity): Level { protected function getLoggerLevelFromVerbosity (int $verbosity): int {
if ($verbosity <= OutputInterface::VERBOSITY_QUIET) { if ($verbosity <= OutputInterface::VERBOSITY_QUIET) {
return Level::Error; return Logger::ERROR;
} }
elseif ($verbosity <= OutputInterface::VERBOSITY_NORMAL) { elseif ($verbosity <= OutputInterface::VERBOSITY_NORMAL) {
return Level::Notice; return Logger::NOTICE;
} }
elseif ($verbosity <= OutputInterface::VERBOSITY_VERBOSE) { elseif ($verbosity <= OutputInterface::VERBOSITY_VERBOSE) {
return Level::Info; return Logger::INFO;
} }
elseif ($verbosity <= OutputInterface::VERBOSITY_VERY_VERBOSE) { elseif ($verbosity <= OutputInterface::VERBOSITY_VERY_VERBOSE) {
return Level::Info; return Logger::INFO;
} }
elseif ($verbosity <= OutputInterface::VERBOSITY_DEBUG) { elseif ($verbosity <= OutputInterface::VERBOSITY_DEBUG) {
return Level::Debug; return Logger::DEBUG;
} }
else { else {
return Level::Notice; return Logger::NOTICE;
} }
} }
} }

@ -50,7 +50,7 @@ trait TMonologApplication {
* *
* @return $this * @return $this
*/ */
public function setLogMainDirectory (string $logMainDirectory): static { public function setLogMainDirectory (string $logMainDirectory): self {
$this->logMainDirectory = $logMainDirectory; $this->logMainDirectory = $logMainDirectory;
return $this; return $this;
} }

@ -1,5 +1,4 @@
<?php <?php
/** @noinspection PhpMissingFieldTypeInspection */
namespace jrosset\CliProgram\Output; namespace jrosset\CliProgram\Output;
@ -33,9 +32,9 @@ class OutputWithLogger extends OutputWrapper {
* @param OutputInterface $output The real output interface * @param OutputInterface $output The real output interface
* @param TLogger|null $logger The logger * @param TLogger|null $logger The logger
* *
* @noinspection PhpDocSignatureInspection * @noinspection PhpMissingParamTypeInspection
*/ */
public function __construct (OutputInterface $output, LoggerInterface $logger = null) { public function __construct (OutputInterface $output, $logger = null) {
parent::__construct($output); parent::__construct($output);
$this->setLogger($logger); $this->setLogger($logger);
} }
@ -43,8 +42,8 @@ class OutputWithLogger extends OutputWrapper {
/** /**
* @inheritDoc * @inheritDoc
*/ */
public function getErrorOutput (): static { public function getErrorOutput (): self {
return new static(CliHelper::getErrorOutput($this->getOutput()), $this->logger); return new static(CliHelper::getErrorOutput($this->getOutput()), $this->getLogger());
} }
/** /**
@ -64,9 +63,13 @@ class OutputWithLogger extends OutputWrapper {
* *
* @throws InvalidArgumentException If the logger is not null or an instance for {@see LoggerInterface} * @throws InvalidArgumentException If the logger is not null or an instance for {@see LoggerInterface}
* *
* @noinspection PhpDocSignatureInspection * @noinspection PhpMissingParamTypeInspection
*/ */
public function setLogger (LoggerInterface $logger): self { public function setLogger ($logger): self {
if ($logger !== null && !$logger instanceof LoggerInterface) {
throw new InvalidArgumentException('The logger must be null or a ' . LoggerInterface::class . ' instance');
}
$this->logger = $logger; $this->logger = $logger;
return $this; return $this;
} }
@ -80,7 +83,7 @@ class OutputWithLogger extends OutputWrapper {
* *
* @return void * @return void
*/ */
protected function writeToLogger (iterable|string $messages, bool $newline = false, int $options = 0): void { protected function writeToLogger ($messages, bool $newline = false, int $options = 0): void {
if ($this->logger === null || ($options & static::OPTION_SKIP_LOGGER) === static::OPTION_SKIP_LOGGER) { if ($this->logger === null || ($options & static::OPTION_SKIP_LOGGER) === static::OPTION_SKIP_LOGGER) {
return; return;
} }
@ -109,23 +112,24 @@ class OutputWithLogger extends OutputWrapper {
* @param int $verbosity The verbosity * @param int $verbosity The verbosity
* *
* @return mixed The logger level * @return mixed The logger level
* @noinspection PhpReturnDocTypeMismatchInspection
*/ */
protected function getLoggerLevelFromVerbosity (int $verbosity): mixed { protected function getLoggerLevelFromVerbosity (int $verbosity) {
return $verbosity; return $verbosity;
} }
/** /**
* @inheritDoc * @inheritDoc
*/ */
public function write (iterable|string $messages, bool $newline = false, int $options = 0) { public function write ($messages, bool $newline = false, int $options = 0) {
$this->writeToLogger($messages, $newline, $options); $this->writeToLogger($messages, $newline, $options);
return parent::write($messages, $newline, $options); return parent::write($messages, $newline, $options);
} }
/** /**
* @inheritDoc * @inheritDoc
*/ */
public function writeln (iterable|string $messages, int $options = 0) { public function writeLn ($messages, int $options = 0) {
$this->writeToLogger($messages, true, $options); $this->writeToLogger($messages, true, $options);
return parent::writeln($messages, $options); return parent::writeLn($messages, $options);
} }
} }

@ -18,7 +18,7 @@ abstract class OutputWrapper implements OutputInterface {
/** /**
* Initialization * Initialization
* *
* @param OutputInterface $output The real output interface * @param OutputInterface $output The real output interface
*/ */
public function __construct (OutputInterface $output) { public function __construct (OutputInterface $output) {
$this->output = $output; $this->output = $output;
@ -37,20 +37,20 @@ abstract class OutputWrapper implements OutputInterface {
* *
* @return static The “error” output * @return static The “error” output
*/ */
public function getErrorOutput (): static { public function getErrorOutput (): self {
return new static(CliHelper::getErrorOutput($this->getOutput())); return new static(CliHelper::getErrorOutput($this->getOutput()));
} }
/** /**
* @inheritDoc * @inheritDoc
*/ */
public function write (iterable|string $messages, bool $newline = false, int $options = 0) { public function write ($messages, bool $newline = false, int $options = 0) {
return $this->getOutput()->write($messages, $newline, $options); return $this->getOutput()->write($messages, $newline, $options);
} }
/** /**
* @inheritDoc * @inheritDoc
*/ */
public function writeln (iterable|string $messages, int $options = 0) { public function writeln ($messages, int $options = 0) {
return $this->getOutput()->writeln($messages, $options); return $this->getOutput()->writeln($messages, $options);
} }

@ -3,7 +3,6 @@
namespace jrosset\CliProgram\Requirements; namespace jrosset\CliProgram\Requirements;
use jrosset\CliProgram\CliHelper; use jrosset\CliProgram\CliHelper;
use ReflectionAttribute;
use ReflectionClass; use ReflectionClass;
use Symfony\Component\Console\ConsoleEvents; use Symfony\Component\Console\ConsoleEvents;
use Symfony\Component\Console\Event\ConsoleCommandEvent; use Symfony\Component\Console\Event\ConsoleCommandEvent;
@ -26,7 +25,7 @@ trait TRequirementsApplication {
*/ */
protected final function registerCommandRequirementsListener (?EventDispatcherInterface $dispatcher = null): void { protected final function registerCommandRequirementsListener (?EventDispatcherInterface $dispatcher = null): void {
$dispatcher ??= new EventDispatcher(); $dispatcher ??= new EventDispatcher();
$dispatcher->addListener(ConsoleEvents::COMMAND, $this->checkCommandRequirements(...)); $dispatcher->addListener(ConsoleEvents::COMMAND, [$this, 'checkCommandRequirements']);
$this->setDispatcher($dispatcher); $this->setDispatcher($dispatcher);
} }
@ -37,7 +36,7 @@ trait TRequirementsApplication {
* *
* @return void * @return void
*/ */
private function checkCommandRequirements (ConsoleCommandEvent $event): void { public function checkCommandRequirements (ConsoleCommandEvent $event): void {
$commandInput = $event->getInput(); $commandInput = $event->getInput();
$commandOutput = $event->getOutput(); $commandOutput = $event->getOutput();
try { try {
@ -48,13 +47,6 @@ trait TRequirementsApplication {
$commandReflection = new ReflectionClass($command); $commandReflection = new ReflectionClass($command);
//endregion //endregion
//region Check attribute requirements
foreach ($commandReflection->getAttributes(IRequirements::class, ReflectionAttribute::IS_INSTANCEOF) as $commandRequirementsAttribute) {
/** @var IRequirements $commandRequirementsAttributeInstance */
$commandRequirementsAttributeInstance = $commandRequirementsAttribute->newInstance();
$commandRequirementsAttributeInstance->checkRequirements($commandInput, $commandOutput);
}
//endregion
//region Contrôle pré-requis (implémentation directe) //region Contrôle pré-requis (implémentation directe)
if ($command instanceof IRequirements) { if ($command instanceof IRequirements) {
$command->checkRequirements($commandInput, $commandOutput); $command->checkRequirements($commandInput, $commandOutput);

@ -7,12 +7,12 @@ use jrosset\CliProgram\Validation\Validators\InvalidValueException;
use jrosset\CliProgram\Validation\Validators\IValidator; use jrosset\CliProgram\Validation\Validators\IValidator;
use ReflectionFunction; use ReflectionFunction;
use Stringable; use Stringable;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Exception\InvalidArgumentException; use Symfony\Component\Console\Exception\InvalidArgumentException;
use Symfony\Component\Console\Input\InputArgument; use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption; use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface; use Symfony\Component\Console\Output\OutputInterface;
use Throwable;
/** /**
* Provide a value validation process ({@see IValidator}) to {@see self::addArgument()} and {@see self::addOption()} * Provide a value validation process ({@see IValidator}) to {@see self::addArgument()} and {@see self::addOption()}
@ -34,6 +34,8 @@ trait TCommandWithValidation {
/** /**
* @inheritDoc * @inheritDoc
*
* @throws Throwable If an argument or option is not valid
*/ */
protected function initialize (InputInterface $input, OutputInterface $output): void { protected function initialize (InputInterface $input, OutputInterface $output): void {
parent::setCode( parent::setCode(
@ -49,8 +51,10 @@ trait TCommandWithValidation {
} }
/** /**
* @inheritDoc * @inheritDoc
*
* @noinspection PhpMissingReturnTypeInspection
*/ */
public function setCode (callable $code): static { public function setCode (callable $code) {
if ($code instanceof Closure) { if ($code instanceof Closure) {
/** @noinspection PhpUnhandledExceptionInspection */ /** @noinspection PhpUnhandledExceptionInspection */
$codeReflection = new ReflectionFunction($code); $codeReflection = new ReflectionFunction($code);
@ -69,9 +73,6 @@ trait TCommandWithValidation {
} }
} }
} }
else {
$code = $code(...);
}
// {@see Command::$code} is used to perform input validation before execution, so set {@see self::$realCode} instead // {@see Command::$code} is used to perform input validation before execution, so set {@see self::$realCode} instead
$this->realCode = $code; $this->realCode = $code;
@ -193,31 +194,24 @@ trait TCommandWithValidation {
* @param mixed $default The default value (for InputArgument::OPTIONAL mode only) * @param mixed $default The default value (for InputArgument::OPTIONAL mode only)
* @param string $description The argument description * @param string $description The argument description
* @param IValidator|null $validator The validator or Null if none * @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 * @return $this
* *
* @throws InvalidArgumentException When argument mode is not valid * @throws InvalidArgumentException When argument mode is not valid
*/ */
public function addArgument ( public function addArgument (
string $name, string $name,
int $mode = null, int $mode = null,
string $description = '', string $description = '',
mixed $default = null, $default = null,
?IValidator $validator = null, ?IValidator $validator = null
Closure|array|null $suggestedValues = null ): self {
): static {
$default = static::treatStringableDefaultValue($default); $default = static::treatStringableDefaultValue($default);
if ($validator !== null) { if ($validator !== null) {
$default = $validator->getValidDefault($default); $default = $validator->getValidDefault($default);
} }
if ($suggestedValues !== null) { parent::addArgument($name, $mode, $description, $default);
parent::addArgument($name, $mode, $description, $default, $suggestedValues);
}
else {
parent::addArgument($name, $mode, $description, $default);
}
return $this->setArgumentValidator($name, $validator); return $this->setArgumentValidator($name, $validator);
} }
/** /**
@ -230,7 +224,7 @@ trait TCommandWithValidation {
* *
* @throws InvalidArgumentException If the {@see InputArgument} doesn't exist * @throws InvalidArgumentException If the {@see InputArgument} doesn't exist
*/ */
public function setArgumentValidator (string $name, ?IValidator $validator): static { public function setArgumentValidator (string $name, ?IValidator $validator): self {
if (!$this->getDefinition()->hasArgument($name)) { if (!$this->getDefinition()->hasArgument($name)) {
throw new InvalidArgumentException(sprintf('The "%s" argument does not exist', $name)); throw new InvalidArgumentException(sprintf('The "%s" argument does not exist', $name));
} }
@ -266,32 +260,25 @@ trait TCommandWithValidation {
* @param string $description The argument description * @param string $description The argument description
* @param mixed $default The default value (must be null for InputOption::VALUE_NONE) * @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 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 * @return $this
* *
* @throws InvalidArgumentException When argument mode is not valid * @throws InvalidArgumentException When argument mode is not valid
*/ */
public function addOption ( public function addOption (
string $name, string $name,
string|array|null $shortcut = null, $shortcut = null,
int $mode = null, int $mode = null,
string $description = '', string $description = '',
mixed $default = null, $default = null,
?IValidator $validator = null, ?IValidator $validator = null
Closure|array|null $suggestedValues = null ): self {
): static {
$default = static::treatStringableDefaultValue($default); $default = static::treatStringableDefaultValue($default);
if ($validator !== null) { if ($validator !== null) {
$default = $validator->getValidDefault($default); $default = $validator->getValidDefault($default);
} }
if ($suggestedValues !== null) { parent::addOption($name, $shortcut, $mode, $description, $default);
parent::addOption($name, $shortcut, $mode, $description, $default, $suggestedValues);
}
else {
parent::addOption($name, $shortcut, $mode, $description, $default);
}
return $this->setOptionValidator($name, $validator); return $this->setOptionValidator($name, $validator);
} }
/** /**
@ -304,7 +291,7 @@ trait TCommandWithValidation {
* *
* @throws InvalidArgumentException If the {@see InputOption} doesn't exist * @throws InvalidArgumentException If the {@see InputOption} doesn't exist
*/ */
public function setOptionValidator (string $name, ?IValidator $validator): static { public function setOptionValidator (string $name, ?IValidator $validator): self {
if (!$this->getDefinition()->hasOption($name)) { if (!$this->getDefinition()->hasOption($name)) {
throw new InvalidArgumentException(sprintf('The "--%s" option does not exist', $name)); throw new InvalidArgumentException(sprintf('The "--%s" option does not exist', $name));
} }
@ -338,7 +325,7 @@ trait TCommandWithValidation {
* *
* @return mixed The default value * @return mixed The default value
*/ */
protected static function treatStringableDefaultValue (mixed $default): mixed { protected static function treatStringableDefaultValue ($default) {
if ($default instanceof Stringable) { if ($default instanceof Stringable) {
return $default->__toString(); return $default->__toString();
} }

@ -20,7 +20,7 @@ abstract class AbstractDateTimeValidator extends BasedValidator {
/** /**
* @inheritDoc * @inheritDoc
*/ */
public function validate (mixed $value): bool { public function validate ($value): bool {
$this->getInternalValidator()->setPattern($this->getPattern()); $this->getInternalValidator()->setPattern($this->getPattern());
return parent::validate($value); return parent::validate($value);
} }

@ -11,14 +11,16 @@ abstract class BasedValidator implements IValidator {
/** /**
* @var TValidator The internal validator * @var TValidator The internal validator
*/ */
private IValidator $internalValidator; private $internalValidator;
/** /**
* Create a validator * Create a validator
* *
* @param TValidator $internalValidator The internal validator * @param TValidator $internalValidator The internal validator
*
* @noinspection PhpMissingParamTypeInspection
*/ */
public function __construct (IValidator $internalValidator) { public function __construct ($internalValidator) {
$this->internalValidator = $internalValidator; $this->internalValidator = $internalValidator;
} }
/** /**
@ -26,14 +28,14 @@ abstract class BasedValidator implements IValidator {
* *
* @return TValidator The internal validator * @return TValidator The internal validator
*/ */
protected function getInternalValidator (): IValidator { protected function getInternalValidator () {
return $this->internalValidator; return $this->internalValidator;
} }
/** /**
* @inheritDoc * @inheritDoc
*/ */
public function validate (mixed $value): bool { public function validate ($value): bool {
return $this->internalValidator->validate($value); return $this->internalValidator->validate($value);
} }
} }

@ -15,27 +15,21 @@ class DateTimeValidator extends AbstractDateTimeValidator {
* @var bool Is the time part mandatory ? * @var bool Is the time part mandatory ?
*/ */
private bool $timeMandatory; private bool $timeMandatory;
/**
* @var DateTimeInterface The time part (if not mandatory)
*/
private DateTimeInterface $timePart;
/** /**
* Create a validator * Create a validator
* *
* @param bool $timeMandatory Is the time part mandatory ? * @param bool $mandatoryDate Is the time part mandatory ?
* @param DateTimeInterface|null $timePart The time part (if not mandatory)
*/ */
public function __construct (bool $timeMandatory = false, ?DateTimeInterface $timePart = null) { public function __construct (bool $mandatoryDate = false) {
$this->setTimeMandatory($timeMandatory); $this->setTimeMandatory($mandatoryDate);
$this->setTimePart($timePart ?? (new DateTimeImmutable())->setTimestamp(0));
parent::__construct(); parent::__construct();
} }
/** /**
* @inheritDoc * @inheritDoc
*/ */
public function getValidDefault (mixed $default): string|bool|int|float|array|null { public function getValidDefault ($default) {
if ($default instanceof DateTimeInterface) { if ($default instanceof DateTimeInterface) {
return $default->format('Y-m-d H:i:s'); return $default->format('Y-m-d H:i:s');
} }
@ -62,9 +56,9 @@ class DateTimeValidator extends AbstractDateTimeValidator {
(int)$this->getInternalValidator()->getMatch('day'), (int)$this->getInternalValidator()->getMatch('day'),
); );
return $value->setTime( return $value->setTime(
(int)($this->getInternalValidator()->getMatch('hour') ?? (int)$this->getTimePart()->format('%H')), (int)($this->getInternalValidator()->getMatch('hour') ?? 0),
(int)($this->getInternalValidator()->getMatch('minute') ?? (int)$this->getTimePart()->format('%i')), (int)($this->getInternalValidator()->getMatch('minute') ?? 0),
(int)($this->getInternalValidator()->getMatch('second') ?? (int)$this->getTimePart()->format('%s')), (int)($this->getInternalValidator()->getMatch('second') ?? 0),
); );
} }
@ -87,24 +81,4 @@ class DateTimeValidator extends AbstractDateTimeValidator {
$this->timeMandatory = $timeMandatory; $this->timeMandatory = $timeMandatory;
return $this; return $this;
} }
/**
* The time part (if not mandatory)
*
* @return DateTimeInterface The time part (if not mandatory)
*/
public function getTimePart (): DateTimeInterface {
return $this->timePart;
}
/**
* Set the time part (if not mandatory)
*
* @param DateTimeInterface $timePart The time part
*
* @return $this
*/
public function setTimePart (DateTimeInterface $timePart): self {
$this->timePart = $timePart;
return $this;
}
} }

@ -29,7 +29,7 @@ class DateValidator extends AbstractDateTimeValidator {
/** /**
* @inheritDoc * @inheritDoc
*/ */
public function getValidDefault (mixed $default): string|bool|int|float|array|null { public function getValidDefault ($default) {
if ($default instanceof DateTimeInterface) { if ($default instanceof DateTimeInterface) {
return $default->format('Y-m-d'); return $default->format('Y-m-d');
} }

@ -56,7 +56,7 @@ class DecimalValidator extends BasedValidator {
/** /**
* @inheritDoc * @inheritDoc
*/ */
public function validate (mixed $value): bool { public function validate ($value): bool {
if (!parent::validate($value)) { if (!parent::validate($value)) {
return false; return false;
} }

@ -21,7 +21,7 @@ class DirectoryValidator implements IValidator {
* *
* @param FilesystemValidationOptionsList|FilesystemValidationOption[]|FilesystemValidationOption|null $options The options * @param FilesystemValidationOptionsList|FilesystemValidationOption[]|FilesystemValidationOption|null $options The options
*/ */
public function __construct (FilesystemValidationOptionsList|array|FilesystemValidationOption|null $options = null) { public function __construct ($options = null) {
$this->setOptions($options); $this->setOptions($options);
} }
@ -35,7 +35,7 @@ class DirectoryValidator implements IValidator {
* @throws InvalidArgumentException If the default value is not a string or null * @throws InvalidArgumentException If the default value is not a string or null
* @throws InvalidArgumentException If the default directory doesn't match the options * @throws InvalidArgumentException If the default directory doesn't match the options
*/ */
public function getValidDefault (mixed $default): string|bool|int|float|array|null { public function getValidDefault ($default) {
if ($default === null) { if ($default === null) {
return null; return null;
} }
@ -43,13 +43,13 @@ class DirectoryValidator implements IValidator {
throw new InvalidArgumentException('The default value must be a string or null'); throw new InvalidArgumentException('The default value must be a string or null');
} }
if ($this->getOptions()->contains(FilesystemValidationOption::IsReadable) && !is_readable($default)) { if ($this->getOptions()->contains(FilesystemValidationOption::IS_READABLE) && !is_readable($default)) {
throw new InvalidArgumentException('The default value is not readable'); throw new InvalidArgumentException('The default value is not readable');
} }
if ($this->getOptions()->contains(FilesystemValidationOption::IsWritable) && !is_writable($default)) { if ($this->getOptions()->contains(FilesystemValidationOption::IS_WRITABLE) && !is_writable($default)) {
throw new InvalidArgumentException('The default value is not writable'); throw new InvalidArgumentException('The default value is not writable');
} }
if ($this->getOptions()->contains(FilesystemValidationOption::MustExists) && !file_exists($default)) { if ($this->getOptions()->contains(FilesystemValidationOption::MUST_EXISTS) && !file_exists($default)) {
throw new InvalidArgumentException('The default value doesn\'t exist'); throw new InvalidArgumentException('The default value doesn\'t exist');
} }
return $default; return $default;
@ -58,18 +58,18 @@ class DirectoryValidator implements IValidator {
/** /**
* @inheritDoc * @inheritDoc
*/ */
public function validate (mixed $value): bool { public function validate ($value): bool {
if (!is_string($value) && !$value instanceof Stringable) { if (!is_string($value) && !$value instanceof Stringable) {
return false; return false;
} }
if ($this->getOptions()->contains(FilesystemValidationOption::IsReadable->name) && !is_readable($value)) { if ($this->getOptions()->contains(FilesystemValidationOption::IS_READABLE) && !is_readable($value)) {
return false; return false;
} }
if ($this->getOptions()->contains(FilesystemValidationOption::IsWritable->name) && !is_writable($value)) { if ($this->getOptions()->contains(FilesystemValidationOption::IS_WRITABLE) && !is_writable($value)) {
return false; return false;
} }
if ($this->getOptions()->contains(FilesystemValidationOption::MustExists->name) && !file_exists($value)) { if ($this->getOptions()->contains(FilesystemValidationOption::MUST_EXISTS) && !file_exists($value)) {
return false; return false;
} }
@ -92,13 +92,22 @@ class DirectoryValidator implements IValidator {
* *
* @return $this * @return $this
*/ */
public function setOptions (FilesystemValidationOptionsList|array|FilesystemValidationOption|null $options = null): static { public function setOptions ($options = null): self {
$this->options = match (true) { if ($options instanceof FilesystemValidationOptionsList) {
$options instanceof FilesystemValidationOptionsList => $options, $this->options = $options;
$options instanceof FilesystemValidationOption => new FilesystemValidationOptionsList([$options]), }
is_array($options) => new FilesystemValidationOptionsList($options), elseif (is_int($options)) {
$options === null => new FilesystemValidationOptionsList(), $this->options = new FilesystemValidationOptionsList([$options]);
}; }
elseif (is_array($options)) {
$this->options = new FilesystemValidationOptionsList($options);
}
elseif ($options === null) {
$this->options = new FilesystemValidationOptionsList();
}
else {
throw new InvalidArgumentException();
}
return $this; return $this;
} }
} }

@ -12,7 +12,7 @@ class EmailValidator implements IValidator {
/** /**
* @inheritDoc * @inheritDoc
*/ */
public function validate (mixed $value): bool { public function validate ($value): bool {
if (!filter_var($value, FILTER_VALIDATE_EMAIL)) { if (!filter_var($value, FILTER_VALIDATE_EMAIL)) {
return false; return false;
} }

@ -1,60 +0,0 @@
<?php
namespace jrosset\CliProgram\Validation\Validators;
use Arrayy\Type\StringCollection;
use ReflectionEnum;
use ReflectionException;
use UnitEnum;
/**
* An argument/option value validator based on an enumeration
*
* @template TEnum of UnitEnum
* @template-implements IValidator<TEnum>
* @template-implements TInternalValueValidator<TEnum>
*/
class EnumValidator extends BasedValidator {
/**
* @var ReflectionEnum The enumeration
*/
private ReflectionEnum $enum;
/**
* Create a validator
*
* @param class-string<UnitEnum> $enumClass The enumeration class
*
* @throws ReflectionException If the enumeration class doesn't exist
*/
public function __construct (string $enumClass) {
$this->enum = new ReflectionEnum($enumClass);
$enumCases = new StringCollection();
foreach ($this->enum->getCases() as $enumCase) {
$enumCases->add($enumCase->getName());
}
parent::__construct(new ListValidator($enumCases));
}
/**
* @inheritDoc
*/
public function getValidDefault (mixed $default): string|bool|int|float|array|null {
if ($default instanceof UnitEnum) {
$default = $default->name;
}
return $default;
}
/**
* @inheritDoc
*
* @throws ReflectionException
*/
public function getValue (): UnitEnum {
$enumCase = $this->getInternalValidator()->getValue();
return $this->enum->getCase($enumCase)->getValue();
}
}

@ -19,7 +19,7 @@ class FileValidator extends DirectoryValidator {
* @param string|null $extensionsPattern The allowed extensions (regex pattern) * @param string|null $extensionsPattern The allowed extensions (regex pattern)
* @param FilesystemValidationOptionsList|FilesystemValidationOption[]|FilesystemValidationOption|null $options The options * @param FilesystemValidationOptionsList|FilesystemValidationOption[]|FilesystemValidationOption|null $options The options
*/ */
public function __construct (?string $extensionsPattern = null, FilesystemValidationOptionsList|FilesystemValidationOption|array|null $options = null) { public function __construct (?string $extensionsPattern = null, $options = null) {
parent::__construct($options); parent::__construct($options);
$this->setExtensionsPattern($extensionsPattern); $this->setExtensionsPattern($extensionsPattern);
} }
@ -27,7 +27,7 @@ class FileValidator extends DirectoryValidator {
/** /**
* @inheritDoc * @inheritDoc
*/ */
public function getValidDefault (mixed $default): string|bool|int|float|array|null { public function getValidDefault ($default) {
if (($default = parent::getValidDefault($default)) === null) { if (($default = parent::getValidDefault($default)) === null) {
return null; return null;
} }
@ -39,7 +39,7 @@ class FileValidator extends DirectoryValidator {
/** /**
* @inheritDoc * @inheritDoc
*/ */
public function validate (mixed $value): bool { public function validate ($value): bool {
if (!parent::validate($value)) { if (!parent::validate($value)) {
return false; return false;
} }
@ -64,7 +64,7 @@ class FileValidator extends DirectoryValidator {
* *
* @return $this * @return $this
*/ */
public function setExtensionsPattern (?string $extensionsPattern = null): static { public function setExtensionsPattern (?string $extensionsPattern = null): self {
$this->extensionsPattern = $extensionsPattern; $this->extensionsPattern = $extensionsPattern;
return $this; return $this;
} }

@ -5,8 +5,8 @@ namespace jrosset\CliProgram\Validation\Validators;
/** /**
* Options of a filesystem based validator * Options of a filesystem based validator
*/ */
enum FilesystemValidationOption { abstract class FilesystemValidationOption {
case MustExists; public const MUST_EXISTS = 1;
case IsReadable; public const IS_READABLE = 2;
case IsWritable; public const IS_WRITABLE = 3;
} }

@ -14,6 +14,6 @@ class FilesystemValidationOptionsList extends AbstractCollection {
* @inheritDoc * @inheritDoc
*/ */
public function getType (): string { public function getType (): string {
return FilesystemValidationOption::class; return 'int';
} }
} }

@ -13,9 +13,9 @@ interface IValidator {
* *
* @param mixed $default The initial/given default value * @param mixed $default The initial/given default value
* *
* @return string|bool|int|float|array|null The valid default value * @return string|bool|int|float|array The valid default value
*/ */
public function getValidDefault (mixed $default): string|bool|int|float|array|null; public function getValidDefault ($default);
/** /**
* Validate a value * Validate a value
@ -24,7 +24,7 @@ interface IValidator {
* *
* @return bool True if the value is valid, else False * @return bool True if the value is valid, else False
*/ */
public function validate (mixed $value): bool; public function validate ($value): bool;
/** /**
* Get the value, after it's validation * Get the value, after it's validation
* *

@ -50,7 +50,7 @@ class IntegerValidator extends BasedValidator {
/** /**
* @inheritDoc * @inheritDoc
*/ */
public function validate (mixed $value): bool { public function validate ($value): bool {
if (!parent::validate($value)) { if (!parent::validate($value)) {
return false; return false;
} }

@ -32,7 +32,7 @@ class ListValidator implements IValidator {
/** /**
* @inheritDoc * @inheritDoc
*/ */
public function validate (mixed $value): bool { public function validate ($value): bool {
if ($this->getAllowedValues()->contains($value)) { if ($this->getAllowedValues()->contains($value)) {
$this->setValue($value); $this->setValue($value);
return true; return true;

@ -32,7 +32,7 @@ class RegexValidator implements IValidator {
/** /**
* @inheritDoc * @inheritDoc
*/ */
public function validate (mixed $value): bool { public function validate ($value): bool {
return preg_match($this->pattern, $value, $this->matches) === 1; return preg_match($this->pattern, $value, $this->matches) === 1;
} }
@ -71,7 +71,7 @@ class RegexValidator implements IValidator {
* *
* @return string|null The capturing group or Null if not set * @return string|null The capturing group or Null if not set
*/ */
public function getMatch (int|string $group): ?string { public function getMatch ($group): ?string {
return $this->getMatches()[$group] ?? null; return $this->getMatches()[$group] ?? null;
} }
/** /**
@ -81,7 +81,7 @@ class RegexValidator implements IValidator {
* *
* @return bool True if the capturing group exists and is not null * @return bool True if the capturing group exists and is not null
*/ */
public function hasMatch (int|string $group): bool { public function hasMatch ($group): bool {
return $this->getMatches()[$group] !== null; return $this->getMatches()[$group] !== null;
} }

@ -11,7 +11,7 @@ trait TIdenticalValidDefaultValidator {
/** /**
* @inheritDoc * @inheritDoc
*/ */
public function getValidDefault (mixed $default): string|bool|int|float|array|null { public function getValidDefault ($default) {
return $default; return $default;
} }
} }

@ -13,7 +13,7 @@ namespace jrosset\CliProgram\Validation\Validators;
*/ */
trait TInternalValueValidator { trait TInternalValueValidator {
/** /**
* @var TValue|null The current value, after it's validation * @var TValue|null The current value, after
*/ */
private $value = null; private $value = null;
@ -32,7 +32,7 @@ trait TInternalValueValidator {
* *
* @return $this * @return $this
*/ */
protected function setValue ($value): static { protected function setValue ($value): self {
$this->value = $value; $this->value = $value;
return $this; return $this;
} }

@ -29,7 +29,7 @@ class TimeValidator extends AbstractDateTimeValidator {
/** /**
* @inheritDoc * @inheritDoc
*/ */
public function getValidDefault (mixed $default): string|bool|int|float|array|null { public function getValidDefault ($default) {
if ($default instanceof DateTimeInterface) { if ($default instanceof DateTimeInterface) {
return $default->format('H:i:s'); return $default->format('H:i:s');
} }

@ -2,9 +2,10 @@
namespace jrosset\Tests\Commands; namespace jrosset\Tests\Commands;
use jrosset\Tests\FailedRequirement; use LogicException;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
#[FailedRequirement]
class FailedHello extends Hello { class FailedHello extends Hello {
/** /**
* @inheritDoc * @inheritDoc
@ -12,4 +13,11 @@ class FailedHello extends Hello {
public function __construct () { public function __construct () {
parent::__construct('failedHello'); parent::__construct('failedHello');
} }
/**
* @inheritDoc
*/
public function checkRequirements (InputInterface $input, OutputInterface $output): void {
throw new LogicException('The "foo" requirement failed');
}
} }

@ -4,28 +4,34 @@ namespace jrosset\Tests\Commands;
use DateTimeImmutable; use DateTimeImmutable;
use DateTimeInterface; use DateTimeInterface;
use jrosset\CliProgram\CliCommand;
use jrosset\CliProgram\Output\OutputWithLogger; use jrosset\CliProgram\Output\OutputWithLogger;
use jrosset\CliProgram\Requirements\IRequirements;
use jrosset\CliProgram\Validation\CommandWithValidation; use jrosset\CliProgram\Validation\CommandWithValidation;
use jrosset\CliProgram\Validation\Validators\DateValidator; use jrosset\CliProgram\Validation\Validators\DateValidator;
use jrosset\CliProgram\Validation\Validators\EnumValidator;
use jrosset\CliProgram\Validation\Validators\IntegerValidator; use jrosset\CliProgram\Validation\Validators\IntegerValidator;
use jrosset\Tests\Lang;
use jrosset\Tests\SuccessRequirement;
use Symfony\Component\Console\Command\Command; use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputArgument; use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption; use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface; use Symfony\Component\Console\Output\OutputInterface;
#[CliCommand(null, 'Say hello')] class Hello extends CommandWithValidation implements IRequirements {
#[SuccessRequirement] /**
class Hello extends CommandWithValidation { * @inheritdoc
*/
protected static $defaultDescription = 'Say hello';
/** /**
* @inheritDoc * @inheritDoc
*/ */
public function __construct (?string $name = null) { public function __construct (?string $name = null) {
parent::__construct($name ?? 'hello', ['bonjour']); parent::__construct($name ?? 'hello', 'bonjour');
}
/**
* @inheritDoc
*/
public function checkRequirements (InputInterface $input, OutputInterface $output): void {
} }
/** /**
@ -42,14 +48,6 @@ class Hello extends CommandWithValidation {
new DateValidator() new DateValidator()
); );
$this->addOption(
'lang',
'l',
InputOption::VALUE_REQUIRED,
'The lang',
null,
new EnumValidator(Lang::class)
);
$this->addOption( $this->addOption(
'repeat', 'repeat',
'r', 'r',
@ -66,7 +64,7 @@ class Hello extends CommandWithValidation {
protected function execute (InputInterface $input, OutputInterface $output): int { protected function execute (InputInterface $input, OutputInterface $output): int {
$output->writeln('<info>Command : ' . __CLASS__ . '</info>', OutputInterface::VERBOSITY_DEBUG); $output->writeln('<info>Command : ' . __CLASS__ . '</info>', OutputInterface::VERBOSITY_DEBUG);
$text = ($input->getOption('lang') ?? Lang::English)->value; $text = 'Hello';
$repeat = $input->getOption('repeat'); $repeat = $input->getOption('repeat');
/** @var DateTimeInterface $day */ /** @var DateTimeInterface $day */

@ -29,7 +29,7 @@ class ReadFile extends CommandWithValidation {
null, null,
new FileValidator( new FileValidator(
/** @lang PhpRegExp */ '#\.txt$#i', /** @lang PhpRegExp */ '#\.txt$#i',
FilesystemValidationOption::IsReadable FilesystemValidationOption::IS_READABLE
) )
); );
} }

@ -1,19 +0,0 @@
<?php
namespace jrosset\Tests;
use Attribute;
use jrosset\CliProgram\Requirements\IRequirements;
use LogicException;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
#[Attribute(Attribute::TARGET_CLASS)]
class FailedRequirement implements IRequirements {
/**
* @inheritDoc
*/
public function checkRequirements (InputInterface $input, OutputInterface $output): void {
throw new LogicException('The "foo" requirement failed');
}
}

@ -1,9 +0,0 @@
<?php
namespace jrosset\Tests;
enum Lang: string {
case French = 'Bonjour';
case English = 'Good morning';
case American = 'Hello';
}

@ -1,17 +0,0 @@
<?php
namespace jrosset\Tests;
use Attribute;
use jrosset\CliProgram\Requirements\IRequirements;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
#[Attribute(Attribute::TARGET_CLASS)]
class SuccessRequirement implements IRequirements {
/**
* @inheritDoc
*/
public function checkRequirements (InputInterface $input, OutputInterface $output): void {
}
}
Loading…
Cancel
Save