在 PHP 開發中,抓取遠端資源是常見需求。最常用的三種方式——file_get_contents、fsockopen、cURL——各有優劣。本文從功能支援、效能穩定性、使用複雜度、適用場景等維度深度比較,助你選擇最適合的工具。
| 特性 | file_get_contents | fsockopen | cURL |
|---|---|---|---|
| 支援協定 | HTTP/HTTPS/FTP (需 allow_url_fopen) | TCP/UDP 底層 Socket | HTTP/HTTPS/FTP/FTPS/SMTP/POP3/LDAP 等 20+ 種 |
| HTTP 方法 | GET/POST (需 stream_context) | 完全手動組裝 | GET/POST/PUT/DELETE/HEAD/PATCH 等完整支援 |
| Header 控制 | 有限 (stream_context) | 完全自訂 | 完整控制 (CURLOPT_HTTPHEADER) |
| Cookie 管理 | 手動處理 | 手動處理 | 內建 Cookie Jar (CURLOPT_COOKIEJAR/FILE) |
| SSL/TLS | 基礎支援 | 需自行實作 | 完整支援驗證、憑證、Cipher 選項 |
| 代理支援 | stream_context | 手動 CONNECT | HTTP/SOCKS4/5 完整支援 |
| 超時控制 | 不穩定 (stream_context timeout 常失效) | 手動 select() | 精確控制 (連線/執行/名稱解析分離) |
| 斷點續傳 | 不支援 | 手動實作 | 內建 Range 標頭支援 |
| 並發請求 | 不支援 | 需多進程 | cURL Multi 介面原生支援 |
| DNS 快取 | 無 | 無 | 內建 DNS 快取 (效能亮點) |
| 效能 | 單次請求尚可,持久連線差 | 最底層,開銷最小但開發成本高 | 最佳:連線複用、管線化、非同步多工 |
| 學習曲線 | 最低 | 最高 (需懂 HTTP 協定細節) | 中等 (參數多但文件完善) |
file_get_contents 耗時(秒): 2.31, 2.30, 2.22, 3.31, 2.30 → 平均 ~2.49s cURL 耗時(秒): 0.69, 0.65, 0.64, 0.82, 0.64 → 平均 ~0.69s 結論:cURL 速度快 3-4 倍,且穩定度高得多。
<?php
$url = 'https://api.example.com/data';
$html = file_get_contents($url);
echo $html;
?>
<?php
$data = http_build_query(['foo' => 'bar']);
$opts = [
'http' => [
'method' => 'POST',
'header' => "Content-type: application/x-www-form-urlencoded\r\n" .
"Content-Length: " . strlen($data) . "\r\n",
'content' => $data,
'timeout' => 10 // 注意:此超時在某些 PHP 版本不生效
]
];
$context = stream_context_create($opts);
$result = file_get_contents('https://api.example.com/endpoint', false, $context);
?>
<?php
function httpRequest($url, $method = 'GET', $data = [], $headers = []) {
$parsed = parse_url($url);
$host = $parsed['host'];
$port = $parsed['port'] ?? ($parsed['scheme'] === 'https' ? 443 : 80);
$path = $parsed['path'] . ($parsed['query'] ? '?' . $parsed['query'] : '');
$fp = fsockopen(($parsed['scheme'] === 'https' ? 'ssl://' : '') . $host, $port, $errno, $errstr, 10);
if (!$fp) return false;
$request = "$method $path HTTP/1.1\r\n";
$request .= "Host: $host\r\n";
$request .= "Connection: Close\r\n";
foreach ($headers as $k => $v) $request .= "$k: $v\r\n";
if ($method === 'POST') {
$body = http_build_query($data);
$request .= "Content-Type: application/x-www-form-urlencoded\r\n";
$request .= "Content-Length: " . strlen($body) . "\r\n";
}
$request .= "\r\n";
if ($method === 'POST') $request .= $body;
fwrite($fp, $request);
$response = '';
while (!feof($fp)) $response .= fgets($fp, 4096);
fclose($fp);
return $response;
}
?>
<?php
// 通用封裝函數
function curlRequest($url, $options = []) {
$ch = curl_init();
$defaults = [
CURLOPT_URL => $url,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_TIMEOUT => 10,
CURLOPT_CONNECTTIMEOUT => 5,
CURLOPT_FOLLOWLOCATION => true,
CURLOPT_MAXREDIRS => 5,
CURLOPT_SSL_VERIFYPEER => true, // 生產環境建議開啟
CURLOPT_USERAGENT => 'Mozilla/5.0 (compatible; PHP Bot)',
];
curl_setopt_array($ch, $defaults + $options);
$response = curl_exec($ch);
$error = curl_error($ch);
$info = curl_getinfo($ch);
curl_close($ch);
return ['body' => $response, 'error' => $error, 'info' => $info];
}
// GET 請求
$result = curlRequest('https://api.example.com/users');
// POST JSON 請求
$result = curlRequest('https://api.example.com/users', [
CURLOPT_POST => true,
CURLOPT_POSTFIELDS => json_encode(['name' => 'John']),
CURLOPT_HTTPHEADER => ['Content-Type: application/json', 'Authorization: Bearer xxx']
]);
// 檔案上傳 (PHP 5.5+ 使用 CURLFile)
$result = curlRequest('https://api.example.com/upload', [
CURLOPT_POST => true,
CURLOPT_POSTFIELDS => ['file' => new CURLFile('/path/to/file.jpg', 'image/jpeg', 'photo.jpg')]
]);
// 模擬瀏覽器登入 (Cookie 自動管理)
$cookieJar = tempnam(sys_get_temp_dir(), 'cookie_');
$ch = curl_init();
curl_setopt_array($ch, [
CURLOPT_URL => 'https://site.com/login',
CURLOPT_POST => true,
CURLOPT_POSTFIELDS => ['username' => 'user', 'password' => 'pass'],
CURLOPT_COOKIEJAR => $cookieJar,
CURLOPT_COOKIEFILE => $cookieJar,
CURLOPT_RETURNTRANSFER => true,
]);
curl_exec($ch); // 登入並儲存 Cookie
// 後續請求自動攜帶 Cookie
curl_setopt($ch, CURLOPT_URL, 'https://site.com/dashboard');
$dashboard = curl_exec($ch);
curl_close($ch);
?>
趙永斌:file_get_contents 容易超時,換 cURL 即解決。cURL 效率較高,因為會自動快取 DNS 资訊。
范佳鵬:公司演進歷程:file_get_contents → fsockopen → cURL。cURL 支援 Keep-Alive 持久連線,適合大量採集。
潘少寧 (騰訊):file_get_contents 只能拉資料;cURL 支援 POST/GET/Header;Socket 最底層,UDP/TCP 都能玩。Socket 能做的 cURL 不一定能做,但 file_get_contents/cURL 能做的 Socket 都能做。
file_get_contents (確認 allow_url_fopen=On)fsockopen / stream_socket_client總結:file_get_contents 適合簡單場景,但生產環境特別是採集、API 串接、高併發場景,cURL 是事實標準。投資學習 cURL 參數(CURLOPT_*)回報率極高。