Observable.php 7.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244
  1. <?php
  2. /**
  3. * 重庆赤晓店信息科技有限公司
  4. * https://www.chixiaodian.com
  5. * Copyright (c) 2023 赤店商城 All rights reserved.
  6. */
  7. /*
  8. * This file is part of the overtrue/wechat.
  9. *
  10. * (c) overtrue <i@overtrue.me>
  11. *
  12. * This source file is subject to the MIT license that is bundled
  13. * with this source code in the file LICENSE.
  14. */
  15. namespace ByteDance\Kernel\Traits;
  16. use ByteDance\Kernel\Clauses\Clause;
  17. use ByteDance\Kernel\Contracts\EventHandlerInterface;
  18. use ByteDance\Kernel\Decorators\FinallyResult;
  19. use ByteDance\Kernel\Decorators\TerminateResult;
  20. use ByteDance\Kernel\Exceptions\InvalidArgumentException;
  21. use ByteDance\Kernel\ServiceContainer;
  22. /**
  23. * Trait Observable.
  24. *
  25. * @author overtrue <i@overtrue.me>
  26. */
  27. trait Observable
  28. {
  29. /**
  30. * @var array
  31. */
  32. protected $handlers = [];
  33. /**
  34. * @var array
  35. */
  36. protected $clauses = [];
  37. /**
  38. * @param \Closure|EventHandlerInterface|string $handler
  39. * @param \Closure|EventHandlerInterface|string $condition
  40. *
  41. * @throws \ByteDance\Kernel\Exceptions\InvalidArgumentException
  42. * @throws \ReflectionException
  43. *
  44. * @return \ByteDance\Kernel\Clauses\Clause
  45. */
  46. public function push($handler, $condition = '*')
  47. {
  48. list($handler, $condition) = $this->resolveHandlerAndCondition($handler, $condition);
  49. if (!isset($this->handlers[$condition])) {
  50. $this->handlers[$condition] = [];
  51. }
  52. array_push($this->handlers[$condition], $handler);
  53. return $this->newClause($handler);
  54. }
  55. /**
  56. * @param \Closure|EventHandlerInterface|string $handler
  57. * @param \Closure|EventHandlerInterface|string $condition
  58. *
  59. * @throws \ByteDance\Kernel\Exceptions\InvalidArgumentException
  60. * @throws \ReflectionException
  61. *
  62. * @return \ByteDance\Kernel\Clauses\Clause
  63. */
  64. public function unshift($handler, $condition = '*')
  65. {
  66. list($handler, $condition) = $this->resolveHandlerAndCondition($handler, $condition);
  67. if (!isset($this->handlers[$condition])) {
  68. $this->handlers[$condition] = [];
  69. }
  70. array_unshift($this->handlers[$condition], $handler);
  71. return $this->newClause($handler);
  72. }
  73. /**
  74. * @param string $condition
  75. * @param \Closure|EventHandlerInterface|string $handler
  76. *
  77. * @throws \ByteDance\Kernel\Exceptions\InvalidArgumentException
  78. *
  79. * @return \ByteDance\Kernel\Clauses\Clause
  80. */
  81. public function observe($condition, $handler)
  82. {
  83. return $this->push($handler, $condition);
  84. }
  85. /**
  86. * @param string $condition
  87. * @param \Closure|EventHandlerInterface|string $handler
  88. *
  89. * @throws \ByteDance\Kernel\Exceptions\InvalidArgumentException
  90. *
  91. * @return \ByteDance\Kernel\Clauses\Clause
  92. */
  93. public function on($condition, $handler)
  94. {
  95. return $this->push($handler, $condition);
  96. }
  97. /**
  98. * @param string|int $event
  99. * @param mixed ...$payload
  100. *
  101. * @return mixed|null
  102. */
  103. public function dispatch($event, $payload)
  104. {
  105. return $this->notify($event, $payload);
  106. }
  107. /**
  108. * @param string|int $event
  109. * @param mixed ...$payload
  110. *
  111. * @return mixed|null
  112. */
  113. public function notify($event, $payload)
  114. {
  115. $result = null;
  116. foreach ($this->handlers as $condition => $handlers) {
  117. if ('*' === $condition || ($condition & $event) === $event) {
  118. foreach ($handlers as $handler) {
  119. if ($clause = $this->clauses[spl_object_hash((object) $handler)] ?? null) {
  120. if ($clause->intercepted($payload)) {
  121. continue;
  122. }
  123. }
  124. $response = $this->callHandler($handler, $payload);
  125. switch (true) {
  126. case $response instanceof TerminateResult:
  127. return $response->content;
  128. case true === $response:
  129. continue 2;
  130. case false === $response:
  131. break 2;
  132. case !empty($response) && !($result instanceof FinallyResult):
  133. $result = $response;
  134. }
  135. }
  136. }
  137. }
  138. return $result instanceof FinallyResult ? $result->content : $result;
  139. }
  140. /**
  141. * @return array
  142. */
  143. public function getHandlers()
  144. {
  145. return $this->handlers;
  146. }
  147. /**
  148. * @param mixed $handler
  149. *
  150. * @return \ByteDance\Kernel\Clauses\Clause
  151. */
  152. protected function newClause($handler): Clause
  153. {
  154. return $this->clauses[spl_object_hash((object) $handler)] = new Clause();
  155. }
  156. /**
  157. * @param callable $handler
  158. * @param mixed $payload
  159. *
  160. * @return mixed
  161. */
  162. protected function callHandler(callable $handler, $payload)
  163. {
  164. try {
  165. return $handler($payload);
  166. } catch (\Exception $e) {
  167. if (property_exists($this, 'app') && $this->app instanceof ServiceContainer) {
  168. $this->app['logger']->error($e->getCode().': '.$e->getMessage(), [
  169. 'code' => $e->getCode(),
  170. 'message' => $e->getMessage(),
  171. 'file' => $e->getFile(),
  172. 'line' => $e->getLine(),
  173. ]);
  174. }
  175. }
  176. }
  177. /**
  178. * @param $handler
  179. *
  180. * @throws \ByteDance\Kernel\Exceptions\InvalidArgumentException
  181. * @throws \ReflectionException
  182. *
  183. * @return \Closure
  184. */
  185. protected function makeClosure($handler)
  186. {
  187. if (is_callable($handler)) {
  188. return $handler;
  189. }
  190. if (is_string($handler)) {
  191. if (!class_exists($handler)) {
  192. throw new InvalidArgumentException(sprintf('Class "%s" not exists.', $handler));
  193. }
  194. if (!in_array(EventHandlerInterface::class, (new \ReflectionClass($handler))->getInterfaceNames(), true)) {
  195. throw new InvalidArgumentException(sprintf('Class "%s" not an instance of "%s".', $handler, EventHandlerInterface::class));
  196. }
  197. return function ($payload) use ($handler) {
  198. return (new $handler($this->app ?? null))->handle($payload);
  199. };
  200. }
  201. if ($handler instanceof EventHandlerInterface) {
  202. return function () use ($handler) {
  203. return $handler->handle(...func_get_args());
  204. };
  205. }
  206. throw new InvalidArgumentException('No valid handler is found in arguments.');
  207. }
  208. /**
  209. * @param $handler
  210. * @param $condition
  211. *
  212. * @throws \ByteDance\Kernel\Exceptions\InvalidArgumentException
  213. * @throws \ReflectionException
  214. *
  215. * @return array
  216. */
  217. protected function resolveHandlerAndCondition($handler, $condition): array
  218. {
  219. if (is_int($handler) || (is_string($handler) && !class_exists($handler))) {
  220. list($handler, $condition) = [$condition, $handler];
  221. }
  222. return [$this->makeClosure($handler), $condition];
  223. }
  224. }