Passed
Pull Request — dev (#6)
by Romain
03:32
created

EventRunner::__construct()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 12
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 5
nc 1
nop 5
dl 0
loc 12
rs 9.4285
c 0
b 0
f 0
1
<?php
2
3
/*
4
 * Copyright (C) 2017
5
 * Nathan Boiron <[email protected]>
6
 * Romain Canon <[email protected]>
7
 *
8
 * This file is part of the TYPO3 NotiZ project.
9
 * It is free software; you can redistribute it and/or modify it
10
 * under the terms of the GNU General Public License, either
11
 * version 3 of the License, or any later version.
12
 *
13
 * For the full copyright and license information, see:
14
 * http://www.gnu.org/licenses/gpl-3.0.html
15
 */
16
17
namespace CuyZ\Notiz\Event\Runner;
18
19
use Closure;
20
use Exception;
21
use CuyZ\Notiz\Definition\Tree\EventGroup\Event\EventDefinition;
22
use CuyZ\Notiz\Event\Event;
23
use CuyZ\Notiz\Event\Exception\CancelEventDispatch;
24
use CuyZ\Notiz\Event\Service\EventFactory;
25
use CuyZ\Notiz\Notification\Notification;
26
use CuyZ\Notiz\Notification\NotificationDispatcher;
27
use CuyZ\Notiz\Service\ExtensionConfigurationService;
28
use Throwable;
29
use TYPO3\CMS\Extbase\SignalSlot\Dispatcher;
30
31
/**
32
 * This class is used as a bridge between the trigger of an event and a
33
 * notification dispatch.
34
 */
35
class EventRunner
36
{
37
    const SIGNAL_EVENT_WAS_DISPATCHED = 'eventWasDispatched';
38
39
    const SIGNAL_EVENT_DISPATCH_ERROR = 'eventDispatchError';
40
41
    /**
42
     * @var EventDefinition
43
     */
44
    protected $eventDefinition;
45
46
    /**
47
     * @var Dispatcher
48
     */
49
    protected $signalDispatcher;
50
51
    /**
52
     * @var ExtensionConfigurationService
53
     */
54
    protected $extensionConfigurationService;
55
56
    /**
57
     * @var NotificationDispatcher
58
     */
59
    protected $notificationDispatcher;
60
61
    /**
62
     * @var EventFactory
63
     */
64
    protected $eventFactory;
65
66
    /**
67
     * @param EventDefinition $eventDefinition
68
     * @param EventFactory $eventFactory
69
     * @param NotificationDispatcher $notificationDispatcher
70
     * @param Dispatcher $signalDispatcher
71
     * @param ExtensionConfigurationService $extensionConfigurationService
72
     */
73
    public function __construct(
74
        EventDefinition $eventDefinition,
75
        EventFactory $eventFactory,
76
        NotificationDispatcher $notificationDispatcher,
77
        Dispatcher $signalDispatcher,
78
        ExtensionConfigurationService $extensionConfigurationService
79
    ) {
80
        $this->eventDefinition = $eventDefinition;
81
        $this->signalDispatcher = $signalDispatcher;
82
        $this->extensionConfigurationService = $extensionConfigurationService;
83
        $this->notificationDispatcher = $notificationDispatcher;
84
        $this->eventFactory = $eventFactory;
85
    }
86
87
    /**
88
     * @param mixed ...$arguments
89
     */
90
    public function process(...$arguments)
91
    {
92
        $notifications = $this->notificationDispatcher->fetchNotifications($this->eventDefinition);
93
94
        foreach ($notifications as $notification => $dispatchCallback) {
95
            $event = $this->eventFactory->create($this->eventDefinition, $notification);
96
            $doDispatch = true;
97
98
            if (is_callable([$event, 'run'])) {
99
                try {
100
                    /** @noinspection PhpUndefinedMethodInspection */
101
                    $event->run(...$arguments);
102
                } catch (CancelEventDispatch $exception) {
103
                    $doDispatch = false;
104
                }
105
            }
106
107
            if ($doDispatch) {
108
                $this->dispatchEvent($dispatchCallback, $event, $notification);
109
            }
110
111
            unset($event);
112
        }
113
    }
114
115
    /**
116
     * Does the actual dispatch work. Two signals are sent:
117
     *
118
     * - When the dispatch ran well;
119
     * - If an error occurred during the dispatch.
120
     *
121
     * @see \CuyZ\Notiz\Event\Runner\EventRunner::SIGNAL_EVENT_WAS_DISPATCHED
122
     * @see \CuyZ\Notiz\Event\Runner\EventRunner::SIGNAL_EVENT_DISPATCH_ERROR
123
     *
124
     * @param Closure $callback
125
     * @param Event $event
126
     * @param Notification $notification
127
     *
128
     * @throws Throwable
129
     */
130
    protected function dispatchEvent(Closure $callback, Event $event, Notification $notification)
131
    {
132
        $exception = null;
133
134
        try {
135
            $callback($event);
136
137
            $this->signalDispatcher->dispatch(
138
                __CLASS__,
139
                self::SIGNAL_EVENT_WAS_DISPATCHED,
140
                [$event, $notification]
141
            );
142
        } catch (Throwable $exception) {
0 ignored issues
show
Coding Style Comprehensibility introduced by
Consider adding a comment why this CATCH block is empty.
Loading history...
143
        } catch (Exception $exception) {
144
            // @PHP7
145
        }
146
147
        if ($exception) {
148
            $this->signalDispatcher->dispatch(
149
                __CLASS__,
150
                self::SIGNAL_EVENT_DISPATCH_ERROR,
151
                [$exception, $event, $notification]
152
            );
153
154
            $gracefulMode = $this->extensionConfigurationService->getConfigurationValue('dispatch.graceful_mode');
155
156
            if (!$gracefulMode) {
157
                throw $exception;
158
            }
159
        }
160
    }
161
162
    /**
163
     * @return callable
164
     */
165
    public function getCallable()
166
    {
167
        return [$this, 'process'];
0 ignored issues
show
Bug Best Practice introduced by
The expression return array($this, 'process') returns the type array<integer,CuyZ\Notiz...ner\EventRunner|string> which is incompatible with the documented return type callable.
Loading history...
168
    }
169
170
    /**
171
     * @return EventDefinition
172
     */
173
    public function getEventDefinition()
174
    {
175
        return $this->eventDefinition;
176
    }
177
178
    /**
179
     * This object should never be serialized, because it contains services that
180
     * can have properties filled with closures (a closure can't be serialized).
181
     *
182
     * We need to make sure it won't happen, because there are cases where TYPO3
183
     * can serialize this object, because the TYPO3 slot dispatcher contains a
184
     * reference to it; if a closure is registered, an exception is thrown.
185
     *
186
     * @return array
187
     */
188
    public function __sleep()
189
    {
190
        return [];
191
    }
192
}
193