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