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

PHP HTTP 請求利器:file_get_contents vs cURL 深度比較與最佳實踐

     編輯:PHP 相關知識

核心差異一覽

特性file_get_contentscURL (curl_get_contents)
支援協定HTTP/HTTPS、FTP、本地檔案HTTP/HTTPS、FTP、SFTP、LDAP、SMTP 等 20+ 協定
超時控制依賴 default_socket_timeout、stream_context原生 CURLOPT_TIMEOUT、CURLOPT_CONNECTTIMEOUT 毫秒級精準
錯誤處理回傳 false,需配合 error_get_last()curl_errno()/curl_error() 詳細錯誤碼與訊息
HTTP 功能基本 GET/POST、Header、Cookie完整 REST 動詞、多部分表單、斷點續傳、代理、SSL 驗證、HTTP/2
效能/穩定度大檔案/慢回應易導致進程阻塞、CPU 100%非阻塞選項、多工處理 (curl_multi)、生產級穩定
本地檔案讀取原生支援、記憶體映射優化不支援 (需 fopen)

file_get_contents 陷阱與修正

❌ 典型失控場景

  • 遠端伺服器無回應 → PHP 進程阻塞至 max_execution_time 或 default_socket_timeout (預設 60s)
  • 大量併發請求 → 進程堆積、CPU 飆升、記憶體耗盡
  • 無法區分「連線失敗」與「HTTP 4xx/5xx」

✅ 正確設定超時 (三種方式)

<?php
// 方式 1: stream_context (推薦,精確控制讀取超時)
$ctx = stream_context_create([
    'http' => [
        'timeout' => 5,          // 總超時 5 秒 (含連線+讀取)
        'method'  => 'GET',
        'header'  => "User-Agent: MyApp/1.0\r\nAccept: application/json\r\n",
    ],
    'ssl' => [
        'verify_peer'      => true,
        'verify_peer_name' => true,
    ],
]);
$content = @file_get_contents('https://api.example.com/data', false, $ctx);
if ($content === false) {
    $err = error_get_last();
    // 記錄 $err['message']
}

// 方式 2: ini_set (影響全域 socket)
ini_set('default_socket_timeout', 5);

// 方式 3: php.ini (永久生效)
; default_socket_timeout = 5

生產級 cURL 封裝函式

<?php
/**
 * 通用 cURL GET 請求
 * @param string $url
 * @param int $timeout 總超時秒數
 * @param int $connectTimeout 連線超時秒數
 * @param array $headers 額外 Header ['Key: Value', ...]
 * @return array ['code'=>HTTP狀態碼, 'body'=>回應內容, 'error'=>錯誤訊息]
 */
function curl_get($url, $timeout = 10, $connectTimeout = 5, $headers = []) {
    $ch = curl_init();
    curl_setopt_array($ch, [
        CURLOPT_URL            => $url,
        CURLOPT_RETURNTRANSFER => true,
        CURLOPT_TIMEOUT        => $timeout,
        CURLOPT_CONNECTTIMEOUT => $connectTimeout,
        CURLOPT_HTTPHEADER     => array_merge([
            'User-Agent: MyApp/1.0 (+https://example.com/bot)',
            'Accept: application/json, text/html',
        ], $headers),
        CURLOPT_SSL_VERIFYPEER => true,   // 生產環境必須驗證
        CURLOPT_SSL_VERIFYHOST => 2,
        CURLOPT_FOLLOWLOCATION => true,   // 自動跟隨 3xx 重導
        CURLOPT_MAXREDIRS      => 5,
        CURLOPT_ENCODING       => 'gzip, deflate', // 自動解壓
        CURLOPT_IPRESOLVE      => CURL_IPRESOLVE_V4, // 或 V6 / ANY
    ]);
    
    $body = curl_exec($ch);
    $errno = curl_errno($ch);
    $error = $errno ? curl_error($ch) : '';
    $code  = $errno ? 0 : (int)curl_getinfo($ch, CURLINFO_HTTP_CODE);
    curl_close($ch);
    
    return compact('code', 'body', 'error');
}

// 使用範例
$res = curl_get('https://httpbin.org/get', 5, 2);
if ($res['code'] === 200) {
    $data = json_decode($res['body'], true);
    // 處理資料...
} else {
    error_log("API請求失敗: HTTP {$res['code']} - {$res['error']}");
}
?>

進階:併發請求 curl_multi (批量 API 呼叫)

<?php
function curl_multi_get(array $urls, $timeout = 10) {
    $mh = curl_multi_init();
    $handles = [];
    
    foreach ($urls as $i => $url) {
        $ch = curl_init($url);
        curl_setopt_array($ch, [
            CURLOPT_RETURNTRANSFER => true,
            CURLOPT_TIMEOUT        => $timeout,
            CURLOPT_SSL_VERIFYPEER => true,
        ]);
        curl_multi_add_handle($mh, $ch);
        $handles[$i] = $ch;
    }
    
    $running = null;
    do {
        curl_multi_exec($mh, $running);
        curl_multi_select($mh, 0.1); // 避免 CPU 空轉
    } while ($running > 0);
    
    $results = [];
    foreach ($handles as $i => $ch) {
        $results[$i] = [
            'code'  => curl_getinfo($ch, CURLINFO_HTTP_CODE),
            'body'  => curl_multi_getcontent($ch),
            'error' => curl_error($ch),
        ];
        curl_multi_remove_handle($mh, $ch);
        curl_close($ch);
    }
    curl_multi_close($mh);
    return $results;
}

// 同時請求 5 個 API,總耗時 ≈ 最慢的那一個
$urls = [
    'https://api1.example.com',
    'https://api2.example.com',
    // ...
];
$responses = curl_multi_get($urls, 5);
?>

效能診斷實戰:用 strace 找出 file_get_contents 卡死進程

  1. top 找出 CPU 100% 的 php-cgi PID
  2. strace -p 觀察系統呼叫
  3. 若大量重複出現:
    select(7, [6], [6], [], {15, 0}) = 1 (out [6], left {15, 0})
    poll([{fd=6, events=POLLIN}], 1, 0) = 0 (Timeout)

    → 確定是 file_get_contents 等待網路回應導致阻塞

選型決策樹

需要讀取本地檔案? ──Yes──→ file_get_contents (原生最優)
       │
       No
       │
需要 POST/PUT/DELETE/PATCH? ──Yes──→ cURL
       │
       No
       │
簡單 GET、可容忍偶發阻塞? ──Yes──→ file_get_contents + stream_context 超時
       │
       No
       │
生產環境 / 高併發 / 需精準錯誤處理 / HTTPS 雙向認證 / 代理 / HTTP/2 ──→ cURL (唯一正解)

現代化替代方案:Guzzle / Http\Client (PSR-18)

新專案**強烈建議直接使用 PSR-18 相容的 HTTP Client** (Guzzle 7+, Symfony HttpClient, HTTPlug),優點:

  • 介面標準化,易於測試與替換底層驅動
  • 內建重試、中介軟、非同步 (Promise)、日誌整合
  • 型別提示完整,IDE 友善
// Guzzle 7 範例
use GuzzleHttp\Client;
$client = new Client(['timeout' => 5, 'verify' => true]);
$response = $client->get('https://api.example.com/data');
$data = json_decode($response->getBody()->getContents(), true);
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved