首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> 網頁程式設計 >> PHP 程式設計 >> PHP 綜合知識

PHP 發送 POST 請求的三大核心方案:Stream / Socket / cURL 全解析

     編輯:PHP 綜合知識

方案對比總覽

方案依賴特性適用場景
Stream Context無擴展原生、輕量、支援 HTTPS簡單 GET/POST、無 cURL 環境
Socket (fsockopen)無擴展底層控制、支援持久連接、自定義協議特殊協議、長連接、高性能微服務調用
cURL需 php_curl.dll功能最全、多協議、並發、代理、證書、限速生產環境首選、複雜 HTTP 交互

方案一:Stream Context(原生 file_get_contents)

<?php
/**
 * 發送 POST 請求(Stream 版)
 * @param string $url 請求地址
 * @param array $postData 鍵值對數據
 * @param int $timeout 超時秒數
 * @return string|false 響應體或失敗
 */
function sendPostStream($url, array $postData, $timeout = 15) {
    $postData = http_build_query($postData);
    $options = [
        'http' => [
            'method'  => 'POST',
            'header'  => "Content-Type: application/x-www-form-urlencoded\r\nContent-Length: " . strlen($postData),
            'content' => $postData,
            'timeout' => $timeout,
            'ignore_errors' => true, // 獲取 4xx/5xx 響應體
        ],
        'ssl' => [
            'verify_peer' => false,      // 生產環境建議 true 並配置 CA
            'verify_peer_name' => false,
        ]
    ];
    $context = stream_context_create($options);
    $result = @file_get_contents($url, false, $context);
    return $result;
}

// 使用示例
$response = sendPostStream('https://api.example.com/login', [
    'username' => 'admin',
    'password' => 'secret'
]);
?>

方案二:Socket 底層實現(教學/特殊需求)

<?php
/**
 * Socket 發送原始 HTTP 請求
 * @param string $host 主機名
 * @param string $path 路徑
 * @param string $postString 已編碼的 POST 數據字符串
 * @param int $port 端口
 * @param int $timeout 超時
 * @return string 響應全文(含頭部)
 */
function requestBySocket($host, $path, $postString, $port = 80, $timeout = 30) {
    $fp = @fsockopen($host, $port, $errno, $errstr, $timeout);
    if (!$fp) throw new Exception("$errstr ($errno)");

    $request = "POST $path HTTP/1.1\r\n";
    $request .= "Host: $host\r\n";
    $request .= "User-Agent: PHP-Socket-Client\r\n";
    $request .= "Content-Type: application/x-www-form-urlencoded\r\n";
    $request .= "Content-Length: " . strlen($postString) . "\r\n";
    $request .= "Connection: Close\r\n\r\n";
    $request .= $postString;

    fwrite($fp, $request);

    $response = '';
    while (!feof($fp)) {
        $response .= fgets($fp, 4096);
    }
    fclose($fp);
    return $response;
}

// 使用示例
$postString = http_build_query(['app' => 'socket', 'version' => 'beta']);
$rawResponse = requestBySocket('api.example.com', '/endpoint', $postString);
// 需手動分離頭部與體:list($header, $body) = explode("\r\n\r\n", $rawResponse, 2);
?>

方案三:cURL 完整封裝(生產環境標準)

<?php
/**
 * cURL 發送 POST 請求(通用封裝)
 * @param string $url
 * @param array|string $postData
 * @param array $options 額外 curl_setopt 選項
 * @return array ['code'=>http_code, 'body'=>string, 'header'=>string, 'info'=>array]
 */
function sendPostCurl($url, $postData, array $options = []) {
    $ch = curl_init();
    $defaultOptions = [
        CURLOPT_URL            => $url,
        CURLOPT_POST           => true,
        CURLOPT_POSTFIELDS     => is_array($postData) ? http_build_query($postData) : $postData,
        CURLOPT_RETURNTRANSFER => true,
        CURLOPT_HEADER         => true,          // 返回頭部用於解析狀態碼
        CURLOPT_TIMEOUT        => 15,
        CURLOPT_CONNECTTIMEOUT => 5,
        CURLOPT_SSL_VERIFYPEER => false,         // 生產環境請設 true 並配置 CURLOPT_CAINFO
        CURLOPT_SSL_VERIFYHOST => false,
        CURLOPT_USERAGENT      => 'PHP-cURL-Client/1.0',
        CURLOPT_FOLLOWLOCATION => true,
        CURLOPT_MAXREDIRS      => 5,
    ];
    curl_setopt_array($ch, array_merge($defaultOptions, $options));
    $response = curl_exec($ch);
    $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
    $errno = curl_errno($ch);
    $error = curl_error($ch);
    curl_close($ch);

    if ($errno) throw new Exception("cURL Error ($errno): $error");

    // 分離頭部與體
    $headerSize = curl_getinfo($ch, CURLINFO_HEADER_SIZE);
    $header = substr($response, 0, $headerSize);
    $body = substr($response, $headerSize);

    return [
        'code'   => $httpCode,
        'body'   => $body,
        'header' => $header,
        'info'   => curl_getinfo($ch)
    ];
}

// 使用示例
try {
    $res = sendPostCurl('https://api.example.com/order', [
        'order_id' => '20240101001',
        'amount'   => 99.9
    ]);
    echo "HTTP $res[code]\n";
    echo $res['body'];
} catch (Exception $e) {
    echo "請求失敗: " . $e->getMessage();
}
?>

進階:統一請求類(策略模式)

<?php
class HttpClient {
    public static function post($url, $data = [], $timeout = 5) {
        // 優先 cURL,降級 Stream
        if (function_exists('curl_init')) {
            return self::postCurl($url, $data, $timeout);
        }
        return self::postStream($url, $data, $timeout);
    }
    // ... 內部實現 postCurl / postStream / postSocket
}
?>

關鍵最佳實踐

  • 超時控制:務必設置連接超時(CURLOPT_CONNECTTIMEOUT)與總超時(CURLOPT_TIMEOUT),防止阻塞進程。
  • SSL 驗證:生產環境必須開啟 CURLOPT_SSL_VERIFYPEER 並配置 CA 憑證(CURLOPT_CAINFO)。
  • 錯誤處理:檢查 HTTP 狀態碼(2xx 成功)、cURL 錯誤碼、業務錯誤碼三層。
  • 並發請求:大批量調用使用 curl_multi_* 或 Guzzle 異步客戶端。
  • 數據格式:API 多為 JSON,發送時設置 Content-Type: application/json 並 json_encode($data)。
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved