Uruchom proces z wyjściem w czasie rzeczywistym w PHP

Próbuję uruchomić proces na stronie internetowej, który zwróci jego wynik w czasie rzeczywistym. Na przykład, jeśli uruchamiam proces 'ping' powinien on aktualizować moją stronę za każdym razem, gdy zwraca nową linię (teraz, gdy używam exec (polecenie, wyjście) jestem zmuszony użyć opcji-c i czekać, aż proces się zakończy, aby zobaczyć wyjście na mojej stronie). Czy można to zrobić w php?

Zastanawiam się również, jaki jest prawidłowy sposób na zabicie tego rodzaju procesu, gdy ktoś opuszcza stronę. W przypadku 'ping' proces nadal jestem w stanie zobaczyć proces uruchomiony w monitorze systemu (co ma sens).

Author: Maksim Vi., 2009-08-15

11 answers

To mi pomogło:

$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>";
 79
Author: egafni,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-02-07 22:42:01

Jest to dobry sposób na pokazanie w czasie rzeczywistym wyników poleceń powłoki:

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

Będzie potrzebna ta funkcja, aby zapobiec buforowaniu wyjścia:

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

To nie działa na każdym serwerze, na którym próbowałem go jednak, chciałbym móc zaoferować radę, czego szukać w konfiguracji php, aby określić, czy powinieneś wyciągnąć włosy próbując uzyskać tego typu zachowanie do pracy na serwerze! Ktoś jeszcze wie?

Oto przykład w prostym PHP:

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

disable_ob();

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

Mam nadzieję, że to pomoże innym, którzy wygooglowali swoją drogę tutaj.

 35
Author: Robb,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-02-07 22:44:22

Try this (tested on Windows machine + 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();
 5
Author: raminious,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-07-15 17:05:23

Lepsze rozwiązanie tego starego problemu przy użyciu nowoczesnych zdarzeń po stronie serwera HTML5 jest opisane tutaj:

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


Przykład:

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

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

(zaimplementowany jako moduł w Agile Toolkit framework)

 5
Author: romaninsh,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-05-04 18:04:37

Próbowałem różnych poleceń PHP w systemie Windows i okazało się, że różnią się one dość dużo.

  • Don ' t work for streaming: shell_exec, exec, passthru
  • rodzaj prac: proc_open, popen -- "rodzaj" ponieważ nie można przekazać argumentów do polecenia (tzn. nie będzie działać z my.exe --something, będzie działać z _my_something.bat).

Najlepsze (najłatwiejsze) podejście to:

  1. musisz upewnić się, że Twój exe jest Flushing poleceń(zobacz printf flushing problem ). Bez tego najprawdopodobniej otrzymasz partie około 4096 bajtów tekstu, cokolwiek zrobisz.
  2. jeśli możesz, użyj header('Content-Type: text/event-stream'); (zamiast header('Content-Type: text/plain; charset=...');). Nie będzie to jednak działać we wszystkich przeglądarkach/klientach! Streaming będzie działał bez tego, ale przynajmniej pierwsze linie będą buforowane przez przeglądarkę.
  3. Możesz również wyłączyć pamięć podręczną header('Cache-Control: no-cache');.
  4. Wyłącz buforowanie wyjścia (w php.ini lub z ini_set('output_buffering', 'off');). Może to być również konieczne w Apache / Nginx / niezależnie od serwera, w którym używasz przód.
  5. obrót kompresji (albo w php.ini lub z ini_set('zlib.output_compression', false);). Może to być również konieczne w Apache / Nginx / niezależnie od serwera używanego z przodu.

Więc w programie C++ robisz coś w stylu (ponownie, po inne rozwiązania patrz printf):

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

W PHP robisz coś takiego:

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
Author: Nux,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 11:54:39

Sprawdziłem wszystkie odpowiedzi, nic nie działa...

Znaleziono rozwiązanie tutaj

Działa na windows (myślę, że ta odpowiedź jest pomocna dla użytkowników szukających tam)

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

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

?>
 3
Author: ვანი ჩკაა,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-09-07 16:37:51

Najpierw sprawdź czy flush () działa dla ciebie. Jeśli tak, to dobrze, jeśli nie, to prawdopodobnie oznacza, że serwer WWW z jakiegoś powodu buforuje, na przykład mod_gzip jest włączony.

Dla czegoś takiego jak ping, najprostszą techniką jest pętla wewnątrz PHP, uruchomienie" ping-c 1 " wiele razy i wywołanie flush () po każdym wyjściu. Zakładając, że PHP jest skonfigurowane do przerwania, gdy połączenie HTTP jest zamknięte przez użytkownika (co zwykle jest domyślne, lub możesz wywołać ignore_user_abort (false) aby się upewnić), nie musisz też martwić się o uciekające procesy ping.

Jeśli jest naprawdę konieczne, aby uruchomić proces potomny tylko raz i wyświetlać jego wyjście w sposób ciągły, może to być trudniejsze -- prawdopodobnie będziesz musiał uruchomić go w tle, przekierować wyjście do strumienia, a następnie mieć echo PHP tego strumienia z powrotem do użytkownika, przeplatane regularnymi wywołaniami flush ().

 2
Author: Todd Owen,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-08-15 05:14:40

Jeśli chcesz uruchomić polecenia systemowe przez PHP, zajrzyj do dokumentacji exec .

Nie polecałbym tego robić na stronie o dużym natężeniu ruchu, rozwidlenie procesu dla każdego żądania jest dość dużym procesem. Niektóre programy oferują opcję zapisu identyfikatora procesu do pliku, który można sprawdzić i zakończyć proces do woli, ale dla poleceń takich jak ping, nie jestem pewien, czy jest to możliwe, Sprawdź strony podręcznika.

Możesz być lepiej obsługiwany przez po prostu otwieranie gniazda na porcie, którego spodziewasz się słuchać (IE: port 80 dla HTTP) na zdalnym hoście, dzięki czemu wiesz, że wszystko idzie dobrze w userland, jak również w sieci.

Jeśli próbujesz wydrukować dane binarne, zajrzyj do funkcji nagłówka php i upewnij się, że ustawiłeś odpowiednie content-type i content-disposition . Przejrzyj dokumentację , aby uzyskać więcej informacji na temat używania / wyłączania bufora wyjściowego.

 2
Author: Nathacof,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-08-15 06:54:55

Do użycia w wierszu poleceń:

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

Jeśli próbujesz uruchomić plik, musisz najpierw nadać mu uprawnienia wykonawcze:

chmod('/path/to/script',0755);
 2
Author: mpen,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-04-15 17:49:30

Spróbuj zmienić php.plik INI ustawiony "output_buffering = Off". Powinieneś być w stanie uzyskać wynik w czasie rzeczywistym na stronie Użyj polecenia systemowego zamiast exec.. polecenie systemowe usunie wyjście

 0
Author: ,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-09-15 20:38:14

Dlaczego po prostu nie przełączyć wyjścia do pliku dziennika, a następnie użyć tego pliku, aby zwrócić zawartość do klienta. nie całkiem realny czas, ale może wystarczająco dobry?

 0
Author: greenone83,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-08-09 15:28:40