php wykonuje proces w tle


259

Muszę wykonać kopię katalogu po akcji użytkownika, ale katalogi są dość duże, więc chciałbym móc wykonać taką akcję bez uświadomienia użytkownikowi czasu potrzebnego na wykonanie kopii.

Wszelkie sugestie będą mile widziane.


3
Ten stackoverflow.com/questions/5367261/… wyjaśnia, jak to zrobić w systemie Windows
shealtiel,

Odpowiedzi:


365

Zakładając, że działa na komputerze z systemem Linux, zawsze postępowałem w ten sposób:

exec(sprintf("%s > %s 2>&1 & echo $! >> %s", $cmd, $outputfile, $pidfile));

Uruchamia to polecenie $cmd, przekierowuje dane wyjściowe polecenia $outputfilei zapisuje identyfikator procesu $pidfile.

To pozwala łatwo monitorować, co robi proces i czy nadal działa.

function isRunning($pid){
    try{
        $result = shell_exec(sprintf("ps %d", $pid));
        if( count(preg_split("/\n/", $result)) > 2){
            return true;
        }
    }catch(Exception $e){}

    return false;
}

2
Czy konfiguracja sudo ma działać bez pytania o hasło? Wszelkie polecenia wymagające wkładu użytkownika nie będą działać.
Mark Biek

17
Ten stackoverflow.com/questions/5367261/… wyjaśnia, jak zrobić to samo w systemie Windows
Shealtiel,

13
Użyłem tego, ale zaktualizowałem go nieco, więc nie musiałem zapisywać PID do pliku. Używam więc tego formatu: <code> exec (sprintf ("$ s> $ s 2> & 1 & echo $ 1", $ cmd, $ outputfile), $ pidArr); </code> Wynikowy proces PID jest w $ pidArr [0]
Kaiesh

3
@Kiesies: powinno być „echo $!”
brunobg

8
Kaiesh zrobił anoter literówkę „$” zamiast „%”. Poprawiona wersja: exec (sprintf ("% s>% s 2> & 1 & echo $!", $ Cmd, $ outputfile), $ pidArr)
Yuri Gor

23

Napisz proces jako skrypt po stronie serwera w dowolnym języku (php / bash / perl / etc), a następnie wywołaj go z funkcji kontroli procesu w skrypcie php.

Funkcja prawdopodobnie wykrywa, czy jako strumień wyjściowy zastosowano standardowe io, a jeśli tak, to ustawi wartość zwracaną .. jeśli nie, to kończy się

proc_close( proc_open( "./command --foo=1 &", array(), $foo ) );

Przetestowałem to szybko z wiersza poleceń, używając polecenia „sleep 25s” jako polecenia i działało to jak urok.

( Znaleziono odpowiedź tutaj )


3
To jedyny sposób, w jaki mogłem zmusić go do pracy na centach. Dziękuję Ci!
Deac Karns,

Dzięki ! Istnieje niezgodność PHP i BASH. Jeśli uruchomisz nowszy system, uruchomienie w tle nie powiedzie się za pomocą system () lub exec (). Deskryptory plików wydają się blokować nawet w przypadku przekierowania. Twoja metoda zadziałała. Inną alternatywą jest użycie starego binarnego basha dla PHP, ale to wariactwo
Jan

22

Możesz spróbować dołączyć to do swojego polecenia

>/dev/null 2>/dev/null &

na przykład.

shell_exec('service named reload >/dev/null 2>/dev/null &');

Miałem na myśli> / dev / null 2> / dev / null &
wlf

To działa dla mnie i użyję makefile, aby zrobić coś na stronie serwera, dziękuję! (np. make, make restart)
Chu-Siang Lai

Jest to o wiele prostsze niż zaakceptowana odpowiedź! (Tak długo, jak nie potrzebujesz nic bardziej złożonego, na przykład sprawdzania, czy proces nadal działa.)
Sean the Bean

18

Chciałbym tylko dodać bardzo prosty przykład testowania tej funkcji w systemie Windows:

Utwórz następujące dwa pliki i zapisz je w katalogu internetowym:

foreground.php:

<?php

ini_set("display_errors",1);
error_reporting(E_ALL);

echo "<pre>loading page</pre>";

function run_background_process()
{
    file_put_contents("testprocesses.php","foreground start time = " . time() . "\n");
    echo "<pre>  foreground start time = " . time() . "</pre>";

    // output from the command must be redirected to a file or another output stream 
    // http://ca.php.net/manual/en/function.exec.php

    exec("php background.php > testoutput.php 2>&1 & echo $!", $output);

    echo "<pre>  foreground end time = " . time() . "</pre>";
    file_put_contents("testprocesses.php","foreground end time = " . time() . "\n", FILE_APPEND);
    return $output;
}

echo "<pre>calling run_background_process</pre>";

$output = run_background_process();

echo "<pre>output = "; print_r($output); echo "</pre>";
echo "<pre>end of page</pre>";
?>

background.php:

<?
file_put_contents("testprocesses.php","background start time = " . time() . "\n", FILE_APPEND);
sleep(10);
file_put_contents("testprocesses.php","background end time = " . time() . "\n", FILE_APPEND);
?>

Zezwól IUSR na zapis w katalogu, w którym utworzono powyższe pliki

Zezwól IUSR na CZYTANIE I WYKONYWANIE C: \ Windows \ System32 \ cmd.exe

Hit foreground.php z przeglądarki internetowej

Poniższe powinny zostać wyświetlone w przeglądarce z aktualnymi znacznikami czasu i zasobem lokalnym # w tablicy wyjściowej:

loading page
calling run_background_process
  foreground start time = 1266003600
  foreground end time = 1266003600
output = Array
(
    [0] => 15010
)
end of page

Powinieneś zobaczyć testoutput.php w tym samym katalogu, w którym zostały zapisane powyższe pliki, i powinien być pusty

Powinieneś zobaczyć testprocesses.php w tym samym katalogu, w którym zostały zapisane powyższe pliki, i powinien zawierać następujący tekst z aktualnymi znacznikami czasu:

foreground start time = 1266003600
foreground end time = 1266003600
background start time = 1266003600
background end time = 1266003610

10

Jeśli potrzebujesz zrobić coś w tle bez oczekiwania strony PHP na zakończenie, możesz użyć innego (w tle) skryptu PHP, który jest „wywoływany” poleceniem wget. Ten skrypt PHP w tle będzie wykonywany z uprawnieniami, oczywiście, jak każdy inny skrypt PHP w twoim systemie.

Oto przykład systemu Windows używającego wget z pakietów gnuwin32.

Kod w tle (plik test-proc-bg.php) jako przykład ...

sleep(5);   // some delay
file_put_contents('test.txt', date('Y-m-d/H:i:s.u')); // writes time in a file

Skrypt na pierwszym planie, ten wywołujący ...

$proc_command = "wget.exe http://localhost/test-proc-bg.php -q -O - -b";
$proc = popen($proc_command, "r");
pclose($proc);

Musisz użyć popen / pclose, aby to działało poprawnie.

Opcje wget:

-q    keeps wget quiet.
-O -  outputs to stdout.
-b    works on background

7

Oto funkcja umożliwiająca uruchomienie procesu w tle w PHP. W końcu stworzyłem taki, który faktycznie działa również w systemie Windows, po wielu odczytach i testach różnych podejść i parametrów.

function LaunchBackgroundProcess($command){
  // Run command Asynchroniously (in a separate thread)
  if(PHP_OS=='WINNT' || PHP_OS=='WIN32' || PHP_OS=='Windows'){
    // Windows
    $command = 'start "" '. $command;
  } else {
    // Linux/UNIX
    $command = $command .' /dev/null &';
  }
  $handle = popen($command, 'r');
  if($handle!==false){
    pclose($handle);
    return true;
  } else {
    return false;
  }
}

Uwaga 1: W systemie Windows nie używaj /Bparametru jak sugerowano gdzie indziej. Zmusza proces do uruchomienia tego samego okna konsoli co startsamo polecenie, co powoduje, że proces jest przetwarzany synchronicznie. Aby uruchomić proces w osobnym wątku (asynchronicznie), nie używaj /B.

Uwaga 2: Puste podwójne cudzysłowy start ""są wymagane, jeśli polecenie jest ścieżką cytowaną. startpolecenie interpretuje pierwszy cytowany parametr jako tytuł okna.


6

Znalazłem nieco szybszą i łatwiejszą wersję do użycia

shell_exec('screen -dmS $name_of_screen $command'); 

i to działa.


@ Alpha.Dev Testowałem to na Linuksie, ale nie wiem, czy zadziała na innym systemie.
Morfidon

4

Czy potrafisz zorganizować oddzielny proces, a następnie uruchomić kopię w tle? Minęło trochę czasu odkąd zrobiłem PHP, ale funkcja pcntl-fork wygląda obiecująco.


To nie daje odpowiedzi na pytanie. Aby skrytykować lub poprosić autora o wyjaśnienia, zostaw komentarz pod postem.
Rizier123

13
Dzięki - Komentarze nie istniały w '08, ale będę o tym pamiętać :)
Matt Sheppard

4

Użyj tej funkcji, aby uruchomić program w tle. Jest wieloplatformowy iw pełni konfigurowalny.

<?php
function startBackgroundProcess(
    $command,
    $stdin = null,
    $redirectStdout = null,
    $redirectStderr = null,
    $cwd = null,
    $env = null,
    $other_options = null
) {
    $descriptorspec = array(
        1 => is_string($redirectStdout) ? array('file', $redirectStdout, 'w') : array('pipe', 'w'),
        2 => is_string($redirectStderr) ? array('file', $redirectStderr, 'w') : array('pipe', 'w'),
    );
    if (is_string($stdin)) {
        $descriptorspec[0] = array('pipe', 'r');
    }
    $proc = proc_open($command, $descriptorspec, $pipes, $cwd, $env, $other_options);
    if (!is_resource($proc)) {
        throw new \Exception("Failed to start background process by command: $command");
    }
    if (is_string($stdin)) {
        fwrite($pipes[0], $stdin);
        fclose($pipes[0]);
    }
    if (!is_string($redirectStdout)) {
        fclose($pipes[1]);
    }
    if (!is_string($redirectStderr)) {
        fclose($pipes[2]);
    }
    return $proc;
}

Zauważ, że po uruchomieniu polecenia ta funkcja domyślnie zamyka standardowe wejście i standardowe wyjście uruchomionego procesu. Możesz przekierować dane wyjściowe do jakiegoś pliku za pomocą argumentów $ redirectStdout i $ redirectStderr.

Uwaga dla użytkowników systemu Windows:
Nie można przekierować stdout / stderr nulw następujący sposób:

startBackgroundProcess('ping yandex.com', null, 'nul', 'nul');

Możesz to jednak zrobić:

startBackgroundProcess('ping yandex.com >nul 2>&1');

Uwagi dla użytkowników * nix:

1) Użyj polecenia powłoki exec, jeśli chcesz uzyskać rzeczywisty PID:

$proc = startBackgroundProcess('exec ping yandex.com -c 15', null, '/dev/null', '/dev/null');
print_r(proc_get_status($proc));

2) Użyj argumentu $ stdin, jeśli chcesz przekazać niektóre dane do danych wejściowych programu:

startBackgroundProcess('cat > input.txt', "Hello world!\n");

3

Możesz wypróbować system kolejkowania, taki jak Resque . Następnie możesz wygenerować zadanie, które przetwarza informacje i dość szybko powraca z obrazem „przetwarzającym”. Dzięki takiemu podejściu nie będziesz wiedział, kiedy zostanie zakończony.

To rozwiązanie jest przeznaczone do zastosowań na większą skalę, gdzie nie chcesz, aby twoje maszyny czołowe wykonywały ciężkie podnoszenie, aby mogły przetwarzać żądania użytkowników. Dlatego może, ale nie musi, pracować z danymi fizycznymi, takimi jak pliki i foldery, ale do przetwarzania bardziej skomplikowanej logiki lub innych zadań asynchronicznych (tj. Nowych wiadomości rejestracyjnych) dobrze jest mieć i jest bardzo skalowalny.


2

Działające rozwiązanie zarówno dla systemu Windows, jak i Linux. Znajdź więcej na stronie Mój github .

function run_process($cmd,$outputFile = '/dev/null', $append = false){
                    $pid=0;
                if (strtoupper(substr(PHP_OS, 0, 3)) === 'WIN') {//'This is a server using Windows!';
                        $cmd = 'wmic process call create "'.$cmd.'" | find "ProcessId"';
                        $handle = popen("start /B ". $cmd, "r");
                        $read = fread($handle, 200); //Read the output 
                        $pid=substr($read,strpos($read,'=')+1);
                        $pid=substr($pid,0,strpos($pid,';') );
                        $pid = (int)$pid;
                        pclose($handle); //Close
                }else{
                    $pid = (int)shell_exec(sprintf('%s %s %s 2>&1 & echo $!', $cmd, ($append) ? '>>' : '>', $outputFile));
                }
                    return $pid;
            }
            function is_process_running($pid){
                if (strtoupper(substr(PHP_OS, 0, 3)) === 'WIN') {//'This is a server using Windows!';
                        //tasklist /FI "PID eq 6480"
                    $result = shell_exec('tasklist /FI "PID eq '.$pid.'"' );
                    if (count(preg_split("/\n/", $result)) > 0 && !preg_match('/No tasks/', $result)) {
                        return true;
                    }
                }else{
                    $result = shell_exec(sprintf('ps %d 2>&1', $pid));
                    if (count(preg_split("/\n/", $result)) > 2 && !preg_match('/ERROR: Process ID out of range/', $result)) {
                        return true;
                    }
                }
                return false;
            }
            function stop_process($pid){
                    if (strtoupper(substr(PHP_OS, 0, 3)) === 'WIN') {//'This is a server using Windows!';
                            $result = shell_exec('taskkill /PID '.$pid );
                        if (count(preg_split("/\n/", $result)) > 0 && !preg_match('/No tasks/', $result)) {
                            return true;
                        }
                    }else{
                            $result = shell_exec(sprintf('kill %d 2>&1', $pid));
                        if (!preg_match('/No such process/', $result)) {
                            return true;
                        }
                    }
            }


1

Zamiast inicjować proces w tle, co z tworzeniem pliku wyzwalającego i planowaniem, takim jak cron lub autosys, co jakiś czas wykonuje skrypt, który wyszukuje i działa na pliki wyzwalające? Wyzwalacze mogą zawierać instrukcje lub nawet surowe polecenia (jeszcze lepiej, po prostu uczyń go skryptem powłoki).



1

Mocno używam fast_cgi_finish_request()

W połączeniu z zamknięciem i register_shutdown_function ()

$message ='job executed';
$backgroundJob = function() use ($message) {
     //do some work here
    echo $message;
}

Następnie zarejestruj to zamknięcie, które ma zostać wykonane przed wyłączeniem.

register_shutdown_function($backgroundJob);

Wreszcie, gdy odpowiedź została wysłana do klienta, możesz zamknąć połączenie z klientem i kontynuować pracę z procesem PHP:

fast_cgi_finish_request();

Zamknięcie zostanie wykonane po fast_cgi_finish_request.

Wiadomość $ nie będzie widoczna w żadnym momencie. Możesz zarejestrować tyle zamknięć, ile chcesz, ale zadbaj o czas wykonania skryptu. Działa to tylko wtedy, gdy PHP działa jako moduł Fast CGI (czy to prawda ?!)


0

Skrypty PHP nie są podobne do innych aplikacji rozwijających język. W językach aplikacji komputerowych możemy ustawić wątki demonów, aby uruchamiały proces w tle, ale w PHP proces występuje, gdy użytkownik żąda strony. Możliwe jest jednak ustawienie zadania w tle za pomocą funkcji zadania cron serwera, którą uruchamia skrypt php.


0

Dla tych z nas korzystających z systemu Windows spójrz na to:

Odniesienie: http://php.net/manual/en/function.exec.php#43917

Ja też zmagałem się z uruchomieniem programu w tle w systemie Windows, podczas gdy skrypt nadal działa. Ta metoda, w przeciwieństwie do innych rozwiązań, pozwala uruchomić dowolny program zminimalizowany, zmaksymalizowany lub bez okna. Rozwiązanie llbra @ phpbrasil działa, ale czasami tworzy niechciane okno na pulpicie, gdy naprawdę chcesz, aby zadanie działało ukryte.

uruchom zminimalizowany Notepad.exe w tle:

<?php 
$WshShell = new COM("WScript.Shell"); 
$oExec = $WshShell->Run("notepad.exe", 7, false); 
?> 

uruchom polecenie powłoki niewidoczne w tle:

<?php 
$WshShell = new COM("WScript.Shell"); 
$oExec = $WshShell->Run("cmd /C dir /S %windir%", 0, false); 
?> 

uruchom MSPaint zmaksymalizowany i poczekaj na zamknięcie go przed kontynuowaniem skryptu:

<?php 
$WshShell = new COM("WScript.Shell"); 
$oExec = $WshShell->Run("mspaint.exe", 3, true); 
?> 

Więcej informacji na temat metody Run () znajduje się na stronie: http://msdn.microsoft.com/library/en-us/script56/html/wsMthRun.asp

Edytowany adres URL:

Przejdź do https://technet.microsoft.com/en-us/library/ee156605.aspx zamiast tego, ponieważ powyższy link już nie istnieje.


1
Co należy zrobić, aby uruchomić ten kod? DostajęFatal error: Class 'COM' not found
Alph.Dev

Ps Link do MSDN jest zepsuty
Alph.Dev

@ Alph.Dev: Wygląda na to, że dokument msdn został tymczasowo usunięty. Zawierał więcej przykładów i wyjaśnień dotyczących korzystania z WScript.Shell.
Jimmy Ilenloa

@ Alph.Dev: Dla klasy COM nie znaleziono, to zupełnie inna sprawa. Sprawdź to w Google. Możesz także sprawdzić to php.net/manual/en/com.installation.php
Jimmy Ilenloa

@ Alph.Dev proszę sprawdzić na stronie technet.microsoft.com/en-us/library/ee156605.aspx, aby uzyskać aktualne informacje na temat funkcji Run ()
Jimmy Ilenloa

-12

Wiem, że to post ma 100 lat, ale w każdym razie pomyślałem, że może się komuś przydać. Możesz umieścić niewidoczny obraz gdzieś na stronie wskazujący adres URL, który musi być uruchomiony w tle, w następujący sposób:

<img src="run-in-background.php" border="0" alt="" width="1" height="1" />


12
Naprawdę złe rozwiązanie. Jeśli użytkownik opuści stronę, proces zostanie przerwany.
Sergey P. aka lazur

3
„niewidoczny obraz” i jego link są widoczne w kodzie źródłowym strony.
Tony Gil
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.