EventDispatcher::clearErrors()   A
last analyzed

Complexity

Conditions 1
Paths 1

Size

Total Lines 6
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 4
CRAP Score 1

Importance

Changes 0
Metric Value
cc 1
eloc 3
nc 1
nop 0
dl 0
loc 6
ccs 4
cts 4
cp 1
crap 1
rs 10
c 0
b 0
f 0
1
<?php
2
3
/**
4
 * Event Dispatcher Class
5
 *
6
 * @package     BlueEvent
7
 * @author      Michał Adamiak    <[email protected]>
8
 * @copyright   chajr/bluetree
9
 */
10
11
declare(strict_types=1);
12
13
namespace BlueEvent\Event\Base;
14
15
use BlueEvent\Event\Base\Interfaces\EventDispatcherInterface;
16
use BlueEvent\Event\Base\Interfaces\EventInterface;
17
use BlueEvent\Event\BaseEvent;
18
19
class EventDispatcher implements EventDispatcherInterface
20
{
21
    public const EVENT_STATUS_OK       = 'ok';
22
    public const EVENT_STATUS_ERROR    = 'error';
23
    public const EVENT_STATUS_BREAK    = 'propagation_stop';
24
25
    /**
26
     * @var bool
27
     */
28
    protected $hasErrors = false;
29
30
    /**
31
     * store all errors
32
     *
33
     * @var
34
     */
35
    protected $errorList = [];
36
37
    /**
38
     * store logger instance
39
     *
40
     * @var EventLog
41
     */
42
    protected $loggerInstance;
43
44
    /**
45
     * store default options for event dispatcher
46
     *
47
     * @var array
48
     */
49
    protected $options = [
50
        'type' => 'array',
51
        'log_events' => false,
52
        'log_all_events' => true,
53
        'from_file' => false,
54
        'log_object' => false,
55
        'log_config' => [
56
            'log_path' => './log',
57
            'level' => 'debug',
58
            'storage' => \SimpleLog\Storage\File::class,
59
        ],
60
        'events' => [],
61
    ];
62
63
    /**
64
     * create manage instance
65
     *
66
     * @param array $options
67
     * @throws \InvalidArgumentException|\ReflectionException
68
     */
69 21
    public function __construct(array $options = [])
70
    {
71 21
        $this->options = array_replace_recursive($this->options, $options);
72
73 21
        if ($this->options['from_file']) {
74 1
            $this->readEventConfiguration(
75 1
                $this->options['from_file'],
76 1
                $this->options['type']
77
            );
78
        }
79
80 21
        $this->loggerInstance = new EventLog($this->options);
81
    }
82
83
    /**
84
     * return event object or create it if not exist
85
     *
86
     * @param string $eventName
87
     * @param array $data
88
     * @return EventInterface
89
     * @throws \LogicException
90
     * @throws \InvalidArgumentException
91
     */
92 15
    protected function createEventObject(string $eventName, array $data): EventInterface
93
    {
94 15
        if (!\array_key_exists($eventName, $this->options['events'])) {
95 1
            throw new \InvalidArgumentException('Event is not defined.');
96
        }
97
98 14
        $namespace = $this->options['events'][$eventName]['object'];
99 14
        $instance = new $namespace($eventName, $data);
100
101 14
        if (!($instance instanceof EventInterface)) {
102 1
            throw new \LogicException('Invalid interface of event object');
103
        }
104
105 13
        return $instance;
106
    }
107
108
    /**
109
     * add event configuration into event dispatcher
110
     *
111
     * @param array $events
112
     * @return $this
113
     */
114 13
    public function setEventConfiguration(array $events): self
115
    {
116 13
        $this->options['events'] = array_merge_recursive(
117 13
            $this->options['events'],
118
            $events
119
        );
120
121 13
        return $this;
122
    }
123
124
    /**
125
     * trigger new event with automatic call all subscribed listeners
126
     *
127
     * @param string $name
128
     * @param array $data
129
     * @return $this
130
     * @throws \LogicException
131
     */
132 15
    public function triggerEvent(string $name, array $data = []): self
133
    {
134
        try {
135 15
            $event = $this->createEventObject($name, $data);
136 2
        } catch (\InvalidArgumentException $exception) {
137 1
            return $this;
138
        }
139
140 13
        foreach ($this->options['events'][$name]['listeners'] as $eventListener) {
141 13
            if ($event->isPropagationStopped()) {
142 1
                $this->loggerInstance->makeLogEvent($name, $eventListener, self::EVENT_STATUS_BREAK);
143 1
                break;
144
            }
145
146 13
            $this->executeListener($eventListener, $event, $name);
147
        }
148
149 13
        return $this;
150
    }
151
152
    /**
153
     * @param callable|string $eventListener
154
     * @param EventInterface $event
155
     * @param string $name
156
     * @return $this
157
     */
158 13
    protected function executeListener($eventListener, EventInterface $event, string $name): self
159
    {
160
        try {
161 13
            $this->callFunction($eventListener, $event);
162 12
            $status = self::EVENT_STATUS_OK;
163 2
        } catch (\Exception $e) {
164 2
            $this->addError($e);
165 2
            $status = self::EVENT_STATUS_ERROR;
166
        }
167
168 13
        $this->loggerInstance->makeLogEvent($name, $eventListener, $status);
169
170 13
        return $this;
171
    }
172
173
    /**
174
     * dynamically add new listener or listeners for given event name
175
     * listeners are added at end of the list
176
     *
177
     * @param string $eventName
178
     * @param array $listeners
179
     * @return $this
180
     */
181 2
    public function addEventListener(string $eventName, array $listeners): self
182
    {
183 2
        if (!\array_key_exists($eventName, $this->options['events'])) {
184 1
            $this->options['events'][$eventName] = [
185
                'object' => BaseEvent::class,
186
                'listeners' => $listeners,
187
            ];
188
        }
189
190 2
        $this->options['events'][$eventName]['listeners'] = array_merge(
191 2
            $this->options['events'][$eventName]['listeners'],
192
            $listeners
193
        );
194
195 2
        return $this;
196
    }
197
198
    /**
199
     * allow to call event listeners functions
200
     *
201
     * @param callable|string $listener
202
     * @param EventInterface $event
203
     */
204 13
    protected function callFunction($listener, EventInterface $event): void
205
    {
206 13
        if (is_callable($listener)) {
207 12
            $listener($event);
208
        }
209
    }
210
211
    /**
212
     * read configuration from file
213
     *
214
     * @param string $path
215
     * @param string $type
216
     * @return $this
217
     * @throws \InvalidArgumentException
218
     */
219 3
    public function readEventConfiguration(string $path, string $type): self
220
    {
221 3
        if (!\file_exists($path)) {
222 1
            throw new \InvalidArgumentException('File ' . $path . 'don\'t exists.');
223
        }
224
225 2
        $name = '\BlueEvent\Event\Config\\' . ucfirst($type) . 'Config';
226
227 2
        if (!class_exists($name)) {
228 1
            throw new \InvalidArgumentException('Incorrect configuration type: ' . $type);
229
        }
230
231
        /** @var \BlueEvent\Event\Config\ConfigReader $reader */
232 1
        $reader = new $name();
233
234 1
        return $this->setEventConfiguration($reader->readConfig($path));
235
    }
236
237
    /**
238
     * disable or enable event logging (true to enable)
239
     *
240
     * @param bool $logEvents
241
     * @return $this
242
     */
243 4
    public function setEventLog($logEvents): self
244
    {
245 4
        $this->options['log_events'] = (bool)$logEvents;
246 4
        return $this;
247
    }
248
249
    /**
250
     * log given events by given name
251
     *
252
     * @param array $events
253
     * @return $this
254
     */
255 1
    public function logEvent(array $events = []): self
256
    {
257 1
        foreach ($events as $event) {
258 1
            if (!in_array($event, $this->loggerInstance->logEvents, true)) {
259 1
                $this->loggerInstance->logEvents[] = $event;
260
            }
261
        }
262
263 1
        return $this;
264
    }
265
266
    /**
267
     * enable or disable log all events
268
     *
269
     * @param bool $log
270
     * @return $this
271
     */
272 1
    public function logAllEvents(bool $log = true): self
273
    {
274 1
        $this->options['log_all_events'] = $log;
275 1
        return $this;
276
    }
277
278
    /**
279
     * get complete object configuration or value of single option
280
     *
281
     * @param $option string|null
282
     * @return mixed
283
     */
284 3
    public function getConfiguration(?string $option = null)
285
    {
286 3
        if ($option !== null) {
287 2
            return $this->options[$option];
288
        }
289
290 1
        return $this->options;
291
    }
292
293
    /**
294
     * return list of all events to log
295
     *
296
     * @return array
297
     */
298 1
    public function getAllEventsToLog(): array
299
    {
300 1
        return $this->loggerInstance->logEvents;
301
    }
302
303
    /**
304
     * return current event configuration
305
     *
306
     * @return array
307
     */
308 2
    public function getEventConfiguration(): array
309
    {
310 2
        return $this->options['events'];
311
    }
312
313
    /**
314
     * return all event dispatcher errors
315
     *
316
     * @return array
317
     */
318 1
    public function getErrors(): array
319
    {
320 1
        return $this->errorList;
321
    }
322
323
    /**
324
     * return information that event dispatcher has some errors
325
     *
326
     * @return bool
327
     */
328 2
    public function hasErrors(): bool
329
    {
330 2
        return $this->hasErrors;
331
    }
332
333
    /**
334
     * clear all event dispatcher errors
335
     *
336
     * @return $this
337
     */
338 1
    public function clearErrors(): self
339
    {
340 1
        $this->errorList = [];
341 1
        $this->hasErrors = false;
342
343 1
        return $this;
344
    }
345
346
    /**
347
     * add new error to list
348
     *
349
     * @param \Exception $exception
350
     * @return $this
351
     */
352 2
    protected function addError(\Exception $exception): self
353
    {
354 2
        $this->errorList[$exception->getCode()] = [
355 2
            'message'   => $exception->getMessage(),
356 2
            'line'      => $exception->getLine(),
357 2
            'file'      => $exception->getFile(),
358 2
            'trace'     => $exception->getTraceAsString(),
359
        ];
360 2
        $this->hasErrors = true;
361
362 2
        return $this;
363
    }
364
}
365