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: |
|
|
|
|
131
|
|
|
return $response->content; |
132
|
|
|
case true === $response: |
|
|
|
|
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()); |
|
|
|
|
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
|
|
|
|
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.
To learn more about the PSR-2 coding standard, please refer to the PHP-Fig.