BusUndoable::redo()   A
last analyzed

Complexity

Conditions 1
Paths 1

Size

Total Lines 8
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 1
eloc 4
c 1
b 0
f 0
nc 1
nop 0
dl 0
loc 8
rs 10
1
<?php
2
3
namespace Joselfonseca\LaravelTactician;
4
5
use Joselfonseca\LaravelTactician\Handler\MethodNameInflectorUndoable;
6
use Joselfonseca\LaravelTactician\Handler\CommandNameExtractorUndoable;
7
use Joselfonseca\LaravelTactician\Locator\LocatorInterface;
8
use Joselfonseca\LaravelTactician\Middleware\LockingMiddlewareUndoable;
9
use Joselfonseca\LaravelTactician\Handler\CommandHandlerMiddlewareUndoable;
10
use Joselfonseca\LaravelTactician\Exception\CommandHistoryIsEmptyException;
11
use Joselfonseca\LaravelTactician\Exception\CommandHistoryWasEmptiedException;
12
use Joselfonseca\LaravelTactician\Exception\RedoCommandHistoryIsEmptyException;
13
14
/**
15
 * The default Command bus Using Tactician, this is an implementation to dispatch commands to their handlers
16
 * trough a middleware stack, every class is resolved from the laravel's service container.
17
 *
18
 * @package Joselfonseca\LaravelTactician
19
 */
