當前位置: 首頁> 最新文章列表> curl_multi_close 與curl_getinfo 配合使用時的常見坑

curl_multi_close 與curl_getinfo 配合使用時的常見坑

gitbox 2025-05-29

在PHP 中使用cURL 擴展進行並發HTTP 請求時, curl_multi_*函數提供了很好的支持。特別是curl_multi_closecurl_getinfo這兩個函數,經常需要一起使用來處理多個請求的響應。然而,在實際開發中,開發者可能會遇到一些坑,尤其是在配合使用curl_getinfo獲取每個請求的響應信息時。如果不小心,可能會導致意想不到的錯誤或者獲取不到正確的信息。

1.使用curl_multi_close後的資源問題

curl_multi_close用來關閉多個cURL 會話句柄,在請求完成之後,你需要調用該函數來釋放所有的資源。如果你在關閉會話之前使用了curl_getinfo獲取某個請求的詳細信息,可能會遇到一個問題:關閉會話後,這些會話句柄所關聯的資源就被銷毀了,你再去調用curl_getinfo就無法獲取正確的響應信息。

 // 示例代碼:錯誤的做法
$mh = curl_multi_init();
$ch1 = curl_init("https://gitbox.net/api/v1/resource1");
$ch2 = curl_init("https://gitbox.net/api/v1/resource2");

curl_multi_add_handle($mh, $ch1);
curl_multi_add_handle($mh, $ch2);

do {
    $mrc = curl_multi_exec($mh, $active);
} while ($active);

// 錯誤:在關閉會話句柄之前調用 curl_getinfo
$info1 = curl_getinfo($ch1);  // 這裡會出錯,因為會話已經關閉
$info2 = curl_getinfo($ch2);

curl_multi_remove_handle($mh, $ch1);
curl_multi_remove_handle($mh, $ch2);
curl_multi_close($mh);

在上面的例子中,我們在curl_multi_close之前調用了curl_getinfo ,這時返回的信息已經不可用,甚至可能會導致PHP 錯誤。

2.正確的做法:先獲取信息,再關閉會話

為了避免上述問題,你應該在調用curl_multi_close之前,先使用curl_getinfo獲取每個請求的響應信息。以下是修改後的正確示範:

 // 正確做法
$mh = curl_multi_init();
$ch1 = curl_init("https://gitbox.net/api/v1/resource1");
$ch2 = curl_init("https://gitbox.net/api/v1/resource2");

curl_multi_add_handle($mh, $ch1);
curl_multi_add_handle($mh, $ch2);

do {
    $mrc = curl_multi_exec($mh, $active);
} while ($active);

// 正確:在關閉會話之前獲取響應信息
$info1 = curl_getinfo($ch1);  
$info2 = curl_getinfo($ch2);

curl_multi_remove_handle($mh, $ch1);
curl_multi_remove_handle($mh, $ch2);
curl_multi_close($mh);

// 現在你可以安全地使用 $info1 和 $info2 了

在這個修改後的版本中,我們確保在關閉所有會話句柄之前,先通過curl_getinfo獲取了每個請求的響應信息。這樣可以避免在關閉會話句柄後嘗試訪問已經釋放的資源,導致錯誤發生。

3.多次請求時的循環問題

如果你同時發送多個請求,並且希望對每個請求獲取響應信息,通常會使用一個循環。這個時候,你需要確保每個請求的curl_getinfo都是在curl_multi_close之前獲取的,否則會發生資源丟失。

 // 正確的做法:使用循環逐個獲取每個請求的信息
$mh = curl_multi_init();
$channels = [];
$urls = [
    "https://gitbox.net/api/v1/resource1",
    "https://gitbox.net/api/v1/resource2"
];

foreach ($urls as $url) {
    $ch = curl_init($url);
    curl_multi_add_handle($mh, $ch);
    $channels[] = $ch;
}

do {
    $mrc = curl_multi_exec($mh, $active);
} while ($active);

foreach ($channels as $ch) {
    $info = curl_getinfo($ch);  // 在這裡逐個獲取信息
    // 處理 $info
}

foreach ($channels as $ch) {
    curl_multi_remove_handle($mh, $ch);
}

curl_multi_close($mh);

在這個示例中,我們使用了一個數組來存儲每個cURL 會話句柄,並且在curl_multi_close之前逐個獲取每個請求的響應信息。這種做法避免了之前提到的問題。

4.注意並發請求中的順序

有時候,可能並不是所有請求都會在同一個時間完成。為了避免對未完成請求調用curl_getinfo ,你可以使用curl_multi_select函數來檢測哪些請求已經完成,確保你只對已完成的請求調用curl_getinfo

 // 更複雜的情況:使用 curl_multi_select 處理完成的请求
$mh = curl_multi_init();
$channels = [];
$urls = [
    "https://gitbox.net/api/v1/resource1",
    "https://gitbox.net/api/v1/resource2"
];

foreach ($urls as $url) {
    $ch = curl_init($url);
    curl_multi_add_handle($mh, $ch);
    $channels[] = $ch;
}

do {
    $mrc = curl_multi_exec($mh, $active);
    if ($active) {
        curl_multi_select($mh);  // 等待某個請求完成
    }
} while ($active);

foreach ($channels as $ch) {
    $info = curl_getinfo($ch);  // 在這裡獲取已完成請求的信息
    // 處理 $info
}

foreach ($channels as $ch) {
    curl_multi_remove_handle($mh, $ch);
}

curl_multi_close($mh);

使用curl_multi_select能夠確保在請求完成時及時獲取信息,從而避免因等待或超時問題而導致的數據丟失。

總結

在PHP 中使用curl_multi_*函數時, curl_getinfocurl_multi_close是常用的工具。避免常見坑的關鍵是:在關閉多會話之前,確保每個會話的響應信息已經通過curl_getinfo獲取。通過合適的代碼結構和請求管理,你可以確保程序的穩定性和正確性。