CommandDispatcher::getHandlerForCommand()   A
last analyzed

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
c 1
b 0
f 0
dl 0
loc 4
rs 10
cc 1
eloc 2
nc 1
nop 1
1
<?php
2
3
namespace PhpDDD\DomainDrivenDesign\Command;
4
5
use PhpDDD\DomainDrivenDesign\Exception\CommandDispatcherException;
6
7
/**
8
 * Implementation of the CommandDispatcherInterface.
9
 */
10
final class CommandDispatcher implements CommandDispatcherInterface
11
{
12
    /**
13
     * List of command handler subscribed per command.
14
     * This is mainly used for debug (exceptions).
15
     *
16
     * @var CommandHandlerInterface[]
17
     */
18
    private $handlers = [];
19
    /**
20
     * List of methods to call when a command is triggered.
21
     *
22
     * @var callable[]
23
     */
24
    private $handlingMethods = [];
25
26
    /**
27
     * Get the handler link to the command (if any) and run the callable associated.
28
     * This method will try to look for handlers registered to:
29
     * - the command class name
30
     * - a parent of the current command
31
     * - an interface that the current command may implement.
32
     *
33
     * @param AbstractCommand $command
34
     *
35
     * @return AbstractCommand
36
     */
37
    public function handle(AbstractCommand $command)
38
    {
39
        $handlingMethods = $this->getHandlingMethodForCommand($command);
40
        $nbMethods       = count($handlingMethods);
41
        if (0 === $nbMethods) {
42
            throw CommandDispatcherException::commandHandlerNotFound($command);
43
        } elseif (1 !== $nbMethods) {
44
            throw CommandDispatcherException::tooManyCommandHandler($command, $this->getHandlerForCommand($command));
45
        }
46
        $method = current($handlingMethods);
47
48
        call_user_func($method, $command);
49
50
        return $command;
51
    }
52
53
    /**
54
     * @param CommandHandlerInterface $handler
55
     */
56
    public function register(CommandHandlerInterface $handler)
57
    {
58
        foreach ($handler->getHandlingMethods() as $commandClassName => $callable) {
59
            if (isset($this->handlers[$commandClassName])) {
60
                throw CommandDispatcherException::commandHandlerAlreadyDefinedForCommand(
61
                    $commandClassName,
62
                    $this->handlers[$commandClassName],
63
                    $handler
64
                );
65
            }
66
67
            // if callable is a string, then it should be a method name of the command handler
68
            if (!is_callable($callable)) {
69
                $callable = [$handler, $callable];
70
            }
71
72
            $this->handlingMethods[$commandClassName] = $callable;
73
            $this->handlers[$commandClassName]        = $handler;
74
        }
75
    }
76
77
    /**
78
     * @return CommandHandlerInterface[]
79
     */
80
    public function getHandlers()
81
    {
82
        return $this->handlers;
83
    }
84
85
    /**
86
     * @param AbstractCommand $command
87
     *
88
     * @return callable[]
89
     */
90
    private function getHandlingMethodForCommand(AbstractCommand $command)
91
    {
92
        return $this->filterForCommand($this->handlingMethods, $command);
93
    }
94
95
    /**
96
     * @param AbstractCommand $command
97
     *
98
     * @return CommandHandlerInterface[]
99
     */
100
    private function getHandlerForCommand(AbstractCommand $command)
101
    {
102
        return $this->filterForCommand($this->handlers, $command);
103
    }
104
105
    /**
106
     * @param array           $property
107
     * @param AbstractCommand $command
108
     *
109
     * @return array
110
     */
111
    private function filterForCommand(array $property, AbstractCommand $command)
112
    {
113
        $filtered = [];
114
        foreach ($property as $commandClassName => $handler) {
115
            if ($command instanceof $commandClassName) {
116
                // After that, we still need to check if there is no other handler defined for this command.
117
                // This is because we allow command handler to listen to parent class or interface.
118
                $filtered[] = $handler;
119
            }
120
        }
121
122
        return $filtered;
123
    }
124
}
125