Read STDOUT and STDERR in non blocking way on proc_open

<?php

// The command to run.
$command = 'ping 127.0.0.1';

$descriptors = [
    0 => ['pipe', 'r'], // STDIN
    1 => ['pipe', 'w'], // STDOUT
    2 => ['pipe', 'w'], // STDERR
];

$process = proc_open($command, $descriptors, $pipes);
if (is_resource($process)) {
    fclose($pipes[0]);

    $read_output = $read_error = false;
    $buffer_len  = $prev_buffer_len = 0;
    $ms          = 10;
    $output      = '';
    $read_output = true;
    $error       = '';
    $read_error  = true;
    stream_set_blocking($pipes[1], 0);
    stream_set_blocking($pipes[2], 0);

    // Dual reading of STDOUT and STDERR stops one full pipe blocking
    // the other because the external script is waiting
    while ($read_error != false or $read_output != false) {
        if ($read_output != false) {
            if (feof($pipes[1])) {
                fclose($pipes[1]);
                $read_output = false;
            } else {
                // STDOUT line.
                // Check conditions here.
                $str = trim(fgets($pipes[1]));
                $len = strlen($str);
                if ($len) {
                    $buffer_len += $len;
                }
            }
        }
       
        if ($read_error != false) {
            if (feof($pipes[2])) {
                fclose($pipes[2]);
                $read_error = false;
            } else {
                // STDERR line.
                // Check conditions here.
                $str = trim(fgets($pipes[2]));
                $len = strlen($str);
                if ($len) {
                    $buffer_len += $len;
                }
            }
        }
       
        if ($buffer_len > $prev_buffer_len) {
            $prev_buffer_len = $buffer_len;
            $ms = 10;
        } else {
            usleep($ms * 1000); // sleep for $ms milliseconds
            if ($ms < 160) {
                $ms = $ms * 2;
            }
        }
    }

    proc_close($process);
}
Tags