Start · Sprachen · PHP · Referenz · curl_multi_select

curl_multi_select

Funktion

Blockiert, bis auf einer der cURL-Multi-Handle-Verbindungen gelesen oder geschrieben werden kann, oder ein Timeout eintritt.

seit PHP 5.0.0 Kategorie: http

Signatur

curl_multi_select(CurlMultiHandle $multi_handle, float $timeout = 1.0): int

Beschreibung

curl_multi_select() wartet darauf, dass mindestens eine der Verbindungen innerhalb eines cURL-Multi-Handles bereit ist, Daten zu lesen oder zu schreiben. Sie ist das PHP-Pendant zur C-Funktion select() und dient dazu, eine Busy-Wait-Schleife zu vermeiden, wenn parallele HTTP-Anfragen mit curl_multi_exec() verarbeitet werden.

Der Parameter timeout legt die maximale Wartezeit in Sekunden fest. Solange keine Verbindung bereit ist, schläft der Prozess und gibt CPU-Zeit frei. Sobald eine Verbindung aktiviert wird oder der Timeout abläuft, kehrt die Funktion zurück, und man kann curl_multi_exec() erneut aufrufen, um die Übertragungen fortzusetzen.

Ein typisches Verwendungsmuster ist eine Schleife, in der zunächst curl_multi_exec() aufgerufen wird, danach curl_multi_select(), um effizient auf weitere I/O-Ereignisse zu warten. Ohne diesen Aufruf würde eine reine while-Schleife die CPU unnötig belasten.

Bei einem Rückgabewert von -1 trat ein interner select()-Fehler auf. In diesem Fall empfiehlt es sich, kurz zu schlafen (z. B. mit usleep()), bevor man die Schleife fortsetzt, um eine potenzielle Endlosschleife unter Last zu vermeiden.

Parameter

Name Typ Default Beschreibung
$multi_handle Pflicht CurlMultiHandle Ein gültiges cURL-Multi-Handle, das mit curl_multi_init() erstellt wurde.
$timeout float 1.0 Maximale Wartezeit in Sekunden. Darf auch einen Bruchteil einer Sekunde angeben, z. B. 0.5 für 500 Millisekunden. Wird 0 übergeben, kehrt die Funktion sofort zurück.

Rückgabewert

Typ
int
Beschreibung
Gibt die Anzahl der beschreibbaren oder lesbaren Deskriptoren zurück, 0 bei Timeout, oder -1 bei einem internen Fehler von select().

Beispiele

Parallele HTTP-Anfragen effizient ausführen

<?php
$urls = [
    'https://example.com/api/endpoint1',
    'https://example.com/api/endpoint2',
    'https://example.com/api/endpoint3',
];

$multiHandle = curl_multi_init();
$handles = [];

foreach ($urls as $i => $url) {
    $ch = curl_init($url);
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
    curl_setopt($ch, CURLOPT_TIMEOUT, 10);
    curl_multi_add_handle($multiHandle, $ch);
    $handles[$i] = $ch;
}

$running = null;
do {
    $status = curl_multi_exec($multiHandle, $running);

    if ($running) {
        // Blockiert bis zu 1 Sekunde — schont die CPU
        $selected = curl_multi_select($multiHandle, 1.0);
        if ($selected === -1) {
            // Interner select()-Fehler: kurz schlafen, dann erneut versuchen
            usleep(100);
        }
    }
} while ($running > 0 && $status === CURLM_OK);

foreach ($handles as $i => $ch) {
    $response = curl_multi_getcontent($ch);
    echo "Antwort von URL $i: " . strlen($response) . " Bytes\n";
    curl_multi_remove_handle($multiHandle, $ch);
    curl_close($ch);
}

curl_multi_close($multiHandle);
Antwort von URL 0: 1234 Bytes Antwort von URL 1: 5678 Bytes Antwort von URL 2: 910 Bytes

Sehr kurzer Timeout für nicht-blockierendes Verhalten

<?php
$mh = curl_multi_init();

$ch1 = curl_init('https://httpbin.org/delay/2');
curl_setopt($ch1, CURLOPT_RETURNTRANSFER, true);
curl_multi_add_handle($mh, $ch1);

$running = null;
$iterations = 0;

do {
    curl_multi_exec($mh, $running);

    // Timeout von 50 ms: erlaubt es, zwischendurch andere Arbeit zu erledigen
    $ready = curl_multi_select($mh, 0.05);
    $iterations++;

    // Hier könnte andere Logik ausgeführt werden ...
} while ($running > 0);

echo "Benötigte Iterationen: $iterations\n";
echo curl_multi_getcontent($ch1);

curl_multi_remove_handle($mh, $ch1);
curl_close($ch1);
curl_multi_close($mh);
Benötigte Iterationen: 42

// Wichtig · Fallstricke

-1-Rückgabewert: Auf manchen Systemen kann curl_multi_select() gelegentlich -1 zurückgeben, selbst ohne echten Fehler (bekannter Bug mit bestimmten libcurl-Versionen). Daher sollte die Schleife bei -1 nicht sofort abbrechen, sondern mit einem kurzen usleep() fortfahren.

Keine vollständige Alternative zu reaktiven I/O-Modellen: Für sehr viele gleichzeitige Verbindungen (Hunderte bis Tausende) sind eventbasierte Alternativen wie ReactPHP oder Amphp besser geeignet, da select() auf vielen Systemen auf 1024 Dateideskriptoren begrenzt ist.

Timeout-Präzision: Der tatsächliche Timeout kann von der Betriebssystemgenauigkeit und der internen libcurl-Zeitplanung abhängen. Sehr kleine Werte (z. B. 0.001) sind möglicherweise nicht exakt.