Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit acc9af5

Browse files
committed
update: update some proc util methods
1 parent 5044aad commit acc9af5

File tree

3 files changed

+440
-348
lines changed

3 files changed

+440
-348
lines changed

‎src/Proc/PcntlFunc.php

Lines changed: 284 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,284 @@
1+
<?php declare(strict_types=1);
2+
3+
namespace Toolkit\Sys\Proc;
4+
5+
use Closure;
6+
use RuntimeException;
7+
use Toolkit\Stdlib\Helper\Assert;
8+
use Toolkit\Stdlib\OS;
9+
use function function_exists;
10+
use function getmypid;
11+
use function pcntl_alarm;
12+
use function pcntl_async_signals;
13+
use function pcntl_fork;
14+
use function pcntl_signal;
15+
use function pcntl_signal_dispatch;
16+
use function pcntl_signal_get_handler;
17+
use function pcntl_sigwaitinfo;
18+
use function pcntl_waitpid;
19+
use function pcntl_wexitstatus;
20+
use function posix_getpid;
21+
use function posix_setsid;
22+
use function time;
23+
use function usleep;
24+
use const WNOHANG;
25+
26+
/**
27+
* class PcntlFunc
28+
*
29+
* @author inhere
30+
*/
31+
class PcntlFunc
32+
{
33+
/**
34+
* @return bool
35+
*/
36+
public static function hasPcntl(): bool
37+
{
38+
return !OS::isWindows() && function_exists('pcntl_fork');
39+
}
40+
41+
public static function assertPcntl(): void
42+
{
43+
if (!self::hasPcntl()) {
44+
throw new RuntimeException('the extension "pcntl" is required');
45+
}
46+
}
47+
48+
/**
49+
* get current process id
50+
*
51+
* @return int
52+
*/
53+
public static function getPid(): int
54+
{
55+
return function_exists('posix_getpid') ? posix_getpid() : getmypid();
56+
}
57+
58+
/**
59+
* install signal
60+
*
61+
* @param int $signal e.g: SIGTERM SIGINT(Ctrl+C) SIGUSR1 SIGUSR2 SIGHUP
62+
* @param callable $handler
63+
*
64+
* @return bool
65+
*/
66+
public static function installSignal(int $signal, callable $handler): bool
67+
{
68+
return pcntl_signal($signal, $handler, false);
69+
}
70+
71+
/**
72+
* dispatch signal
73+
*
74+
* @return bool
75+
*/
76+
public static function dispatchSignal(): bool
77+
{
78+
// receive and dispatch signal
79+
return pcntl_signal_dispatch();
80+
}
81+
82+
/**
83+
* Get the current handler for specified signal.
84+
*
85+
* @param int $signal
86+
*
87+
* @return resource|false
88+
*/
89+
public static function getSignalHandler(int $signal)
90+
{
91+
return pcntl_signal_get_handler($signal);
92+
}
93+
94+
/**
95+
* Enable/disable asynchronous signal handling or return the old setting
96+
*
97+
* @param bool|null $on bool - Enable or disable; null - Return old setting.
98+
*
99+
* @return bool
100+
*/
101+
public static function asyncSignal(bool $on = null): bool
102+
{
103+
return pcntl_async_signals($on);
104+
}
105+
106+
/**
107+
* @return int
108+
*/
109+
public static function clearAlarm(): int
110+
{
111+
return pcntl_alarm(-1);
112+
}
113+
114+
/**********************************************************************
115+
* create child process `pcntl`
116+
*********************************************************************/
117+
118+
/**
119+
* Fork/create a child process.
120+
*
121+
* **Example:**
122+
*
123+
* ```php
124+
* $info = ProcessUtil::fork(function(int $pid, int $id) {
125+
* // do something...
126+
*
127+
* // exit worker
128+
* exit(0);
129+
* });
130+
* ```
131+
*
132+
* @param callable(int, int):void $onStart Will running on the child process start.
133+
* @param null|callable(int):void $onForkError Call on fork process error
134+
* @param int $id The process index number. when use `forks()`
135+
*
136+
* @return array{id: int, pid: int, startAt: int}
137+
*/
138+
public static function fork(callable $onStart, callable $onForkError = null, int $id = 0): array
139+
{
140+
$info = [];
141+
$pid = pcntl_fork();
142+
143+
// at parent, get forked child info
144+
if ($pid > 0) {
145+
$info = [
146+
'id' => $id,
147+
'pid' => $pid, // child pid
148+
'startAt' => time(),
149+
];
150+
} elseif ($pid === 0) {
151+
// at child
152+
$workerPid = self::getPid();
153+
$onStart($workerPid, $id);
154+
} elseif ($onForkError) {
155+
$onForkError($pid);
156+
} else {
157+
throw new RuntimeException('Fork child process failed!');
158+
}
159+
160+
return $info;
161+
}
162+
163+
/**
164+
* Alias of fork()
165+
*
166+
* @param callable $onStart
167+
* @param callable|null $onError
168+
* @param int $id
169+
*
170+
* @return array|false
171+
* @see ProcessUtil::fork()
172+
*/
173+
public static function create(callable $onStart, callable $onError = null, int $id = 0): bool|array
174+
{
175+
return self::fork($onStart, $onError, $id);
176+
}
177+
178+
/**
179+
* Daemon, detach and run in the background
180+
*
181+
* @param Closure|null $beforeQuit
182+
*
183+
* @return int Return new process PID
184+
* @throws RuntimeException
185+
*/
186+
public static function daemonRun(Closure $beforeQuit = null): int
187+
{
188+
if (!self::hasPcntl()) {
189+
return 0;
190+
}
191+
192+
// umask(0);
193+
$pid = pcntl_fork();
194+
switch ($pid) {
195+
case 0: // at new process
196+
$pid = self::getPid();
197+
if (posix_setsid() < 0) {
198+
throw new RuntimeException('posix_setsid() execute failed! exiting');
199+
}
200+
201+
// chdir('/');
202+
// umask(0);
203+
break;
204+
case -1: // fork failed.
205+
throw new RuntimeException('Fork new process is failed! exiting');
206+
default: // at parent
207+
if ($beforeQuit) {
208+
$beforeQuit($pid);
209+
}
210+
exit(0);
211+
}
212+
213+
return $pid;
214+
}
215+
216+
/**
217+
* Alias of forks()
218+
*
219+
* @param int $number
220+
* @param callable $onStart
221+
* @param callable|null $onForkError
222+
*
223+
* @return array
224+
* @see ProcessUtil::forks()
225+
*/
226+
public static function multi(int $number, callable $onStart, callable $onForkError = null): array
227+
{
228+
return self::forks($number, $onStart, $onForkError);
229+
}
230+
231+
/**
232+
* fork/create multi child processes.
233+
*
234+
* @param int $number
235+
* @param callable $onStart Will running on the child processes.
236+
* @param callable|null $onForkError on fork process error
237+
*
238+
* @return array<int, array{id: int, pid: int, startTime: int}>
239+
* @throws RuntimeException
240+
*/
241+
public static function forks(int $number, callable $onStart, callable $onForkError = null): array
242+
{
243+
Assert::intShouldGt0($number, 'process number', true);
244+
245+
$pidAry = [];
246+
for ($id = 0; $id < $number; $id++) {
247+
$info = self::fork(function () use ($onStart) {
248+
$onStart();
249+
exit(0);
250+
}, $onForkError, $id);
251+
252+
// log
253+
$pidAry[$info['pid']] = $info;
254+
}
255+
256+
return $pidAry;
257+
}
258+
259+
/**
260+
* Wait all child processes exit.
261+
*
262+
* @param callable(int, int, int):void $onChildExit On child process exited callback.
263+
*/
264+
public static function wait(callable $onChildExit): void
265+
{
266+
$status = 0;
267+
268+
// pid < 0:子进程都没了
269+
// pid > 0:捕获到一个子进程退出的情况
270+
// pid = 0:没有捕获到退出的子进程
271+
while (($pid = pcntl_waitpid(-1, $status, WNOHANG)) >= 0) {
272+
if ($pid) {
273+
// TIP: get signal
274+
// $singal = pcntl_wifsignaled($status);
275+
276+
// handler(int $pid, int $exitCode, int $status)
277+
$onChildExit($pid, pcntl_wexitstatus($status), $status);
278+
} else {
279+
// sleep 50ms
280+
usleep(50000);
281+
}
282+
}
283+
}
284+
}

