Completed
Pull Request — master (#19)
by Tomas
03:41
created

Dispatcher::__construct()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 7
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 6
CRAP Score 1

Importance

Changes 4
Bugs 2 Features 0
Metric Value
c 4
b 2
f 0
dl 0
loc 7
ccs 6
cts 6
cp 1
rs 9.4285
cc 1
eloc 5
nc 1
nop 3
crap 1
1
<?php
2
3
namespace Tomaj\Hermes;
4
5
use Exception;
6
use Nette\Application\AbortException;
7
use Psr\Log\LoggerInterface;
8
use Psr\Log\LogLevel;
9
use Tomaj\Hermes\Handler\HandlerInterface;
10
use Tomaj\Hermes\Driver\DriverInterface;
11
use Tomaj\Hermes\Restart\RestartException;
12
use Tomaj\Hermes\Restart\RestartInterface;
13
use Tracy\Debugger;
14
use DateTime;
15
16
class Dispatcher implements DispatcherInterface
17
{
18
    /**
19
     * Dispatcher driver
20
     *
21
     * @var DriverInterface
22
     */
23
    private $driver;
24
25
    /**
26
     * Logger
27
     *
28
     * @var LoggerInterface
29
     */
30
    private $logger;
31
32
    /**
33
     * Restart
34
     *
35
     * @var RestartInterface
36
     */
37
    private $restart;
38
39
    /**
40
     * All registered handlers
41
     *
42
     * @var HandlerInterface[][]
43
     */
44
    private $handlers = [];
45
46
    /**
47
     * @var DateTime
48
     */
49
    private $startTime;
50
51
    /**
52
     * Create new Dispatcher
53
     *
54
     * @param DriverInterface $driver
55
     * @param LoggerInterface $logger
56
     * @param RestartInterface $restart
57
     */
58 24
    public function __construct(DriverInterface $driver, LoggerInterface $logger = null, RestartInterface $restart = null)
59
    {
60 24
        $this->driver = $driver;
61 24
        $this->logger = $logger;
62 24
        $this->restart = $restart;
63 24
        $this->startTime = new DateTime();
64 24
    }
65
66
    /**
67
     * {@inheritdoc}
68
     */
69 6
    public function emit(MessageInterface $message)
70
    {
71 6
        $this->driver->send($message);
72
73 6
        $this->log(
74 6
            LogLevel::INFO,
75 6
            "Dispatcher send message #{$message->getId()} to driver " . get_class($this->driver),
76 6
            $this->messageLoggerContext($message)
77 6
        );
78 6
        return $this;
79
    }
80
81
    /**
82
     * Basic method for background job to star listening.
83
     *
84
     * This method hook to driver wait() method and start listening events.
85
     * Method is blocking, so when you call it all processing will stop.
86
     * WARNING! Don't use it on web server calls. Run it only with cli.
87
     *
88
     * @return void
89
     */
90
    public function handle()
91
    {
92
        try {
93 18
            $this->driver->wait(function (MessageInterface $message) {
94 18
                $this->log(
95 18
                    LogLevel::INFO,
96 18
                    "Start handle message #{$message->getId()} ({$message->getType()})",
97 18
                    $this->messageLoggerContext($message)
98 18
                );
99
100 18
                $result = $this->dispatch($message);
101
102 18
                if ($this->restart && $this->restart->shouldRestart($this->startTime)) {
103 3
                    throw new RestartException('Restart');
104
                }
105
106 15
                return $result;
107 18
            });
108 18
        } catch (RestartException $e) {
109 3
            $this->log(LogLevel::NOTICE, 'Existing hermes dispatcher - restart');
110
        }
111 18
    }
112
113
    /**
114
     * Dispatch message
115
     *
116
     * @param MessageInterface $message
117
     *
118
     * @return bool
119
     */
120 18
    private function dispatch(MessageInterface $message)
121
    {
122 18
        $type = $message->getType();
123
124 18
        if (!$this->hasHandlers($type)) {
125 6
            return true;
126
        }
127
128 15
        $result = true;
129
130 15
        foreach ($this->handlers[$type] as $handler) {
131 15
            $handlerResult = $this->handleMessage($handler, $message);
132
133 15
            if ($result && !$handlerResult) {
134 6
                $result = false;
135 6
            }
136 15
        }
137
138 15
        return $result;
139
    }
140
141
    /**
142
     * Handle given message with given handler
143
     *
144
     * @param HandlerInterface $handler
145
     * @param MessageInterface $message
146
     *
147
     * @return bool
148
     */
149 15
    private function handleMessage(HandlerInterface $handler, MessageInterface $message)
150
    {
151
        // check if handler implements Psr\Log\LoggerAwareInterface (you can use \Psr\Log\LoggerAwareTrait)
152 15
        if ($this->logger && method_exists($handler, 'setLogger')) {
153
            $handler->setLogger($this->logger);
0 ignored issues
show
Bug introduced by
The method setLogger() does not seem to exist on object<Tomaj\Hermes\Handler\HandlerInterface>.

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
154
        }
155
156
        try {
157 15
            $result = $handler->handle($message);
158
159 12
            $this->log(
160 12
                LogLevel::INFO,
161 12
                "End handle message #{$message->getId()} ({$message->getType()})",
162 12
                $this->messageLoggerContext($message)
163 12
            );
164 15
        } catch (Exception $e) {
165 3
            $this->log(
166 3
                LogLevel::ERROR,
167 3
                "Handler " . get_class($handler) . " throws exception - {$e->getMessage()}",
168 3
                ['error' => $e, 'message' => $this->messageLoggerContext($message), 'exception' => $e]
169 3
            );
170 3
            Debugger::log($e, Debugger::EXCEPTION);
171 3
            $result = false;
172
        }
173 15
        return $result;
174
    }
175
176
    /**
177
     * Check if actual dispatcher has handler for given type
178
     *
179
     * @param string $type
180
     *
181
     * @return bool
182
     */
183 18
    private function hasHandlers($type)
184
    {
185 18
        return isset($this->handlers[$type]) && count($this->handlers[$type]) > 0;
186
    }
187
188
    /**
189
     * {@inheritdoc}
190
     */
191 18
    public function registerHandler($type, HandlerInterface $handler)
192
    {
193 18
        if (!isset($this->handlers[$type])) {
194 18
            $this->handlers[$type] = [];
195 18
        }
196
197 18
        $this->handlers[$type][] = $handler;
198 18
    }
199
200
    /**
201
     * Serialize message to logger context
202
     *
203
     * @param MessageInterface $message
204
     *
205
     * @return array
206
     */
207 24
    private function messageLoggerContext(MessageInterface $message)
208
    {
209
        return [
210 24
            'id' => $message->getId(),
211 24
            'created' => $message->getCreated(),
212 24
            'type' => $message->getType(),
213 24
            'payload' => $message->getPayload(),
214 24
        ];
215
    }
216
217
    /**
218
     * Interal log method wrapper
219
     *
220
     * @param string $level
221
     * @param string $message
222
     * @param array $context
223
     *
224
     * @return void
225
     */
226 24
    private function log($level, $message, array $context = array())
227
    {
228 24
        if ($this->logger) {
229 6
            $this->logger->log($level, $message, $context);
230 6
        }
231 24
    }
232
}
233