PageRenderTime 57ms CodeModel.GetById 27ms RepoModel.GetById 1ms app.codeStats 0ms

/vendor/laravelcollective/bus/src/Dispatcher.php

https://gitlab.com/heruujoko/ilearnman
PHP | 430 lines | 182 code | 51 blank | 197 comment | 21 complexity | f6920941fc8db929b2e54e90db36ca5e MD5 | raw file
  1. <?php
  2. namespace Collective\Bus;
  3. use ArrayAccess;
  4. use Closure;
  5. use Illuminate\Contracts\Bus\Dispatcher as DispatcherContract;
  6. use Illuminate\Contracts\Bus\QueueingDispatcher;
  7. use Illuminate\Contracts\Bus\SelfHandling;
  8. use Illuminate\Contracts\Container\Container;
  9. use Illuminate\Contracts\Queue\Queue;
  10. use Illuminate\Contracts\Queue\ShouldQueue;
  11. use Illuminate\Pipeline\Pipeline;
  12. use Illuminate\Support\Collection;
  13. use InvalidArgumentException;
  14. use ReflectionClass;
  15. use ReflectionParameter;
  16. use RuntimeException;
  17. class Dispatcher implements DispatcherContract, QueueingDispatcher, HandlerResolver
  18. {
  19. /**
  20. * The container implementation.
  21. *
  22. * @var \Illuminate\Contracts\Container\Container
  23. */
  24. protected $container;
  25. /**
  26. * The pipeline instance for the bus.
  27. *
  28. * @var \Illuminate\Pipeline\Pipeline
  29. */
  30. protected $pipeline;
  31. /**
  32. * The pipes to send commands through before dispatching.
  33. *
  34. * @var array
  35. */
  36. protected $pipes = [];
  37. /**
  38. * The queue resolver callback.
  39. *
  40. * @var \Closure|null
  41. */
  42. protected $queueResolver;
  43. /**
  44. * All of the command-to-handler mappings.
  45. *
  46. * @var array
  47. */
  48. protected $mappings = [];
  49. /**
  50. * The fallback mapping Closure.
  51. *
  52. * @var \Closure
  53. */
  54. protected $mapper;
  55. /**
  56. * Create a new command dispatcher instance.
  57. *
  58. * @param \Illuminate\Contracts\Container\Container $container
  59. * @param \Closure|null $queueResolver
  60. *
  61. * @return void
  62. */
  63. public function __construct(Container $container, Closure $queueResolver = null)
  64. {
  65. $this->container = $container;
  66. $this->queueResolver = $queueResolver;
  67. $this->pipeline = new Pipeline($container);
  68. }
  69. /**
  70. * Marshal a command and dispatch it to its appropriate handler.
  71. *
  72. * @param mixed $command
  73. * @param array $array
  74. *
  75. * @return mixed
  76. */
  77. public function dispatchFromArray($command, array $array)
  78. {
  79. return $this->dispatch($this->marshalFromArray($command, $array));
  80. }
  81. /**
  82. * Marshal a command and dispatch it to its appropriate handler.
  83. *
  84. * @param mixed $command
  85. * @param \ArrayAccess $source
  86. * @param array $extras
  87. *
  88. * @return mixed
  89. */
  90. public function dispatchFrom($command, ArrayAccess $source, array $extras = [])
  91. {
  92. return $this->dispatch($this->marshal($command, $source, $extras));
  93. }
  94. /**
  95. * Marshal a command from the given array.
  96. *
  97. * @param string $command
  98. * @param array $array
  99. *
  100. * @return mixed
  101. */
  102. protected function marshalFromArray($command, array $array)
  103. {
  104. return $this->marshal($command, new Collection(), $array);
  105. }
  106. /**
  107. * Marshal a command from the given array accessible object.
  108. *
  109. * @param string $command
  110. * @param \ArrayAccess $source
  111. * @param array $extras
  112. *
  113. * @return mixed
  114. */
  115. protected function marshal($command, ArrayAccess $source, array $extras = [])
  116. {
  117. $injected = [];
  118. $reflection = new ReflectionClass($command);
  119. if ($constructor = $reflection->getConstructor()) {
  120. $injected = array_map(function ($parameter) use ($command, $source, $extras) {
  121. return $this->getParameterValueForCommand($command, $source, $parameter, $extras);
  122. }, $constructor->getParameters());
  123. }
  124. return $reflection->newInstanceArgs($injected);
  125. }
  126. /**
  127. * Get a parameter value for a marshaled command.
  128. *
  129. * @param string $command
  130. * @param \ArrayAccess $source
  131. * @param \ReflectionParameter $parameter
  132. * @param array $extras
  133. *
  134. * @return mixed
  135. */
  136. protected function getParameterValueForCommand($command, ArrayAccess $source,
  137. ReflectionParameter $parameter, array $extras = [])
  138. {
  139. if (array_key_exists($parameter->name, $extras)) {
  140. return $extras[$parameter->name];
  141. }
  142. if (isset($source[$parameter->name])) {
  143. return $source[$parameter->name];
  144. }
  145. if ($parameter->isDefaultValueAvailable()) {
  146. return $parameter->getDefaultValue();
  147. }
  148. MarshalException::whileMapping($command, $parameter);
  149. }
  150. /**
  151. * Dispatch a command to its appropriate handler.
  152. *
  153. * @param mixed $command
  154. * @param \Closure|null $afterResolving
  155. *
  156. * @return mixed
  157. */
  158. public function dispatch($command, Closure $afterResolving = null)
  159. {
  160. if ($this->queueResolver && $this->commandShouldBeQueued($command)) {
  161. return $this->dispatchToQueue($command);
  162. } else {
  163. return $this->dispatchNow($command, $afterResolving);
  164. }
  165. }
  166. /**
  167. * Dispatch a command to its appropriate handler in the current process.
  168. *
  169. * @param mixed $command
  170. * @param \Closure|null $afterResolving
  171. *
  172. * @return mixed
  173. */
  174. public function dispatchNow($command, Closure $afterResolving = null)
  175. {
  176. return $this->pipeline->send($command)->through($this->pipes)->then(function ($command) use ($afterResolving) {
  177. if ($command instanceof SelfHandling) {
  178. return $this->container->call([$command, 'handle']);
  179. }
  180. $handler = $this->resolveHandler($command);
  181. if ($afterResolving) {
  182. call_user_func($afterResolving, $handler);
  183. }
  184. return call_user_func(
  185. [$handler, $this->getHandlerMethod($command)], $command
  186. );
  187. });
  188. }
  189. /**
  190. * Determine if the given command should be queued.
  191. *
  192. * @param mixed $command
  193. *
  194. * @return bool
  195. */
  196. protected function commandShouldBeQueued($command)
  197. {
  198. if ($command instanceof ShouldQueue) {
  199. return true;
  200. }
  201. return (new ReflectionClass($this->getHandlerClass($command)))->implementsInterface(
  202. 'Illuminate\Contracts\Queue\ShouldQueue'
  203. );
  204. }
  205. /**
  206. * Dispatch a command to its appropriate handler behind a queue.
  207. *
  208. * @param mixed $command
  209. *
  210. * @throws \RuntimeException
  211. *
  212. * @return mixed
  213. */
  214. public function dispatchToQueue($command)
  215. {
  216. $queue = call_user_func($this->queueResolver);
  217. if (!$queue instanceof Queue) {
  218. throw new RuntimeException('Queue resolver did not return a Queue implementation.');
  219. }
  220. if (method_exists($command, 'queue')) {
  221. return $command->queue($queue, $command);
  222. } else {
  223. return $this->pushCommandToQueue($queue, $command);
  224. }
  225. }
  226. /**
  227. * Push the command onto the given queue instance.
  228. *
  229. * @param \Illuminate\Contracts\Queue\Queue $queue
  230. * @param mixed $command
  231. *
  232. * @return mixed
  233. */
  234. protected function pushCommandToQueue($queue, $command)
  235. {
  236. if (isset($command->queue, $command->delay)) {
  237. return $queue->laterOn($command->queue, $command->delay, $command);
  238. }
  239. if (isset($command->queue)) {
  240. return $queue->pushOn($command->queue, $command);
  241. }
  242. if (isset($command->delay)) {
  243. return $queue->later($command->delay, $command);
  244. }
  245. return $queue->push($command);
  246. }
  247. /**
  248. * Get the handler instance for the given command.
  249. *
  250. * @param mixed $command
  251. *
  252. * @return mixed
  253. */
  254. public function resolveHandler($command)
  255. {
  256. if ($command instanceof SelfHandling) {
  257. return $command;
  258. }
  259. return $this->container->make($this->getHandlerClass($command));
  260. }
  261. /**
  262. * Get the handler class for the given command.
  263. *
  264. * @param mixed $command
  265. *
  266. * @return string
  267. */
  268. public function getHandlerClass($command)
  269. {
  270. if ($command instanceof SelfHandling) {
  271. return get_class($command);
  272. }
  273. return $this->inflectSegment($command, 0);
  274. }
  275. /**
  276. * Get the handler method for the given command.
  277. *
  278. * @param mixed $command
  279. *
  280. * @return string
  281. */
  282. public function getHandlerMethod($command)
  283. {
  284. if ($command instanceof SelfHandling) {
  285. return 'handle';
  286. }
  287. return $this->inflectSegment($command, 1);
  288. }
  289. /**
  290. * Get the given handler segment for the given command.
  291. *
  292. * @param mixed $command
  293. * @param int $segment
  294. *
  295. * @return string
  296. */
  297. protected function inflectSegment($command, $segment)
  298. {
  299. $className = get_class($command);
  300. if (isset($this->mappings[$className])) {
  301. return $this->getMappingSegment($className, $segment);
  302. } elseif ($this->mapper) {
  303. return $this->getMapperSegment($command, $segment);
  304. }
  305. throw new InvalidArgumentException("No handler registered for command [{$className}]");
  306. }
  307. /**
  308. * Get the given segment from a given class handler.
  309. *
  310. * @param string $className
  311. * @param int $segment
  312. *
  313. * @return string
  314. */
  315. protected function getMappingSegment($className, $segment)
  316. {
  317. return explode('@', $this->mappings[$className])[$segment];
  318. }
  319. /**
  320. * Get the given segment from a given class handler using the custom mapper.
  321. *
  322. * @param mixed $command
  323. * @param int $segment
  324. *
  325. * @return string
  326. */
  327. protected function getMapperSegment($command, $segment)
  328. {
  329. return explode('@', call_user_func($this->mapper, $command))[$segment];
  330. }
  331. /**
  332. * Register command-to-handler mappings.
  333. *
  334. * @param array $commands
  335. *
  336. * @return void
  337. */
  338. public function maps(array $commands)
  339. {
  340. $this->mappings = array_merge($this->mappings, $commands);
  341. }
  342. /**
  343. * Register a fallback mapper callback.
  344. *
  345. * @param \Closure $mapper
  346. *
  347. * @return void
  348. */
  349. public function mapUsing(Closure $mapper)
  350. {
  351. $this->mapper = $mapper;
  352. }
  353. /**
  354. * Map the command to a handler within a given root namespace.
  355. *
  356. * @param mixed $command
  357. * @param string $commandNamespace
  358. * @param string $handlerNamespace
  359. *
  360. * @return string
  361. */
  362. public static function simpleMapping($command, $commandNamespace, $handlerNamespace)
  363. {
  364. $command = str_replace($commandNamespace, '', get_class($command));
  365. return $handlerNamespace.'\\'.trim($command, '\\').'Handler@handle';
  366. }
  367. /**
  368. * Set the pipes through which commands should be piped before dispatching.
  369. *
  370. * @param array $pipes
  371. *
  372. * @return $this
  373. */
  374. public function pipeThrough(array $pipes)
  375. {
  376. $this->pipes = $pipes;
  377. return $this;
  378. }
  379. }