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 簡單) | 高(需精通協議細節) |
<?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);
?>
CURLOPT_SSL_VERIFYPEER => false。正確做法是配置 CURLOPT_CAINFO 指向最新的 cacert.pem(可從 curl.se 下載)。CURLOPT_TIMEOUT 是總超時(含 DNS、連接、傳輸),若需精確控制連接階段,必須同時設置 CURLOPT_CONNECTTIMEOUT。CURLOPT_POSTFIELDS 傳入數組時,cURL 自動發送 multipart/form-data;若需發送 application/x-www-form-urlencoded,請傳入 http_build_query($data) 字符串。CURLOPT_RETURNTRANSFER 會將整個響應體載入內存。下載大文件請改用 CURLOPT_WRITEFUNCTION 回調寫入磁盤,或直接用 fopen() + CURLOPT_FILE。curl_multi_* 系列函數實現並發,單進程可輕鬆管理數百並發連接。file_get_contents: 平均 2.3 秒(含 DNS、TLS 握手、無連接復用)
cURL (單請求): 平均 0.65 秒
cURL (Keep-Alive): 平均 0.12 秒(復用連接,僅發送請求行)
差距主要來自:cURL 內建 DNS 緩存、TCP 連接復用、更高效的 SSL 會話復用、原生 C 實現避免 PHP 用戶態拷貝。
其餘 99% 場景,**請直接使用 cURL**。