Signatur
Beschreibung
stream_set_blocking() ermöglicht es, das I/O-Verhalten eines Streams umzuschalten. Im blockierenden Modus (Standard) wartet PHP bei Lese- oder Schreiboperationen so lange, bis Daten verfügbar sind oder die Operation abgeschlossen ist. Im nicht-blockierenden Modus kehren Funktionen wie fread() oder fgets() sofort zurück, auch wenn (noch) keine Daten vorliegen.
Der nicht-blockierende Modus ist besonders nützlich bei der Verarbeitung mehrerer Streams gleichzeitig – etwa in Kombination mit stream_select() – da hier keine einzelne Operation den gesamten Prozess blockieren soll. Typische Anwendungsfälle sind Netzwerk-Server, asynchrone Kommunikation mit Child-Prozessen über proc_open() sowie das gleichzeitige Lesen von stdout und stderr.
Wird ein Stream im nicht-blockierenden Modus gelesen und sind keine Daten verfügbar, gibt fread() einen leeren String zurück, anstatt zu warten. Daher sollte der Rückgabewert in einer Schleife mit geeigneten Wartestrategien (z. B. usleep()) behandelt werden, um CPU-Busy-Loops zu vermeiden.
Die Funktion entspricht dem POSIX-Aufruf O_NONBLOCK und funktioniert zuverlässig mit dateibasierten, Netzwerk- und Prozess-Streams. Bei bestimmten Wrapper-Typen (z. B. Plain-File-Streams unter Windows) kann der nicht-blockierende Modus keine Wirkung haben.
Parameter
| Name | Typ | Default | Beschreibung |
|---|---|---|---|
| $stream Pflicht | resource | Der Stream, dessen Blockier-Modus geändert werden soll. Kann ein Datei-, Netzwerk- oder Prozess-Stream sein. | |
| $enable Pflicht | bool | true aktiviert den blockierenden Modus (Standard), false aktiviert den nicht-blockierenden Modus. |
Rückgabewert
true bei Erfolg zurück, false bei einem Fehler (z. B. wenn der Stream-Typ den Modus nicht unterstützt).Beispiele
Nicht-blockierendes Lesen von stdout und stderr eines Child-Prozesses
<?php
$descriptors = [
0 => ['pipe', 'r'], // stdin
1 => ['pipe', 'w'], // stdout
2 => ['pipe', 'w'], // stderr
];
$process = proc_open('ping -c 3 localhost', $descriptors, $pipes);
if (is_resource($process)) {
// Beide Ausgabe-Streams auf nicht-blockierend setzen
stream_set_blocking($pipes[1], false);
stream_set_blocking($pipes[2], false);
while (proc_get_status($process)['running']) {
$stdout = fread($pipes[1], 4096);
$stderr = fread($pipes[2], 4096);
if ($stdout !== '') {
echo 'STDOUT: ' . $stdout;
}
if ($stderr !== '') {
echo 'STDERR: ' . $stderr;
}
usleep(100000); // 100 ms warten, CPU schonen
}
fclose($pipes[1]);
fclose($pipes[2]);
proc_close($process);
}
Nicht-blockierender TCP-Socket mit stream_select
<?php
$socket = stream_socket_client('tcp://example.com:80', $errno, $errstr, 5);
if (!$socket) {
die("Verbindungsfehler: $errstr ($errno)\n");
}
// Stream auf nicht-blockierend setzen
stream_set_blocking($socket, false);
// HTTP-Anfrage senden
fwrite($socket, "GET / HTTP/1.0\r\nHost: example.com\r\n\r\n");
$response = '';
$read = [$socket];
$write = null;
$except = null;
// Auf Daten warten (max. 5 Sekunden)
while (stream_select($read, $write, $except, 5) > 0) {
$chunk = fread($socket, 4096);
if ($chunk === '' || $chunk === false) {
break;
}
$response .= $chunk;
$read = [$socket]; // Zurücksetzen für nächsten Durchlauf
}
fclose($socket);
echo substr($response, 0, 200);
// Wichtig · Fallstricke
Windows-Einschränkung: Unter Windows hat der nicht-blockierende Modus bei gewöhnlichen Datei-Streams (fopen() auf lokale Dateien) keine Wirkung, da das Windows-API keine asynchrone I/O auf diese Art unterstützt. Für Prozess-Pipes und Netzwerk-Sockets funktioniert er jedoch korrekt.
CPU-Busy-Loop vermeiden: Wird im nicht-blockierenden Modus in einer engen Schleife ohne usleep() oder stream_select() gelesen, kann die CPU unnötig stark ausgelastet werden, da die Schleife bei fehlenden Daten sehr schnell iteriert.
Rückgabewert prüfen: fread() gibt im nicht-blockierenden Modus einen leeren String zurück, wenn keine Daten verfügbar sind – dies ist kein Fehler. Erst false signalisiert einen echten Lesefehler oder EOF. Die Unterscheidung ist wichtig, um vorzeitiges Beenden der Leseschleife zu vermeiden.