Issues (31)

src/Kernel/Traits/Observable.php (1 issue)

Labels
Severity
1
<?php
2
3
namespace EasyIM\Kernel\Traits;
4
5
use EasyIM\Kernel\Clauses\Clause;
6
use EasyIM\Kernel\Contracts\EventHandlerInterface;
7
use EasyIM\Kernel\Decorators\FinallyResult;
8
use EasyIM\Kernel\Decorators\TerminateResult;
9
use EasyIM\Kernel\Exceptions\InvalidArgumentException;
10
use EasyIM\Kernel\ServiceContainer;
11
12
/**
13
 * Trait Observable.
14
 */
15
trait Observable
16
{
17
    /**
18
     * @var array
19
     */
20
    protected $handlers = [];
21
22
    /**
23
     * @var array
24
     */
25
    protected $clauses = [];
26
27
    /**
28
     * @param \Closure|EventHandlerInterface|callable|string $handler
29
     * @param \Closure|EventHandlerInterface|callable|string $condition
30
     *
31
     * @return \EasyIM\Kernel\Clauses\Clause
32
     *
33
     * @throws \EasyIM\Kernel\Exceptions\InvalidArgumentException
34
     * @throws \ReflectionException
35
     */
36 15
    public function push($handler, $condition = '*')
37
    {
38 15
        list($handler, $condition) = $this->resolveHandlerAndCondition($handler, $condition);
39
40 14
        if (!isset($this->handlers[$condition])) {
41 14
            $this->handlers[$condition] = [];
42
        }
43
44 14
        array_push($this->handlers[$condition], $handler);
45
46 14
        return $this->newClause($handler);
47
    }
48
49
    /**
50
     * @param array $handlers
51
     *
52
     * @return $this
53
     */
54
    public function setHandlers(array $handlers = [])
55
    {
56
        $this->handlers = $handlers;
57
58
        return $this;
59
    }
60
61
    /**
62
     * @param \Closure|EventHandlerInterface|string $handler
63
     * @param \Closure|EventHandlerInterface|string $condition
64
     *
65
     * @return \EasyIM\Kernel\Clauses\Clause
66
     *
67
     * @throws \EasyIM\Kernel\Exceptions\InvalidArgumentException
68
     * @throws \ReflectionException
69
     */
70 1
    public function unshift($handler, $condition = '*')
71
    {
72 1
        list($handler, $condition) = $this->resolveHandlerAndCondition($handler, $condition);
73
74 1
        if (!isset($this->handlers[$condition])) {
75 1
            $this->handlers[$condition] = [];
76
        }
77
78 1
        array_unshift($this->handlers[$condition], $handler);
79
80 1
        return $this->newClause($handler);
81
    }
82
83
    /**
84
     * @param string                                $condition
85
     * @param \Closure|EventHandlerInterface|string $handler
86
     *
87
     * @return \EasyIM\Kernel\Clauses\Clause
88
     *
89
     * @throws \EasyIM\Kernel\Exceptions\InvalidArgumentException
90
     * @throws \ReflectionException
91
     */
92 1
    public function observe($condition, $handler)
93
    {
94 1
        return $this->push($handler, $condition);
95
    }
96
97
    /**
98
     * @param string                                $condition
99
     * @param \Closure|EventHandlerInterface|string $handler
100
     *
101
     * @return \EasyIM\Kernel\Clauses\Clause
102
     *
103
     * @throws \EasyIM\Kernel\Exceptions\InvalidArgumentException
104
     * @throws \ReflectionException
105
     */
106 1
    public function on($condition, $handler)
107
    {
108 1
        return $this->push($handler, $condition);
109
    }
110
111
    /**
112
     * @param string|int $event
113
     * @param mixed      ...$payload
114
     *
115
     * @return mixed|null
116
     */
117 1
    public function dispatch($event, $payload)
118
    {
119 1
        return $this->notify($event, $payload);
120
    }
121
122
    /**
123
     * @param string|int $event
124
     * @param mixed      ...$payload
125
     *
126
     * @return mixed|null
127
     */
128 8
    public function notify($event, $payload)
129
    {
130 8
        $result = null;
131
132 8
        foreach ($this->handlers as $condition => $handlers) {
133 8
            if ('*' === $condition || ($condition & $event) === $event) {
134 8
                foreach ($handlers as $handler) {
135 8
                    if ($clause = $this->clauses[$this->getHandlerHash($handler)] ?? null) {
136 8
                        if ($clause->intercepted($payload)) {
137 2
                            continue;
138
                        }
139
                    }
140
141 8
                    $response = $this->callHandler($handler, $payload);
142
143
                    switch (true) {
144 8
                        case $response instanceof TerminateResult:
145 1
                            return $response->content;
146 7
                        case true === $response:
147 1
                            continue 2;
148 7
                        case false === $response:
149 1
                            break 2;
150 7
                        case !empty($response) && !($result instanceof FinallyResult):
151 7
                            $result = $response;
152
                    }
153
                }
154
            }
155
        }
156
157 7
        return $result instanceof FinallyResult ? $result->content : $result;
158
    }
159
160
    /**
161
     * @return array
162
     */
163 6
    public function getHandlers()
164
    {
165 6
        return $this->handlers;
166
    }
167
168
    /**
169
     * @param mixed $handler
170
     *
171
     * @return \EasyIM\Kernel\Clauses\Clause
172
     */
173 14
    protected function newClause($handler): Clause
174
    {
175 14
        return $this->clauses[$this->getHandlerHash($handler)] = new Clause();
176
    }
177
178
    /**
179
     * @param mixed $handler
180
     *
181
     * @return string
182
     */
183 14
    protected function getHandlerHash($handler)
184
    {
185 14
        if (is_string($handler)) {
186
            return $handler;
187
        }
188
189 14
        if (is_array($handler)) {
190 2
            return is_string($handler[0])
191
                ? $handler[0].'::'.$handler[1]
192 2
                : get_class($handler[0]).$handler[1];
193
        }
194
195 13
        return spl_object_hash($handler);
196
    }
197
198
    /**
199
     * @param callable $handler
200
     * @param mixed    $payload
201
     *
202
     * @return mixed
203
     */
204 8
    protected function callHandler(callable $handler, $payload)
205
    {
206
        try {
207 8
            return call_user_func_array($handler, [$payload]);
208 1
        } catch (\Exception $e) {
209 1
            if (property_exists($this, 'app') && $this->app instanceof ServiceContainer) {
210 1
                $this->app['logger']->error($e->getCode().': '.$e->getMessage(), [
211 1
                    'code' => $e->getCode(),
212 1
                    'message' => $e->getMessage(),
213 1
                    'file' => $e->getFile(),
214 1
                    'line' => $e->getLine(),
215
                ]);
216
            }
217
        }
218 1
    }
219
220
    /**
221
     * @param mixed $handler
222
     *
223
     * @return \Closure
224
     *
225
     * @throws \EasyIM\Kernel\Exceptions\InvalidArgumentException
226
     * @throws \ReflectionException
227
     */
228 15
    protected function makeClosure($handler)
229
    {
230 15
        if (is_callable($handler)) {
231 3
            return $handler;
232
        }
233
234 14
        if (is_string($handler) && '*' !== $handler) {
235 2
            if (!class_exists($handler)) {
236 1
                throw new InvalidArgumentException(sprintf('Class "%s" not exists.', $handler));
237
            }
238
239 2
            if (!in_array(EventHandlerInterface::class, (new \ReflectionClass($handler))->getInterfaceNames(), true)) {
240 1
                throw new InvalidArgumentException(sprintf('Class "%s" not an instance of "%s".', $handler, EventHandlerInterface::class));
241
            }
242
243
            return function ($payload) use ($handler) {
244 1
                return (new $handler($this->app ?? null))->handle($payload);
245 1
            };
246
        }
247
248 14
        if ($handler instanceof EventHandlerInterface) {
249
            return function () use ($handler) {
250 9
                return $handler->handle(...func_get_args());
0 ignored issues
show
func_get_args() is expanded, but the parameter $payload of EasyIM\Kernel\Contracts\...dlerInterface::handle() 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

250
                return $handler->handle(/** @scrutinizer ignore-type */ ...func_get_args());
Loading history...
251 13
            };
252
        }
253
254 1
        throw new InvalidArgumentException('No valid handler is found in arguments.');
255
    }
256
257
    /**
258
     * @param mixed $handler
259
     * @param mixed $condition
260
     *
261
     * @return array
262
     *
263
     * @throws \EasyIM\Kernel\Exceptions\InvalidArgumentException
264
     * @throws \ReflectionException
265
     */
266 15
    protected function resolveHandlerAndCondition($handler, $condition): array
267
    {
268 15
        if (is_int($handler) || (is_string($handler) && !class_exists($handler))) {
269 3
            list($handler, $condition) = [$condition, $handler];
270
        }
271
272 15
        return [$this->makeClosure($handler), $condition];
273
    }
274
}
275