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

PHP Curl 封裝類 Curl.class.php 實戰解析:GET/POST、Header、認證、錯誤處理

     編輯:PHP 綜合知識

為何需要封裝?

原生 cURL 選項繁多、錯誤處理分散、重複代碼多。封裝類提供:**統一介面、預設安全選項、自動錯誤轉換、支援鏈式調用**。

核心類別碼分析 (修正版,修正原始碼語法錯誤)

ch = curl_init();
        $this->setDefaults();
        $this->setOptions($defaultOptions);
    }

    private function setDefaults(): void {
        $this->options = [
            CURLOPT_RETURNTRANSFER => true,
            CURLOPT_HEADER         => false,
            CURLOPT_FOLLOWLOCATION => true,
            CURLOPT_MAXREDIRS      => 5,
            CURLOPT_TIMEOUT        => 30,
            CURLOPT_CONNECTTIMEOUT => 10,
            CURLOPT_SSL_VERIFYPEER => true,  // 生產環境保持 true
            CURLOPT_SSL_VERIFYHOST => 2,
            CURLOPT_USERAGENT      => 'Mozilla/5.0 (compatible; PHP-Curl/1.0)',
            CURLOPT_ENCODING       => 'gzip,deflate', // 自動解壓
        ];
    }

    public function setOptions(array $options): self {
        foreach ($options as $key => $val) {
            $this->options[$key] = $val;
        }
        return $this;
    }

    public function setOpt(int $option, $value): self {
        $this->options[$option] = $value;
        return $this;
    }

    /** 核心執行 */
    public function execute(string $method, string $url, $data = null, array $headers = []): string {
        $this->applyOptions();

        curl_setopt($this->ch, CURLOPT_URL, $url);
        curl_setopt($this->ch, CURLOPT_CUSTOMREQUEST, strtoupper($method));

        if ($data !== null) {
            if (is_array($data)) {
                // POST fields: 自動判斷是否為檔案上傳 (CURLFile)
                $hasFile = false;
                foreach ($data as $v) {
                    if ($v instanceof \CURLFile) { $hasFile = true; break; }
                }
                if (!$hasFile) {
                    $data = http_build_query($data);
                }
            }
            curl_setopt($this->ch, CURLOPT_POSTFIELDS, $data);
        }

        if ($headers) {
            $headerLines = [];
            foreach ($headers as $k => $v) {
                $headerLines[] = "$k: $v";
            }
            curl_setopt($this->ch, CURLOPT_HTTPHEADER, $headerLines);
        }

        $response = curl_exec($this->ch);
        $errno = curl_errno($this->ch);
        $error = curl_error($this->ch);
        $httpCode = curl_getinfo($this->ch, CURLINFO_HTTP_CODE);

        if ($errno) {
            throw new \RuntimeException("cURL error ($errno): $error", $errno);
        }

        if ($httpCode >= 400) {
            throw new \HttpException("HTTP $httpCode", $httpCode, $response);
        }

        return $response;
    }

    private function applyOptions(): void {
        curl_setopt_array($this->ch, $this->options);
    }

    // 便利方法
    public function get(string $url, array $headers = []): string {
        return $this->execute('GET', $url, null, $headers);
    }

    public function post(string $url, $data = null, array $headers = []): string {
        return $this->execute('POST', $url, $data, $headers);
    }

    public function put(string $url, $data = null, array $headers = []): string {
        return $this->execute('PUT', $url, $data, $headers);
    }

    public function delete(string $url, array $headers = []): string {
        return $this->execute('DELETE', $url, null, $headers);
    }

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

// 自訂例外
class HttpException extends \Exception {
    public $response;
    public function __construct(string $message, int $code, $response = null) {
        parent::__construct($message, $code);
        $this->response = $response;
    }
}
?>

實戰使用範例

1. 簡單 GET

get('https://httpbin.org/get');
    echo $html;
} catch (\Exception $e) {
    echo "請求失敗: " . $e->getMessage();
}
?>

2. POST JSON API

setOpt(CURLOPT_HTTPHEADER, ['Content-Type: application/json', 'Accept: application/json']);

$payload = ['name' => 'John', 'email' => 'john@example.com'];
try {
    $response = $curl->post('https://httpbin.org/post', json_encode($payload));
    $data = json_decode($response, true);
    print_r($data['json']); // 回傳的 JSON 資料
} catch (\HttpException $e) {
    echo "API 錯誤 {$e->getCode()}: " . $e->getMessage();
    // $e->response 包含原始回應
} catch (\Exception $e) {
    echo "網路錯誤: " . $e->getMessage();
}
?>

3. 檔案上傳

 new \CURLFile('/path/to/image.jpg', 'image/jpeg', 'avatar.jpg'),
    'user_id' => 123,
];
$response = $curl->post('https://api.example.com/upload', $data);
?>

4. Basic Auth / Bearer Token

setOpt(CURLOPT_USERPWD, 'username:password');

// Bearer Token
$curl->setOpt(CURLOPT_HTTPHEADER, ['Authorization: Bearer ' . $accessToken]);
?>

5. 進階:Cookie 管理 (模擬登入)

setOptions([
    CURLOPT_COOKIEJAR  => $cookieFile, // 儲存 Cookie
    CURLOPT_COOKIEFILE => $cookieFile, // 讀取 Cookie
]);

// 1. 登入
$curl->post('https://example.com/login', ['user' => 'a', 'pass' => 'b']);
// 2. 後續請求自動攜帶 Cookie
$html = $curl->get('https://example.com/dashboard');
?>

常見坑與最佳實務

問題解決
SSL 憑證驗證失敗正確安裝 CA 憑證 yum install ca-certificates,勿關閉 CURLOPT_SSL_VERIFYPEER
大檔案下載記憶體爆炸用 CURLOPT_WRITEFUNCTION 寫入檔案流,不回傳字串
重定向循環設定 CURLOPT_MAXREDIRS、CURLOPT_FOLLOWLOCATION
並發請求單實例不支援並發,改用 curl_multi 或 Guzzle Promise
超時設定CURLOPT_CONNECTTIMEOUT (連線)、CURLOPT_TIMEOUT (整體)

現代替代方案:Guzzle / Symfony HttpClient / HTTPlug

  • **Guzzle**:中介軟體、Promise 非同步、重試、測試雙工
  • **Symfony HttpClient**:PSR-18 相容、自動解碼、異步、開箱即用
  • **HTTPlug**:介面抽象,庫作者不綁定具體客戶端

新專案**強烈建議直接使用 Guzzle 或 Symfony HttpClient**,僅維護舊專案才手寫封裝類。

結語

Curl 封裝類核心價值在於**統一錯誤處理、預設安全選項、簡化常用介面**。理解 cURL 關鍵選項(SSL、超時、重定向、Cookie、Header)比封裝類本身更重要。現代開發請擁抱 PSR-18/PSR-17 標準客戶端,減少造輪子,專注業務邏輯。

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