Passed
Pull Request — master (#1678)
by mingyoung
27:10
created

Observable::callHandler()   A

Complexity

Conditions 4
Paths 3

Size

Total Lines 11
Code Lines 9

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 9
CRAP Score 4

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 4
eloc 9
c 1
b 0
f 0
nc 3
nop 2
dl 0
loc 11
rs 9.9666
ccs 9
cts 9
cp 1
crap 4
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 \Closure|EventHandlerInterface|string $handler
62
     * @param \Closure|EventHandlerInterface|string $condition
63
     *
64
     * @return \EasyWeChat\Kernel\Clauses\Clause
65
     *
66
     * @throws \EasyWeChat\Kernel\Exceptions\InvalidArgumentException
67
     * @throws \ReflectionException
68
     */
69 1
    public function unshift($handler, $condition = '*')
70
    {
71 1
        list($handler, $condition) = $this->resolveHandlerAndCondition($handler, $condition);
72
73 1
        if (!isset($this->handlers[$condition])) {
74 1
            $this->handlers[$condition] = [];
75
        }
76
77 1
        array_unshift($this->handlers[$condition], $handler);
78
79 1
        return $this->newClause($handler);
80
    }
81
82
    /**
83
     * @param string                                $condition
84
     * @param \Closure|EventHandlerInterface|string $handler
85
     *
86
     * @return \EasyWeChat\Kernel\Clauses\Clause
87
     *
88
     * @throws \EasyWeChat\Kernel\Exceptions\InvalidArgumentException
89
     * @throws \ReflectionException
90
     */
91 1
    public function observe($condition, $handler)
92
    {
93 1
        return $this->push($handler, $condition);
94
    }
95
96
    /**
97
     * @param string                                $condition
98
     * @param \Closure|EventHandlerInterface|string $handler
99
     *
100
     * @return \EasyWeChat\Kernel\Clauses\Clause
101
     *
102
     * @throws \EasyWeChat\Kernel\Exceptions\InvalidArgumentException
103
     * @throws \ReflectionException
104
     */
105 2
    public function on($condition, $handler)
106
    {
107 2
        return $this->push($handler, $condition);
108
    }
109
110
    /**
111
     * @param string|int $event
112
     * @param mixed      ...$payload
113
     *
114
     * @return mixed|null
115
     */
116 2
    public function dispatch($event, $payload)
117
    {
118 2
        return $this->notify($event, $payload);
119
    }
120
121
    /**
122
     * @param string|int $event
123
     * @param mixed      ...$payload
124
     *
125
     * @return mixed|null
126
     */
127 9
    public function notify($event, $payload)
128
    {
129 9
        $result = null;
130
131 9
        foreach ($this->handlers as $condition => $handlers) {
132 9
            if ('*' === $condition || ($condition & $event) === $event) {
133 9
                foreach ($handlers as $handler) {
134 9
                    if ($clause = $this->clauses[$this->getHandlerHash($handler)] ?? null) {
135 9
                        if ($clause->intercepted($payload)) {
136 2
                            continue;
137
                        }
138
                    }
139
140 9
                    $response = $this->callHandler($handler, $payload);
141
142
                    switch (true) {
143 9
                        case $response instanceof TerminateResult:
144 1
                            return $response->content;
145 8
                        case true === $response:
146 1
                            continue 2;
147 8
                        case false === $response:
148 1
                            break 2;
149 6
                        case !empty($response) && !($result instanceof FinallyResult):
150 8
                            $result = $response;
151
                    }
152
                }
153
            }
154
        }
155
156 8
        return $result instanceof FinallyResult ? $result->content : $result;
157
    }
158
159
    /**
160
     * @return array
161
     */
162 6
    public function getHandlers()
163
    {
164 6
        return $this->handlers;
165
    }
166
167
    /**
168
     * @param mixed $handler
169
     *
170
     * @return \EasyWeChat\Kernel\Clauses\Clause
171
     */
172 19
    protected function newClause($handler): Clause
173
    {
174 19
        return $this->clauses[$this->getHandlerHash($handler)] = new Clause();
175
    }
176
177
    /**
178
     * @param mixed $handler
179
     *
180
     * @return string
181
     */
182 19
    protected function getHandlerHash($handler)
183
    {
184 19
        if (is_string($handler)) {
185
            return $handler;
186
        }
187
188 19
        if (is_array($handler)) {
189 2
            return is_string($handler[0])
190
                ? $handler[0].'::'.$handler[1]
191 2
                : get_class($handler[0]).$handler[1];
192
        }
193
194 18
        return spl_object_hash($handler);
195
    }
196
197
    /**
198
     * @param callable $handler
199
     * @param mixed    $payload
200
     *
201
     * @return mixed
202
     */
203 9
    protected function callHandler(callable $handler, $payload)
204
    {
205
        try {
206 9
            return call_user_func_array($handler, [$payload]);
207 1
        } catch (\Exception $e) {
208 1
            if (property_exists($this, 'app') && $this->app instanceof ServiceContainer) {
209 1
                $this->app['logger']->error($e->getCode().': '.$e->getMessage(), [
210 1
                    'code' => $e->getCode(),
211 1
                    'message' => $e->getMessage(),
212 1
                    'file' => $e->getFile(),
213 1
                    'line' => $e->getLine(),
214
                ]);
215
            }
216
        }
217 1
    }
218
219
    /**
220
     * @param mixed $handler
221
     *
222
     * @return \Closure
223
     *
224
     * @throws \EasyWeChat\Kernel\Exceptions\InvalidArgumentException
225
     * @throws \ReflectionException
226
     */
227 20
    protected function makeClosure($handler)
228
    {
229 20
        if (is_callable($handler)) {
230 3
            return $handler;
231
        }
232
233 19
        if (is_string($handler)) {
234 3
            if (!class_exists($handler)) {
235 1
                throw new InvalidArgumentException(sprintf('Class "%s" not exists.', $handler));
236
            }
237
238 3
            if (!in_array(EventHandlerInterface::class, (new \ReflectionClass($handler))->getInterfaceNames(), true)) {
239 1
                throw new InvalidArgumentException(sprintf('Class "%s" not an instance of "%s".', $handler, EventHandlerInterface::class));
240
            }
241
242
            return function ($payload) use ($handler) {
243 2
                return (new $handler($this->app ?? null))->handle($payload);
244 2
            };
245
        }
246
247 18
        if ($handler instanceof EventHandlerInterface) {
248
            return function () use ($handler) {
249 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

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