方案一:原生 fsockopen(底層 Socket,無依賴)
<?php
function sock_post($url, $data, $timeout = 10) {
$parsed = parse_url($url);
$host = $parsed['host'];
$path = $parsed['path'] ?? '/';
$query = $parsed['query'] ?? '';
$port = $parsed['port'] ?? 80;
$fp = fsockopen($host, $port, $errno, $errstr, $timeout);
if (!$fp) throw new Exception("Connect failed: $errstr ($errno)");
$payload = is_array($data) ? http_build_query($data) : $data;
$header = "POST $path HTTP/1.1\r\n";
$header .= "Host: $host\r\n";
$header .= "Content-Type: application/x-www-form-urlencoded\r\n";
$header .= "Content-Length: " . strlen($payload) . "\r\n";
$header .= "Connection: Close\r\n\r\n";
$header .= $payload;
fwrite($fp, $header);
$response = '';
while (!feof($fp)) {
$response .= fread($fp, 8192);
}
fclose($fp);
// 簡易分離 header/body
list($headers, $body) = explode("\r\n\r\n", $response, 2);
return ['headers' => $headers, 'body' => $body];
}
// GET 封裝
function sock_get($url, $timeout = 10) {
$parsed = parse_url($url);
$host = $parsed['host'];
$path = ($parsed['path'] ?? '/') . ($parsed['query'] ? '?' . $parsed['query'] : '');
$port = $parsed['port'] ?? 80;
$fp = fsockopen($host, $port, $errno, $errstr, $timeout);
if (!$fp) throw new Exception("Connect failed: $errstr ($errno)");
$header = "GET $path HTTP/1.1\r\nHost: $host\r\nConnection: Close\r\n\r\n";
fwrite($fp, $header);
$response = '';
while (!feof($fp)) $response .= fread($fp, 8192);
fclose($fp);
list($headers, $body) = explode("\r\n\r\n", $response, 2);
return ['headers' => $headers, 'body' => $body];
}
方案二:cURL(功能豐富、效能最佳、標準選擇)
<?php
function curl_request($url, $method = 'GET', $data = null, $options = []) {
$ch = curl_init();
$defaultOpts = [
CURLOPT_URL => $url,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_TIMEOUT => 10,
CURLOPT_CONNECTTIMEOUT => 5,
CURLOPT_SSL_VERIFYPEER => false, // 生產環境請設 true 並配置 CA
CURLOPT_SSL_VERIFYHOST => 2,
CURLOPT_FOLLOWLOCATION => true,
CURLOPT_MAXREDIRS => 5,
CURLOPT_USERAGENT => 'Mozilla/5.0 (PHP/' . PHP_VERSION . ')',
CURLOPT_HEADER => true, // 回傳含 header
];
if ($method === 'POST') {
$defaultOpts[CURLOPT_POST] = true;
$defaultOpts[CURLOPT_POSTFIELDS] = is_array($data) ? http_build_query($data) : $data;
}
curl_setopt_array($ch, array_merge($defaultOpts, $options));
$response = curl_exec($ch);
$errno = curl_errno($ch);
$error = curl_error($ch);
$info = curl_getinfo($ch);
curl_close($ch);
if ($errno) throw new Exception("cURL error $errno: $error");
$headerSize = $info['header_size'];
return [
'headers' => substr($response, 0, $headerSize),
'body' => substr($response, $headerSize),
'info' => $info,
];
}
方案三:HttpClient 庫(ICUTIO HttpClient,輕量封裝)
<?php
require 'HttpClient.php'; // 官網 https://scripts.incutio.com/httpclient/
// 靜態快速 POST
$resp = HttpClient::quickPost('https://api.example.com/endpoint', [
'name' => 'John',
'email' => 'john@example.com',
]);
echo $resp;
// 完整控制
$client = new HttpClient('api.example.com');
$client->setDebug(true);
$client->setUserAgent('MyApp/1.0');
$client->setAuthorization('Bearer', 'token123');
if ($client->post('/endpoint', ['foo' => 'bar'])) {
$body = $client->getContent();
$headers = $client->getHeaders();
$status = $client->getStatus();
}
三方案對比
| 維度 | fsockopen | cURL | HttpClient |
| 依賴 | 無(核心) | 需 curl 擴充 | 需引入類別檔 |
| HTTPS | 需自行實作 SSL | 原生支援 | 支援(底層用 fsockopen/cURL) |
| Cookie/Session | 手動管理 | CURLOPT_COOKIEJAR/FILE | 內建管理 |
| 檔案上傳 | 手動組 multipart | CURLFile (PHP 5.5+) | 支援 |
| 代理 | 手動 | 原生 | 支援 |
| 效能 | 中 | 高(連線複用、多工) | 中 |
| 維護性 | 低(樣板碼多) | 中 | 高(語意化 API) |
生產環境建議
- 首選 cURL:功能完整、效能佳、社群支援廣。封裝成 Service 類別統一管理超時、重試、日誌、熔斷。
- 現代替代:Guzzle 7(PSR-18、PSR-7、Middleware、非同步)、Symfony HttpClient、Saloon(API 整合導向)。
- 安全:驗證 SSL 憑證、限制重導向次數、設定嚴格超時、過濾回應內容、記錄審計日誌。
- 測試:用 Mock Handler(Guzzle)或
php-vcr 錄製/重播 HTTP 互動。