Pipeline.php 4.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165
  1. <?php
  2. declare(strict_types=1);
  3. /**
  4. * This file is part of Hyperf.
  5. *
  6. * @link https://www.hyperf.io
  7. * @document https://hyperf.wiki
  8. * @contact group@hyperf.io
  9. * @license https://github.com/hyperf/hyperf/blob/master/LICENSE
  10. */
  11. namespace Hyperf\Pipeline;
  12. use Closure;
  13. use Psr\Container\ContainerInterface;
  14. /**
  15. * This file mostly code come from illuminate/pipe,
  16. * thanks Laravel Team provide such a useful class.
  17. */
  18. class Pipeline
  19. {
  20. /**
  21. * The object being passed through the pipeline.
  22. */
  23. protected mixed $passable = null;
  24. /**
  25. * The array of class pipes.
  26. */
  27. protected array $pipes = [];
  28. /**
  29. * The method to call on each pipe.
  30. */
  31. protected string $method = 'handle';
  32. public function __construct(protected ContainerInterface $container)
  33. {
  34. }
  35. /**
  36. * Set the object being sent through the pipeline.
  37. */
  38. public function send(mixed $passable): self
  39. {
  40. $this->passable = $passable;
  41. return $this;
  42. }
  43. /**
  44. * Set the array of pipes.
  45. *
  46. * @param array|mixed $pipes
  47. */
  48. public function through($pipes): self
  49. {
  50. $this->pipes = is_array($pipes) ? $pipes : func_get_args();
  51. return $this;
  52. }
  53. /**
  54. * Set the method to call on the pipes.
  55. */
  56. public function via(string $method): self
  57. {
  58. $this->method = $method;
  59. return $this;
  60. }
  61. /**
  62. * Run the pipeline with a final destination callback.
  63. */
  64. public function then(Closure $destination)
  65. {
  66. $pipeline = array_reduce(array_reverse($this->pipes), $this->carry(), $this->prepareDestination($destination));
  67. return $pipeline($this->passable);
  68. }
  69. /**
  70. * Run the pipeline and return the result.
  71. */
  72. public function thenReturn()
  73. {
  74. return $this->then(fn ($passable) => $passable);
  75. }
  76. /**
  77. * Get the final piece of the Closure onion.
  78. */
  79. protected function prepareDestination(Closure $destination): Closure
  80. {
  81. return static function ($passable) use ($destination) {
  82. return $destination($passable);
  83. };
  84. }
  85. /**
  86. * Get a Closure that represents a slice of the application onion.
  87. */
  88. protected function carry(): Closure
  89. {
  90. return function ($stack, $pipe) {
  91. return function ($passable) use ($stack, $pipe) {
  92. if (is_callable($pipe)) {
  93. // If the pipe is an instance of a Closure, we will just call it directly, but
  94. // otherwise we'll resolve the pipes out of the container and call it with
  95. // the appropriate method and arguments, returning the results back out.
  96. return $pipe($passable, $stack);
  97. }
  98. if (! is_object($pipe)) {
  99. [$name, $parameters] = $this->parsePipeString($pipe);
  100. // If the pipe is a string we will parse the string and resolve the class out
  101. // of the dependency injection container. We can then build a callable and
  102. // execute the pipe function giving in the parameters that are required.
  103. $pipe = $this->container->get($name);
  104. $parameters = array_merge([$passable, $stack], $parameters);
  105. } else {
  106. // If the pipe is already an object we'll just make a callable and pass it to
  107. // the pipe as-is. There is no need to do any extra parsing and formatting
  108. // since the object we're given was already a fully instantiated object.
  109. $parameters = [$passable, $stack];
  110. }
  111. $carry = method_exists($pipe, $this->method) ? $pipe->{$this->method}(...$parameters) : $pipe(...$parameters);
  112. return $this->handleCarry($carry);
  113. };
  114. };
  115. }
  116. /**
  117. * Parse full pipe string to get name and parameters.
  118. *
  119. * @param string $pipe
  120. * @return array
  121. */
  122. protected function parsePipeString($pipe)
  123. {
  124. [$name, $parameters] = array_pad(explode(':', $pipe, 2), 2, []);
  125. if (is_string($parameters)) {
  126. $parameters = explode(',', $parameters);
  127. }
  128. return [$name, $parameters];
  129. }
  130. /**
  131. * Handle the value returned from each pipe before passing it to the next.
  132. *
  133. * @param mixed $carry
  134. * @return mixed
  135. */
  136. protected function handleCarry($carry)
  137. {
  138. return $carry;
  139. }
  140. }