DelayedEventDispatcher::dispatch()   A
last analyzed

Complexity

Conditions 2
Paths 2

Size

Total Lines 9
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 2
eloc 4
c 1
b 0
f 0
nc 2
nop 1
dl 0
loc 9
rs 10
1
<?php
2
3
/*
4
 * This file is part of the olvlvl/delayed-event-dispatcher package.
5
 *
6
 * (c) Olivier Laviale <[email protected]>
7
 *
8
 * For the full copyright and license information, please view the LICENSE
9
 * file that was distributed with this source code.
10
 */
11
12
namespace olvlvl\DelayedEventDispatcher;
13
14
use Psr\EventDispatcher\EventDispatcherInterface;
15
use Throwable;
16
17
class DelayedEventDispatcher implements EventDispatcherInterface
18
{
19
    /**
20
     * @var EventDispatcherInterface
21
     */
22
    private $eventDispatcher;
23
24
    /**
25
     * @var bool
26
     */
27
    private $enabled;
28
29
    /**
30
     * @var callable
31
     */
32
    private $delayArbiter;
33
34
    /**
35
     * @var callable
36
     */
37
    private $exceptionHandler;
38
39
    /**
40
     * @var callable
41
     */
42
    private $flusher;
43
44
    /**
45
     * @var object[]
46
     */
47
    private $queue = [];
48
49
    /**
50
     * @param EventDispatcherInterface $eventDispatcher
51
     * @param bool $disabled
52
     * @param callable|null $delayArbiter The delay arbiter determines whether an event should be delayed or not. It's
53
     *     a callable with the following signature: `function($event, string $eventName = null): bool`. The
54
     *     default delay arbiter just returns `true`, all events are delayed. Note: The delay arbiter is only invoked
55
     *     if delaying events is enabled.
56
     * @param callable|null $exceptionHandler This callable handles exceptions thrown during event dispatching. It's a
57
     *     callable with the following signature:
58
     *     `function(\Throwable $exception, $event, string $eventName = null): void`. The default exception
59
     *     handler just throws the exception.
60
     * @param callable|null $flusher By default, delayed events are dispatched with the decorated event dispatcher
61
     *     when flushed, but you can choose another solution entirely, like sending them to consumers using RabbitMQ or
62
     *     Kafka. The callable has the following signature: `function($event, string $eventName = null): void`.
63
     */
64
    public function __construct(
65
        EventDispatcherInterface $eventDispatcher,
66
        bool $disabled = false,
67
        callable $delayArbiter = null,
68
        callable $exceptionHandler = null,
69
        callable $flusher = null
70
    ) {
71
        $this->eventDispatcher = $eventDispatcher;
72
        $this->enabled = !$disabled;
73
        $this->delayArbiter = $delayArbiter ?: function () {
74
            return true;
75
        };
76
        $this->exceptionHandler = $exceptionHandler ?: function (Throwable $exception) {
77
            throw $exception;
78
        };
79
        $this->flusher = $flusher ?: function (object $event): object {
80
            return $this->eventDispatcher->dispatch($event);
81
        };
82
    }
83
84
    /**
85
     * @inheritdoc
86
     */
87
    public function dispatch(object $event): object
88
    {
89
        if ($this->shouldDelay($event)) {
90
            $this->queue[] = $event;
91
92
            return $event;
93
        }
94
95
        return $this->eventDispatcher->dispatch($event);
96
    }
97
98
    /**
99
     * Dispatch all the events in the queue.
100
     *
101
     * Note: Exceptions raised during dispatching are caught and forwarded to the exception handler defined during
102
     * construct.
103
     */
104
    public function flush()
105
    {
106
        while (($event = array_shift($this->queue))) {
107
            try {
108
                ($this->flusher)($event);
109
            } catch (Throwable $e) {
110
                ($this->exceptionHandler)($e, $event);
111
            }
112
        }
113
    }
114
115
    private function shouldDelay(object $event): bool
116
    {
117
        return $this->enabled && ($this->delayArbiter)($event);
118
    }
119
}
120