Web

Как запустить процесс с выводом в реальном времени в PHP

Я пытаюсь запустить процесс на веб-странице, который будет возвращать свой вывод в реальном времени. Например, если я запускаю процесс 'ping', он должен обновлять мою страницу каждый раз, когда возвращается результат операции (сейчас, когда я использую exec(command, output), я вынужден использовать опцию -c и ждать окончания процесса, чтобы увидеть вывод на моей веб-странице). Возможно ли это сделать в php? Мне также интересно, как правильно завершить такой процесс, когда кто-то покидает страницу. В случае с процессом 'ping' я все еще могу видеть запущенный процесс в системном мониторе (что не имеет смысла).

 

Ответ 1

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

$cmd = "ping 127.0.0.1";

$descriptorspec = array(

   0 => array("pipe", "r"),   // stdin это пайп, из которого дочерний процесс будет читать

   1 => array("pipe", "w"),   // stdout – этот пайп, в который дочерний процесс будет писать

   2 => array("pipe", "w")    // stderr – это пайп в который дочерний процесс будет выводить ошибки (если будут)

);

flush();

$process = proc_open($cmd, $descriptorspec, $pipes, realpath('./'), array());

echo "<pre>";

if (is_resource($process)) {

    while ($s = fgets($pipes[1])) {

        print $s;

        flush();

    }

}

echo "</pre>";

 

 Ответ 2

Вот хороший способ показать вывод команд оболочки в реальном времени:

<?php

header("Content-type: text/plain");

// сообщите php, чтобы он автоматически фильтровал данные после каждого вывода.

// включая строки вывода, производимые командами shell

disable_ob();

 

$command = 'rsync -avz /your/directory1 /your/directory2';

system($command);

 Эта функция понадобится вам для предотвращения буферизации вывода:

function disable_ob() {

    // Выключить буферизацию вывода

    ini_set('output_buffering', 'off');

    // Отключить сжатие вывода PHP

    ini_set('zlib.output_compression', false);

    // Неявная очистка буфера(ов)

    ini_set('implicit_flush', true);

    ob_implicit_flush(true);

    // Очистить и выключить буферизацию вывода

    while (ob_get_level() > 0) {

        // Получить текущий уровень

        $level = ob_get_level();

        // Завершение буферизации

        ob_end_clean();

        // Если текущий уровень не изменился, прервать работу

        if (ob_get_level() == $level) break;

    }

    // Отключить буферизацию/компрессию вывода apache

    if (function_exists('apache_setenv')) {

        apache_setenv('no-gzip', '1');

        apache_setenv('dont-vary', '1');

    }

}

 Однако этот код не работает на всех серверах, на которых я его пробовал. Вот фиктивный пример на обычном PHP:

<?php

header("Content-type: text/plain");

disable_ob();

for($i=0;$i<10;$i++)  {

    echo $i . "\n";

    usleep(300000);

}

 

 Ответ 3

Для использования в командной строке:

function execute($cmd) {

    $proc = proc_open($cmd, [['pipe','r'],['pipe','w'],['pipe','w']], $pipes);

    while(($line = fgets($pipes[1])) !== false) {

        fwrite(STDOUT,$line);

    }

    while(($line = fgets($pipes[2])) !== false) {

        fwrite(STDERR,$line);

    }

    fclose($pipes[0]);

    fclose($pipes[1]);

    fclose($pipes[2]);

    return proc_close($proc);

}

 Если вы пытаетесь запустить файл, вам может потребоваться сначала предоставить ему разрешения на выполнение:

chmod('/path/to/script',0755);

 


Ответ 4

Попробуйте следующее (проверено на машине Windows + сервер wamp):

       header('Content-Encoding: none;');

        set_time_limit(0);

        $handle = popen("<<< Your Shell Command >>>", "r");

        if (ob_get_level() == 0) 

            ob_start();

        while(!feof($handle)) {

            $buffer = fgets($handle);

            $buffer = trim(htmlspecialchars($buffer));

            echo $buffer . "<br />";

            echo str_pad('', 4096);    

            ob_flush();

            flush();

            sleep(1);

        }

        pclose($handle);

        ob_end_flush();

 

Ответ 5

Я пробовал различные команды выполнения PHP под Windows и обнаружил, что они довольно сильно различаются:

  1. Не работают для потоковой передачи: shell_exec, exec, passthru.

  2. Вроде как работают: proc_open, popen -- «вроде как», потому что вы не можете передать аргументы команде (т. е. не будет работать с my.exe --something, будет работать с _my_something.bat).

Лучший (самый простой) подход заключается в следующем:

  1. Вы должны убедиться, что ваш exe выполняет фильтрацию команд (см. проблему фильтрации printf). Без этого вы, скорее всего, будете получать порции текста размером около 4096 байт, что бы вы ни делали.

  2. Если можете, используйте header('Content-Type: text/event-stream'); (вместо header('Content-Type: text/plain; charset=...');). Однако это будет работать не во всех браузерах/клиентах! Потоковая передача будет работать и без этого, но, по крайней мере, первые строки будут буферизироваться браузером.

  3. Вы также можете отключить кэширование header('Cache-Control: no-cache');.

  4. Отключите буферизацию вывода (либо в php.ini, либо с помощью ini_set('output_buffering', 'off');). Это также может быть необходимо сделать в Apache/Nginx/каком-либо другом сервере, который вы используете.

  5. Отключение сжатия (либо в php.ini, либо с помощью ini_set('zlib.output_compression', false);). Это также может быть необходимо сделать в Apache/Nginx/в любом другом сервере, который вы используете.

Итак, в вашей программе на C++ вы делаете что-то вроде этого (опять же, для других решений смотрите printf flushing problem):

Logger::log(...) {

  printf (text);

  fflush(stdout);

}

В PHP же необходимо сделать что-то вроде этого:

function setupStreaming() {

    // Выключить буферизацию вывода

    ini_set('output_buffering', 'off');

   // Отключить сжатие вывода PHP

    ini_set('zlib.output_compression', false);

    // Отключить буферизацию/компрессию вывода Apache

    if (function_exists('apache_setenv')) {

        apache_setenv('no-gzip', '1');

        apache_setenv('dont-vary', '1');

    }

}

function runStreamingCommand($cmd){

    echo "\nrunning $cmd\n";

    system($cmd);

}

...

setupStreaming();

runStreamingCommand($cmd);

 

 Ответ 6

Сначала проверьте, работает ли у вас flush(). Если да хорошо, если нет, то, скорее всего, веб-сервер по какой-то причине буферизирует вывод, например, включен mod_gzip.

 Для чего-то вроде ping самая простая техника — зациклиться в PHP, выполняя «ping -c 1» несколько раз и вызывая flush() после каждого вывода. Если PHP настроен на прерывание при закрытии HTTP-соединения пользователем (что обычно происходит по умолчанию, или вы можете вызвать ignore_user_abort(false), чтобы убедиться в этом), то вам не нужно беспокоиться о запущенных процессах ping. Если вам действительно необходимо запустить дочерний процесс только один раз и отображать его вывод постоянно, то это может быть сложнее — вероятно, вам придется запускать его в фоновом режиме, перенаправлять вывод в поток, а затем заставлять PHP эхом возвращать этот поток пользователю, перемежая его регулярными вызовами flush().

 

Ответ 7

Если вы хотите выполнять системные команды через PHP, посмотрите документацию по exec.

Однако я бы не рекомендовал делать это на сайте с высокой посещаемостью, поскольку форк процесса для каждого запроса это довольно тяжелый процесс. Некоторые программы предоставляют возможность записывать идентификатор процесса в файл, чтобы вы могли проверить и завершить процесс по желанию, но для таких команд, как ping, я не уверен, что это возможно. Проверьте страницы руководства.

Возможно, вам лучше просто открыть сокет на порту, который вы ожидаете прослушивать (IE: порт 80 для HTTP) на удаленном хосте; таким образом, вы будете знать, что все идет хорошо как в пользовательской среде, так и в сети. Если вы пытаетесь вывести двоичные данные, посмотрите на функцию заголовков php и убедитесь, что вы установили правильный content-type и content-disposition. Просмотрите документацию для получения дополнительной информации об использовании/отключении буфера вывода.

Схожие статьи

Web

Доступ к $_COOKIE сразу после setcookie()

Web

Отношения Laravel: попытка получить "описание" свойства не-объекта

Web

Использование файла .php для создания дампа MySQL

Web

Что это — stdin, stdout и stderr?

×