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 3; |
|
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
![]() |
|||
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 |