20
class BusUndoable extends Bus implements CommandBusUndoableInterface
21
{
22
    private $commandHistoryCounter = -1; // history is empty
23
    private $reDoCommandHistoryCounter = -1; // redo history is empty
24
    private $commandHistory = [];
25
    private $reDoCommandHistory = [];
26
27
    public function __construct(
28
        MethodNameInflectorUndoable $MethodNameInflector,
29
        CommandNameExtractorUndoable $CommandNameExtractor,
30
        LocatorInterface $HandlerLocator
31
    ) {
32
        parent::__construct($MethodNameInflector, $CommandNameExtractor, $HandlerLocator);
33
34
        // $this->commandHistoryCounter = $this->reDoCommandHistoryCounter = -1;
35
    }
36
37
    /**
38
     * Dispatch a command and recall it
39
     *
40
     * @param  object $command    Command to be dispatched
41
     * @param  array  $input      Array of input to map to the command
42
     * @param  array  $middleware Array of middleware class name to add to the stack, they are resolved from the laravel container
43
     * @return mixed
44
     */
45
    public function dispatch($command, array $input = [], array $middleware = [])
46
    {
47
        $commandDispatched = parent::dispatch($command, $input, $middleware);
48
49
        $this->pushCommandHistory( [clone $commandDispatched, $middleware] ); // Memento Pattern
50
51
        return $commandDispatched;
52
    }
53
54
    private function pushCommandHistory (array $commandDispatchedAndMiddleware)
55
    {
56
        $this->commandHistory[++$this->commandHistoryCounter] = $commandDispatchedAndMiddleware;
57
    }
58
59
    public function undo()
60
    {
61
        try{
62
            $commandDispatchedAndMiddleware = $this->popCommandHistory();
63
            $this->pushRedoCommandHistory($commandDispatchedAndMiddleware);
64
65
            $this->handleTheUndoCommand($commandDispatchedAndMiddleware[0], $commandDispatchedAndMiddleware[1]);
66
67
            return $this->topCommandHistory()[0];
68
69
        }catch (CommandHistoryWasEmptiedException $exception){}
0 ignored issues
show
Coding Style Comprehensibility introduced by
Consider adding a comment why this CATCH block is empty.
Loading history...
70
    }
71
72
    private function popCommandHistory()
73
    {
74
        if ($this->commandHistoryCounter == -1) {
75
            throw new CommandHistoryIsEmptyException();
76
        }
77
78
        $commandDispatchedAndMiddleware = $this->commandHistory[$this->commandHistoryCounter];
79
        unset($this->commandHistory[$this->commandHistoryCounter]);
80
        --$this->commandHistoryCounter;
81
82
        return $commandDispatchedAndMiddleware;
83
    }
84
85
    private function topCommandHistory()
86
    {
87
        if ($this->commandHistoryCounter == -1){
88
            throw new CommandHistoryWasEmptiedException();
89
        }
90
91
        return $this->commandHistory[$this->commandHistoryCounter];
92
    }
93
94
    private function pushRedoCommandHistory(array $commandDispatchedAndMiddleware)
95
    {
96
        $this->reDoCommandHistory[++$this->reDoCommandHistoryCounter] = $commandDispatchedAndMiddleware;
97
    }
98
99
    private function handleTheUndoCommand($commandDispatched, array $middleware): void
100
    {
101
        $this->bus = new CommandBusUndoable(
102
            array_merge(
103
                [new LockingMiddlewareUndoable()],
104
                $this->resolveMiddleware(array_reverse($middleware)), // in reversed order
105
                [new CommandHandlerMiddlewareUndoable($this->CommandNameExtractor, $this->HandlerLocator, $this->MethodNameInflector)]
106
            )
107
        );
108
        $this->bus->handlerUndo($commandDispatched);
109
    }
110
111
    public function redo()
112
    {
113
        $commandDispatchedAndMiddleware = $this->popRedoCommandHistory();
114
        $this->pushCommandHistory($commandDispatchedAndMiddleware);
115
116
        $this->handleTheRedoCommand($commandDispatchedAndMiddleware[0], $commandDispatchedAndMiddleware[1]);
117
118
        return $commandDispatchedAndMiddleware[0];
119
    }
120
121
    private function popRedoCommandHistory()
122
    {
123
        if( $this->reDoCommandHistoryCounter == -1 ) {
124
            throw new RedoCommandHistoryIsEmptyException();
125
        }
126
127
        $commandDispatchedAndMiddleware = $this->reDoCommandHistory[$this->reDoCommandHistoryCounter];
128
        unset($this->reDoCommandHistory[$this->reDoCommandHistoryCounter]);
129
        --$this->reDoCommandHistoryCounter;
130
131
        return $commandDispatchedAndMiddleware;
132
    }
133
134
    private function handleTheRedoCommand($commandDispatched, $middleware): void
135
    {
136
        $this->bus = new CommandBusUndoable(
137
            array_merge(
138
                [new LockingMiddlewareUndoable()],
139
                $this->resolveMiddleware($middleware), // in original order again !!!!
140
                [new CommandHandlerMiddlewareUndoable($this->CommandNameExtractor, $this->HandlerLocator, $this->MethodNameInflector)]
141
            )
142
        );
143
        $this->bus->handlerRedo($commandDispatched);
144
    }
145
146
    public function clearHistory()
147
    {
148
        $this->commandHistory = [];
149
        $this->reDoCommandHistory = [];
150
        $this->commandHistoryCounter = $this->reDoCommandHistoryCounter = -1;
151
    }
152
153
    public function dump()
154
    {return;
155
        echo PHP_EOL.'commandHistoryCounter: '.$this->commandHistoryCounter;
0 ignored issues
show
Unused Code introduced by
echo Joselfonseca\Larave...->commandHistoryCounter is not reachable.

This check looks for unreachable code. It uses sophisticated control flow analysis techniques to find statements which will never be executed.

Unreachable code is most often the result of return, die or exit statements that have been added for debug purposes.

function fx() {
    try {
        doSomething();
        return true;
    }
    catch (\Exception $e) {
        return false;
    }

    return false;
}

In the above example, the last return false will never be executed, because a return statement has already been met in every possible execution path.

Loading history...
156
        echo PHP_EOL.'reDoCommandHistoryCounter: '.$this->reDoCommandHistoryCounter.PHP_EOL;
157
    }
158
}
159