1
|
|
|
<?php |
2
|
|
|
|
3
|
|
|
namespace Kaylyu\Alipay\Traits; |
4
|
|
|
|
5
|
|
|
use Kaylyu\Alipay\Kernel\Clauses\Clause; |
6
|
|
|
use Kaylyu\Alipay\Kernel\Contracts\EventHandlerInterface; |
7
|
|
|
use Kaylyu\Alipay\Kernel\Decorators\FinallyResult; |
8
|
|
|
use Kaylyu\Alipay\Kernel\Decorators\TerminateResult; |
9
|
|
|
use Kaylyu\Alipay\Kernel\Exceptions\InvalidArgumentException; |
10
|
|
|
use Kaylyu\Alipay\Kernel\ServiceContainer; |
11
|
|
|
|
12
|
|
|
|
13
|
|
|
/** |
14
|
|
|
* Trait Observable. |
15
|
|
|
*/ |
16
|
|
|
trait Observable |
17
|
|
|
{ |
18
|
|
|
/** |
19
|
|
|
* @var array |
20
|
|
|
*/ |
21
|
|
|
protected $handlers = []; |
22
|
|
|
|
23
|
|
|
/** |
24
|
|
|
* @var array |
25
|
|
|
*/ |
26
|
|
|
protected $clauses = []; |
27
|
|
|
|
28
|
|
|
/** |
29
|
|
|
* @param \Closure|EventHandlerInterface|string $handler |
30
|
|
|
* @param \Closure|EventHandlerInterface|string $condition |
31
|
|
|
* |
32
|
|
|
* @return \Kaylyu\Alipay\Kernel\Clauses\Clause |
33
|
|
|
* |
34
|
|
|
* @throws \Kaylyu\Alipay\Kernel\Exceptions\InvalidArgumentException |
35
|
|
|
* @throws \ReflectionException |
36
|
|
|
*/ |
37
|
|
View Code Duplication |
public function push($handler, $condition = '*') |
|
|
|
|
38
|
|
|
{ |
39
|
|
|
list($handler, $condition) = $this->resolveHandlerAndCondition($handler, $condition); |
40
|
|
|
|
41
|
|
|
if (!isset($this->handlers[$condition])) { |
42
|
|
|
$this->handlers[$condition] = []; |
43
|
|
|
} |
44
|
|
|
|
45
|
|
|
array_push($this->handlers[$condition], $handler); |
46
|
|
|
|
47
|
|
|
return $this->newClause($handler); |
48
|
|
|
} |
49
|
|
|
|
50
|
|
|
/** |
51
|
|
|
* @param \Closure|EventHandlerInterface|string $handler |
52
|
|
|
* @param \Closure|EventHandlerInterface|string $condition |
53
|
|
|
* |
54
|
|
|
* @return \Kaylyu\Alipay\Kernel\Clauses\Clause |
55
|
|
|
* |
56
|
|
|
* @throws \Kaylyu\Alipay\Kernel\Exceptions\InvalidArgumentException |
57
|
|
|
* @throws \ReflectionException |
58
|
|
|
*/ |
59
|
|
View Code Duplication |
public function unshift($handler, $condition = '*') |
|
|
|
|
60
|
|
|
{ |
61
|
|
|
list($handler, $condition) = $this->resolveHandlerAndCondition($handler, $condition); |
62
|
|
|
|
63
|
|
|
if (!isset($this->handlers[$condition])) { |
64
|
|
|
$this->handlers[$condition] = []; |
65
|
|
|
} |
66
|
|
|
|
67
|
|
|
array_unshift($this->handlers[$condition], $handler); |
68
|
|
|
|
69
|
|
|
return $this->newClause($handler); |
70
|
|
|
} |
71
|
|
|
|
72
|
|
|
/** |
73
|
|
|
* @param string $condition |
74
|
|
|
* @param \Closure|EventHandlerInterface|string $handler |
75
|
|
|
* |
76
|
|
|
* @return \Kaylyu\Alipay\Kernel\Clauses\Clause |
77
|
|
|
* |
78
|
|
|
* @throws \Kaylyu\Alipay\Kernel\Exceptions\InvalidArgumentException |
79
|
|
|
*/ |
80
|
|
|
public function observe($condition, $handler) |
81
|
|
|
{ |
82
|
|
|
return $this->push($handler, $condition); |
83
|
|
|
} |
84
|
|
|
|
85
|
|
|
/** |
86
|
|
|
* @param string $condition |
87
|
|
|
* @param \Closure|EventHandlerInterface|string $handler |
88
|
|
|
* |
89
|
|
|
* @return \Kaylyu\Alipay\Kernel\Clauses\Clause |
90
|
|
|
* |
91
|
|
|
* @throws \Kaylyu\Alipay\Kernel\Exceptions\InvalidArgumentException |
92
|
|
|
*/ |
93
|
|
|
public function on($condition, $handler) |
94
|
|
|
{ |
95
|
|
|
return $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; |
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 mixed $handler |
156
|
|
|
* |
157
|
|
|
* @return \Kaylyu\Alipay\Kernel\Clauses\Clause |
158
|
|
|
*/ |
159
|
|
|
protected function newClause($handler): Clause |
160
|
|
|
{ |
161
|
|
|
return $this->clauses[spl_object_hash((object) $handler)] = new Clause(); |
162
|
|
|
} |
163
|
|
|
|
164
|
|
|
/** |
165
|
|
|
* @param callable $handler |
166
|
|
|
* @param mixed $payload |
167
|
|
|
* |
168
|
|
|
* @return mixed |
169
|
|
|
*/ |
170
|
|
|
protected function callHandler(callable $handler, $payload) |
171
|
|
|
{ |
172
|
|
|
try { |
173
|
|
|
return $handler($payload); |
174
|
|
|
} catch (\Exception $e) { |
175
|
|
|
if (property_exists($this, 'app') && $this->app instanceof ServiceContainer) { |
176
|
|
|
$this->app['logger']->error($e->getCode().': '.$e->getMessage(), [ |
|
|
|
|
177
|
|
|
'code' => $e->getCode(), |
178
|
|
|
'message' => $e->getMessage(), |
179
|
|
|
'file' => $e->getFile(), |
180
|
|
|
'line' => $e->getLine(), |
181
|
|
|
]); |
182
|
|
|
} |
183
|
|
|
} |
184
|
|
|
} |
185
|
|
|
|
186
|
|
|
/** |
187
|
|
|
* @param $handler |
188
|
|
|
* @author kaylv <[email protected]> |
189
|
|
|
* @return \Closure |
190
|
|
|
* @throws InvalidArgumentException |
191
|
|
|
*/ |
192
|
|
|
protected function makeClosure($handler) |
193
|
|
|
{ |
194
|
|
|
if (is_callable($handler)) { |
195
|
|
|
return $handler; |
196
|
|
|
} |
197
|
|
|
|
198
|
|
|
if (is_string($handler)) { |
199
|
|
|
if (!class_exists($handler)) { |
200
|
|
|
throw new InvalidArgumentException(sprintf('Class "%s" not exists.', $handler)); |
201
|
|
|
} |
202
|
|
|
|
203
|
|
|
if (!in_array(EventHandlerInterface::class, (new \ReflectionClass($handler))->getInterfaceNames(), true)) { |
204
|
|
|
throw new InvalidArgumentException(sprintf('Class "%s" not an instance of "%s".', $handler, EventHandlerInterface::class)); |
205
|
|
|
} |
206
|
|
|
|
207
|
|
|
return function ($payload) use ($handler) { |
208
|
|
|
return (new $handler($this->app ?? null))->handle($payload); |
209
|
|
|
}; |
210
|
|
|
} |
211
|
|
|
|
212
|
|
|
if ($handler instanceof EventHandlerInterface) { |
213
|
|
|
return function () use ($handler) { |
214
|
|
|
return $handler->handle(...func_get_args()); |
215
|
|
|
}; |
216
|
|
|
} |
217
|
|
|
|
218
|
|
|
throw new InvalidArgumentException('No valid handler is found in arguments.'); |
219
|
|
|
} |
220
|
|
|
|
221
|
|
|
/** |
222
|
|
|
* @param $handler |
223
|
|
|
* @param $condition |
224
|
|
|
* |
225
|
|
|
* @return array |
226
|
|
|
* |
227
|
|
|
* @throws \Kaylyu\Alipay\Exceptions\InvalidArgumentException |
228
|
|
|
* @throws \ReflectionException |
229
|
|
|
*/ |
230
|
|
|
protected function resolveHandlerAndCondition($handler, $condition): array |
231
|
|
|
{ |
232
|
|
|
if (is_int($handler) || (is_string($handler) && !class_exists($handler))) { |
233
|
|
|
list($handler, $condition) = [$condition, $handler]; |
234
|
|
|
} |
235
|
|
|
|
236
|
|
|
return [$this->makeClosure($handler), $condition]; |
237
|
|
|
} |
238
|
|
|
} |
239
|
|
|
|
Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.
You can also find more detailed suggestions in the “Code” section of your repository.