在PHP中使用实时输出运行过程


72

我试图在网页上运行一个进程,该进程将实时返回其输出。例如,如果我运行“ ping”进程,则每次返回新行时都应更新页面(现在,当我使用exec(command,output)时,我被迫使用-c选项,等到进程完成后才能看到在我的网页上输出)。是否可以在php中做到这一点?

我也想知道当有人离开页面时杀死这种过程的正确方法是什么。在“ ping”进程的情况下,我仍然能够在系统监视器中看到正在运行的进程(有意义)。


1
Nginx X-Accel缓冲可能会阻止接受的答案起作用,要使其正常工作,请在以下标题之前添加此标题:```header('X-Accel-Buffering:no'); ```
Nylls

对我来说,Nginx缓冲是上面评论解决了我的问题的问题。
Aryaman Gupta

Answers:


95

这对我有用:

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

这对我没有任何帮助,在Fedora php 5+上
Doug Molineux

20
+1对我有效,但ob_implicit_flush(true);ob_end_flush();
前提

1
将其与Robb的答案结合起来,您就会感到高兴。
Petah

我将其包装在一个函数中,因此可以像一样调用它shell_exec。如果您只想显示输出而不对其进行任何处理,则效果很好。
jxmallett

proc_open() PHP手册页
sobi3ch

36

这是显示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);
}

我希望这对其他在这里搜索过的人有所帮助。


1
这不适用于exec()system()虽然工作正常。
divinedragon 2013年

这对我来说真的很好,它不会经常更新,但经常更新。再好不过了。谢谢。
Exoon

这对CentOS 6.5 / apache 2.2 / php 5.3产生了预期的效果。干杯!
mxmader 2014年

这对我来说非常有效(OS X 10.12,Apache 2.2.3,PHP 7.0.9),但是由于某些原因,仅当还设置了X-Content-Type-Options标头时:header('X-Content-Type-Options: nosniff');
Jack Sleight

由于浏览器通常会等待一些初始数据包,因此您可以在开始时发送很多空格,以便填充初始缓冲区:for ($i=0; $i < 100000; $i++) { echo " "; } echo "\n"; flush();
Martin Pecka

12

检查所有答案,没有任何效果...

在这里找到解决方案

它在Windows上有效(我认为此答案对在那边搜索的用户很有帮助)

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

    pclose($a); 
?>

1
当我必须运行python脚本时,这对我有用。其他人也没有工作。
J. Martin


6

对于命令行用法:

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

5

试试看(在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();

2
这个答案是错误的。问题是如何实时获取页面输出。使用ob_start()可以做相反的事情:将所有输出写入内部服务器缓冲区,而不是立即将其发送给浏览器!
Elmue 2015年

1
我已经尝试过您的代码,实际上它不是实时显示shell命令,而只是等待shell命令完成加载所有文本并使用sleep(1)帮助一点一点地显示它。抱歉,您应得票数
Ryan Arief

5

我在Windows上尝试了各种PHP执行命令,发现它们之间有很大的不同。

  • 不要为工作流:shell_execexecpassthru
  • 种类的工作:proc_openpopen-之类的,因为您无法将参数传递给命令(即,不会与一起使用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

首先检查一下flush()是否适合您。如果可以,则很好,如果不能,则可能意味着Web服务器由于某种原因正在缓冲,例如已启用mod_gzip。

对于类似ping的东西,最简单的技术是在PHP中循环,多次运行“ ping -c 1”,并在每次输出后调用flush()。假设将PHP配置为在用户关闭HTTP连接时中止(通常是默认设置,或者您可以调用ignore_user_abort(false)来确保),那么您无需担心ping操作失控。

如果确实有必要只运行一次子进程并连续显示其输出,那可能会更困难-您可能必须在后台运行它,将输出重定向到流,然后让PHP回显该流返回用户,并穿插常规的flush()调用。


3

如果您希望通过PHP运行系统命令,请参阅exec文档

我不建议在流量高的站点上执行此操作,因为为每个请求分叉一个过程是相当繁重的过程。某些程序提供将其进程ID写入文件的选项,以便您可以随意检查并终止该进程,但是对于ping之类的命令,我不确定是否可以检查手册页。

只需在远程主机上您希望侦听的端口(IE:HTTP的端口80)上打开一个套接字,便可以更好地为您服务,这样您就可以知道用户区以及网络上一切正常。

如果您尝试输出二进制数据,请查看php的header函数,并确保设置了正确的content-typecontent-disposition。查看该文件,关于使用/禁止输出缓冲区的更多信息。


1

尝试更改php.ini文件集“ output_buffering = Off”。您应该能够在使用系统命令而不是exec。页面上获得实时输出。系统命令将刷新输出


0

为什么不将输出通过管道传送到日志文件,然后使用该文件将内容返回给客户端。不太实时,但也许足够好?

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.