Запустить процесс с выводом в реальном времени в PHP
Я пытаюсь запустить процесс на веб-странице, которая будет возвращать свои выходные данные в режиме реального времени. Например, если я запускаю процесс "ping", он должен обновлять мою страницу каждый раз, когда он возвращает новую строку (прямо сейчас, когда я использую exec(command, output), я вынужден использовать опцию-c и ждать, пока процесс не завершится, чтобы увидеть вывод на моей веб-странице). Можно ли это сделать в php?
Мне также интересно, Как правильно убить этот процесс, когда кто-то покидает страницу. В случае процесс "ping" я все еще могу видеть процесс, запущенный на системном мониторе (что имеет смысл).
11 ответов:
это сработало для меня:
$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 machine + 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 Инструментарий framework)
я пробовал различные команды выполнения PHP В Windows и обнаружил, что они довольно сильно отличаются.
- не работает для потокового:
shell_exec,exec,passthru- работает:
proc_open,popen-- "вид", потому что вы не можете передать аргументы своей команде (т. е. не будете работать сmy.exe --something, будет работать с_my_something.bat).лучший (самый простой) подход:
- вы должны убедиться, что ваш exe промывает команды (см. проблема промывки printf). Без этого вы, скорее всего, получите пакеты около 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 / любом сервере, который вы используете спереди.Итак, в вашей программе на 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);
проверил все ответы, ничего не работает...
нашли решение здесь
он работает на windows (я думаю, что этот ответ полезен для пользователей, ищущих там)
<?php $a = popen('ping www.google.com', 'r'); while($b = fgets($a, 2048)) { echo $b."<br>\n"; ob_flush();flush(); } pclose($a); ?>
сначала проверьте, есть ли flush () работает для вас. Если это так, хорошо, если это не так, это, вероятно, означает, что веб-сервер буферизуется по какой-то причине, например mod_gzip включен.
для чего-то вроде ping самый простой метод-это цикл в PHP, запуск "ping-c 1" несколько раз и вызов flush() после каждого вывода. Предполагая, что PHP настроен на прерывание, когда HTTP-соединение закрыто пользователем (обычно это значение по умолчанию, или вы можете вызвать ignore_user_abort (false), чтобы убедиться), то вам не нужно беспокоиться о процессах запуска ping либо.
Если это действительно необходимо, чтобы вы запускали только дочерний процесс после и отображать его вывод непрерывно, что может быть сложнее-вам, вероятно, придется запускать его в фоновом режиме, перенаправлять вывод в поток, а затем иметь PHP-эхо, которое возвращается пользователю, перемежаясь регулярными вызовами flush ().
Если вы хотите запустить системные команды через PHP, посмотрите,документация exec.
Я бы не рекомендовал делать это на сайте с высоким трафиком, хотя разветвление процесса для каждого запроса-довольно тяжелый процесс. Некоторые программы предоставляют возможность записи своего идентификатора процесса в файл, чтобы вы могли проверить и завершить процесс по желанию, но для таких команд, как ping, я не уверен, что это возможно, проверьте man-страницы.
вы можете быть лучше всего обслуживать, просто открыв сокет на порту, который вы ожидаете слушать (т. е. порт 80 для HTTP) на удаленном хосте, таким образом, вы знаете, что все идет хорошо в userland, а также в сети.
Если вы пытаетесь вывести двоичные данные, посмотрите в функция заголовка php, и убедитесь, что вы выберите правильный content-type и content-disposition. Комментарий документация, для получения дополнительной информации о использование/отключение выходного буфера.
для использования в командной строке:
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.набор ini-файлов "output_buffering = Off". Вы должны быть в состоянии получить вывод в реальном времени на странице Используйте системную команду вместо exec.. системная команда очистит вывод
Почему бы просто не передать вывод в файл журнала, а затем использовать этот файл для возврата содержимого клиенту. не совсем в реальном времени, но, возможно, достаточно хорошо?
Comments