Signatur
Beschreibung
curl_multi_exec() verarbeitet alle Verbindungen, die einem cURL-Multi-Handle zugeordnet sind. Sie sollte in einer Schleife aufgerufen werden, bis der per Referenz übergebene Parameter $still_running den Wert 0 erreicht, was bedeutet, dass alle Transfers abgeschlossen sind.
Die Funktion ist das Herzstück des nicht-blockierenden cURL-Paralleltransfers: Statt auf jede HTTP-Anfrage einzeln zu warten (curl_exec()), können mehrere Anfragen gleichzeitig gestartet und bearbeitet werden. Das reduziert die Gesamtlaufzeit erheblich, wenn viele externe Ressourcen abgerufen werden müssen.
In modernen PHP-Versionen (ab PHP 7.1) sollte curl_multi_exec() zusammen mit curl_multi_select() verwendet werden, um CPU-intensives Busy-Waiting zu vermeiden. curl_multi_select() blockiert den Prozess, bis mindestens ein Handle wieder Aktivität meldet, und spart so CPU-Zeit.
Der Rückgabewert ist eine der CURLM_*-Konstanten. Nur CURLM_OK signalisiert fehlerfreie Ausführung. Nach Abschluss aller Transfers sollten die Ergebnisse mit curl_multi_info_read() ausgelesen und die Einzel-Handles mit curl_multi_remove_handle() entfernt werden.
Parameter
| Name | Typ | Default | Beschreibung |
|---|---|---|---|
| $multi_handle Pflicht | CurlMultiHandle | Ein mit curl_multi_init() erzeugtes cURL-Multi-Handle, dem zuvor mit curl_multi_add_handle() Einzel-Handles hinzugefügt wurden. |
|
| $still_running Pflicht | int | Per Referenz übergebene Variable, die nach jedem Aufruf die Anzahl der noch laufenden Transfers enthält. Sobald dieser Wert 0 ist, sind alle Übertragungen abgeschlossen. |
Rückgabewert
CURLM_*-Konstante zurück. CURLM_OK (0) bedeutet Erfolg. Mögliche Fehlerwerte sind u. a. CURLM_BAD_HANDLE, CURLM_BAD_EASY_HANDLE, CURLM_OUT_OF_MEMORY und CURLM_INTERNAL_ERROR.Beispiele
Mehrere URLs parallel abrufen
<?php
$urls = [
'https://httpbin.org/get?a=1',
'https://httpbin.org/get?a=2',
'https://httpbin.org/get?a=3',
];
$multiHandle = curl_multi_init();
$handles = [];
// Einzel-Handles anlegen und zum Multi-Handle hinzufügen
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;
}
// Transfers ausführen
$stillRunning = 0;
do {
$status = curl_multi_exec($multiHandle, $stillRunning);
if ($stillRunning > 0) {
// Blockiert bis Aktivität auf einem Handle – spart CPU
curl_multi_select($multiHandle);
}
} while ($stillRunning > 0 && $status === CURLM_OK);
// Ergebnisse auslesen
foreach ($handles as $i => $ch) {
$response = curl_multi_getcontent($ch);
$error = curl_error($ch);
if ($error) {
echo "Fehler bei URL $i: $error\n";
} else {
echo "Antwort URL $i: " . substr($response, 0, 80) . "...\n";
}
curl_multi_remove_handle($multiHandle, $ch);
curl_close($ch);
}
curl_multi_close($multiHandle);
Fehlerbehandlung mit curl_multi_info_read()
<?php
$multiHandle = curl_multi_init();
$handles = [];
$urls = [
'https://httpbin.org/status/200',
'https://httpbin.org/status/404',
'https://httpbin.org/delay/1',
];
foreach ($urls as $i => $url) {
$ch = curl_init($url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_TIMEOUT, 5);
curl_multi_add_handle($multiHandle, $ch);
$handles[$i] = $ch;
}
$stillRunning = 0;
do {
$status = curl_multi_exec($multiHandle, $stillRunning);
if ($status !== CURLM_OK) {
echo 'Multi-Fehler: ' . curl_multi_strerror($status) . PHP_EOL;
break;
}
if ($stillRunning > 0) {
curl_multi_select($multiHandle);
}
// Bereits fertige Handles sofort auswerten
while ($info = curl_multi_info_read($multiHandle)) {
$ch = $info['handle'];
$curlCode = $info['result'];
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
$url = curl_getinfo($ch, CURLINFO_EFFECTIVE_URL);
echo "URL: $url | HTTP: $httpCode | cURL-Code: $curlCode\n";
curl_multi_remove_handle($multiHandle, $ch);
curl_close($ch);
}
} while ($stillRunning > 0);
curl_multi_close($multiHandle);
// Wichtig · Fallstricke
Busy-Waiting vermeiden: Wird curl_multi_select() weggelassen, dreht die Schleife bei wartenden Verbindungen mit 100 % CPU-Last. In manchen Umgebungen (z. B. mit bestimmten cURL-Versionen und libcurl-Backends) kann curl_multi_select() -1 zurückgeben – in diesem Fall empfiehlt sich ein kurzes usleep(100), um die CPU zu entlasten.
Ressourcenlecks: Jeder mit curl_multi_add_handle() hinzugefügte Einzel-Handle muss nach Abschluss mit curl_multi_remove_handle() entfernt und anschließend mit curl_close() freigegeben werden. Andernfalls entstehen Speicherlecks, besonders in lang laufenden Prozessen.
Timeouts: Timeouts (gesetzt mit CURLOPT_TIMEOUT) gelten je Einzel-Handle. Ein globaler Timeout für das gesamte Multi-Handle existiert nicht – ggf. muss dieser manuell per microtime() implementiert werden.
PHP-Version: Ab PHP 8.0 sind die cURL-Handles vom Typ CurlMultiHandle statt resource. Der Aufruf und das Verhalten sind identisch, aber der Typ-Hinweis in Funktionssignaturen ändert sich.