Code Coverage |
||||||||||
Classes and Traits |
Functions and Methods |
Lines |
||||||||
Total | |
100.00% |
1 / 1 |
|
100.00% |
11 / 11 |
CRAP | |
100.00% |
56 / 56 |
ProcessHandler | |
100.00% |
1 / 1 |
|
100.00% |
11 / 11 |
25 | |
100.00% |
56 / 56 |
__construct | |
100.00% |
1 / 1 |
1 | |
100.00% |
3 / 3 |
|||
addOptions | |
100.00% |
1 / 1 |
1 | |
100.00% |
2 / 2 |
|||
run | |
100.00% |
1 / 1 |
1 | |
100.00% |
3 / 3 |
|||
start | |
100.00% |
1 / 1 |
1 | |
100.00% |
3 / 3 |
|||
startDetached | |
100.00% |
1 / 1 |
1 | |
100.00% |
5 / 5 |
|||
isRunning | |
100.00% |
1 / 1 |
2 | |
100.00% |
3 / 3 |
|||
getKey | |
100.00% |
1 / 1 |
1 | |
100.00% |
1 / 1 |
|||
getInfos | |
100.00% |
1 / 1 |
3 | |
100.00% |
10 / 10 |
|||
setEnv | |
100.00% |
1 / 1 |
1 | |
100.00% |
2 / 2 |
|||
createProcess | |
100.00% |
1 / 1 |
10 | |
100.00% |
18 / 18 |
|||
defaultCallback | |
100.00% |
1 / 1 |
3 | |
100.00% |
6 / 6 |
1 | <?php |
2 | |
3 | namespace Qmp\Laravel\AsyncProcessFactory; |
4 | |
5 | use Closure; |
6 | use Illuminate\Support\Collection; |
7 | use Illuminate\Support\Str; |
8 | use Qmp\Laravel\AsyncProcessFactory\Exceptions\SubProcessException; |
9 | use Symfony\Component\Process\Process; |
10 | |
11 | class ProcessHandler |
12 | { |
13 | /** |
14 | * The current Process |
15 | * |
16 | * @var Symfony\Component\Process\Process |
17 | */ |
18 | protected $process; |
19 | |
20 | /** |
21 | * The current command |
22 | * |
23 | * @var string |
24 | */ |
25 | protected $command; |
26 | |
27 | /** |
28 | * Wether the process is detached or not |
29 | * |
30 | * @var boolean |
31 | */ |
32 | protected $detached = false; |
33 | |
34 | /** |
35 | * The current command options |
36 | * |
37 | * @var array |
38 | */ |
39 | protected $options = []; |
40 | |
41 | /** |
42 | * The current callback callback |
43 | * |
44 | * @var callable |
45 | */ |
46 | protected $callback = null; |
47 | |
48 | /** |
49 | * The current process key in the stack |
50 | * |
51 | * @var string |
52 | */ |
53 | protected $key; |
54 | |
55 | /** |
56 | * Undocumented variable |
57 | * |
58 | * @var Collection |
59 | */ |
60 | protected $envs; |
61 | |
62 | |
63 | /** |
64 | * Create a new process Handler |
65 | * |
66 | * @param string $command |
67 | * @param string $key |
68 | */ |
69 | public function __construct(string $command) |
70 | { |
71 | $this->command = $command; |
72 | $this->key = bin2hex(random_bytes(6)); |
73 | } |
74 | |
75 | /** |
76 | * Add options to process |
77 | * |
78 | * @param array $options |
79 | * @return Qmp\Laravel\AsyncProcessFactory\ProcessHandler |
80 | */ |
81 | public function addOptions(array $options): ProcessHandler |
82 | { |
83 | $this->options = $options; |
84 | |
85 | return $this; |
86 | } |
87 | |
88 | /** |
89 | * Run Process one by one |
90 | * |
91 | * @param callable $callback |
92 | * @return void |
93 | */ |
94 | public function run(callable $callback = null): void |
95 | { |
96 | $this->callback = $callback; |
97 | $this->createProcess()->run(Closure::fromCallable([$this, 'defaultCallback'])); |
98 | } |
99 | |
100 | /** |
101 | * Start Process in parralel |
102 | * |
103 | * @param callable $callback |
104 | * @return void |
105 | */ |
106 | public function start(callable $callback = null): void |
107 | { |
108 | $this->callback = $callback; |
109 | $this->createProcess()->start(Closure::fromCallable([$this, 'defaultCallback'])); |
110 | } |
111 | |
112 | /** |
113 | * Start Process in parralel |
114 | * |
115 | * @param callable $callback |
116 | * @return boolean |
117 | */ |
118 | public function startDetached(): bool |
119 | { |
120 | $this->detached = true; |
121 | $this->createProcess()->start(); |
122 | |
123 | return $this->process->waitUntil(function ($type, $datas) { |
124 | return trim($datas) === 'process started'; |
125 | }); |
126 | } |
127 | |
128 | /** |
129 | * Check if the current Process isrunning |
130 | * |
131 | * @return boolean |
132 | */ |
133 | public function isRunning(): bool |
134 | { |
135 | if ($this->process) { |
136 | return $this->process->isRunning(); |
137 | } |
138 | return false; |
139 | } |
140 | |
141 | /** |
142 | * Return the current process key |
143 | * |
144 | * @return string |
145 | */ |
146 | public function getKey(): string |
147 | { |
148 | return $this->key; |
149 | } |
150 | |
151 | /** |
152 | * Return the current process informations |
153 | * |
154 | * @param bool|null $started |
155 | * @return Collection |
156 | */ |
157 | public function getInfos($started = null): Collection |
158 | { |
159 | $infos = collect([ |
160 | 'command' => $this->command, |
161 | 'options' => collect($this->options), |
162 | 'pid' => $this->process ? $this->process->getPid() : null, |
163 | 'task' => $this->key, |
164 | 'detached' => $this->detached, |
165 | 'envs' => $this->envs |
166 | ]); |
167 | |
168 | if ($started) { |
169 | $infos->put('started', $started); |
170 | } |
171 | |
172 | return $infos; |
173 | } |
174 | |
175 | /** |
176 | * Set environment vars |
177 | * |
178 | * @param Collection $envs |
179 | * @return self |
180 | */ |
181 | public function setEnv(Collection $envs) |
182 | { |
183 | $this->envs = $envs; |
184 | return $this; |
185 | } |
186 | |
187 | /** |
188 | * Create and return a new process |
189 | * |
190 | * @return Process |
191 | */ |
192 | protected function createProcess(): Process |
193 | { |
194 | $options = collect($this->options) |
195 | ->map(function ($value, $option) { |
196 | if (is_bool($value) && Str::startsWith($option, '*')) { |
197 | if ($value) { |
198 | return "--" . str_replace('*', '', $option); |
199 | } |
200 | return ''; |
201 | } |
202 | $value = is_bool($value) ? ($value ? 'true' : 'false') : $value; |
203 | return is_numeric($option) ? $value : "--$option=$value"; |
204 | }); |
205 | |
206 | if ($this->detached) { |
207 | $options->push('&'); |
208 | } |
209 | |
210 | $envs = ""; |
211 | |
212 | if ($this->envs && $this->envs->count()) { |
213 | $envs = $this->envs->implodePair("=", " ") . " "; |
214 | } |
215 | |
216 | $cmd = 'php ' . base_path('artisan') . " $this->command " . trim($options->implode(' ')); |
217 | $this->process = Process::fromShellCommandline($envs . $cmd); |
218 | |
219 | $this->process->setTimeout(0); |
220 | |
221 | return $this->process; |
222 | } |
223 | |
224 | /** |
225 | * The default callback for stdout/stderr |
226 | * |
227 | * @param string $type |
228 | * @param string $datas |
229 | * @return void |
230 | */ |
231 | protected function defaultCallback(string $type, string $datas): void |
232 | { |
233 | if (strtolower($type) === "err") { |
234 | throw new SubProcessException($datas, $this->key); |
235 | } |
236 | |
237 | if ($this->callback) { |
238 | $callback = $this->callback; |
239 | $callback($datas); |
240 | } |
241 | } |
242 | } |