Тайм-аут выполнения процесса PHP - PullRequest
2 голосов
/ 15 марта 2011

У меня есть следующий код:

/**
 * Executes a program and waits for it to finish, taking pipes into account.
 * @param string $cmd Command line to execute, including any arguments.
 * @param string $input Data for standard input.
 * @param integer $timeout How much to wait from program in msecs (-1 to wait indefinitely).
 * @return array Array of "stdout", "stderr" and "return".
 */
function execute($cmd,$stdin=null,$timeout=-1){
    $proc=proc_open(
        $cmd,
        array(array('pipe','r'),array('pipe','w'),array('pipe','w')),
        $pipes=null
    );
    fwrite($pipes[0],$stdin);                  fclose($pipes[0]);
    $stdout=stream_get_contents($pipes[1]);    fclose($pipes[1]);
    $stderr=stream_get_contents($pipes[2]);    fclose($pipes[2]);
    $return=proc_close($proc);
    return array(
        'stdout' => $stdout,
        'stderr' => $stderr,
        'return' => $return
    );
}

У него две "проблемы".

  • Код является синхронным;он зависает до тех пор, пока целевой процесс не закроется.
  • Пока что я не смог его "заморозить" без выполнения команд другого типа (таких как $cmd > /dev/null & в linux и start /B $cmd в windows)

Я вообще не против "заморозки".Мне просто нужно реализовать это время ожидания.

Примечание: Важно, чтобы решение было кросс-платформенным.Также важно, чтобы $cmd не нужно было менять - я выполняю некоторые сложные команды и боюсь, что могут быть некоторые проблемы, однако это зависит от типа исправления - я рад слышатьэто только то, что я бы предпочел другую альтернативу.

Я нашел несколько ресурсов, которые могут помочь:

Ответы [ 3 ]

2 голосов
/ 06 мая 2013

В коде есть несколько ошибок.

Это действительно работает:

function execute($cmd, $stdin = null, $timeout = -1)
{
    $proc=proc_open(
        $cmd,
        array(array('pipe','r'), array('pipe','w'), array('pipe','w')),
        $pipes
    );
    var_dump($pipes);
    if (isset($stdin))
    {
        fwrite($pipes[0],$stdin);
    }
    fclose($pipes[0]);

    stream_set_timeout($pipes[1], 0);
    stream_set_timeout($pipes[2], 0);

    $stdout = '';

    $start = microtime();

    while ($data = fread($pipes[1], 4096))
    {
        $meta = stream_get_meta_data($pipes[1]);
        if (microtime()-$start>$timeout) break;
        if ($meta['timed_out']) continue;
        $stdout .= $data;
    }

    $stdout .= stream_get_contents($pipes[1]);
    $stderr = stream_get_contents($pipes[2]);
    $return = proc_close($proc);

    return array(
        'stdout' => $stdout,
        'stderr' => $stderr,
        'return' => $return
    );
}
1 голос
/ 16 марта 2011

Вместо stream_get_contents вы можете использовать fread, чтобы получить более точный контроль над тем, что делает ваш код.Это в сочетании с stream_set_timeout может дать вам то, что вы ищете.

Я бросил что-то вместе, чтобы продемонстрировать, что, как я думал, может сработать - этот код полностью не проверен и не имеетгарантии, но может отправить вас в правильном направлении.;)

function execute($cmd,$stdin=null,$timeout=-1){
    $proc=proc_open(
        $cmd,
        array(array('pipe','r'),array('pipe','w'),array('pipe','w')),
        $pipes=null
    );
    fwrite($pipes[0],$stdin);                  fclose($pipes[0]);

    stream_set_timeout($pipes[1], 0);
    stream_set_timeout($pipes[2], 0);

    $stdout = '';

    $start = microtime();

    while ($data = fread($pipes[1], 4096))
    {
        $meta = stream_get_meta_data($pipes[1]);
        if (microtime()-$start>$timeout) break;
        if ($meta['timed_out']) continue;
        $stdout .= $data;
    }

    $return = proc_close($proc);
    $stdout .= stream_get_contents($pipes[1]);
    $stderr = stream_get_contents($pipes[2]);

    return array(
        'stdout' => $stdout,
        'stderr' => $stderr,
        'return' => $return
    );
}
0 голосов
/ 12 декабря 2016

Это, кажется, работает для меня:

public function toPDF() {
    $doc = $this->getDocument();

    $descriptor = [
        ['pipe','r'],
        ['pipe','w'],
        ['file','/dev/null','w'], // STDERR
    ];
    $proc = proc_open('/usr/local/project/scripts/dompdf_cli.php',$descriptor,$pipes,sys_get_temp_dir());
    fwrite($pipes[0],"$doc[paper]\n$doc[html]");
    fclose($pipes[0]);

    $timeout = 30;

    stream_set_blocking($pipes[1], false);

    $pdf = '';

    $now = microtime(true);

    try {
        do {
            $elapsed = microtime(true) - $now;

            if($elapsed > $timeout) {
                throw new \Exception("PDF generation timed out after $timeout seconds");
            }
            $data = fread($pipes[1], 4096);
            if($data === false) {
                throw new \Exception("Read failed");
            }
            if(strlen($data) === 0) {
                usleep(50);
                continue;
            }
            $pdf .= $data;
        } while(!feof($pipes[1]));

        fclose($pipes[1]);
        $ret = proc_close($proc);
    } catch(\Exception $ex) {
        fclose($pipes[1]);
        proc_terminate($proc); // proc_close tends to hang if the process is timing out
        throw $ex;
    } 


    if($ret !== 0) {
        throw new \Exception("dompdf_cli returned non-zero exit status: $ret");
    }

    // dump('returning pdf');
    return $pdf;
}

Я не уверен, какова цель stream_set_timeout - это просто устанавливает тайм-аут на чтение, но если вы хотите ограничитьобщее время, вы просто должны установить поток в неблокирующем режиме, а затем время, сколько это займет.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...