it-swarm.com.de

Prozess mit Echtzeitausgabe in ausführen PHP

Ich versuche, einen Prozess auf einer Webseite auszuführen, der seine Ausgabe in Echtzeit liefert. Wenn ich beispielsweise den Ping-Prozess ausführte, sollte er meine Seite jedes Mal aktualisieren, wenn eine neue Zeile zurückgegeben wird. Wenn ich jetzt exec (Befehl, Ausgabe) verwende, muss ich die Option -c verwenden und warten, bis der Prozess abgeschlossen ist, um die Ausgabe auf meiner Webseite). Ist es möglich, dies in PHP zu tun?

Ich frage mich auch, was der richtige Weg ist, diesen Prozess zu beenden, wenn jemand die Seite verlässt. Im Falle eines "Ping" -Prozesses kann ich immer noch den laufenden Prozess im Systemmonitor sehen (was sinnvoll ist).

62
Maksim Vi.

Das hat für mich funktioniert:

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

Dies ist eine nette Methode, um die Echtzeitausgabe Ihrer Shell-Befehle anzuzeigen:

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

Sie benötigen diese Funktion, um die Pufferung der Ausgabe zu verhindern:

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

Es funktioniert nicht auf jedem Server, auf dem ich es ausprobiert habe. Ich wünschte, ich könnte Ratschläge geben, worauf Sie in Ihrer PHP-Konfiguration achten sollten, um zu entscheiden, ob Sie Ihre Haare herausziehen sollten, wenn Sie versuchen, diese Art von Verhalten zum Laufen zu bringen auf deinem Server! Weiß noch jemand?

Hier ist ein Dummy-Beispiel in einfachem PHP:

<?php
header("Content-type: text/plain");

disable_ob();

for($i=0;$i<10;$i++) 
{
    echo $i . "\n";
    usleep(300000);
}

Ich hoffe, das hilft anderen, die sich hier hingesetzt haben.

35
Robb

Eine bessere Lösung für dieses alte Problem mit modernen HTML5-Server-Side-Events wird hier beschrieben:

http://www.w3schools.com/html/html5_serversentevents.asp


Beispiel:

http://sink.agiletoolkit.org/realtime/console

Code: https://github.com/atk4/sink/blob/master/admin/page/realtime/console.php#L40

(Implementiert als Modul im Agile Toolkit-Framework)

6
romaninsh

Alle Antworten überprüft, nichts funktioniert ...

Lösung gefunden hier

Es funktioniert unter Windows (ich denke, diese Antwort ist hilfreich für Benutzer, die dort suchen)

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

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

?>
4
Pixsa

