Подтвердить что ты не робот

Запустить процесс с выходом в реальном времени в PHP

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

Мне также интересно, какой правильный способ убить такой процесс, когда кто-то покидает страницу. В случае процесса "ping" я все еще могу видеть процесс, выполняющийся на системном мониторе (что имеет смысл).

4b9b3361

Ответ 1

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

$cmd = "ping 127.0.0.1";

$descriptorspec = array(
   0 => array("pipe", "r"),   // stdin is a pipe that the child will read from
   1 => array("pipe", "w"),   // stdout is a pipe that the child will write to
   2 => array("pipe", "w")    // stderr is a pipe that the child will write to
);
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");

// tell php to automatically flush after every output
// including lines of output produced by shell commands
disable_ob();

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

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

function disable_ob() {
    // Turn off output buffering
    ini_set('output_buffering', 'off');
    // Turn off PHP output compression
    ini_set('zlib.output_compression', false);
    // Implicitly flush the buffer(s)
    ini_set('implicit_flush', true);
    ob_implicit_flush(true);
    // Clear, and turn off output buffering
    while (ob_get_level() > 0) {
        // Get the curent level
        $level = ob_get_level();
        // End the buffering
        ob_end_clean();
        // If the current level has not changed, abort
        if (ob_get_level() == $level) break;
    }
    // Disable apache output buffering/compression
    if (function_exists('apache_setenv')) {
        apache_setenv('no-gzip', '1');
        apache_setenv('dont-vary', '1');
    }
}

Он не работает на каждом сервере, на котором я его пробовал, хотя хотел бы посоветовать, что искать в вашей конфигурации php, чтобы определить, нужно ли вам вытаскивать волосы, пытаясь получить этот тип поведение для работы на вашем сервере! Кто-нибудь еще знает?

Вот фиктивный пример в простом PHP:

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

disable_ob();

for($i=0;$i<10;$i++) 
{
    echo $i . "\n";
    usleep(300000);
}

Надеюсь, это поможет другим людям, которые отправились сюда по пути.

Ответ 3

попробуйте это (протестировано на компьютере Windows + wamp server)

        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();

Ответ 4

Более эффективное решение этой старой проблемы с использованием современных событий на стороне сервера HTML5 описано здесь:

http://www.w3schools.com/html/html5_serversentevents.asp


Пример:

http://sink.agiletoolkit.org/realtime/console

Код: https://github.com/atk4/sink/blob/master/admin/page/realtime/console.php#L40

(Реализован как модуль в инфраструктуре Agile Toolkit)

Ответ 5

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

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

Вам может быть лучше обслуживать, просто открыв сокет на порту, который вы ожидаете прослушивать (IE: порт 80 для HTTP) на удаленном хосте, таким образом, вы знаете, что все идет хорошо в userland, а также на сеть.

Если вы пытаетесь вывести двоичные данные в функцию заголовка php и убедитесь, что вы выбрали правильный content-type и контент-диспозиция. Просмотрите документацию, для получения дополнительной информации об использовании/отключении выходного буфера.

Ответ 6

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

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

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

Ответ 7

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

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);

Ответ 8

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

  • Не работает для потоковой передачи: shell_exec, exec, passthru
  • Вид работ: proc_open, popen - "вид", потому что вы не можете передавать аргументы своей команде (т.е. не работать с my.exe --something, будет работать с _my_something.bat).

Самый лучший (простой) подход:

  • Вы должны убедиться, что ваш exe выполняет команды очистки (см. проблема с очисткой печати). Без этого вы, скорее всего, получите партии из примерно 4096 байтов текста, что бы вы ни делали.
  • Если вы можете, используйте header('Content-Type: text/event-stream'); (вместо header('Content-Type: text/plain; charset=...');). Однако это не будет работать во всех браузерах/клиентах! Потоковая передача будет работать без этого, но по крайней мере первые строки будут буферизованы браузером.
  • Вы также можете отключить кеш header('Cache-Control: no-cache');.
  • Отключите буферизацию вывода (либо в php.ini, либо с помощью ini_set('output_buffering', 'off');). Это также может быть сделано в Apache/Nginx/любом сервере, который вы используете спереди.
  • Поворот сжатия (либо в php.ini, либо с помощью ini_set('zlib.output_compression', false);). Это также может быть сделано в Apache/Nginx/любом сервере, который вы используете спереди.

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

Logger::log(...) {
  printf (text);
  fflush(stdout);
}

В PHP вы делаете что-то вроде:

function setupStreaming() {
    // Turn off output buffering
    ini_set('output_buffering', 'off');
    // Turn off PHP output compression
    ini_set('zlib.output_compression', false);
    // Disable Apache output buffering/compression
    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);

Ответ 9

Проверено все ответы, ничего не работает...

Найденное решение Здесь

Он работает на windows (я думаю, что этот ответ полезен для пользователей, ищущих там)

<?php
$a = popen('ping www.google.com', 'r'); 

while($b = fgets($a, 2048)) { 
echo $b."<br>\n"; 
ob_flush();flush(); 
} 
pclose($a); 

?>

Ответ 10

Попробуйте изменить набор файлов php.ini "output_buffering = Off". Вы должны иметь возможность получать вывод в реальном времени на странице Использовать системную команду вместо команды exec.., система сбросит вывод