我正在尝试在网页上运行一个进程,该进程将实时返回其输出。例如,如果我运行“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>";
这是显示 shell 命令实时输出的好方法:
<?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(我认为这个答案对在那里搜索的用户很有帮助)
<?php
$a = popen('ping www.google.com', 'r');
while($b = fgets($a, 2048)) {
echo $b."<br>\n";
ob_flush();flush();
}
pclose($a);
?>
此处描述了使用现代 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 框架中作为模块实现)
对于命令行用法:
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);
试试这个(在 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();
我在 Windows 上尝试了各种 PHP 执行命令,发现它们差别很大。
shell_exec
, exec
,passthru
proc_open
, popen
-- “种类”,因为您不能将参数传递给您的命令(即不会与 一起使用my.exe --something
,将与 一起使用_my_something.bat
)。最好(最简单)的方法是:
header('Content-Type: text/event-stream');
(而不是header('Content-Type: text/plain; charset=...');
)。不过,这不适用于所有浏览器/客户端!没有这个,流式传输将起作用,但至少第一行将被浏览器缓冲。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 运行系统命令,请查看exec 文档。
不过,我不建议在高流量站点上执行此操作,为每个请求分叉一个进程是一个相当大的过程。一些程序提供将其进程 ID 写入文件的选项,以便您可以检查并随意终止进程,但对于 ping 等命令,我不确定是否可行,请查看手册页。
只需在远程主机上您希望侦听的端口(IE:HTTP 的端口 80)上打开一个套接字,您可能会得到更好的服务,这样您就知道用户空间和网络上的一切进展顺利。
如果您尝试输出二进制数据,请查看php 的 header 函数,并确保设置正确的content-type和content-disposition。查看文档,了解有关使用/禁用输出缓冲区的更多信息。
首先检查flush()是否适合您。如果有,很好,如果没有,则可能意味着 Web 服务器由于某种原因正在缓冲,例如启用了 mod_gzip。
对于 ping 之类的东西,最简单的技术是在 PHP 中循环,多次运行“ping -c 1”,并在每次输出后调用 flush()。假设 PHP 配置为在用户关闭 HTTP 连接时中止(这通常是默认设置,或者您可以调用 ignore_user_abort(false) 来确保),那么您也不必担心失控的 ping 进程.
如果确实有必要只运行一次子进程并连续显示其输出,那可能会更困难——您可能必须在后台运行它,将输出重定向到流,然后让 PHP 回显该流返回给用户,穿插常规的 flush() 调用。
尝试更改 php.ini 文件集“output_buffering = Off”。您应该能够在页面上获得实时输出使用系统命令而不是 exec.. 系统命令将刷新输出
为什么不将输出通过管道传输到日志文件中,然后使用该文件将内容返回给客户端。不是很实时,但也许足够好?