Completed
Pull Request — master (#1319)
by mingyoung
02:42
created

Observable::notify()   C

Complexity

Conditions 13
Paths 22

Size

Total Lines 29
Code Lines 18

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 182

Importance

Changes 0
Metric Value
cc 13
eloc 18
nc 22
nop 2
dl 0
loc 29
ccs 0
cts 18
cp 0
crap 182
rs 6.6166
c 0
b 0
f 0

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|string $handler
40
     * @param \Closure|EventHandlerInterface|string $condition
41
     *
42
     * @return \EasyWeChat\Kernel\Clauses\Clause
43
     *
44
     * @throws \EasyWeChat\Kernel\Exceptions\InvalidArgumentException
45
     */
46
    public function push($handler, $condition = '*')
47
    {
48
        list($handler, $condition) = $this->resolveHandlerAndCondition($handler, $condition);
49
50
        if (!isset($this->handlers[$condition])) {
51
            $this->handlers[$condition] = [];
52
        }
53
54
        array_push($this->handlers[$condition], $handler);
55
56
        return $this->newClause($handler);
57
    }
58
59
    /**
60
     * @param \Closure|EventHandlerInterface|string $handler
61
     * @param \Closure|EventHandlerInterface|string $condition
62
     *
63
     * @return \EasyWeChat\Kernel\Clauses\Clause
64
     *
65
     * @throws \EasyWeChat\Kernel\Exceptions\InvalidArgumentException
66
     */
67
    public function unshift($handler, $condition = '*')
68
    {
69
        list($handler, $condition) = $this->resolveHandlerAndCondition($handler, $condition);
70
71
        if (!isset($this->handlers[$condition])) {
72
            $this->handlers[$condition] = [];
73
        }
74
75
        array_unshift($this->handlers[$condition], $handler);
76
77
        return $this->newClause($handler);
78
    }
79
80
    /**
81
     * @param string                                $condition
82
     * @param \Closure|EventHandlerInterface|string $handler
83
     *
84
     * @return \EasyWeChat\Kernel\Clauses\Clause
85
     *
86
     * @throws \EasyWeChat\Kernel\Exceptions\InvalidArgumentException
87
     */
88
    public function observe($condition, $handler)
89
    {
90
        return $this->push($handler, $condition);
91
    }
92
93
    /**
94
     * @param string                                $condition
95
     * @param \Closure|EventHandlerInterface|string $handler
96
     *
97
     * @return \EasyWeChat\Kernel\Clauses\Clause
98
     *
99
     * @throws \EasyWeChat\Kernel\Exceptions\InvalidArgumentException
100
     */
101
    public function on($condition, $handler)
102
    {
103
        return $this->push($handler, $condition);
104
    }
105
106
    /**
107
     * @param string|int $event
108
     * @param mixed      ...$payload
109
     *
110
     * @return mixed|null
111
     */
112
    public function dispatch($event, $payload)
113
    {
114
        return $this->notify($event, $payload);
115
    }
116
117
    /**
118
     * @param string|int $event
119
     * @param mixed      ...$payload
120
     *
121
     * @return mixed|null
122
     */
123
    public function notify($event, $payload)
124
    {
125
        $result = null;
126
127
        foreach ($this->handlers as $condition => $handlers) {
128
            if ('*' === $condition || ($condition & $event) === $event) {
129
                foreach ($handlers as $handler) {
130
                    if ($clause = $this->clauses[spl_object_hash((object) $handler)] ?? null) {
131
                        if ($clause->intercepted($payload)) {
132
                            continue 2;
133
                        }
134
                    }
135
                    $response = $this->callHandler($handler, $payload);
136
137
                    switch (true) {
138
                        case $response instanceof TerminateResult:
0 ignored issues
show
Coding Style introduced by
case statements should be defined using a colon.

As per the PSR-2 coding standard, case statements should not be wrapped in curly braces. There is no need for braces, since each case is terminated by the next break.

There is also the option to use a semicolon instead of a colon, this is discouraged because many programmers do not even know it works and the colon is universal between programming languages.

switch ($expr) {
    case "A": { //wrong
        doSomething();
        break;
    }
    case "B"; //wrong
        doSomething();
        break;
    case "C": //right
        doSomething();
        break;
}

To learn more about the PSR-2 coding standard, please refer to the PHP-Fig.

Loading history...
139
                            return $response->content;
140
                        case true === $response:
0 ignored issues
show
Coding Style introduced by
case statements should be defined using a colon.

As per the PSR-2 coding standard, case statements should not be wrapped in curly braces. There is no need for braces, since each case is terminated by the next break.

There is also the option to use a semicolon instead of a colon, this is discouraged because many programmers do not even know it works and the colon is universal between programming languages.

switch ($expr) {
    case "A": { //wrong
        doSomething();
        break;
    }
    case "B"; //wrong
        doSomething();
        break;
    case "C": //right
        doSomething();
        break;
}

To learn more about the PSR-2 coding standard, please refer to the PHP-Fig.

Loading history...
141
                            continue 2;
142
                        case false === $response:
143
                            break 2;
144
                        case !empty($response) && !($result instanceof FinallyResult):
145
                            $result = $response;
146
                    }
147
                }
148
            }
149
        }
150
151
        return $result instanceof FinallyResult ? $result->content : $result;
152
    }
153
154
    /**
155
     * @return array
156
     */
157
    public function getHandlers()
158
    {
159
        return $this->handlers;
160
    }
161
162
    /**
163
     * @param mixed $handler
164
     *
165
     * @return \EasyWeChat\Kernel\Clauses\Clause
166
     */
167
    protected function newClause($handler): Clause
168
    {
169
        return $this->clauses[spl_object_hash((object) $handler)] = new Clause();
170
    }
171
172
    /**
173
     * @param callable $handler
174
     * @param mixed    $payload
175
     *
176
     * @return mixed
177
     */
178
    protected function callHandler(callable $handler, $payload)
179
    {
180
        try {
181
            return $handler($payload);
182
        } catch (\Exception $e) {
183
            if (property_exists($this, 'app') && $this->app instanceof ServiceContainer) {
184
                $this->app['logger']->error($e->getCode().': '.$e->getMessage(), [
185
                    'code' => $e->getCode(),
186
                    'message' => $e->getMessage(),
187
                    'file' => $e->getFile(),
188
                    'line' => $e->getLine(),
189
                ]);
190
            }
191
        }
192
    }
193
194
    /**
195
     * @param $handler
196
     *
197
     * @return \Closure
198
     *
199
     * @throws \EasyWeChat\Kernel\Exceptions\InvalidArgumentException
200
     * @throws \ReflectionException
201
     */
202
    protected function makeClosure($handler)
203
    {
204
        if (is_callable($handler)) {
205
            return $handler;
206
        }
207
208
        if (is_string($handler)) {
209
            if (!class_exists($handler)) {
210
                throw new InvalidArgumentException(sprintf('Class "%s" not exists.', $handler));
211
            }
212
213
            if (!in_array(EventHandlerInterface::class, (new \ReflectionClass($handler))->getInterfaceNames(), true)) {
214
                throw new InvalidArgumentException(sprintf('Class "%s" not an instance of "%s".', $handler, EventHandlerInterface::class));
215
            }
216
217
            return function ($payload) use ($handler) {
218
                return (new $handler($this->app ?? null))->handle($payload);
219
            };
220
        }
221
222
        if ($handler instanceof EventHandlerInterface) {
223
            return function () use ($handler) {
224
                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

224
                return $handler->handle(/** @scrutinizer ignore-type */ ...func_get_args());
Loading history...
225
            };
226
        }
227
228
        throw new InvalidArgumentException('No valid handler is found in arguments.');
229
    }
230
231
    /**
232
     * @param $handler
233
     * @param $condition
234
     *
235
     * @return array
236
     *
237
     * @throws \EasyWeChat\Kernel\Exceptions\InvalidArgumentException
238
     * @throws \ReflectionException
239
     */
240
    protected function resolveHandlerAndCondition($handler, $condition): array
241
    {
242
        if (is_int($handler) || (is_string($handler) && !class_exists($handler))) {
243
            list($handler, $condition) = [$condition, $handler];
244
        }
245
246
        return [$this->makeClosure($handler), $condition];
247
    }
248
}
249