Pipeline.php 5.1KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193
  1. <?php
  2. namespace Illuminate\Pipeline;
  3. use Closure;
  4. use RuntimeException;
  5. use Illuminate\Http\Request;
  6. use Illuminate\Contracts\Container\Container;
  7. use Illuminate\Contracts\Support\Responsable;
  8. use Illuminate\Contracts\Pipeline\Pipeline as PipelineContract;
  9. class Pipeline implements PipelineContract
  10. {
  11. /**
  12. * The container implementation.
  13. *
  14. * @var \Illuminate\Contracts\Container\Container
  15. */
  16. protected $container;
  17. /**
  18. * The object being passed through the pipeline.
  19. *
  20. * @var mixed
  21. */
  22. protected $passable;
  23. /**
  24. * The array of class pipes.
  25. *
  26. * @var array
  27. */
  28. protected $pipes = [];
  29. /**
  30. * The method to call on each pipe.
  31. *
  32. * @var string
  33. */
  34. protected $method = 'handle';
  35. /**
  36. * Create a new class instance.
  37. *
  38. * @param \Illuminate\Contracts\Container\Container|null $container
  39. * @return void
  40. */
  41. public function __construct(Container $container = null)
  42. {
  43. $this->container = $container;
  44. }
  45. /**
  46. * Set the object being sent through the pipeline.
  47. *
  48. * @param mixed $passable
  49. * @return $this
  50. */
  51. public function send($passable)
  52. {
  53. $this->passable = $passable;
  54. return $this;
  55. }
  56. /**
  57. * Set the array of pipes.
  58. *
  59. * @param array|mixed $pipes
  60. * @return $this
  61. */
  62. public function through($pipes)
  63. {
  64. $this->pipes = is_array($pipes) ? $pipes : func_get_args();
  65. return $this;
  66. }
  67. /**
  68. * Set the method to call on the pipes.
  69. *
  70. * @param string $method
  71. * @return $this
  72. */
  73. public function via($method)
  74. {
  75. $this->method = $method;
  76. return $this;
  77. }
  78. /**
  79. * Run the pipeline with a final destination callback.
  80. *
  81. * @param \Closure $destination
  82. * @return mixed
  83. */
  84. public function then(Closure $destination)
  85. {
  86. $pipeline = array_reduce(
  87. array_reverse($this->pipes), $this->carry(), $this->prepareDestination($destination)
  88. );
  89. return $pipeline($this->passable);
  90. }
  91. /**
  92. * Get the final piece of the Closure onion.
  93. *
  94. * @param \Closure $destination
  95. * @return \Closure
  96. */
  97. protected function prepareDestination(Closure $destination)
  98. {
  99. return function ($passable) use ($destination) {
  100. return $destination($passable);
  101. };
  102. }
  103. /**
  104. * Get a Closure that represents a slice of the application onion.
  105. *
  106. * @return \Closure
  107. */
  108. protected function carry()
  109. {
  110. return function ($stack, $pipe) {
  111. return function ($passable) use ($stack, $pipe) {
  112. if (is_callable($pipe)) {
  113. // If the pipe is an instance of a Closure, we will just call it directly but
  114. // otherwise we'll resolve the pipes out of the container and call it with
  115. // the appropriate method and arguments, returning the results back out.
  116. return $pipe($passable, $stack);
  117. } elseif (! is_object($pipe)) {
  118. list($name, $parameters) = $this->parsePipeString($pipe);
  119. // If the pipe is a string we will parse the string and resolve the class out
  120. // of the dependency injection container. We can then build a callable and
  121. // execute the pipe function giving in the parameters that are required.
  122. $pipe = $this->getContainer()->make($name);
  123. $parameters = array_merge([$passable, $stack], $parameters);
  124. } else {
  125. // If the pipe is already an object we'll just make a callable and pass it to
  126. // the pipe as-is. There is no need to do any extra parsing and formatting
  127. // since the object we're given was already a fully instantiated object.
  128. $parameters = [$passable, $stack];
  129. }
  130. $response = method_exists($pipe, $this->method)
  131. ? $pipe->{$this->method}(...$parameters)
  132. : $pipe(...$parameters);
  133. return $response instanceof Responsable
  134. ? $response->toResponse($this->container->make(Request::class))
  135. : $response;
  136. };
  137. };
  138. }
  139. /**
  140. * Parse full pipe string to get name and parameters.
  141. *
  142. * @param string $pipe
  143. * @return array
  144. */
  145. protected function parsePipeString($pipe)
  146. {
  147. list($name, $parameters) = array_pad(explode(':', $pipe, 2), 2, []);
  148. if (is_string($parameters)) {
  149. $parameters = explode(',', $parameters);
  150. }
  151. return [$name, $parameters];
  152. }
  153. /**
  154. * Get the container instance.
  155. *
  156. * @return \Illuminate\Contracts\Container\Container
  157. * @throws \RuntimeException
  158. */
  159. protected function getContainer()
  160. {
  161. if (! $this->container) {
  162. throw new RuntimeException('A container instance has not been passed to the Pipeline.');
  163. }
  164. return $this->container;
  165. }
  166. }