Skip to content

Commit

Permalink
Give command handlers the ability to save and restore their state bef…
Browse files Browse the repository at this point in the history
…ore injecting input / output objects. Do not inject into objects that do not do this.
  • Loading branch information
greg-1-anderson committed Aug 28, 2020
1 parent 4f7a58b commit ddb522e
Show file tree
Hide file tree
Showing 11 changed files with 155 additions and 84 deletions.
17 changes: 2 additions & 15 deletions src/AnnotatedCommand.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@
use Consolidation\AnnotatedCommand\Hooks\HookManager;
use Consolidation\AnnotatedCommand\Output\OutputAwareInterface;
use Consolidation\AnnotatedCommand\Parser\CommandInfo;
use Consolidation\AnnotatedCommand\State\State;
use Consolidation\AnnotatedCommand\State\StateHelper;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputAwareInterface;
Expand Down Expand Up @@ -276,7 +278,6 @@ public function optionsHookForHookAnnotations($commandInfoList)
*/
protected function interact(InputInterface $input, OutputInterface $output)
{
$this->injectIntoCommandfileInstance($input, $output);
$this->commandProcessor()->interact(
$input,
$output,
Expand All @@ -287,7 +288,6 @@ protected function interact(InputInterface $input, OutputInterface $output)

protected function initialize(InputInterface $input, OutputInterface $output)
{
$this->injectIntoCommandfileInstance($input, $output);
// Allow the hook manager a chance to provide configuration values,
// if there are any registered hooks to do that.
$this->commandProcessor()->initializeHook($input, $this->getNames(), $this->annotationData);
Expand All @@ -298,7 +298,6 @@ protected function initialize(InputInterface $input, OutputInterface $output)
*/
protected function execute(InputInterface $input, OutputInterface $output)
{
$this->injectIntoCommandfileInstance($input, $output);
// Validate, run, process, alter, handle results.
return $this->commandProcessor()->process(
$output,
Expand All @@ -316,7 +315,6 @@ protected function execute(InputInterface $input, OutputInterface $output)
*/
public function processResults(InputInterface $input, OutputInterface $output, $results)
{
$this->injectIntoCommandfileInstance($input, $output);
$commandData = $this->createCommandData($input, $output);
$commandProcessor = $this->commandProcessor();
$names = $this->getNames();
Expand Down Expand Up @@ -353,15 +351,4 @@ protected function createCommandData(InputInterface $input, OutputInterface $out

return $commandData;
}

/**
* Inject $input and $output into the command instance if it is set up to receive them.
*
* @param callable $commandCallback
* @param CommandData $commandData
*/
public function injectIntoCommandfileInstance(InputInterface $input, OutputInterface $output)
{
InjectionHelper::injectIntoCallbackObject($this->commandCallback, $input, $output);
}
}
11 changes: 4 additions & 7 deletions src/Hooks/Dispatchers/CommandEventHookDispatcher.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@

use Consolidation\AnnotatedCommand\AnnotatedCommand;
use Consolidation\AnnotatedCommand\Hooks\HookManager;
use Consolidation\AnnotatedCommand\InjectionHelper;
use Consolidation\AnnotatedCommand\State\State;
use Consolidation\AnnotatedCommand\State\StateHelper;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\ConsoleEvents;
use Symfony\Component\Console\Event\ConsoleCommandEvent;
Expand All @@ -21,14 +22,9 @@ class CommandEventHookDispatcher extends HookDispatcher
*/
public function callCommandEventHooks(ConsoleCommandEvent $event)
{
$command = $event->getCommand();
$input = $event->getInput();
$output = $event->getOutput();

if ($command instanceof AnnotatedCommand) {
$command->injectIntoCommandfileInstance($input, $output);
}

$hooks = [
HookManager::PRE_COMMAND_EVENT,
HookManager::COMMAND_EVENT,
Expand All @@ -40,8 +36,9 @@ public function callCommandEventHooks(ConsoleCommandEvent $event)
$commandEvent->dispatch($event, ConsoleEvents::COMMAND);
}
if (is_callable($commandEvent)) {
InjectionHelper::injectIntoCallbackObject($commandEvent, $input, $output);
$state = StateHelper::injectIntoCallbackObject($commandEvent, $input, $output);
$commandEvent($event);
$state->restore();
}
}
}
Expand Down
12 changes: 10 additions & 2 deletions src/Hooks/Dispatchers/InitializeHookDispatcher.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@
use Consolidation\AnnotatedCommand\AnnotationData;
use Consolidation\AnnotatedCommand\Hooks\HookManager;
use Consolidation\AnnotatedCommand\Hooks\InitializeHookInterface;
use Consolidation\AnnotatedCommand\InjectionHelper;
use Consolidation\AnnotatedCommand\State\State;
use Consolidation\AnnotatedCommand\State\StateHelper;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputInterface;

Expand All @@ -31,7 +32,14 @@ public function initialize(

protected function callInitializeHook($provider, $input, AnnotationData $annotationData)
{
InjectionHelper::injectIntoCallbackObject($provider, $input);
$state = StateHelper::injectIntoCallbackObject($provider, $input);
$result = $this->doInitializeHook($provider, $input, $annotationData);
$state->restore();
return $result;
}

private function doInitializeHook($provider, $input, AnnotationData $annotationData)
{
if ($provider instanceof InitializeHookInterface) {
return $provider->initialize($input, $annotationData);
}
Expand Down
12 changes: 10 additions & 2 deletions src/Hooks/Dispatchers/InteractHookDispatcher.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@
use Consolidation\AnnotatedCommand\AnnotationData;
use Consolidation\AnnotatedCommand\Hooks\HookManager;
use Consolidation\AnnotatedCommand\Hooks\InteractorInterface;
use Consolidation\AnnotatedCommand\InjectionHelper;
use Consolidation\AnnotatedCommand\State\State;
use Consolidation\AnnotatedCommand\State\StateHelper;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;

Expand All @@ -32,7 +33,14 @@ public function interact(

protected function callInteractor($interactor, $input, $output, AnnotationData $annotationData)
{
InjectionHelper::injectIntoCallbackObject($interactor, $input, $output);
$state = StateHelper::injectIntoCallbackObject($interactor, $input, $output);
$result = $this->doInteractor($interactor, $input, $output, $annotationData);
$state->restore();
return $result;
}

private function doInteractor($interactor, $input, $output, AnnotationData $annotationData)
{
if ($interactor instanceof InteractorInterface) {
return $interactor->interact($input, $output, $annotationData);
}
Expand Down
12 changes: 10 additions & 2 deletions src/Hooks/Dispatchers/ProcessResultHookDispatcher.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,8 @@
use Consolidation\AnnotatedCommand\CommandData;
use Consolidation\AnnotatedCommand\Hooks\HookManager;
use Consolidation\AnnotatedCommand\Hooks\ProcessResultInterface;
use Consolidation\AnnotatedCommand\InjectionHelper;
use Consolidation\AnnotatedCommand\State\State;
use Consolidation\AnnotatedCommand\State\StateHelper;

/**
* Call hooks
Expand Down Expand Up @@ -39,7 +40,14 @@ public function process($result, CommandData $commandData)

protected function callProcessor($processor, $result, CommandData $commandData)
{
InjectionHelper::injectIntoCallbackObject($processor, $commandData->input(), $commandData->output());
$state = StateHelper::injectIntoCallbackObject($processor, $commandData->input(), $commandData->output());
$result = $this->doProcessor($processor, $result, $commandData);
$state->restore();
return $result;
}

private function doProcessor($processor, $result, CommandData $commandData)
{
$processed = null;
if ($processor instanceof ProcessResultInterface) {
$processed = $processor->process($result, $commandData);
Expand Down
12 changes: 10 additions & 2 deletions src/Hooks/Dispatchers/ValidateHookDispatcher.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,8 @@
use Consolidation\AnnotatedCommand\CommandError;
use Consolidation\AnnotatedCommand\Hooks\HookManager;
use Consolidation\AnnotatedCommand\Hooks\ValidatorInterface;
use Consolidation\AnnotatedCommand\InjectionHelper;
use Consolidation\AnnotatedCommand\State\State;
use Consolidation\AnnotatedCommand\State\StateHelper;

/**
* Call hooks
Expand Down Expand Up @@ -37,7 +38,14 @@ public function validate(CommandData $commandData)

protected function callValidator($validator, CommandData $commandData)
{
InjectionHelper::injectIntoCallbackObject($validator, $commandData->input(), $commandData->output());
$state = StateHelper::injectIntoCallbackObject($validator, $commandData->input(), $commandData->output());
$result = $this->doValidator($validator, $commandData);
$state->restore();
return $result;
}

private function doValidator($validator, CommandData $commandData)
{
if ($validator instanceof ValidatorInterface) {
return $validator->validate($commandData);
}
Expand Down
54 changes: 0 additions & 54 deletions src/InjectionHelper.php

This file was deleted.

10 changes: 10 additions & 0 deletions src/State.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
<?php
namespace Consolidation\AnnotatedCommand;

interface State
{
/**
* Restore state to a previously cached value.
*/
public function restore();
}
12 changes: 12 additions & 0 deletions src/State/SavableState.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
<?php
namespace Consolidation\AnnotatedCommand\State;

interface SavableState
{
/**
* Return the current state of this object.
*
* @return State
*/
public function currentState();
}
10 changes: 10 additions & 0 deletions src/State/State.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
<?php
namespace Consolidation\AnnotatedCommand\State;

interface State
{
/**
* Restore state to a previously cached value.
*/
public function restore();
}
77 changes: 77 additions & 0 deletions src/State/StateHelper.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
<?php
namespace Consolidation\AnnotatedCommand\State;

use Consolidation\AnnotatedCommand\Output\OutputAwareInterface;
use Consolidation\AnnotatedCommand\State\SavableState;
use Consolidation\AnnotatedCommand\State\State;
use Symfony\Component\Console\Input\InputAwareInterface;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;

class StateHelper
{
/**
* Inject $input and $output into the command instance if it is set up to receive them.
*
* @param Callable|object $callback
* @param OutputInterface $output
* @return State
*/
public static function injectIntoCallbackObject($callback, InputInterface $input, OutputInterface $output = null)
{
return static::inject(static::recoverCallbackObject($callback), $input, $output);
}

/**
* Inject $input and $output into the command instance if it is set up to receive them.
*
* @param Callable|object $callback
* @param OutputInterface $output
* @return State
*/
public static function inject($target, InputInterface $input, OutputInterface $output = null)
{
// Do not allow injection unless the target can save its state
if (!$target || !($target instanceof SavableState)) {
return new class implements State {
public function restore()
{
}
};
}

$state = $target->currentState();

if ($target instanceof InputAwareInterface) {
$target->setInput($input);
}
if (isset($output) && $target instanceof OutputAwareInterface) {
$target->setOutput($output);
}

return $state;
}

/**
* If the command callback is a method of an object, return the object.
*
* @param Callable|object $callback
* @return object|bool
*/
protected static function recoverCallbackObject($callback)
{
if (is_object($callback)) {
return $callback;
}

if (!is_array($callback)) {
return false;
}

if (!is_object($callback[0])) {
return false;
}

return $callback[0];
}
}

0 comments on commit ddb522e

Please sign in to comment.