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

PHP 遠程內容獲取:cURL vs file_get_contents vs fsockopen 深度對比

     編輯:PHP 相關知識

核心結論先行

cURL 是功能最全、穩定性最好、性能最優的選擇。file_get_contents() 僅適合極簡單的 GET 請求;fsockopen() 屬底層手工組裝協議,維護成本高,除非有特殊需求(如非標準協議、UDP)不建議直接使用。

三者能力對比表

特性 cURL file_get_contents + stream_context fsockopen
支持協議 HTTP/HTTPS、FTP/FTPS、SFTP、SMTP、POP3、IMAP、LDAP、MQTT 等 20+ HTTP/HTTPS、FTP、FTPS、compress.zlib、data、phar 等 TCP/UDP 原始 Socket,協議需手工實現
HTTP 方法 GET、POST、PUT、DELETE、PATCH、HEAD、OPTIONS 等任意 GET、POST(需構造 context) 任意(手工構造原始請求報文)
HTTPS/SSL 完整支持(驗證、客戶端證書、TLS 版本控制) 支持(依賴 OpenSSL 擴展,配置較繁瑣) 需手工實現 SSL 握手(極不推薦)
Cookie 管理 自動維護 Cookie Jar,支援會話保持 需手工解析 Set-Cookie、拼接 Cookie 頭 完全手工
重定向跟隨 CURLOPT_FOLLOWLOCATION 一鍵啟用 需手工解析 Location、循環請求 完全手工
超時控制 連接超時、總超時、DNS 緩存超時分離設置 單一 timeout 參數,精度有限 手工 stream_set_timeout()
代理支持 HTTP/HTTPS/SOCKS4/5 代理、認證、隧道 僅 HTTP 代理 手工實現
文件上傳 CURLFile、multipart/form-data 原生支持 需手工構造 multipart body 手工構造
斷點續傳 CURLOPT_RANGE 原生支持 需手工發送 Range 頭 手工發送
性能(連接復用) 支持 Keep-Alive、連接池、DNS 緩存 每次請求獨立連接,無復用 手工管理持久連接
錯誤處理 豐富錯誤碼、curl_error()、詳細信息 僅返回 false,需配合 error_get_last() 手工檢查 errno/errstr
學習曲線 中等(選項多但規律清晰) 低(API 簡單) 高(需精通協議細節)

實戰代碼模板:cURL 通用封裝

<?php
/**
 * 通用 cURL 請求封裝
 * @param string $url      請求 URL
 * @param array  $options  覆蓋默認選項
 * @return array|false     ['code' => HTTP狀態碼, 'body' => 響應體, 'headers' => 響應頭數組, 'info' => curl_getinfo 完整信息]
 */
function httpRequest($url, array $options = []) {
    $ch = curl_init();

    // 默認配置
    $defaults = [
        CURLOPT_URL            => $url,
        CURLOPT_RETURNTRANSFER => true,        // 返回字符串而非直接輸出
        CURLOPT_HEADER         => true,        // 包含響應頭,便於分離
        CURLOPT_FOLLOWLOCATION => true,        // 自動跟隨重定向
        CURLOPT_MAXREDIRS      => 5,           // 最大重定向次數
        CURLOPT_CONNECTTIMEOUT => 10,          // 連接超時(秒)
        CURLOPT_TIMEOUT        => 30,          // 總超時(秒)
        CURLOPT_SSL_VERIFYPEER => true,        // 驗證 SSL 證書
        CURLOPT_SSL_VERIFYHOST => 2,
        CURLOPT_USERAGENT      => 'Mozilla/5.0 (compatible; PHP cURL Client)', // 模擬瀏覽器
        CURLOPT_ENCODING       => 'gzip,deflate', // 自動解壓
        CURLOPT_COOKIEJAR      => sys_get_temp_dir() . '/curl_cookie.txt', // Cookie 持久化
        CURLOPT_COOKIEFILE     => sys_get_temp_dir() . '/curl_cookie.txt',
        CURLOPT_IPRESOLVE      => CURL_IPRESOLVE_V4, // 強制 IPv4,避免部分環境 IPv6 故障
    ];

    // 合並用戶選項(用戶選項優先)
    curl_setopt_array($ch, $defaults + $options);

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

    if ($errno) {
        error_log("cURL Error [$errno]: $error | URL: $url");
        return false;
    }

    // 分離頭和體
    $headerSize = $info['header_size'];
    $headersRaw = substr($response, 0, $headerSize);
    $body       = substr($response, $headerSize);

    // 解析響應頭為關聯數組
    $headers = [];
    foreach (explode("\r\n", $headersRaw) as $line) {
        if (strpos($line, ':') !== false) {
            list($key, $value) = explode(':', $line, 2);
            $headers[trim($key)] = trim($value);
        }
    }

    return [
        'code'    => $info['http_code'],
        'body'    => $body,
        'headers' => $headers,
        'info'    => $info,
    ];
}

// === 使用示例 ===

// 1. 簡單 GET
$res = httpRequest('https://api.example.com/users');
if ($res && $res['code'] === 200) {
    $data = json_decode($res['body'], true);
}

// 2. POST JSON
$res = httpRequest('https://api.example.com/login', [
    CURLOPT_POST       => true,
    CURLOPT_POSTFIELDS => json_encode(['username' => 'admin', 'password' => 'secret']),
    CURLOPT_HTTPHEADER => ['Content-Type: application/json', 'Accept: application/json'],
]);

// 3. 文件上傳 (PHP 5.5+ CURLFile)
$res = httpRequest('https://api.example.com/upload', [
    CURLOPT_POST       => true,
    CURLOPT_POSTFIELDS => [
        'file' => new CURLFile('/path/to/image.jpg', 'image/jpeg', 'avatar.jpg'),
        'folder' => 'avatars',
    ],
]);

// 4. 獲取 HTTP 狀態碼(不下載內容)
$ch = curl_init('https://example.com');
curl_setopt_array($ch, [CURLOPT_NOBODY => true, CURLOPT_RETURNTRANSFER => true]);
curl_exec($ch);
$statusCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);
?>

常見避坑指南

  1. HTTPS 證書驗證失敗:生產環境絕不可長期使用 CURLOPT_SSL_VERIFYPEER => false。正確做法是配置 CURLOPT_CAINFO 指向最新的 cacert.pem(可從 curl.se 下載)。
  2. 超時設置陷阱:CURLOPT_TIMEOUT 是總超時(含 DNS、連接、傳輸),若需精確控制連接階段,必須同時設置 CURLOPT_CONNECTTIMEOUT。
  3. POST 欄位陣列自動轉 multipart:當 CURLOPT_POSTFIELDS 傳入數組時,cURL 自動發送 multipart/form-data;若需發送 application/x-www-form-urlencoded,請傳入 http_build_query($data) 字符串。
  4. 大文件下載內存爆炸:啟用 CURLOPT_RETURNTRANSFER 會將整個響應體載入內存。下載大文件請改用 CURLOPT_WRITEFUNCTION 回調寫入磁盤,或直接用 fopen() + CURLOPT_FILE。
  5. 並發請求:使用 curl_multi_* 系列函數實現並發,單進程可輕鬆管理數百並發連接。

性能實測參考(獲取 100KB 頁面,本地網絡)

file_get_contents: 平均 2.3 秒(含 DNS、TLS 握手、無連接復用)
cURL (單請求):    平均 0.65 秒
cURL (Keep-Alive): 平均 0.12 秒(復用連接,僅發送請求行)

差距主要來自:cURL 內建 DNS 緩存、TCP 連接復用、更高效的 SSL 會話復用、原生 C 實現避免 PHP 用戶態拷貝。

何時仍需用到 fsockopen?

  • 非標準 TCP/UDP 協議(如自定義二進制協議、DNS 查詢、WHOIS、Memcached/Redis 原生協議)
  • 極度受限環境(禁用 cURL、stream 擴展)
  • 教學演示 HTTP 協議細節

其餘 99% 場景,**請直接使用 cURL**。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved