$pipes File pointers. */ public function __construct(mixed $process, array $pipes) { $this->process = $process; $status = proc_get_status($this->process); $this->id = $status['pid']; $this->stdin = array_key_exists(0, $pipes) ? new ChildStdin($pipes[0]) : null; $this->stdout = array_key_exists(1, $pipes) ? new ChildStdout($pipes[1]) : null; $this->stderr = array_key_exists(2, $pipes) ? new ChildStderr($pipes[2]) : null; } /** * Returns the OS-assigned process identifier associated with this child. */ public function id(): int { return $this->id; } /** * Waits for the child to exit completely, returning the status that it * exited with. This function will continue to have the same return value * after it has been called at least once. * * The stdin handle to the child process, if any, will be closed before * waiting. This helps avoid deadlock: it ensures that the child does not * block waiting for input from the parent, while the parent waits for the * child to exit. * * @throws ChildException If the resource was already closed */ public function wait(): ExitStatus { if ($this->status) { return $this->status; } if (!is_resource($this->process)) { throw new ChildException('Resource was already closed'); } // Avoid possible deadlock before waiting. $this->stdin?->close(); $status = proc_get_status($this->process); while ($status['running']) { // Suboptimal, but it is what it is... usleep(50); $status = proc_get_status($this->process); }; proc_close($this->process); return $this->status = new ExitStatus( $status['exitcode'], $status['signaled'] ? $status['termsig'] : null, $status['stopped'] ? $status['stopsig'] : null, ); } /** * Simultaneously waits for the child to exit and collect all remaining * output on the stdout/stderr handles, returning an {@see Output} instance. * * The stdin handle to the child process, if any, will be closed before * waiting. This helps avoid deadlock: it ensures that the child does not * block waiting for input from the parent, while the parent waits for the * child to exit. * * By default, stdin, stdout and stderr are inherited from the parent. In * order to capture the output it is necessary to create new pipes between * parent and child. Use the `stdout` and `stderr` functions of {@see * Command}, respectively. * * @throws ChildException If the resource was already closed */ public function waitWithOutput(): Output { if (!is_resource($this->process)) { throw new ChildException('Resource was already closed'); } // Avoid possible deadlock before waiting. $this->stdin?->close(); $stdout = $this->stdout?->getContents(); $stderr = $this->stderr?->getContents(); $status = $this->wait(); return new Output($stdout, $stderr, $status); } /** * Forces the child process to exit. * * This is equivalent to sending a SIGKILL. */ public function kill(): bool { if (!is_resource($this->process)) { return true; } return proc_terminate($this->process, 9); } public function __destruct() { if (is_resource($this->process)) { proc_close($this->process); } } }