Completed
Branch develop (343ae8)
by Michał
02:22
created

EventDispatcher::makeLogEvent()   B

Complexity

Conditions 6
Paths 4

Size

Total Lines 31
Code Lines 19

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 25
CRAP Score 6

Importance

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