Passed
Push — master ( f33465...c685e5 )
by Carlos
03:03
created

Observable::notify()   C

Complexity

Conditions 13
Paths 22

Size

Total Lines 30
Code Lines 18

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 18
CRAP Score 13

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 13
eloc 18
c 1
b 0
f 0
nc 22
nop 2
dl 0
loc 30
ccs 18
cts 18
cp 1
crap 13
rs 6.6166

How to fix   Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

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

261
                return $handler->handle(/** @scrutinizer ignore-type */ ...func_get_args());
Loading history...
262 17
            };
263
        }
264
265 1
        throw new InvalidArgumentException('No valid handler is found in arguments.');
266
    }
267
268
    /**
269
     * @param mixed $handler
270
     * @param mixed $condition
271
     *
272
     * @return array
273
     *
274
     * @throws \EasyWeChat\Kernel\Exceptions\InvalidArgumentException
275
     * @throws \ReflectionException
276
     */
277 20
    protected function resolveHandlerAndCondition($handler, $condition): array
278
    {
279 20
        if (is_int($handler) || (is_string($handler) && !class_exists($handler))) {
280 3
            list($handler, $condition) = [$condition, $handler];
281
        }
282
283 20
        return [$this->makeClosure($handler), $condition];
284
    }
285
}
286