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

PHP 模擬 POST 請求完整實作:fsockopen 與 cURL 兩種方案

     編輯:PHP 相關知識

模擬 POST 請求是爬蟲、API 串接、自動化測試的核心技能。本文對比 fsockopen 底層實作與 cURL 高階封裝,提供完整可用的請求類別。

方案一:fsockopen 原始 Socket 實作(教學用)

<?php
function postWithSocket(string $host, string $path, array $data, int $port = 80, bool $https = false): string {
    $postData = http_build_query($data);
    $protocol = $https ? 'ssl://' : '';
    $target = $protocol . $host;
    $port = $https ? 443 : $port;

    $fp = @fsockopen($target, $port, $errno, $errstr, 10);
    if (!$fp) {
        throw new Exception("連線失敗: $errstr ($errno)");
    }

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

    fwrite($fp, $request);

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

    // 簡易分離 Header 與 Body
    list($header, $body) = explode("\r\n\r\n", $response, 2);
    return $body;
}

// 使用範例
$html = postWithSocket('example.com', '/login.php', [
    'username' => 'user',
    'password' => 'pass',
]);
?>

方案二:cURL 封裝類(生產環境推薦)

<?php
class HttpClient {
    private array $defaultOptions = [
        CURLOPT_RETURNTRANSFER => true,
        CURLOPT_FOLLOWLOCATION => true,
        CURLOPT_MAXREDIRS => 5,
        CURLOPT_CONNECTTIMEOUT => 10,
        CURLOPT_TIMEOUT => 30,
        CURLOPT_SSL_VERIFYPEER => true,
        CURLOPT_USERAGENT => 'Mozilla/5.0 PHP HttpClient',
        CURLOPT_COOKIEJAR => '', // 啟用 Cookie 管理
        CURLOPT_COOKIEFILE => '',
    ];

    public function post(string $url, array $data, array $headers = [], array $options = []): string {
        $ch = curl_init($url);
        
        $opts = array_merge($this->defaultOptions, $options, [
            CURLOPT_POST => true,
            CURLOPT_POSTFIELDS => http_build_query($data),
            CURLOPT_HTTPHEADER => array_merge([
                'Content-Type: application/x-www-form-urlencoded',
            ], $headers),
        ]);

        curl_setopt_array($ch, $opts);
        $response = curl_exec($ch);
        
        if ($response === false) {
            $error = curl_error($ch);
            curl_close($ch);
            throw new Exception("cURL 錯誤: $error");
        }

        $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
        curl_close($ch);

        if ($httpCode < 200 || $httpCode >= 300) {
            throw new Exception("HTTP 錯誤: $httpCode");
        }

        return $response;
    }

    // 支援 JSON POST
    public function postJson(string $url, array $data, array $headers = []): string {
        return $this->post($url, $data, array_merge($headers, [
            'Content-Type: application/json',
            'Accept: application/json',
        ]), [
            CURLOPT_POSTFIELDS => json_encode($data),
        ]);
    }

    // 檔案上傳
    public function postFile(string $url, array $fields, array $files, array $headers = []): string {
        $postData = $fields;
        foreach ($files as $key => $path) {
            $postData[$key] = new CURLFile($path);
        }
        return $this->post($url, $postData, $headers);
    }
}

// 使用範例
$client = new HttpClient();

// 一般表單登入
$html = $client->post('https://example.com/login', [
    'username' => 'user',
    'password' => 'pass',
    'remember' => '1',
]);

// JSON API 呼叫
$response = $client->postJson('https://api.example.com/orders', [
    'product_id' => 123,
    'quantity' => 2,
]);

// 檔案上傳
$response = $client->postFile('https://example.com/upload', [
    'description' => '我的頭像',
], [
    'avatar' => '/path/to/photo.jpg',
]);
?>

關鍵差異比較

特性fsockopencURL
HTTPS 支援需 ssl:// 前綴原生完整
重導向處理手動實作CURLOPT_FOLLOWLOCATION
Cookie 管理手動解析 Set-CookieCOOKIEJAR/COOKIEFILE
檔案上傳手動構建 multipartCURLFile 類別
錯誤處理基礎 errno詳細錯誤碼與訊息
效能較低高(連線重用、管線)

Cookie 同步登入實作

<?php
// 從回應 Header 解析 Cookie 並設定到瀏覽器
function syncCookiesFromResponse(string $header, string $domain): void {
    if (preg_match_all('/Set-Cookie:\s*([^;]+)/i', $header, $matches)) {
        foreach ($matches[1] as $cookie) {
            list($name, $value) = explode('=', $cookie, 2);
            // 解析 path, domain, expires, httponly, secure...
            setcookie($name, urldecode($value), [
                'expires' => time() + 86400 * 7,
                'path' => '/',
                'domain' => $domain,
                'secure' => true,
                'httponly' => true,
                'samesite' => 'Lax',
            ]);
        }
    }
}
?>

現代專案強烈建議使用 cURL 或更高階的 GuzzleHttp/Guzzle,僅在嵌入式環境無 cURL 時考慮 fsockopen。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved