首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> 網頁程式設計 >> PHP 程式設計 >> PHP 相關知識

PHP cURL 完整指南:GET/POST 請求、錯誤處理、進階選項與最佳實務

     編輯:PHP 相關知識

cURL 基礎架構

PHP cURL 擴充套件(libcurl)是最強大的 HTTP 客戶端工具,支援 HTTP/HTTPS、FTP、SFTP、MQTT 等多種協定,提供精細的連線控制。

GET 請求完整範例

<?php
function httpGet(string $url, array $options = []): array {
    $ch = curl_init();

    $defaultOptions = [
        CURLOPT_URL => $url,
        CURLOPT_RETURNTRANSFER => true,      // 回傳字串而非直接輸出
        CURLOPT_FOLLOWLOCATION => true,      // 自動追蹤重導向
        CURLOPT_MAXREDIRS => 5,              // 最大重導向次數
        CURLOPT_CONNECTTIMEOUT => 5,         // 連線逾時(秒)
        CURLOPT_TIMEOUT => 30,               // 總執行逾時(秒)
        CURLOPT_SSL_VERIFYPEER => true,      // 驗證 SSL 憑證(生產環境必開)
        CURLOPT_SSL_VERIFYHOST => 2,
        CURLOPT_USERAGENT => 'Mozilla/5.0 (compatible; MyApp/1.0; +https://example.com/bot)',
        CURLOPT_HTTPHEADER => [
            'Accept: application/json',
            'Accept-Language: zh-TW,zh;q=0.9,en;q=0.8',
        ],
        CURLOPT_ENCODING => 'gzip, deflate', // 自動解壓縮回應
    ];

    curl_setopt_array($ch, array_merge($defaultOptions, $options));

    $response = curl_exec($ch);
    $errno = curl_errno($ch);
    $error = curl_error($ch);
    $info = curl_getinfo($ch);
    curl_close($ch);

    if ($errno) {
        throw new RuntimeException("cURL 錯誤 ($errno): $error");
    }

    // 檢查 HTTP 狀態碼
    $httpCode = $info['http_code'] ?? 0;
    if ($httpCode < 200 || $httpCode >= 300) {
        throw new RuntimeException("HTTP 錯誤 $httpCode: " . substr($response, 0, 200));
    }

    return [
        'body' => $response,
        'info' => $info,
        'headers' => $info['header_size'] ? substr($response, 0, $info['header_size']) : '',
    ];
}

// 使用
try {
    $result = httpGet('https://api.example.com/users/123');
    $data = json_decode($result['body'], true);
    print_r($data);
} catch (Exception $e) {
    error_log("API 呼叫失敗:" . $e->getMessage());
}
?>

POST 請求完整範例

<?php
function httpPost(string $url, array $data, array $options = []): array {
    $ch = curl_init();

    $isMultipart = false;
    foreach ($data as $value) {
        if (is_resource($value) || (is_string($value) && strlen($value) > 0 && $value[0] === '@')) {
            $isMultipart = true;
            break;
        }
    }

    $postFields = $isMultipart ? $data : http_build_query($data);

    $defaultOptions = [
        CURLOPT_URL => $url,
        CURLOPT_POST => true,
        CURLOPT_POSTFIELDS => $postFields,
        CURLOPT_RETURNTRANSFER => true,
        CURLOPT_FOLLOWLOCATION => true,
        CURLOPT_MAXREDIRS => 5,
        CURLOPT_CONNECTTIMEOUT => 10,
        CURLOPT_TIMEOUT => 60,
        CURLOPT_SSL_VERIFYPEER => true,
        CURLOPT_SSL_VERIFYHOST => 2,
        CURLOPT_USERAGENT => 'Mozilla/5.0 (compatible; MyApp/1.0)',
        CURLOPT_HTTPHEADER => [
            'Accept: application/json',
            'Content-Type: ' . ($isMultipart ? 'multipart/form-data' : 'application/x-www-form-urlencoded'),
        ],
    ];

    // 如果是 JSON API
    if (isset($options['json']) && $options['json']) {
        $defaultOptions[CURLOPT_POSTFIELDS] = json_encode($data, JSON_UNESCAPED_UNICODE | JSON_THROW_ON_ERROR);
        $defaultOptions[CURLOPT_HTTPHEADER][] = 'Content-Type: application/json';
        $defaultOptions[CURLOPT_HTTPHEADER][] = 'Content-Length: ' . strlen($defaultOptions[CURLOPT_POSTFIELDS]);
    }

    curl_setopt_array($ch, array_merge($defaultOptions, $options));

    $response = curl_exec($ch);
    $errno = curl_errno($ch);
    $error = curl_error($ch);
    $info = curl_getinfo($ch);
    curl_close($ch);

    if ($errno) {
        throw new RuntimeException("cURL 錯誤 ($errno): $error");
    }

    $httpCode = $info['http_code'] ?? 0;
    if ($httpCode < 200 || $httpCode >= 300) {
        throw new RuntimeException("HTTP 錯誤 $httpCode: " . substr($response, 0, 200));
    }

    return ['body' => $response, 'info' => $info];
}

// 表單 POST
$result = httpPost('https://api.example.com/login', [
    'username' => 'alice',
    'password' => 'secret',
]);

// JSON API POST
$result = httpPost('https://api.example.com/orders', [
    'items' => [['sku' => 'A001', 'qty' => 2]],
    'shipping_address' => 'Taipei...',
], ['json' => true]);

// 檔案上傳(multipart)
$result = httpPost('https://api.example.com/upload', [
    'file' => new CURLFile('/path/to/image.jpg', 'image/jpeg', 'photo.jpg'),
    'description' => 'My photo',
]);
?>

進階:並發請求(curl_multi)

需同時呼叫多個 API 時,使用 curl_multi 非阻塞並發:

<?php
function multiGet(array $urls, int $maxConcurrent = 10): array {
    $mh = curl_multi_init();
    $handles = [];
    $results = [];

    // 建立所有 handle
    foreach ($urls as $i => $url) {
        $ch = curl_init($url);
        curl_setopt_array($ch, [
            CURLOPT_RETURNTRANSFER => true,
            CURLOPT_FOLLOWLOCATION => true,
            CURLOPT_TIMEOUT => 30,
            CURLOPT_SSL_VERIFYPEER => true,
            CURLOPT_USERAGENT => 'MyApp/1.0',
        ]);
        curl_multi_add_handle($mh, $ch);
        $handles[$i] = $ch;
    }

    $running = null;
    do {
        curl_multi_exec($mh, $running);
        curl_multi_select($mh, 0.1); // 等待活動
    } while ($running > 0);

    // 收集結果
    foreach ($handles as $i => $ch) {
        $errno = curl_errno($ch);
        $error = curl_error($ch);
        $info = curl_getinfo($ch);
        $body = curl_multi_getcontent($ch);
        
        $results[$i] = [
            'url' => $urls[$i],
            'success' => $errno === 0 && ($info['http_code'] ?? 0) < 300,
            'body' => $body,
            'info' => $info,
            'error' => $errno ? $error : null,
        ];
        curl_multi_remove_handle($mh, $ch);
        curl_close($ch);
    }
    curl_multi_close($mh);

    return $results;
}

// 使用
$urls = [
    'https://api.example.com/users/1',
    'https://api.example.com/users/2',
    'https://api.example.com/users/3',
];
$results = multiGet($urls);
print_r($results);
?>

常用選項速查表

選項 說明 建議值
CURLOPT_TIMEOUT 總執行逾時(秒) API: 30, 上傳: 300
CURLOPT_CONNECTTIMEOUT 連線建立逾時 5-10 秒
CURLOPT_FOLLOWLOCATION 自動追蹤 3xx 重導向 true
CURLOPT_MAXREDIRS 最大重導向次數 5-10
CURLOPT_SSL_VERIFYPEER 驗證 SSL 憑證 true (生產環境)
CURLOPT_CAINFO 自訂 CA 憑證束路徑 /etc/ssl/certs/ca-certificates.crt
CURLOPT_PROXY 代理伺服器 https://proxy:3128
CURLOPT_HTTPHEADER 自訂請求標頭 ['Authorization: Bearer xxx']
CURLOPT_COOKIEJAR/FILE Cookie 儲存/讀取檔案 /tmp/cookies.txt
CURLOPT_RESUME_FROM 斷點續傳位移 大檔案下載

錯誤處理最佳實務

  1. 區分錯誤層級:網路層錯誤(curl_errno)、HTTP 層錯誤(狀態碼)、業務層錯誤(API 回傳 error code)。
  2. 重試機制:針對 5xx、網路逾時、429 (Too Many Requests) 實作指數退避重試。
  3. 熔斷器:連續失敗達閾值時暫停呼叫,避免雪崩效應。
  4. 日誌記錄:記錄 URL、耗時、狀態碼、錯誤訊息、請求/回應體(敏感資料脫敏)。
  5. 憑證釘選:高安全性場景使用 CURLOPT_PINNEDPUBLICKEY 防中間人攻擊。

現代替代方案:Guzzle / HttpClient (Symfony)

大型專案建議使用成熟的 HTTP Client 套件,提供中介軟、重試、非同步、測試雙工等開箱即用功能:

// Guzzle 範例
$client = new GuzzleHttp\Client([
    'base_uri' => 'https://api.example.com/',
    'timeout' => 30,
    'verify' => true,
]);

$response = $client->post('orders', [
    'json' => ['items' => [...]],
    'headers' => ['Authorization' => 'Bearer ' . $token],
]);
$data = json_decode($response->getBody(), true);
?>
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved