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

PHP 模拟 HTTP 请求的三种核心方法:cURL、Socket、Guzzle 实战对比

     編輯:PHP 綜合知識

前言

PHP 向外部服务发起 HTTP 请求是微服务、支付回调、爬虫、第三方 API 集成的基础操作。本文系统对比三种主流方案:原生 cURL、原生 Socket、Guzzle HTTP Client (推荐),并给出生产级封装建议。

方法一:原生 cURL 扩展 (最常用、性能最好)

GET 请求

function httpGet(string $url, array $headers = [], int $timeout = 10): string
{
    $ch = curl_init();
    curl_setopt_array($ch, [
        CURLOPT_URL => $url,
        CURLOPT_RETURNTRANSFER => true,
        CURLOPT_TIMEOUT => $timeout,
        CURLOPT_CONNECTTIMEOUT => 5,
        CURLOPT_HTTPHEADER => $headers,
        CURLOPT_SSL_VERIFYPEER => true,  // 生产必须验证 SSL
        CURLOPT_FOLLOWLOCATION => true,  // 支持 301/302 跳转
        CURLOPT_MAXREDIRS => 5,
        CURLOPT_USERAGENT => 'Mozilla/5.0 (PHP/' . PHP_VERSION . ')',
    ]);
    $response = curl_exec($ch);
    if ($response === false) {
        throw new \RuntimeException('cURL error: ' . curl_error($ch), curl_errno($ch));
    }
    $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
    curl_close($ch);
    if ($httpCode >= 400) {
        throw new \RuntimeException("HTTP $httpCode: $response");
    }
    return $response;
}

POST 请求 (JSON / Form)

function httpPost(string $url, array $data, bool $isJson = true, array $headers = [], int $timeout = 15): string
{
    $ch = curl_init();
    $opts = [
        CURLOPT_URL => $url,
        CURLOPT_RETURNTRANSFER => true,
        CURLOPT_POST => true,
        CURLOPT_TIMEOUT => $timeout,
        CURLOPT_SSL_VERIFYPEER => true,
    ];

    if ($isJson) {
        $opts[CURLOPT_POSTFIELDS] = json_encode($data, JSON_UNESCAPED_UNICODE);
        $headers[] = 'Content-Type: application/json; charset=utf-8';
    } else {
        $opts[CURLOPT_POSTFIELDS] = http_build_query($data);
        $headers[] = 'Content-Type: application/x-www-form-urlencoded';
    }
    $opts[CURLOPT_HTTPHEADER] = $headers;

    curl_setopt_array($ch, $opts);
    $response = curl_exec($ch);
    if ($response === false) {
        throw new \RuntimeException('cURL error: ' . curl_error($ch), curl_errno($ch));
    }
    curl_close($ch);
    return $response;
}

方法二:原生 Socket (无 cURL 扩展环境、教学理解)

function httpSocket(string $method, string $url, array $data = [], array $headers = []): string
{
    $parsed = parse_url($url);
    $host = $parsed['host'];
    $port = $parsed['port'] ?? ($parsed['scheme'] === 'https' ? 443 : 80);
    $path = ($parsed['path'] ?? '/') . ($parsed['query'] ? '?' . $parsed['query'] : '');

    $body = $method === 'POST' ? ($data ? (is_array($data) ? http_build_query($data) : $data) : '') : '';
    if ($method === 'GET' && $data) {
        $path .= (strpos($path, '?') ? '&' : '?') . http_build_query($data);
    }

    $request = "$method $path HTTP/1.1\r\n";
    $request .= "Host: $host\r\n";
    $request .= "User-Agent: PHP-Socket/" . PHP_VERSION . "\r\n";
    $request .= "Connection: Close\r\n";
    if ($body) {
        $request .= "Content-Length: " . strlen($body) . "\r\n";
        $request .= "Content-Type: application/x-www-form-urlencoded\r\n";
    }
    foreach ($headers as $k => $v) {
        $request .= "$k: $v\r\n";
    }
    $request .= "\r\n$body";

    $scheme = $parsed['scheme'] === 'https' ? 'ssl://' : '';
    $fp = fsockopen($scheme . $host, $port, $errno, $errstr, 10);
    if (!$fp) throw new \RuntimeException("Socket connect failed: $errstr ($errno)");

    fwrite($fp, $request);
    $response = stream_get_contents($fp);
    fclose($fp);

    // 简易分离头体
    list($header, $body) = explode("\r\n\r\n", $response, 2);
    return $body;
}

