web-dev-qa-db-ja.com

PHPのリアルタイム出力でプロセスを実行

リアルタイムで出力を返すプロセスをWebページで実行しようとしています。たとえば、「ping」プロセスを実行する場合、新しい行を返すたびにページを更新する必要があります(現在、exec(command、output)を使用すると、-cオプションを使用し、プロセスが終了するまで待つ必要があります私のウェブページに出力)。 PHPでこれを行うことは可能ですか?

また、誰かがページを離れるときにこの種のプロセスを強制終了する正しい方法は何だろうと思っています。 「ping」プロセスの場合、システムモニターで実行中のプロセスを見ることができます(これは理にかなっています)。

67
Maksim Vi.

これは私のために働いた:

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

これは、シェルコマンドのリアルタイム出力を表示する良い方法です。

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

ここでググってくれた他の人の助けになることを願っています。

36
Robb

最新の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#L4

(Agile Toolkitフレームワークのモジュールとして実装)

6
romaninsh

すべての回答を確認しましたが、何も機能しません...

見つかった解決策 ここ

Windows上で動作します(この答えは、向こうで検索するユーザーに役立つと思います)

<?php
$a = popen('ping www.google.com', 'r'); 

while($b = fgets($a, 2048)) { 
echo $b."<br>\n"; 
ob_flush();flush(); 
} 
pclose($a); 

?>
5
Pixsa

コマンドラインで使用する場合:

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
mpen

これを試してください(Windowsマシン+ワンプサーバーでテスト済み)

        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();
4
raminious

WindowsでさまざまなPHP実行コマンドを試してみましたが、かなり異なることがわかりました。

  • ストリーミングでは動作しません:_Shell_exec_、execpassthru
  • 種類の作業:_proc_open_、popen-コマンドに引数を渡すことができないため(つまり、_my.exe --something_では機能しませんが、__my_something.bat_では機能します) )。

最良の(最も簡単な)アプローチは次のとおりです。

  1. Exeがコマンドをフラッシュしていることを確認する必要があります( printf flushing problem を参照)。これがなければ、何をするにしても約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 flushing problem を参照してください):

_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);
_
4
Nux

PHP look-in、 exec documentation 経由でシステムコマンドを実行する場合。

ただし、トラフィックの多いサイトでこれを行うことはお勧めしません。リクエストごとにプロセスをフォークするのは非常に手間のかかるプロセスです。一部のプログラムでは、プロセスIDをファイルに書き込んで、プロセスを自由に確認して終了できるオプションを提供していますが、pingなどのコマンドについては、マニュアルページを確認してください。

リモートホストでリッスンする予定のポート(IE:HTTPの場合はポート80)でソケットを開くだけで、ユーザーランドおよびネットワーク上ですべてが順調に進んでいることがわかります。

バイナリデータを出力しようとしている場合は、 phpのヘッダー関数 を調べ、適切な content-type および content-disposition を設定してください。 。出力バッファの使用/無効化の詳細については、 ドキュメント を確認してください。

2
Nathacof

最初に flush() が機能するかどうかを確認します。もしそうなら、もしそうでなければ、おそらくmod_gzipが有効になっているなど、何らかの理由でWebサーバーがバッファリングしていることを意味します。

Pingのようなものの場合、最も簡単な方法はPHP内でループし、「ping -c 1」を複数回実行し、各出力の後にflush()を呼び出すことです。 PHPは、HTTP接続がユーザーによって閉じられたときに中止するように設定されています(通常はデフォルトです。または、ignore_user_abort(false)を呼び出して確認できます))実行中のpingプロセスについても心配する必要があります。

子プロセスonceのみを実行し、その出力を継続的に表示することが本当に必要な場合、それはより困難になる可能性があります-おそらく実行する必要がありますバックグラウンドで、出力をストリームにリダイレクトし、その後、PHPストリームをユーザーにエコーバックし、通常のflush()呼び出しを散在させます。

2
Todd Owen

Php.iniファイルセット「output_buffering = Off」を変更してみてください。ページでリアルタイムの出力を取得できるはずです。execの代わりにシステムコマンドを使用してください。システムコマンドは出力をフラッシュします

0
Yatin

出力をログファイルにパイプしてから、そのファイルを使用してコンテンツをクライアントに返すだけではどうでしょうか。かなりリアルタイムではありませんが、おそらく十分でしょうか?

0
greenone83