在無 cURL 擴展、或需精細控制 TCP 連接(如自定義 Header、Keep-Alive、非標準端口)時,使用 fsockopen 手工構造 HTTP 請求是核心技能。本文封裝 post_request() 函數,演示完整的 Socket 級 POST 流程。
function post_request($url, $data, $referer = '') {
// 1. 將關聯數組編碼為 application/x-www-form-urlencoded
$data = http_build_query($data);
// 2. 解析 URL
$parsed = parse_url($url);
if ($parsed['scheme'] !== 'http') {
die('Error: Only HTTP supported!');
}
$host = $parsed['host'];
$path = $parsed['path'] ?: '/';
// 3. 建立 Socket 連接(超時 30s)
$fp = fsockopen($host, 80, $errno, $errstr, 30);
if (!$fp) {
return ['status' => 'err', 'error' => "$errstr ($errno)"];
}
// 4. 發送請求頭與 Body
$request = "POST $path HTTP/1.1\r\n";
$request .= "Host: $host\r\n";
if ($referer) $request .= "Referer: $referer\r\n";
$request .= "Content-Type: application/x-www-form-urlencoded\r\n";
$request .= "Content-Length: " . strlen($data) . "\r\n";
$request .= "Connection: close\r\n\r\n";
$request .= $data;
fwrite($fp, $request);
// 5. 讀取響應
$response = '';
while (!feof($fp)) {
$response .= fgets($fp, 128);
}
fclose($fp);
// 6. 分離 Header 與 Body
list($header, $content) = explode("\r\n\r\n", $response, 2) + ['', ''];
return ['status' => 'ok', 'header' => $header, 'content' => $content];
}
$post_data = ['test' => 'foobar', 'okay' => 'yes', 'number' => 2];
$result = post_request('https://www.example.com/', $post_data);
if ($result['status'] === 'ok') {
echo "Header:\n" . $result['header'] . "\n
\nBody:\n" . $result['content'];
} else {
echo "Error: " . $result['error'];
}
feof() 陷入等待。fsockopen('ssl://'.$host, 443) 並處理 SSL 證書驗證。| 特性 | Socket (fsockopen) | cURL |
|---|---|---|
| 依賴 | 核心函數,無擴展依賴 | 需安裝 php-curl |
| HTTPS | 手工處理 SSL 上下文 | 內建支援 |
| 重定向 | 需手工解析 Location 並遞歸 | CURLOPT_FOLLOWLOCATION |
| 超時控制 | 連接/讀寫分離較麻煩 | CURLOPT_CONNECTTIMEOUT/CURLOPT_TIMEOUT |
| 性能 | 較低(用戶態實現) | 高(C 底層) |