Make reading results from Process execution easier
thekid opened this issue · 1 comments
Since php/php-src#5777, we have the possibility to use select()
on the streams returned by proc_open() on Windows by supplying ["socket"] in the descriptor spec, see https://reactphp.org/child-process/#windows-compatibility. We might be able to execute processes just like the shell does with this ability. However, to get this working in a platform-independent way, we might want to wrap the I/O operations inside a method of the lang.Process
class.
Example
class Process {
// ...
public function select() {
$read= [];
$this->out && $read[1]= $this->out->getHandle();
$this->err && $read[2]= $this->err->getHandle();
while ($read) {
$r= $read;
$w= null;
$e= null;
stream_select($r, $w, $e, null, 0);
foreach ($r as $i => $pipe) {
yield $i => fread($pipe, 8192);
if (feof($pipe)) unset($read[$i]);
}
}
}
}
Windows
Uses ["socket"], only works for PHP 8.0+
$ xp -e '$p= new lang\Process("ping", ["thekid.de"], null, null, [1 => ["socket"], 2 => ["socket"]]);
foreach ($p->select() as $pipe => $return) {
echo date("H:i:s")," @ #$pipe => ", false === $return
? "<EOF>\n"
: addcslashes(iconv("cp850", "utf-8", $return), "\0..\37")."\n"
;
}
$p->close()'
15:22:41 @ #1 => \r\nPing wird ausgeführt für thekid.de [217.160.0.184] mit 32 Bytes Daten:\r\n
15:22:41 @ #1 => Antwort von 217.160.0.184: Bytes=32 Zeit=34ms TTL=58\r\n
15:22:42 @ #1 => Antwort von 217.160.0.184: Bytes=32
15:22:42 @ #1 => Zeit=35ms TTL=58\r\n
15:22:43 @ #1 => Antwort von 217.160.0.184: Bytes=32 Zeit=34ms TTL=58\r\n
15:22:44 @ #1 => Antwort von 217.160.0.184: Bytes=32 Zeit=35ms TTL=58\r\n
15:22:44 @ #1 => \r\nPing-Statistik für 217.160.0.184:\r\n Pakete: Gesendet = 4, Empfangen = 4, Verloren = 0\r\n (0% Verlust),\r\nCa. Zeitangaben in Millisek.:\r\n Minimum = 34ms, Maximum = 35ms, Mittelwert = 34ms\r\n
15:22:44 @ #1 => <EOF>
15:22:44 @ #2 => <EOF>
UN*X
Uses ["pipe", "w"] as before ("socket" is not available, yielding proc_open(): socket is not a valid descriptor spec/mode)
$ xp -e '$p= new lang\Process("ping", ["-c", 4, "thekid.de"], null, null);
foreach ($p->select() as $pipe => $return) {
echo date("H:i:s")," @ #$pipe => ", "" === $return ? "<EOF>" : addcslashes($return, "\0..\37"), "\n";
}
$p->close()'
13:27:43 @ #1 => PING thekid.de (217.160.0.184) 56(84) bytes of data.\n64 bytes from 217-160-0-184.elastic-ssl.ui-r.com (217.160.0.184): icmp_seq=1 ttl=57 time=43.5 ms\n
13:27:44 @ #1 => 64 bytes from 217-160-0-184.elastic-ssl.ui-r.com (217.160.0.184): icmp_seq=2 ttl=57 time=35.1 ms\n
13:27:45 @ #1 => 64 bytes from 217-160-0-184.elastic-ssl.ui-r.com (217.160.0.184): icmp_seq=3 ttl=57 time=38.0 ms\n
13:27:46 @ #1 => 64 bytes from 217-160-0-184.elastic-ssl.ui-r.com (217.160.0.184): icmp_seq=4 ttl=57 time=35.2 ms\n\n--- thekid.de ping statistics ---\n4 packets transmitted, 4 received, 0% packet loss, time 3005ms\nrtt min/avg/max/mdev = 35.111/37.954/43.541/3.424 ms\n
13:27:46 @ #2 => <EOF>
13:27:46 @ #1 => <EOF>
Using sockets should also affect the *in*, *out* and *err* members' types from `io.File` to maybe `io.Stream` or `io.Handle` maybe.
This may also be done in a dedicated process control library, with the Process
class serving a low-level purpose here.