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

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

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

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

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

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

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

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

попробуйте это (протестировано на машине 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(); 

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

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

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

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

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

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

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

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

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

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

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

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

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

  1. Вы должны убедиться, что ваш exe – команды промывки (см. Проблему с промывкой при печати ). Без этого вы, скорее всего, получите партии из примерно 4096 байтов текста, что бы вы ни делали.
  2. Если вы можете, используйте header('Content-Type: text/event-stream'); (вместо header('Content-Type: text/plain; charset=...'); ). Однако это не будет работать во всех браузерах / клиентах! Потоковая передача будет работать без этого, но по крайней мере первые строки будут буферизованы браузером.
  3. Вы также можете отключить header('Cache-Control: no-cache'); кэша 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); 

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

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

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

 <?php $a = popen('ping www.google.com', 'r'); while($b = fgets($a, 2048)) { echo $b."<br>\n"; ob_flush();flush(); } pclose($a); ?> 

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