probiere das (getestet auf Windows-Rechner + 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();
4
raminious

Ich habe verschiedene PHP - Ausführungsbefehle unter Windows ausprobiert und festgestellt, dass sie sich sehr unterscheiden.

  • Für das Streaming nicht arbeiten: Shell_exec, exec, passthru
  • Art der Arbeiten: proc_open, popen - "Art", weil Sie keine Argumente an Ihren Befehl übergeben können (d. H. Nicht mit my.exe --something zusammenarbeiten, funktioniert mit _my_something.bat).

Der beste (einfachste) Ansatz ist:

  1. Sie müssen sicherstellen, dass Ihr Exe Befehle zum Flushen ausführt (siehe printf Flushing Problem ). Andernfalls erhalten Sie höchstwahrscheinlich Stapel von etwa 4096 Bytes Text, was auch immer Sie tun.
  2. Wenn möglich, verwenden Sie header('Content-Type: text/event-stream'); (anstelle von header('Content-Type: text/plain; charset=...');). Dies funktioniert jedoch nicht in allen Browsern/Clients! Streaming funktioniert auch ohne, aber die ersten Zeilen werden vom Browser gepuffert.
  3. Möglicherweise möchten Sie auch den Cache header('Cache-Control: no-cache'); deaktivieren.
  4. Deaktivieren Sie die Ausgabepufferung (entweder in php.ini oder mit ini_set('output_buffering', 'off');). Möglicherweise muss dies auch in Apache/Nginx/dem Server, den Sie zuvor verwenden, ausgeführt werden.
  5. Deaktivierung der Komprimierung (entweder in php.ini oder mit ini_set('zlib.output_compression', false);). Möglicherweise muss dies auch in Apache/Nginx/dem Server, den Sie zuvor verwenden, ausgeführt werden.

In Ihrem C++ - Programm tun Sie etwas (wieder, für andere Lösungen siehe printf Flushing ):

Logger::log(...) {
  printf (text);
  fflush(stdout);
}

In PHP machen Sie so etwas wie:

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
Nux

Wenn Sie Systembefehle über PHP ausführen möchten, schauen Sie in die Exec-Dokumentation .

Ich würde jedoch nicht empfehlen, dies auf einer Website mit viel Verkehr zu tun, einen Prozess für jede Anfrage zu forcieren, ist ein recht heftiger Prozess. Einige Programme bieten die Möglichkeit, ihre Prozess-ID in eine Datei zu schreiben, sodass Sie dies nach Belieben überprüfen und beenden können. Bei Befehlen wie Ping bin ich mir nicht sicher, ob dies möglich ist. Überprüfen Sie die Man-Pages.

Sie können besser bedient werden, indem Sie einfach einen Socket an dem Port öffnen, den Sie erwarten (IE: Port 80 für HTTP) auf dem Remote-Host. Auf diese Weise wissen Sie, dass im Benutzerland und im Netzwerk alles gut läuft.

Wenn Sie versuchen, binäre Daten auszugeben, schauen Sie in die Header-Funktion von php und stellen Sie sicher, dass Sie den richtigen content-type und content-disposition einstellen. Lesen Sie die Dokumentation , um weitere Informationen zur Verwendung/Deaktivierung des Ausgabepuffers zu erhalten.

2
Nathacof

Prüfen Sie zunächst, ob flush () für Sie funktioniert. Wenn dies der Fall ist, ist es gut, wenn dies nicht der Fall ist, bedeutet dies, dass der Webserver aus irgendeinem Grund puffert, beispielsweise ist mod_gzip aktiviert.

Für so etwas wie Ping ist es die einfachste Technik, innerhalb von PHP eine Schleife auszuführen, "ping -c 1" mehrmals auszuführen und nach jeder Ausgabe flush () aufzurufen. Angenommen, PHP ist so konfiguriert, dass der Vorgang abgebrochen wird, wenn die HTTP-Verbindung vom Benutzer geschlossen wird (dies ist normalerweise die Standardeinstellung, oder Sie können ignore_user_abort (false) aufrufen, um sicherzustellen, dass dies der Fall ist). weg Ping-Prozesse entweder.

Wenn es wirklich notwendig ist, dass Sie den untergeordneten Prozess einmal nur einmal ausführen und seine Ausgabe kontinuierlich anzeigen, kann dies schwieriger sein - Sie müssen ihn wahrscheinlich im Hintergrund ausführen, die Ausgabe in einen Stream umleiten und dann haben PHP Echo, das an den Benutzer zurückgegeben wird, unterbrochen von regulären flush () - Aufrufen.

2
Todd Owen

Für die Verwendung der Befehlszeile:

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

Wenn Sie versuchen, eine Datei auszuführen, müssen Sie ihr zunächst Ausführungsberechtigungen erteilen:

chmod('/path/to/script',0755);
2
mpen

Ändern Sie den php.ini-Dateisatz "output_buffering = off". Sie sollten in der Lage sein, die Echtzeitausgabe auf der Seite abzurufen Verwenden Sie den Systembefehl anstelle des Ausführungsbefehls. Mit dem Systembefehl wird die Ausgabe geleert

0
Yatin

warum nicht einfach die Ausgabe in eine Protokolldatei leiten und diese Datei dann verwenden, um Inhalt an den Client zurückzugeben. nicht ganz echt aber vielleicht gut genug?

0
greenone83