缺点:需手动处理 SSL、分块传输、重定向、Cookie、Keep-Alive,极易出 Bug,不推荐生产使用。

方法三:Guzzle HTTP Client (现代标准、功能最全)

composer require guzzlehttp/guzzle
use GuzzleHttp\Client;
use GuzzleHttp\Exception\RequestException;

$client = new Client([
    'base_uri' => 'https://api.example.com/',
    'timeout' => 10,
    'headers' => [
        'Accept' => 'application/json',
        'User-Agent' => 'MyApp/1.0',
    ],
    // 中间件:自动重试、日志、认证等
]);

try {
    // GET
    $response = $client->get('users/1', ['query' => ['include' => 'posts']]);
    // POST JSON
    $response = $client->post('orders', [
        'json' => ['product_id' => 123, 'qty' => 2],
        'headers' => ['Authorization' => 'Bearer ' . $token],
    ]);
    // 流式下载大文件
    $client->get('export.csv', ['sink' => fopen('storage/export.csv', 'w')]);

    $data = json_decode($response->getBody()->getContents(), true);
    $status = $response->getStatusCode();
} catch (RequestException $e) {
    if ($e->hasResponse()) {
        $errBody = $e->getResponse()->getBody()->getContents();
        $errCode = $e->getResponse()->getStatusCode();
    }
    // 记录日志、抛出业务异常
}

对比总结

维度cURL 原生Socket 原生Guzzle
性能★★★★★★★★☆☆★★★★☆ (内部用 cURL)
功能完整度手动实现极简开箱即用 (重试、中间件、异步、流、Cookie、认证)
代码量中大少
维护性易漏配置极难优秀
依赖php-curl 扩展无Composer 包
推荐场景简单项目、无 Composer、极致性能教学、极简环境所有现代项目首选

生产级封装建议:统一 HttpClient 服务类

<?php
namespace App\Service;

use GuzzleHttp\Client;
use GuzzleHttp\Exception\GuzzleException;
use Psr\Log\LoggerInterface;

class HttpClient
{
    private Client $client;
    private LoggerInterface $logger;

    public function __construct(LoggerInterface $logger, array $config = [])
    {
        $this->logger = $logger;
        $this->client = new Client(array_merge([
            'timeout' => 10,
            'connect_timeout' => 3,
            'http_errors' => false, // 不抛异常,统一业务层处理
        ], $config));
    }

    public function request(string $method, string $uri, array $options = []): array
    {
        $start = microtime(true);
        try {
            $response = $this->client->request($method, $uri, $options);
            $body = $response->getBody()->getContents();
            $code = $response->getStatusCode();
            $this->log($method, $uri, $code, microtime(true) - $start);
            return ['code' => $code, 'data' => json_decode($body, true) ?? $body];
        } catch (GuzzleException $e) {
            $this->logger->error('HTTP request failed', [
                'method' => $method, 'uri' => $uri, 'error' => $e->getMessage(),
            ]);
            throw new \RuntimeException("HTTP 请求失败: {$e->getMessage()}", 0, $e);
        }
    }

    private function log(string $method, string $uri, int $code, float $ms): void
    {
        $this->logger->info('HTTP Request', compact('method', 'uri', 'code', 'ms'));
    }
}

结语

现代 PHP 项目请直接使用 Guzzle。它封装了 cURL 的复杂性,提供 PSR-7/18 标准接口,支持中间件扩展(重试、熔断、限流、签名、日志),是微服务架构下的 HTTP 通信基石。仅在无法安装扩展/Composer 的极简环境下,才考虑原生 cURL 封装。

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