Start · Sprachen · PHP · Referenz · proc_open

proc_open

Funktion

Führt ein externes Kommando aus und öffnet konfigurierbare Dateizeiger für dessen Standard-Ein- und Ausgabe.

seit PHP 4.3.0 Kategorie: misc

Signatur

proc_open(array|string $command, array $descriptor_spec, array &$pipes, ?string $cwd = null, ?array $env_vars = null, ?array $options = null): resource|false

Beschreibung

proc_open() ist die mächtigere Alternative zu exec() oder shell_exec(), da sie volle Kontrolle über die Standardein- und -ausgabe (stdin, stdout, stderr) des Kindprozesses ermöglicht. Über den Parameter $descriptor_spec wird festgelegt, welche Dateideskriptoren des Prozesses als Pipes, Dateien oder bestehende Ressourcen geöffnet werden.

Der Rückgabewert ist eine Prozessressource, über die der Prozess mit proc_get_status() überwacht, mit proc_terminate() beendet oder mit proc_close() sauber geschlossen werden kann. Die dabei erzeugten Pipes werden in $pipes als Array von Stream-Ressourcen zurückgegeben und können mit den üblichen Stream-Funktionen wie fwrite() oder fread() verwendet werden.

Typische Anwendungsfälle sind das Ausführen von Shell-Werkzeugen mit interaktiver Dateneingabe (z. B. GPG, ImageMagick, ffmpeg), das Starten von Hintergrundprozessen oder das bidirektionale Kommunizieren mit externen Programmen. Ab PHP 7.4 kann $command auch ein Array sein, wodurch Shell-Escaping umgangen und die Kommandozeile direkt zusammengesetzt wird – empfohlen für Sicherheitskritisches.

Wichtig ist, alle Pipes zu schließen (fclose()) bevor proc_close() aufgerufen wird, da sonst Deadlocks entstehen können, wenn der Kindprozess auf das Leeren seiner Puffer wartet.

Parameter

Name Typ Default Beschreibung
$command Pflicht array|string Das auszuführende Kommando als String (wird über die Shell interpretiert) oder ab PHP 7.4 als Array, wobei das erste Element das Programm und die weiteren Elemente die Argumente sind. Die Array-Form umgeht Shell-Interpolation und ist sicherer.
$descriptor_spec Pflicht array Ein assoziatives Array, das festlegt, wie Dateideskriptoren des Kindprozesses behandelt werden. Schlüssel sind die Dateideskriptor-Nummern (0 = stdin, 1 = stdout, 2 = stderr). Werte sind entweder ['pipe', 'r'/'w'] für eine Pipe, ['file', '/pfad/datei', 'r'/'w'/'a'] für eine Datei oder eine bereits geöffnete Stream-Ressource.
$pipes Pflicht array Wird als Referenz übergeben und nach dem Aufruf mit den geöffneten Stream-Ressourcen befüllt, die den in $descriptor_spec definierten Pipes entsprechen (indiziert nach Dateideskriptor-Nummer).
$cwd ?string null Das Arbeitsverzeichnis, in dem der Kindprozess gestartet wird. Bei null wird das aktuelle Arbeitsverzeichnis des PHP-Prozesses verwendet.
$env_vars ?array null Ein assoziatives Array mit Umgebungsvariablen für den Kindprozess (z. B. ['HOME' => '/tmp', 'PATH' => '/usr/bin']). Bei null wird die aktuelle Umgebung des PHP-Prozesses vererbt.
$options ?array null Optionale zusätzliche Optionen. Unter Windows sind suppress_errors (Fehler unterdrücken) und bypass_shell (Shell umgehen) möglich. Unter Unix steht blocking_pipes zur Verfügung.

Rückgabewert

Typ
resource|false
Beschreibung
Gibt eine Prozessressource zurück, die für weitere Prozess-Funktionen (z. B. proc_get_status(), proc_terminate(), proc_close()) verwendet werden kann. Im Fehlerfall wird false zurückgegeben.

Beispiele

Text durch einen externen Prozess (grep) leiten

<?php
$descriptors = [
    0 => ['pipe', 'r'],  // stdin: PHP schreibt hier hinein
    1 => ['pipe', 'w'],  // stdout: PHP liest hier heraus
    2 => ['pipe', 'w'],  // stderr: Fehlerausgabe
];

$process = proc_open(['grep', 'php'], $descriptors, $pipes);

if (is_resource($process)) {
    // Eingabe an stdin schreiben
    fwrite($pipes[0], "Das ist php\nDas ist Python\nNochmal php hier\n");
    fclose($pipes[0]);

    // Ausgabe von stdout lesen
    $output = stream_get_contents($pipes[1]);
    fclose($pipes[1]);

    // Fehlerausgabe von stderr lesen
    $errors = stream_get_contents($pipes[2]);
    fclose($pipes[2]);

    $exitCode = proc_close($process);

    echo "Ausgabe:\n" . $output;
    echo "Exit-Code: " . $exitCode . "\n";
}
Ausgabe: Das ist php Nochmal php hier Exit-Code: 0

Ausgabe eines Befehls in eine Datei umleiten

<?php
$descriptors = [
    0 => ['pipe', 'r'],                          // stdin
    1 => ['file', '/tmp/ausgabe.txt', 'w'],       // stdout in Datei schreiben
    2 => ['file', '/tmp/fehler.txt',  'w'],       // stderr in Datei schreiben
];

$process = proc_open(
    ['ls', '-la', '/var/www'],
    $descriptors,
    $pipes,
    '/tmp',                           // Arbeitsverzeichnis
    ['HOME' => '/tmp']                // Umgebungsvariablen
);

if (is_resource($process)) {
    fclose($pipes[0]); // stdin sofort schließen
    $exitCode = proc_close($process);
    echo "Prozess beendet mit Exit-Code: $exitCode\n";
    echo file_get_contents('/tmp/ausgabe.txt');
}

Hintergrundprozess starten und Status abfragen

<?php
$descriptors = [
    0 => ['pipe', 'r'],
    1 => ['pipe', 'w'],
    2 => ['pipe', 'w'],
];

// sleep simuliert einen lang laufenden Prozess
$process = proc_open(['sleep', '2'], $descriptors, $pipes);

if (is_resource($process)) {
    $status = proc_get_status($process);
    echo "PID: " . $status['pid'] . "\n";
    echo "Läuft noch: " . ($status['running'] ? 'ja' : 'nein') . "\n";

    // Prozess vorzeitig beenden
    proc_terminate($process);

    fclose($pipes[0]);
    fclose($pipes[1]);
    fclose($pipes[2]);
    proc_close($process);
    echo "Prozess beendet.\n";
}
PID: 12345 Läuft noch: ja Prozess beendet.

// Wichtig · Fallstricke

Sicherheitshinweis: Werden Benutzereingaben in das Kommando eingebaut, muss zwingend escapeshellarg() (bei String-Kommandos) verwendet oder die Array-Form genutzt werden, um Command-Injection zu verhindern. Die Array-Form (ab PHP 7.4) ist grundsätzlich vorzuziehen, da keine Shell-Interpretation stattfindet.

Deadlock-Gefahr: Beim Lesen und Schreiben auf Pipes gleichzeitig kann es zu Deadlocks kommen, wenn der Puffer des Kindprozesses voll ist und PHP blockiert wartet. Bei großen Datenmengen sollte stream_select() zur nicht-blockierenden Kommunikation eingesetzt werden.

Ressourcen-Verwaltung: Alle geöffneten Pipe-Ressourcen aus $pipes müssen mit fclose() geschlossen werden, bevor proc_close() aufgerufen wird. Andernfalls wartet der Kindprozess möglicherweise auf weiteren Input und terminiert nicht.

Auf Systemen mit aktiviertem safe_mode (PHP < 5.4) oder eingeschränktem disable_functions in der php.ini kann proc_open() deaktiviert oder eingeschränkt sein.