‎src/Proc/ProcFunc.php

Lines changed: 37 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,10 +10,13 @@
1010
namespace Toolkit\Sys\Proc;
1111

1212
use RuntimeException;
13+
use function array_keys;
14+
use function fclose;
1315
use function proc_close;
1416
use function proc_get_status;
1517
use function proc_open;
1618
use function proc_terminate;
19+
use function stream_get_contents;
1720

1821
/**
1922
* Class ProcFunc
@@ -55,14 +58,47 @@ public static function open(
5558
*
5659
* @param resource $process
5760
*
58-
* @return array
61+
* @return array{command:string,pid:int,running:bool,signaled:bool,stopped:bool,exitcode:int,termsig:int,stopsig:int}
5962
* @see https://www.php.net/manual/en/function.proc-get-status.php
6063
*/
6164
public static function getStatus($process): array
6265
{
6366
return proc_get_status($process);
6467
}
6568

69+
/**
70+
* @param resource $pipe
71+
*
72+
* @return string
73+
*/
74+
public static function readClosePipe($pipe): string
75+
{
76+
return self::readPipe($pipe, true);
77+
}
78+
79+
/**
80+
* @param resource $pipe
81+
* @param bool $close
82+
*
83+
* @return string
84+
*/
85+
public static function readPipe($pipe, bool $close = false): string
86+
{
87+
$output = stream_get_contents($pipe);
88+
$close && fclose($pipe);
89+
return $output;
90+
}
91+
92+
/**
93+
* @param array $pipes
94+
*/
95+
public static function closePipes(array $pipes): void
96+
{
97+
foreach ($pipes as $pipe) {
98+
fclose($pipe);
99+
}
100+
}
101+
66102
/**
67103
* Close a process opened by `proc_open` and return the exit code of that process
68104
*

0 commit comments

Comments
(0)

AltStyle によって変換されたページ (->オリジナル) /