Completed
Pull Request — master (#1588)
by mingyoung
04:04
created

Observable::dispatch()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 1

Importance

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

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