Add command requirements support

2.x 2.6.0
Julien Rosset 9 months ago
parent 0a9a99f13c
commit 4ce251a8bc

@ -20,6 +20,7 @@
"jrosset/extendedmonolog": "^1.1",
"psr/log": "^1.1",
"symfony/console": "^5.4",
"symfony/event-dispatcher": "^5.4",
"voku/arrayy": "^7.9"
},
"autoload": {

@ -0,0 +1,41 @@
<?php
namespace jrosset\CliProgram;
use Symfony\Component\Console\Application;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Output\ConsoleOutputInterface;
use Symfony\Component\Console\Output\OutputInterface;
/**
* Helper methods
*/
abstract class CliHelper {
/**
* Get the “error” output if exists, else the output itself
*
* @param OutputInterface $output The output
*
* @return OutputInterface The “error” output if exists, else the output itself
*/
public static final function getErrorOutput (OutputInterface $output): OutputInterface {
return $output instanceof ConsoleOutputInterface ? $output->getErrorOutput() : $output;
}
/**
* Get the name of a command class
*
* @param Application $application The application
* @param class-string<Command> $commandClass The command class
*
* @return string|null The command name ; Null if not found
*/
public static final function getCommandNameFromClass (Application $application, string $commandClass): ?string {
foreach ($application->all() as $possibleCommand) {
if (get_class($possibleCommand) == $commandClass) {
return $possibleCommand->getName();
}
}
return null;
}
}

@ -0,0 +1,24 @@
<?php
namespace jrosset\CliProgram\Requirements;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use Throwable;
/**
* Interface for a command requirements
*/
interface IRequirements {
/**
* Check the requirements
*
* @param InputInterface $input The command input
* @param OutputInterface $output The command output
*
* @return void
*
* @throws Throwable If a requirement failed
*/
public function checkRequirements (InputInterface $input, OutputInterface $output): void;
}

@ -0,0 +1,64 @@
<?php
namespace jrosset\CliProgram\Requirements;
use jrosset\CliProgram\CliHelper;
use ReflectionClass;
use Symfony\Component\Console\ConsoleEvents;
use Symfony\Component\Console\Event\ConsoleCommandEvent;
use Symfony\Component\EventDispatcher\EventDispatcher;
use Symfony\Contracts\EventDispatcher\EventDispatcherInterface;
use Throwable;
/**
* An application with command requirements checking
*
* @see IRequirements
*/
trait TRequirementsApplication {
/**
* Register the listener for command requirements
*
* @param EventDispatcherInterface|null $dispatcher The event dispatcher is already existing
*
* @return void
*/
protected final function registerCommandRequirementsListener (?EventDispatcherInterface $dispatcher = null): void {
$dispatcher ??= new EventDispatcher();
$dispatcher->addListener(ConsoleEvents::COMMAND, [$this, 'checkCommandRequirements']);
$this->setDispatcher($dispatcher);
}
/**
* Check the requirements of a command
*
* @param ConsoleCommandEvent $event The command event
*
* @return void
*/
public function checkCommandRequirements (ConsoleCommandEvent $event): void {
$commandInput = $event->getInput();
$commandOutput = $event->getOutput();
try {
//region Check the command is valid
if (($command = $event->getCommand()) === null) {
return;
}
$commandReflection = new ReflectionClass($command);
//endregion
//region Contrôle pré-requis (implémentation directe)
if ($command instanceof IRequirements) {
$command->checkRequirements($commandInput, $commandOutput);
}
//endregion
}
catch (Throwable $exception) {
$this->renderThrowable($exception, CliHelper::getErrorOutput($commandOutput));
$event->disableCommand();
$event->stopPropagation();
return;
}
}
}

@ -6,12 +6,15 @@ use jrosset\CliProgram\ApplicationWithCommandMonolog;
use jrosset\CliProgram\AutoDiscovery\AutoDiscoveryDirectory;
use jrosset\CliProgram\AutoDiscovery\TAutoDiscoveryApplication;
use jrosset\CliProgram\AutoPrefix\AutoPrefixNamespaceManager;
use jrosset\CliProgram\Requirements\TRequirementsApplication;
class Application extends ApplicationWithCommandMonolog {
use TAutoDiscoveryApplication;
use TRequirementsApplication;
public function __construct (string $name = 'UNKNOWN', string $version = 'UNKNOWN') {
parent::__construct(__DIR__ . '/logs/', $name, $version);
$this->registerCommandRequirementsListener();
$spot = new AutoDiscoveryDirectory(__DIR__ . '/Commands');
$spot->getAutoPrefixManagers()->prepend(new AutoPrefixNamespaceManager('\\jrosset\\Tests\\Commands', 'test'));

@ -0,0 +1,23 @@
<?php
namespace jrosset\Tests\Commands;
use LogicException;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
class FailedHello extends Hello {
/**
* @inheritDoc
*/
public function __construct () {
parent::__construct('failedHello');
}
/**
* @inheritDoc
*/
public function checkRequirements (InputInterface $input, OutputInterface $output): void {
throw new LogicException('The "foo" requirement failed');
}
}

@ -5,6 +5,7 @@ namespace jrosset\Tests\Commands;
use DateTimeImmutable;
use DateTimeInterface;
use jrosset\CliProgram\Output\OutputWithLogger;
use jrosset\CliProgram\Requirements\IRequirements;
use jrosset\CliProgram\Validation\CommandWithValidation;
use jrosset\CliProgram\Validation\Validators\DateValidator;
use jrosset\CliProgram\Validation\Validators\IntegerValidator;
@ -14,7 +15,7 @@ use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;
class Hello extends CommandWithValidation {
class Hello extends CommandWithValidation implements IRequirements {
/**
* @inheritdoc
*/
@ -23,8 +24,14 @@ class Hello extends CommandWithValidation {
/**
* @inheritDoc
*/
public function __construct () {
parent::__construct('hello', 'bonjour');
public function __construct (?string $name = null) {
parent::__construct($name ?? 'hello', 'bonjour');
}
/**
* @inheritDoc
*/
public function checkRequirements (InputInterface $input, OutputInterface $output): void {
}
/**
@ -55,7 +62,7 @@ class Hello extends CommandWithValidation {
* @inheritDoc
*/
protected function execute (InputInterface $input, OutputInterface $output): int {
$output->writeln('Command : ' . __CLASS__, OutputInterface::VERBOSITY_DEBUG);
$output->writeln('<info>Command : ' . __CLASS__ . '</info>', OutputInterface::VERBOSITY_DEBUG);
$text = 'Hello';
$repeat = $input->getOption('repeat');

Loading…
Cancel
Save