Completed
Pull Request — master (#1315)
by mingyoung
03:18
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
rs 6.6166
c 0
b 0
f 0
ccs 0
cts 17
cp 0
crap 182

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->clauses[spl_object_hash((object) $handler)] = new Clause();
57
    }
58
59
    /**
60
     * @param \Closure|EventHandlerInterface|string $handler
61
     * @param \Closure|EventHandlerInterface|string $condition
62
     *
63
     * @throws \EasyWeChat\Kernel\Exceptions\InvalidArgumentException
64
     */
65
    public function unshift($handler, $condition = '*')
66
    {
67
        list($handler, $condition) = $this->resolveHandlerAndCondition($handler, $condition);
68
69
        if (!isset($this->handlers[$condition])) {
70
            $this->handlers[$condition] = [];
71
        }
72
73
        array_unshift($this->handlers[$condition], $handler);
74
    }
75
76
    /**
77
     * @param string                                $condition
78
     * @param \Closure|EventHandlerInterface|string $handler
79
     *
80
     * @throws \EasyWeChat\Kernel\Exceptions\InvalidArgumentException
81
     */
82
    public function observe($condition, $handler)
83
    {
84
        $this->push($handler, $condition);
85
    }
86
87
    /**
88
     * @param string                                $condition
89
     * @param \Closure|EventHandlerInterface|string $handler
90
     *
91
     * @throws \EasyWeChat\Kernel\Exceptions\InvalidArgumentException
92
     */
93
    public function on($condition, $handler)
94
    {
95
        $this->push($handler, $condition);
96
    }
97
98
    /**
99
     * @param string|int $event
100
     * @param mixed      ...$payload
101
     *
102
     * @return mixed|null
103
     */
104
    public function dispatch($event, $payload)
105
    {
106
        return $this->notify($event, $payload);
107
    }
108
109
    /**
110
     * @param string|int $event
111
     * @param mixed      ...$payload
112
     *
113
     * @return mixed|null
114
     */
115
    public function notify($event, $payload)
116
    {
117
        $result = null;
118
119
        foreach ($this->handlers as $condition => $handlers) {
120
            if ('*' === $condition || ($condition & $event) === $event) {
121
                foreach ($handlers as $handler) {
122
                    if ($clause = $this->clauses[spl_object_hash((object) $handler)] ?? null) {
123
                        if ($clause->intercepted($payload)) {
124
                            continue 2;
125
                        }
126
                    }
127
                    $response = $this->callHandler($handler, $payload);
128
129
                    switch (true) {
130
                        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...
131
                            return $response->content;
132
                        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...
133
                            continue 2;
134
                        case false === $response:
135
                            break 2;
136
                        case !empty($response) && !($result instanceof FinallyResult):
137
                            $result = $response;
138
                    }
139
                }
140
            }
141
        }
142
143
        return $result instanceof FinallyResult ? $result->content : $result;
144
    }
145
146
    /**
147
     * @return array
148
     */
149
    public function getHandlers()
150
    {
151
        return $this->handlers;
152
    }
153
154
    /**
155
     * @param callable $handler
156
     * @param mixed    $payload
157
     *
158
     * @return mixed
159
     */
160
    protected function callHandler(callable $handler, $payload)
161
    {
162
        try {
163
            return $handler($payload);
164
        } catch (\Exception $e) {
165
            if (property_exists($this, 'app') && $this->app instanceof ServiceContainer) {
166
                $this->app['logger']->error($e->getCode().': '.$e->getMessage(), [
167
                    'code' => $e->getCode(),
168
                    'message' => $e->getMessage(),
169
                    'file' => $e->getFile(),
170
                    'line' => $e->getLine(),
171
                ]);
172
            }
173
        }
174
    }
175
176
    /**
177
     * @param $handler
178
     *
179
     * @return \Closure
180
     *
181
     * @throws \EasyWeChat\Kernel\Exceptions\InvalidArgumentException
182
     * @throws \ReflectionException
183
     */
184
    protected function makeClosure($handler)
185
    {
186
        if (is_callable($handler)) {
187
            return $handler;
188
        }
189
190
        if (is_string($handler)) {
191
            if (!class_exists($handler)) {
192
                throw new InvalidArgumentException(sprintf('Class "%s" not exists.', $handler));
193
            }
194
195
            if (!in_array(EventHandlerInterface::class, (new \ReflectionClass($handler))->getInterfaceNames(), true)) {
196
                throw new InvalidArgumentException(sprintf('Class "%s" not an instance of "%s".', $handler, EventHandlerInterface::class));
197
            }
198
199
            return function ($payload) use ($handler) {
200
                return (new $handler($this->app ?? null))->handle($payload);
201
            };
202
        }
203
204
        if ($handler instanceof EventHandlerInterface) {
205
            return function () use ($handler) {
206
                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

206
                return $handler->handle(/** @scrutinizer ignore-type */ ...func_get_args());
Loading history...
207
            };
208
        }
209
210
        throw new InvalidArgumentException('No valid handler is found in arguments.');
211
    }
212
213
    /**
214
     * @param $handler
215
     * @param $condition
216
     *
217
     * @return array
218
     *
219
     * @throws \EasyWeChat\Kernel\Exceptions\InvalidArgumentException
220
     * @throws \ReflectionException
221
     */
222
    protected function resolveHandlerAndCondition($handler, $condition): array
223
    {
224
        if (is_int($handler) || (is_string($handler) && !class_exists($handler))) {
225
            list($handler, $condition) = [$condition, $handler];
226
        }
227
228
        return [$this->makeClosure($handler), $condition];
229
    }
230
}
231