Start · Sprachen · PHP · Referenz · curl_multi_exec

curl_multi_exec

Funktion

Führt alle aktiven Übertragungen eines cURL-Multi-Handles aus und gibt zurück, wie viele Verbindungen noch aktiv sind.

seit PHP 5.0.0 Kategorie: http

Signatur

curl_multi_exec(CurlMultiHandle $multi_handle, int &$still_running): int

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

Typ
int
Beschreibung
Gibt eine 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);
Antwort URL 0: {\n "args": {\n "a": "1"\n }, ...\nAntwort URL 1: {\n "args": {\n "a": "2"\n }, ...\nAntwort URL 2: {\n "args": {\n "a": "3"\n }, ...

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);
URL: https://httpbin.org/status/200 | HTTP: 200 | cURL-Code: 0 URL: https://httpbin.org/status/404 | HTTP: 404 | cURL-Code: 0 URL: https://httpbin.org/delay/1 | HTTP: 200 | cURL-Code: 0

// 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.