不依賴 cURL 擴展,純 PHP Socket (fsockopen) 構造 HTTP/1.1 POST 請求,實現模擬登錄、Cookie 同步、第三方 API 對接。本文給出完整可復用函數,含 Header 構造、Body 編碼、Chunked 響應處理、Cookie 解析回寫。
function httpPost(string $host, string $path, array $data, int $port = 80, array $headers = []): string {
// 構造 Body
$body = http_build_query($data);
$bodyLen = strlen($body);
// 預設 Header
$defaultHeaders = [
"Host: $host:$port",
"Content-Type: application/x-www-form-urlencoded",
"Content-Length: $bodyLen",
"Connection: Close",
];
$request = "POST $path HTTP/1.1\r\n";
$request .= implode("\r\n", array_merge($defaultHeaders, $headers)) . "\r\n\r\n";
$request .= $body;
// 發送請求
$fp = fsockopen($host, $port, $errno, $errstr, 10);
if (!$fp) throw new Exception("Connect failed: $errstr ($errno)");
fwrite($fp, $request);
// 讀取響應
$response = '';
while (!feof($fp)) {
$response .= fgets($fp, 4096);
}
fclose($fp);
return $response;
}
function bbsLogin(string $user, string $pass, string $host, int $port = 80): void {
$data = [
'user_login' => $user,
'password' => $pass,
'_wp_http_referer' => '/bbpress/',
'remember' => 'true',
];
$fp = fsockopen($host, $port, $errno, $errstr, 10);
if (!$fp) throw new Exception("Connect failed");
$body = http_build_query($data);
$request = "POST /bbpress/bb-login.php HTTP/1.1\r\n";
$request .= "Host: $host:$port\r\n";
$request .= "Content-Type: application/x-www-form-urlencoded\r\n";
$request .= "Content-Length: " . strlen($body) . "\r\n";
$request .= "Connection: Close\r\n\r\n";
$request .= $body;
fwrite($fp, $request);
// 解析響應頭中的 Set-Cookie 並寫入客戶端
while (!feof($fp)) {
$line = fgets($fp);
if (stripos($line, 'Set-Cookie:') === 0) {
// 粗略解析:Set-Cookie: name=value; expires=...; path=...
$parts = explode(' ', $line);
$cookiePair = explode('=', $parts[1], 2);
$name = urldecode($cookiePair[0]);
$value = urldecode(rtrim($cookiePair[1], ";\r\n"));
// 解析 path/expires 等屬性 (略)
setcookie($name, $value, time()+3600*24*7, '/');
}
}
fclose($fp);
}
fsockopen('ssl://' . $host, 443) 或 stream_socket_client('ssl://...'),需啟用 openssl 擴展。Transfer-Encoding: chunked 時需手動解碼塊大小 + 內容。Connection: Keep-Alive + 解析 Content-Length 精確讀取,避免 feof 等待超時。stream_set_timeout($fp, 5) 設置讀寫超時。file_get_contents + stream_context_create |
Socket 方案零依賴、完全可控,適合嵌入式環境、極簡容器、排查 cURL 行為差異等場景。