it-swarm.com.ru

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

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

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

62
Maksim Vi.

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

$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>";
85
egafni

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

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

Я надеюсь, что это поможет другим, кто погуглил их путь здесь.

35
Robb

Лучшее решение этой старой проблемы с использованием современных событий на стороне сервера 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)

6
romaninsh

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

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

Это работает на Windows (я думаю, что этот ответ полезен для пользователей, которые ищут там)

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

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

?>
4
Pixsa

попробуйте это (проверено на 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();
4
raminious

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

  • Не работать для потоковой передачи: Shell_exec, exec, passthru
  • Вид работ: 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 ):

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

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

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

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

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

2
Nathacof

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

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

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

2
Todd Owen

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

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

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

0
Yatin

почему бы просто не направить вывод в файл журнала, а затем использовать этот файл для возврата содержимого клиенту. не совсем в режиме реального времени, но, возможно, достаточно хорошо?

0
greenone83