Issues (3)

src/Flows/FlowEventAbstract.php (1 issue)

Labels
Severity
1
<?php
2
3
/*
4
 * This file is part of NodalFlow.
5
 *     (c) Fabrice de Stefanis / https://github.com/fab2s/NodalFlow
6
 * This source file is licensed under the MIT license which you will
7
 * find in the LICENSE file or at https://opensource.org/licenses/MIT
8
 */
9
10
namespace fab2s\NodalFlow\Flows;
11
12
use fab2s\NodalFlow\Callbacks\CallbackInterface;
13
use fab2s\NodalFlow\Events\CallbackWrapper;
14
use fab2s\NodalFlow\Events\FlowEvent;
15
use fab2s\NodalFlow\Nodes\NodeInterface;
16
use ReflectionException;
17
use ReflectionMethod;
18
use Symfony\Component\EventDispatcher\EventDispatcher;
19
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
20
21
/**
22
 * Abstract Class FlowEventAbstract
23
 */
24
abstract class FlowEventAbstract extends FlowAncestryAbstract
25
{
26
    /**
27
     * Progress modulo to apply
28
     * Set to x if you want to trigger
29
     * progress every x iterations in flow
30
     *
31
     * @var int
32
     */
33
    protected $progressMod = 1024;
34
35
    /**
36
     * @var EventDispatcherInterface
37
     */
38
    protected $dispatcher;
39
40
    /**
41
     * @var array
42
     */
43
    protected $activeEvents;
44
45
    /**
46
     * @var array
47
     */
48
    protected $dispatchArgs = [];
49
50
    /**
51
     * @var int
52
     */
53
    protected $eventInstanceKey = 0;
54
55
    /**
56
     * @var int
57
     */
58
    protected $eventNameKey = 1;
59
60
    /**
61
     * Get current $progressMod
62
     *
63
     * @return int
64
     */
65
    public function getProgressMod(): int
66
    {
67
        return $this->progressMod;
68
    }
69
70
    /**
71
     * Define the progress modulo, Progress Callback will be
72
     * triggered upon each iteration in the flow modulo $progressMod
73
     *
74
     * @param int $progressMod
75
     *
76
     * @return $this
77
     */
78
    public function setProgressMod(int $progressMod): self
79
    {
80
        $this->progressMod = max(1, $progressMod);
81
82
        return $this;
83
    }
84
85
    /**
86
     * @throws ReflectionException
87
     *
88
     * @return EventDispatcherInterface
89
     */
90
    public function getDispatcher(): EventDispatcherInterface
91
    {
92
        if ($this->dispatcher === null) {
93
            $this->dispatcher = new EventDispatcher;
94
            $this->initDispatchArgs(EventDispatcher::class);
95
        }
96
97
        return $this->dispatcher;
98
    }
99
100
    /**
101
     * @param EventDispatcherInterface $dispatcher
102
     *
103
     * @throws ReflectionException
104
     *
105
     * @return $this
106
     */
107
    public function setDispatcher(EventDispatcherInterface $dispatcher): self
108
    {
109
        $this->dispatcher = $dispatcher;
110
111
        return $this->initDispatchArgs(\get_class($dispatcher));
112
    }
113
114
    /**
115
     * Register callback class
116
     *
117
     * @param CallbackInterface $callBack
118
     *
119
     * @throws ReflectionException
120
     *
121
     * @return $this
122
     *
123
     * @deprecated Use Flow events & dispatcher instead
124
     */
125
    public function setCallBack(CallbackInterface $callBack): self
126
    {
127
        $this->getDispatcher()->addSubscriber(new CallbackWrapper($callBack));
128
129
        return $this;
130
    }
131
132
    /**
133
     * @param string             $eventName
134
     * @param NodeInterface|null $node
135
     *
136
     * @return $this
137
     */
138
    protected function triggerEvent(string $eventName, NodeInterface $node = null): self
139
    {
140
        if (isset($this->activeEvents[$eventName])) {
141
            $this->dispatchArgs[$this->eventNameKey] = $eventName;
142
            $this->dispatchArgs[$this->eventInstanceKey]->setNode($node);
143
            $this->dispatcher->dispatch(/* @scrutinizer ignore-type */ ...$this->dispatchArgs);
0 ignored issues
show
$this->dispatchArgs is expanded, but the parameter $event of Symfony\Contracts\EventD...erInterface::dispatch() does not expect variable arguments. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

143
            $this->dispatcher->dispatch(/* @scrutinizer ignore-type */ /** @scrutinizer ignore-type */ ...$this->dispatchArgs);
Loading history...
144
        }
145
146
        return $this;
147
    }
148
149
    /**
150
     * @param bool $reload
151
     *
152
     * @return $this
153
     */
154
    protected function listActiveEvent(bool $reload = false): self
155
    {
156
        if (!isset($this->dispatcher) || (isset($this->activeEvents) && !$reload)) {
157
            return $this;
158
        }
159
160
        $this->activeEvents = [];
161
        $eventList          = FlowEvent::getEventList();
162
        $sortedListeners    = $this->dispatcher->getListeners();
163
        foreach ($sortedListeners as $eventName => $listeners) {
164
            if (isset($eventList[$eventName]) && !empty($listeners)) {
165
                $this->activeEvents[$eventName] = 1;
166
            }
167
        }
168
169
        return $this;
170
    }
171
172
    /**
173
     * I am really wondering wtf happening in their mind when
174
     * they decided to flip argument order on such a low level
175
     * foundation.
176
     *
177
     * This is just one of those cases where usability should win
178
     * over academic principles. Setting name upon event instance is
179
     * just not more convenient than setting it at call time, it's
180
     * just a useless mutation in most IRL cases where the event is
181
     * the same throughout many event slots (you know, practice).
182
     * It's not even so obvious that coupling event with their
183
     * usage is such a good idea, academically speaking.
184
     *
185
     * Now if you add that this results in:
186
     *  - duplicated code in symfony itself
187
     *  - hackish tricks to maintain BC
188
     *  - loss of argument type hinting
189
     *  - making it harder to support multiple version
190
     *    while this is supposed to achieve better compatibility
191
     *    AND no actual feature was added for so long ...
192
     *
193
     * This is pretty close to achieving the opposite of the
194
     * original purpose IMHO
195
     *
196
     * PS:
197
     * Okay, this is also a tribute to Linus memorable rants, but ...
198
     *
199
     * @param string $class
200
     *
201
     * @throws ReflectionException
202
     *
203
     * @return FlowEventAbstract
204
     */
205
    protected function initDispatchArgs(string $class): self
206
    {
207
        $reflection         = new ReflectionMethod($class, 'dispatch');
208
        $firstParam         = $reflection->getParameters()[0];
209
        $this->dispatchArgs = [
210
            new FlowEvent($this),
211
            null,
212
        ];
213
214
        if ($firstParam->getName() !== 'event') {
215
            $this->eventInstanceKey = 1;
216
            $this->eventNameKey     = 0;
217
            $this->dispatchArgs     = array_reverse($this->dispatchArgs);
218
        }
219
220
        return $this;
221
    }
222
}
223