Я пытаюсь запустить процесс на веб-странице, которая вернет свой результат в реальном времени. Например, если я запускаю процесс «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
). Самый лучший (простой) подход:
header('Content-Type: text/event-stream');
(вместо header('Content-Type: text/plain; charset=...');
). Однако это не будет работать во всех браузерах / клиентах! Потоковая передача будет работать без этого, но по крайней мере первые строки будут буферизованы браузером. header('Cache-Control: no-cache');
кэша header('Cache-Control: no-cache');
, ini_set('output_buffering', 'off');
). Это также может быть сделано в Apache / Nginx / любом сервере, который вы используете спереди. 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. Системная команда будет очищать вывод