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

PHP cURL 封裝類最佳實踐:支援 GET/POST/代理/SSL/並發

     編輯:PHP 相關知識

將零散的 curl_setopt 封裝為可復用、可測試、型別安全的 HttpClient 類,是現代 PHP 專案的標配。本文提供兩個版本:輕量級靜態工具類(適合簡單專案)與 完整版物件導向客戶端(支援中介軟體、異常處理、PSR-18 兼容)。

版本一:輕量級靜態工具類

<?php
class Curl {
    /** 執行 GET 請求 */
    public static function get(string $url, array $headers = [], int $timeout = 10): ?string {
        return self::request('GET', $url, null, $headers, $timeout);
    }

    /** 執行 POST 請求 */
    public static function post(string $url, $data = null, array $headers = [], int $timeout = 10): ?string {
        return self::request('POST', $url, $data, $headers, $timeout);
    }

    /** 核心請求邏輯 */
    private static function request(string $method, string $url, $data, array $headers, int $timeout): ?string {
        $ch = curl_init();
        if (!$ch) return null;

        $options = [
            CURLOPT_URL            => $url,
            CURLOPT_CUSTOMREQUEST  => $method,
            CURLOPT_RETURNTRANSFER => true,
            CURLOPT_FOLLOWLOCATION => true,
            CURLOPT_MAXREDIRS      => 5,
            CURLOPT_TIMEOUT        => $timeout,
            CURLOPT_CONNECTTIMEOUT => 5,
            CURLOPT_SSL_VERIFYPEER => true,
            CURLOPT_SSL_VERIFYHOST => 2,
            CURLOPT_USERAGENT      => 'PHP-Curl/1.0',
            CURLOPT_HTTPHEADER     => array_map(fn($k, $v) => "$k: $v", array_keys($headers), $headers),
        ];

        if ($data !== null) {
            $options[CURLOPT_POSTFIELDS] = is_array($data) ? http_build_query($data) : $data;
        }

        curl_setopt_array($ch, $options);
        $response = curl_exec($ch);
        $errno = curl_errno($ch);
        $error = curl_error($ch);
        curl_close($ch);

        if ($errno !== CURLE_OK) {
            error_log("cURL [$errno] $error for $method $url");
            return null;
        }
        return $response;
    }
}

// 使用示例
$html = Curl::get('https://api.example.com/users');
$json = Curl::post('https://api.example.com/login', ['user' => 'a', 'pass' => 'b']);
?>

版本二:完整版 HttpClient(支援 PSR-18、中介軟體、Cookie 持久化)

<?php
use Psr\Http\Client\ClientInterface;
use Psr\Http\Message\RequestFactoryInterface;
use Psr\Http\Message\RequestInterface;
use Psr\Http\Message\ResponseInterface;

class HttpClient implements ClientInterface {
    private $ch;
    private $cookieJar;
    private array $defaultHeaders = ['User-Agent' => 'PHP-HttpClient/2.0'];
    private array $middleware = [];

    public function __construct(?string $cookieFile = null) {
        $this->ch = curl_init();
        $this->cookieJar = $cookieFile ?? sys_get_temp_dir() . '/curl_cookie_' . spl_object_id($this) . '.txt';
        
        curl_setopt_array($this->ch, [
            CURLOPT_RETURNTRANSFER => true,
            CURLOPT_FOLLOWLOCATION => true,
            CURLOPT_MAXREDIRS      => 5,
            CURLOPT_TIMEOUT        => 30,
            CURLOPT_CONNECTTIMEOUT => 10,
            CURLOPT_SSL_VERIFYPEER => true,
            CURLOPT_COOKIEJAR      => $this->cookieJar,
            CURLOPT_COOKIEFILE     => $this->cookieJar,
            CURLOPT_ENCODING       => 'gzip, deflate', // 自動解壓
        ]);
    }

    /** 加入中介軟體:function (RequestInterface $req, callable $next): ResponseInterface */
    public function addMiddleware(callable $mw): self {
        $this->middleware[] = $mw;
        return $this;
    }

    public function sendRequest(RequestInterface $request): ResponseInterface {
        // 構建 cURL 選項
        $opts = [
            CURLOPT_URL            => (string)$request->getUri(),
            CURLOPT_CUSTOMREQUEST  => $request->getMethod(),
            CURLOPT_HTTPHEADER     => $this->buildHeaders($request->getHeaders()),
        ];
        $body = (string)$request->getBody();
        if ($body !== '') {
            $opts[CURLOPT_POSTFIELDS] = $body;
        }
        curl_setopt_array($this->ch, $opts);

        // 執行請求
        $responseBody = curl_exec($this->ch);
        if ($responseBody === false) {
            throw new \RuntimeException(curl_error($this->ch), curl_errno($this->ch));
        }

        // 構建 PSR-7 回應(需 nyholm/psr7 或 guzzlehttp/psr7)
        $response = $this->createResponse($responseBody);
        
        // 執行中介軟體鏈(日誌、重試、快取等)
        return $this->runMiddleware($request, $response);
    }

    private function buildHeaders(array $headers): array {
        $merged = array_merge($this->defaultHeaders, $headers);
        return array_map(fn($k, $v) => "$k: $v", array_keys($merged), $merged);
    }

    private function createResponse(string $body): ResponseInterface {
        $code = curl_getinfo($this->ch, CURLINFO_HTTP_CODE);
        $headers = $this->parseResponseHeaders();
        // 這裡需使用 PSR-7 工廠創建 Response 物件,略過具體實現
        return new \GuzzleHttp\Psr7\Response($code, $headers, $body);
    }

    private function parseResponseHeaders(): array {
        // 需啟用 CURLOPT_HEADER => true 並分離頭部,此處簡化
        return [];
    }

    private function runMiddleware(RequestInterface $req, ResponseInterface $res): ResponseInterface {
        $handler = function ($r) use ($res) { return $res; };
        foreach (array_reverse($this->middleware) as $mw) {
            $handler = fn($r) => $mw($r, $handler);
        }
        return $handler($req);
    }

    public function __destruct() {
        if (is_resource($this->ch)) curl_close($this->ch);
        if (file_exists($this->cookieJar)) @unlink($this->cookieJar);
    }
}

// 使用示例(需 PSR-7 實現)
$client = (new HttpClient())
    ->addMiddleware(function ($req, $next) {
        $start = microtime(true);
        $res = $next($req);
        error_log(sprintf('[%s] %s %s - %dms', date('H:i:s'), $req->getMethod(), $req->getUri(), (microtime(true)-$start)*1000));
        return $res;
    });

$request = (new \GuzzleHttp\Psr7\ServerRequestFactory())->createRequest('GET', 'https://api.example.com/data');
$response = $client->sendRequest($request);
echo $response->getBody();
?>

進階功能對比

功能輕量版完整版
PSR-18 兼容❌✅
Cookie 持久化❌✅
中介軟體/攔截器❌✅
自動 GZIP 解壓❌✅
依賴零psr/http-client, psr/http-factory, nyholm/psr7
適用場景舊專案、簡單腳本、無 Composer現代框架、微服務、需可觀測性

效能優化清單

  • 連線複用:單例模式復用 HttpClient 實例,避免重複 TCP/SSL 握手。
  • DNS 快取:CURLOPT_DNS_CACHE_TIMEOUT = 300(5 分鐘)。
  • Keep-Alive:預設開啟,確保不設 CURLOPT_FORBID_REUSE。
  • 並發請求:IO 密集型場景改用 curl_multi、Swoole、amphp 或 GuzzleHttp\Promise。
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved