PHP-Scripte im Hintergrund ausführen

Gelegentlich kommt es vor, dass man ein PHP-Script aus einem anderen heraus aufrufen möchte. Das ist zunächst kein Problem. Etwas trickreicher wird es jedoch wenn das Script welches aufgerufen wird eine sehr lange Laufzeit hat. Es ergibt sich folgendes Problem: Entweder Script A ruft Script B auf, muss nun warten bis Script B beendet ist, und arbeitet dann den nächsten Befehl ab. Ein typisches Beispiel wäre:

file_get_contents('http://url_zu_script_b.php');
echo 'ich muss auf script b warten...';

Eine zweite Möglichkeit ist Script B aufzurufen ohne das Ergebnis abzuwarten und in Script A direkt mit dem nächsten Befehl weiter zu machen. Das könnte z.B. so aussehen:

$fp = fsockopen('www.host_b.com', 80);
if($fp !== false)
{
    $out = "GET /path/to/script_b.php HTTP/1.1\r\n";
    $out .= "Host: www.host_b.com\r\n";
    $out .= "Connection: Close\r\n\r\n";

    fwrite($fp, $out);
    fclose($fp);
    echo 'ich muss nicht auf script b warten...';
}

Der Nachteil im ersten Fall ist klar. Das aufrufende Script A muss immer warten bis Script B abgerbeitet ist. Bei einer langen Laufzeit von Script B kann es hier schnell zu Timeouts kommen. Der Nachteil von Variante 2 ist weniger offensichtlich und daher schnell Quelle von Fehlern. Sobald das aufrufende Script beendet ist, wird auch der entsprechende Prozess auf dem Server beendet und somit nach kurzer Zeit auch das aufgerufene Script B. Wenn also die Laufzeit von Script B wesentlich länger ist als die des aufrufenden Scripts A kommt es bei dieser Lösung ebenfalls zu Problemen.

Um das Problem zu lösen habe ich nun folgenden Weg gewählt. Das Script B wird von Script A nicht direkt aufgerufen, sondern es wird mittels exec und curl ein neuer Prozess auf dem Server gestartet welcher Script B aufruft.

Auf einem Linux Server kann das dann z.B. so aussehen:

$cmd = 'curl -s "http://url_to/script_b.php"';
exec($cmd . " > /dev/null &");

"> /dev/null" leitet hierbei sämtliche Ausgaben direkt ins Nirvana und das & sorgt dafür dass der Prozess im Hintergrund läuft.

Auf einem Windows-System könnte das Ganze in etwas so aussehen:

$cmd = 'start /B C:\Programme\curl\curl.exe -s "http://url_to/script_b.php"';
pclose(popen($cmd, "r"));

Hinweis zum Schluss: Die beispielhaften Codeschnipsel dienen lediglich der Erklärung. Sie sind nicht schön und auch nicht sicherheitstechnisch optimal, also bitte nicht eins zu eins übernehmen.

UPDATE: Ich habe einen weiteren Beitrag zu diesem Thema geschrieben. Asynchrone PHP Prozesse mit Gearman