Description
Hello,
I wanted to implement signal
and getPid
methods for 2.2 (see #5476).
- Posix signal are very interesting in case of timeout ; when you ask your process to stop, you could specify a timeout when a
SIGKILL
could be sent. (And I'm sure you got lots of ideas to use them other ways) - Pid can be useful for interactions with other programs.
The Story :
There are two functions to implement these methods : proc_get_status
and proc_terminate
.
Actually, these methods might not work as expected inside the Process component because PHP might not run the command directly, but wraps it with sh
(described in #5030).
Reminder
Chained commands
The Process component can handle such command :
new Process('git branch -a && git fetch upstream');
Inside Process, it can be summarized as :
$descriptorspec = array(array("pipe", "r"), array("pipe", "w"), array("pipe", "a"));
$process = proc_open('git branch -a && git fetch upstream', $descriptorspec, $pipes);
The sh wrapper
Let's run a command and let's ask for the Pid
$descriptorspec = array(array("pipe", "r"), array("pipe", "w"), array("pipe", "a"));
$process = proc_open('sleep 3', $descriptorspec, $pipes);
$status = proc_get_status($process);
echo $status['pid'];
will result in :
15888
That can be monitored :
grosroro 15886 1.0 0.0 254140 16164 pts/0 S+ 23:19 0:00 | \_ php proc.php
grosroro 15888 0.0 0.0 3940 580 pts/0 S+ 23:19 0:00 | \_ sh -c sleep 3
grosroro 15889 0.0 0.0 6748 568 pts/0 S+ 23:19 0:00 | \_ sleep 3
The actual Pid is 15889
, not 15888
; The status returned by proc_get_status is not the one we expected (the status of our command), but the one of the sh wrapper.
It is the same behavior with proc_terminate
; the signal is sent to the sh
wrapper instead of the command.
The problem
This leads us to two conclusions :
- All the methods that returns results or use results probed by
proc_get_status
might be wrong (hasBeenSignaled
,getTermSignal
,hasBeenStopped
,getStopSignal
,stop
). - We can not currently easily signal a process or retrieve the pid in the current design.
The solution
The only solution found is to prepend the command with [exec](https://en.wikipedia.org/wiki/Exec_(operating_system\)) :
$descriptorspec = array(array("pipe", "r"), array("pipe", "w"), array("pipe", "a"));
$process = proc_open('exec sleep 3', $descriptorspec, $pipes);
$status = proc_get_status($process);
echo $status['pid'];
will result in :
9441
That can be monitored :
grosroro 9440 1.0 0.0 254140 16188 pts/0 S+ 23:48 0:00 | \_ php proc.php
grosroro 9441 0.0 0.0 6748 564 pts/0 S+ 23:48 0:00 | \_ sleep 3
proc_terminate
and proc_get_status
works as expected in this case.
Backward Compatibility
This solution is quite nice, but it leads to another issue :
This was working well :
$descriptorspec = array(array("pipe", "r"), array("pipe", "w"), array("pipe", "a"));
$process = proc_open('echo "hello" && echo "world"', $descriptorspec, $pipes);
// echoes "hello\nworld";
echo fgets($pipes[1], 4096);
This is not working as expected :
$descriptorspec = array(array("pipe", "r"), array("pipe", "w"), array("pipe", "a"));
$process = proc_open('exec echo "hello" && echo "world"', $descriptorspec, $pipes);
// echoes "hello";
echo fgets($pipes[1], 4096);
Proposals
Parse command line and allow pid/signal access to non-chained commands
this would prepend command line with "exec" only for atomic commands.
- pros : preserve BC 2.1
- cons : Lots of methods might not work as expected (
hasBeenSignaled
,getTermSignal
,hasBeenStopped
,getStopSignal
,stop
)
Split commands in atomic subprocesses / add a chain
method / enforce use of ProcessBuilder
- pros : guarantee the control control over the processes, adds multiple possibilities for chaining strategies), fixes
- cons : breaks the BC with 2.1, disable the ability to run chained commands in the background (There is no way to add a callback when a process finish to start the next one)
Do you have any thoughts about this ?