Start · Sprachen · PHP · Referenz · popen

popen

Funktion

Öffnet einen Pipe-Dateizeiger zu einem externen Prozess und ermöglicht das Lesen seiner Ausgabe oder das Schreiben in seine Eingabe.

seit PHP 4.0.0 Kategorie: io

Signatur

popen(string $command, string $mode): resource|false

Beschreibung

popen() startet einen externen Betriebssystem-Prozess über die Shell und gibt einen Dateizeiger zurück, der entweder zum Lesen der Prozessausgabe (r) oder zum Schreiben in den Prozess (w) genutzt werden kann. Der Zeiger verhält sich wie ein gewöhnlicher Datei-Handle und kann mit Funktionen wie fgets(), fgetcsv() oder fwrite() verwendet werden.

Die Funktion ist nützlich, wenn man mit externen Programmen kommunizieren möchte, ohne temporäre Dateien zu verwenden, z. B. beim Lesen komprimierter Daten (gzip), beim Ausführen von Shell-Skripten oder beim Weiterleiten von Daten an Kommandozeilen-Werkzeuge wie sort oder mail.

Das Schließen des Prozesses erfolgt mit pclose(), das auch den Exit-Code des Prozesses zurückgibt. Im Gegensatz zu proc_open() erlaubt popen() nur eine Richtung (entweder lesen oder schreiben, nicht beides gleichzeitig).

Der Befehl wird über die System-Shell ausgeführt (/bin/sh -c auf Unix, cmd.exe /c auf Windows), was Shell-Expansion und Pipes innerhalb des Befehls ermöglicht, aber auch Sicherheitsrisiken birgt.

Parameter

Name Typ Default Beschreibung
$command Pflicht string Der auszuführende Shell-Befehl als Zeichenkette. Benutzereingaben müssen mit escapeshellcmd() und/oder escapeshellarg() abgesichert werden.
$mode Pflicht string Öffnungsmodus: 'r' zum Lesen der Prozessausgabe (stdout), 'w' zum Schreiben in die Prozesseingabe (stdin). Auf Windows kann zusätzlich 'b' für Binärmodus angegeben werden, z. B. 'rb'.

Rückgabewert

Typ
resource|false
Beschreibung
Gibt einen Dateizeiger (Resource) zurück, der mit pclose() geschlossen werden muss. Bei einem Fehler (z. B. ungültiger Befehl oder fehlende Berechtigung) wird false zurückgegeben.

Beispiele

Ausgabe eines externen Befehls zeilenweise lesen

<?php
$handle = popen('ls -la /tmp', 'r');
if ($handle === false) {
    die('Prozess konnte nicht geöffnet werden.');
}

while (!feof($handle)) {
    $line = fgets($handle);
    if ($line !== false) {
        echo htmlspecialchars($line) . "\n";
    }
}

$exitCode = pclose($handle);
echo "Exit-Code: " . $exitCode . "\n";
// Ausgabe der Verzeichnisliste von /tmp, gefolgt von: Exit-Code: 0

Daten in einen Prozess schreiben (z. B. sendmail)

<?php
$empfaenger = escapeshellarg('empfaenger@example.com');
$handle = popen('sendmail -t -i ' . $empfaenger, 'w');
if ($handle === false) {
    die('sendmail konnte nicht gestartet werden.');
}

fwrite($handle, "To: empfaenger@example.com\n");
fwrite($handle, "Subject: Test-Nachricht\n");
fwrite($handle, "\n");
fwrite($handle, "Dies ist der Nachrichtentext.\n");

$exitCode = pclose($handle);
echo "Exit-Code: " . $exitCode . "\n";
Exit-Code: 0

Komprimierte Datei über gzip lesen

<?php
$datei = escapeshellarg('/var/log/syslog.gz');
$handle = popen('gzip -dc ' . $datei, 'r');
if ($handle === false) {
    die('Datei konnte nicht geöffnet werden.');
}

$zeilenAnzahl = 0;
while (!feof($handle) && $zeilenAnzahl < 5) {
    $zeile = fgets($handle);
    if ($zeile !== false) {
        echo htmlspecialchars($zeile);
        $zeilenAnzahl++;
    }
}

pclose($handle);
// Die ersten 5 Zeilen der entpackten Datei werden ausgegeben

// Wichtig · Fallstricke

Sicherheitshinweis: Da der Befehl über die System-Shell ausgeführt wird, stellt das Einfügen von Benutzereingaben ohne Bereinigung ein erhebliches Command-Injection-Risiko dar. Argumente immer mit escapeshellarg() und den Befehl selbst mit escapeshellcmd() absichern. Niemals rohe Benutzereingaben in den Befehlsstring einbetten.

Nur eine Richtung: popen() unterstützt entweder Lesen oder Schreiben, nicht beides gleichzeitig. Für bidirektionale Kommunikation mit einem Prozess ist proc_open() die geeignetere Wahl.

Fehlerausgabe: Standardmäßig wird stderr des Kindprozesses nicht über den zurückgegebenen Handle zugänglich gemacht. Um stderr ebenfalls zu lesen, kann man im Befehl 2>&1 verwenden, um stderr auf stdout umzuleiten.

safe_mode / open_basedir: In alten PHP-Versionen mit aktiviertem safe_mode war popen() eingeschränkt. Ab PHP 5.4 ist safe_mode entfernt; stattdessen greift ggf. disable_functions in der php.ini.