SequentialCommandBus::handleException()   A
last analyzed

Complexity

Conditions 2
Paths 2

Size

Total Lines 6
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Importance

Changes 2
Bugs 1 Features 0
Metric Value
c 2
b 1
f 0
dl 0
loc 6
rs 9.4285
cc 2
eloc 3
nc 2
nop 2
1
<?php
2
namespace PhpDDD\Command\Bus;
3
4
use Exception;
5
use PhpDDD\Command\CommandInterface;
6
use PhpDDD\Command\Handler\Locator\CommandHandlerLocatorInterface;
7
use PhpDDD\Domain\AbstractAggregateRoot;
8
9
/**
10
 * The sequential command bus works like a stack.
11
 * It wait until a command is fully processed to invoke the next one.
12
 * Each command will be happened to the tail of the stack before being processed.
13
 */
14
class SequentialCommandBus implements CommandBusInterface
15
{
16
17
    /**
18
     * @var CommandHandlerLocatorInterface
19
     */
20
    private $locator;
21
22
    /**
23
     * @var CommandInterface[]
24
     */
25
    private $commandStack = array();
26
27
    /**
28
     * @var bool
29
     */
30
    private $executing = false;
31
32
    /**
33
     * @param CommandHandlerLocatorInterface $locator
34
     */
35
    public function __construct(CommandHandlerLocatorInterface $locator)
36
    {
37
        $this->locator = $locator;
38
    }
39
40
    /**
41
     * Sequentially execute commands
42
     *
43
     * If an exception occurs in any command it will be put on a stack
44
     * of exceptions that is thrown only when all the commands are processed.
45
     *
46
     * [@inheritdoc}
47
     */
48
    public function dispatch(CommandInterface $command)
49
    {
50
        $this->commandStack[] = $command;
51
52
        if ($this->executing) {
53
            return array();
54
        }
55
56
        $first          = true;
57
        $aggregateRoots = array();
58
59
        while ($command = array_shift($this->commandStack)) {
60
            $aggregateRoots[] = $this->invokeHandler($command, $first);
61
            $first            = false;
62
        }
63
64
        return $aggregateRoots;
65
    }
66
67
    /**
68
     * {@inheritdoc}
69
     */
70
    public function getRegisteredCommandHandlers()
71
    {
72
        return $this->locator->getRegisteredCommandHandlers();
73
    }
74
75
    /**
76
     * @param CommandInterface $command
77
     * @param bool             $first
78
     *
79
     * @throws Exception
80
     * @return AbstractAggregateRoot|null
81
     */
82
    protected function invokeHandler(CommandInterface $command, $first)
83
    {
84
        $aggregateRoot = null;
85
        try {
86
            $this->executing = true;
87
88
            $commandHandler = $this->locator->getCommandHandlerForCommand($command);
89
90
            $aggregateRoot = $commandHandler->handle($command);
91
        } catch (Exception $exception) {
92
            $this->executing = false;
93
            $this->handleException($exception, $first);
94
        }
95
96
        $this->executing = false;
97
98
        return $aggregateRoot;
99
    }
100
101
    /**
102
     * Only throw the exception if this is the first dispatch of the sequential dispatching.
103
     * If we have a sub-command that throw an exception, it should not prevent other sub-command to be executed.
104
     * We may need to rollback the whole process.
105
     *
106
     * @param Exception $exception
107
     * @param bool      $first
108
     *
109
     * @throws \Exception
110
     */
111
    protected function handleException(Exception $exception, $first)
112
    {
113
        if ($first) {
114
            throw $exception;
115
        }
116
    }
117
}
118