cURL 是 PHP 進行 HTTP 請求(API 調用、爬蟲、文件上傳/下載)的核心擴展。本文覆蓋安裝驗證、基礎流程、常用選項、高級場景封裝及常見錯誤排查。
yum/dnf/apt install php-curl → 重啟 PHP-FPM/Apache。extension=curl,確保 `libssh2.dll`、`libeay32.dll`、`ssleay32.dll` 在系統 PATH (PHP 目錄) 下 → 重啟 Web 服務器。php -m | grep curl 或 `phpinfo()` 搜 `curl support => enabled`。<?php
// 1. 初始化
$ch = curl_init();
if ($ch === false) die('cURL 初始化失敗: ' . curl_error($ch));
// 2. 設置選項 (核心)
$url = 'https://api.example.com/users';
curl_setopt_array($ch, [
CURLOPT_URL => $url, // 目標 URL
CURLOPT_RETURNTRANSFER => true, // **關鍵**:返回字符串而非直接輸出
CURLOPT_HEADER => false, // 不包含響應頭
CURLOPT_FOLLOWLOCATION => true, // 自動跟隨 301/302 跳轉
CURLOPT_MAXREDIRS => 5, // 最大跳轉次數
CURLOPT_TIMEOUT => 10, // 總超時秒數
CURLOPT_CONNECTTIMEOUT => 5, // 連接超時
CURLOPT_SSL_VERIFYPEER => true, // **生產必開**:驗證 SSL 證書
CURLOPT_SSL_VERIFYHOST => 2, // 驗證域名匹配
// CURLOPT_CAINFO => '/path/to/cacert.pem', // 自定義 CA 憑證路徑
CURLOPT_USERAGENT => 'MyApp/1.0 (+https://example.com)', // 標識爬蟲身份
]);
// 3. 執行請求
$response = curl_exec($ch);
$errno = curl_errno($ch);
$error = curl_error($ch);
$info = curl_getinfo($ch); // 獲取 HTTP 碼、耗時、大小等元數據
// 4. 關閉釋放
curl_close($ch);
// 處理結果
if ($errno) {
error_log("cURL 錯誤 [$errno]: $error | URL: $url");
// 重試邏輯...
} else {
$httpCode = $info['http_code'];
if ($httpCode >= 200 && $httpCode < 300) {
$data = json_decode($response, true); // 假設 JSON 響應
// 業務處理...
} else {
error_log("HTTP 錯誤 [$httpCode]: $response");
}
}
?>
<?php
class HttpClient {
private $ch;
private $defaultOpts = [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HEADER => false,
CURLOPT_FOLLOWLOCATION => true,
CURLOPT_MAXREDIRS => 3,
CURLOPT_TIMEOUT => 15,
CURLOPT_CONNECTTIMEOUT => 5,
CURLOPT_SSL_VERIFYPEER => true,
CURLOPT_SSL_VERIFYHOST => 2,
CURLOPT_USERAGENT => 'HttpClient/1.0',
];
public function __construct() {
$this->ch = curl_init();
if (!$this->ch) throw new Exception('cURL 初始化失敗');
curl_setopt_array($this->ch, $this->defaultOpts);
}
public function __destruct() {
if ($this->ch) curl_close($this->ch);
}
// 通用請求
public function request(string $method, string $url, array $data = [], array $headers = []): array {
$opts = [
CURLOPT_URL => $url,
CURLOPT_CUSTOMREQUEST => strtoupper($method),
];
if (in_array($method, ['POST', 'PUT', 'PATCH'])) {
$opts[CURLOPT_POSTFIELDS] = is_array($data) ? http_build_query($data) : $data;
$headers[] = 'Content-Type: application/x-www-form-urlencoded';
}
if ($headers) {
$opts[CURLOPT_HTTPHEADER] = $headers;
}
curl_setopt_array($this->ch, $opts);
$response = curl_exec($this->ch);
$errno = curl_errno($this->ch);
$error = curl_error($this->ch);
$info = curl_getinfo($this->ch);
// 重置選項供下次復用 (重要!)
curl_setopt_array($this->ch, $this->defaultOpts);
return [
'success' => $errno === 0 && ($info['http_code'] >= 200 && $info['http_code'] < 300),
'http_code' => $info['http_code'],
'body' => $response,
'error' => $errno ? "cURL $errno: $error" : null,
'info' => $info,
];
}
// 便捷方法
public function get(string $url, array $params = [], array $headers = []): array {
if ($params) $url .= (strpos($url, '?') ? '&' : '?') . http_build_query($params);
return $this->request('GET', $url, [], $headers);
}
public function post(string $url, array $data = [], array $headers = []): array { return $this->request('POST', $url, $data, $headers); }
public function postJson(string $url, array $data = [], array $headers = []): array {
$headers[] = 'Content-Type: application/json';
return $this->request('POST', $url, json_encode($data), $headers);
}
}
// 使用示例
$client = new HttpClient();
$res = $client->postJson('https://api.example.com/order', ['goods_id' => 101, 'num' => 2]);
if ($res['success']) {
echo "下單成功: " . json_encode($res['body']);
} else {
echo "失敗: " . $res['error'];
}
?>
| 現象 | 原因 | 解決 |
|---|---|---|
| 返回 bool(false) / 空白 | 未設置 CURLOPT_RETURNTRANSFER => true | 必加此選項 |
| SSL certificate problem | CA 憑證缺失/過期/自簽名 | 下載 cacert.pem 配置 CURLOPT_CAINFO;內網測試暫時關閉驗證 (生產禁用) |
| Connection timed out | 防火牆/安全組/目標 IP 錯誤/超時太短 | 檢查網絡通達性、調大 CURLOPT_TIMEOUT |
| HTTP 400/405 | 請求方法/參數格式/Content-Type 不符 | 抓包對比文檔、確認 http_build_query vs json_encode |
| 中文亂碼 | 編碼不一致 | 確保 URL 編碼 `urlencode`、POST 數據 UTF-8、設置 `Accept-Charset: UTF-8` |
| 內存耗盡 | 下載大文件未流式處理 | 用 CURLOPT_WRITEFUNCTION 回調寫入文件句柄 |
cURL 是 PHP 網絡編程的瑞士軍刀。**務必開啟 `RETURNTRANSFER`、生產環境堅持 `SSL_VERIFYPEER=true`、封裝復用類管理句柄生命周期、善用 `curl_getinfo` 觀測性能**。現代項目亦可考慮 Guzzle (PSR-18 標準客戶端) 獲得更優雅的異步、中間件、重試機制。