Drucken
PHP PHP

Problembeschreibung:

Wenn es mal wieder länger dauert...

Programmiert man mittels PHP Berechnungen oder Prozesse, deren Abarbeitung längere Zeit in Anspruch nehmen, ist es für den Benutzer vor dem Bildschirm nicht zumutbar, diese Zeit ohne entsprechende Pausenüberbrückung (Wartemeldung oder Fortschrittsbalken) zu warten. Außerdem läuft man Gefahr, dass entweder der Browser oder sogar der Webserver selbst den Prozess wegen eines Timeouts abbricht.

Lösungsidee

Die Lösung besteht darin, den lang dauernden Prozess als parallelen Prozess (Hintergrundprozess oder Background Process) in PHP zu starten. Dafür gibt es mehrere Möglichkeiten (exec, system, popen, WScript, etc.), die jedoch je nach Sicherheitseinstellungen und Hostsystem (Windows, Linux) mehr oder weniger ausscheiden.

Hinzu kommt, was bei Linux tadellos funktioniert, funktioniert bei Windows noch lange nicht und umgekehrt. Da Linux hier sehr pflegeleicht ist, werde ich mich auf die funktionierende Windows-Variante beschränken. Außerdem setze ich eine Windows-Installation voraus, deren Zugriffsbeschränkungen (Dateien, Verzeichnisse, Systemprogramme) der Standardeinstellungen nach einer frischen Installation entsprechen.

Um einen parallelen Prozess aus PHP heraus zu starten, welches als FastCGI im Microsoft-Webserver IIS 7 unter Windows Server 2008 R2 läuft, hat sich die Proc_open-Proc_close-Variante als die einzig funktionierende Methode herausgestellt, da es bei Procopen einen Parameter gibt, der es erlaubt, die COMMAND.COM zu umgehen.

Damit die Proc_open-Proc_close-Variante auch funktioniert, müssen folgende Voraussetzungen geschaffen sein:

Einen kleinen Haken hat die Sache allerdings. Das PHP-Skript wartet solange mit der weiteren Ausführung, bis das Skript im Hintergrund abgearbeitet ist, da Windows ohne die COMMAND.COM oder Fremdprogramme keine echten Parallelprozesse starten kann. Aber das Hintergrundskript wird immer vollständig ausgeführt, egal ob das Hauptskript abgebrochen wird.

Programmcode

<?php
  // Aufruf eines parallelen PHP-Prozesses mittels Proc_open und Proc_close
  $handle = proc_open("php \\"C:\\\\inetpub\\\\wwwroot\\\\background_process.php\\"", array(), $pipes, NULL, NULL, array(''bypass_shell''=>true));
  // alle offenen Pipes schließen
  foreach ($pipes as $pI => $pW) {fclose($pipes[$pI]);}
  if ($handle) {proc_close($handle);}
?>