PHP 發送 POST 請求有三種主流方式:cURL 擴充(功能最完整)、stream_context(內建、無依賴)、fsockopen(底層 Socket,適合特殊協定)。本文提供統一介面的 Request 類別,封裝三種實作並附上適用場景分析。
$result, 'code' => $code);
}
/**
* 方式二:stream_context + file_get_contents(無 cURL 環境備援)
* 簡單、無依賴,但功能有限(難以處理重導向、Cookie、代理)
*/
public static function post2($url, $data) {
$postdata = http_build_query($data);
$opts = array('http' => array(
'method' => 'POST',
'header' => 'Content-type: application/x-www-form-urlencoded\r\n' .
'Content-Length: ' . strlen($postdata),
'content' => $postdata,
'timeout' => 5,
));
$context = stream_context_create($opts);
$result = @file_get_contents($url, false, $context);
if ($result === false) {
throw new Exception("stream_context POST 失敗");
}
return $result;
}
/**
* 方式三:fsockopen 底層 Socket(教學、特殊協定、無 HTTP 擴充環境)
* 完全手動組裝 HTTP 協定,極其繁瑣,不建議一般業務使用
*/
public static function post3($host, $path, $query, $others = '') {
$request = "POST $path HTTP/1.1\r\n";
$request .= "Host: $host\r\n";
$request .= "Content-Type: application/x-www-form-urlencoded\r\n";
$request .= $others;
$request .= "User-Agent: PHP/" . PHP_VERSION . "\r\n";
$request .= "Content-Length: " . strlen($query) . "\r\n";
$request .= "Connection: close\r\n\r\n";
$request .= $query;
$fp = @fsockopen($host, 80, $errno, $errstr, 10);
if (!$fp) {
throw new Exception("fsockopen 失敗: $errstr ($errno)");
}
fwrite($fp, $request);
$response = '';
while (!feof($fp)) {
$response .= fread($fp, 8192);
}
fclose($fp);
return $response;
}
}
| 維度 | cURL (post) | stream_context (post2) | fsockopen (post3) |
|---|---|---|---|
| 依賴 | php-curl 擴充 | 內建 (allow_url_fopen=On) | 內建 |
| HTTPS 支援 | 原生完美支援 | 需配置 SSL context | 需手動實作 SSL/TLS |
| 重導向跟隨 | CURLOPT_FOLLOWLOCATION | 自動跟隨 (受限) | 手動解析 Location |
| Cookie 管理 | CURLOPT_COOKIEJAR/FILE | 手動處理 Header | 手動處理 Header |
| 代理支援 | 完整支援 | context proxy 參數 | 手動 CONNECT |
| 多執行緒/並發 | curl_multi_* 完美支援 | 不支援 | 不支援 |
| 上傳檔案 | CURLFile 類別 | 極難實作 multipart | 手動組裝 multipart |
| 效能 | 高(C 底層) | 中 | 低(PHP 迴圈讀取) |
| 除錯困難度 | 低(verbose 模式) | 中 | 高(需手動印 Header/Body) |
class HttpClient {
private $adapter;
public function __construct($adapter = 'curl') {
$this->adapter = $adapter;
}
public function post($url, $data, $options = array()) {
try {
switch ($this->adapter) {
case 'curl':
return Request::post($url, http_build_query($data), $options['timeout'] ?? 5);
case 'stream':
return array('body' => Request::post2($url, $data), 'code' => 200);
default:
throw new Exception("未知適配器: $this->adapter");
}
} catch (Exception $e) {
// 統一錯誤日誌、重試、熔斷邏輯
Yii::log($e->getMessage(), 'error', 'http.client');
throw $e;
}
}
}
// 使用
$client = new HttpClient('curl');
$response = $client->post('https://api.example.com/endpoint', array('key' => 'value'));
封裝統一的 HTTP 客戶端類別,內部以 cURL 為主、stream_context 為備,能讓你的應用在各種部署環境下都保持穩定的外部 API 通訊能力。建議專案中建立 HttpClient 服務,集中管理超時、重試、熔斷、日誌等橫切關注點。