cURL 是 PHP 與外部 HTTP 服務交互的瑞士軍刀。本文精選 10 個高頻實戰場景,提供可直接落地的代碼模板,涵蓋 GET/POST、代理、FTP、JSON API、文件下載、斷點續傳等,助你快速解決 90% 的網絡請求需求。
function httpGet(string $url, array $options = []): ?string {
$ch = curl_init($url);
$defaultOpts = [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HEADER => false,
CURLOPT_FOLLOWLOCATION => true,
CURLOPT_CONNECTTIMEOUT => 5,
CURLOPT_TIMEOUT => 15,
CURLOPT_SSL_VERIFYPEER => true, // 生產環境必開
CURLOPT_USERAGENT => 'MyApp/1.0 (+https://example.com/bot)',
];
curl_setopt_array($ch, array_merge($defaultOpts, $options));
$response = curl_exec($ch);
$errno = curl_errno($ch);
$error = curl_error($ch);
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);
if ($errno !== 0) {
error_log("cURL Error [$errno]: $error for $url");
return null;
}
if ($httpCode < 200 || $httpCode >= 300) {
error_log("HTTP $httpCode for $url");
return null;
}
return $response;
}
function isSiteUp(string $url): bool {
$ch = curl_init($url);
curl_setopt_array($ch, [
CURLOPT_NOBODY => true,
CURLOPT_RETURNTRANSFER=> true,
CURLOPT_FOLLOWLOCATION=> true,
CURLOPT_CONNECTTIMEOUT=> 3,
CURLOPT_TIMEOUT => 5,
]);
curl_exec($ch);
$code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
$err = curl_errno($ch);
curl_close($ch);
return $err === 0 && $code === 200;
}
// 使用
if (isSiteUp('https://api.example.com/health')) {
echo "Service UP";
} else {
echo "Service DOWN";
}
function fetchUrl(string $url): ?string {
$ch = curl_init($url);
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HEADER => false,
CURLOPT_FOLLOWLOCATION => true,
CURLOPT_CONNECTTIMEOUT => 10,
CURLOPT_TIMEOUT => 30,
]);
$data = curl_exec($ch);
if (curl_errno($ch)) {
curl_close($ch);
return null;
}
curl_close($ch);
return $data;
}
function downloadImages(string $html, string $saveDir, int $concurrency = 5): void {
if (!is_dir($saveDir)) mkdir($saveDir, 0755, true);
preg_match_all('~https?://[^\s"\'<>]+\.(?:jpg|jpeg|png|gif|webp)~i', $html, $m);
$urls = array_unique($m[0]);
$mh = curl_multi_init();
$handles = [];
foreach ($urls as $url) {
$filename = basename(parse_url($url, PHP_URL_PATH));
$fp = fopen($saveDir . '/' . $filename, 'wb');
$ch = curl_init($url);
curl_setopt_array($ch, [
CURLOPT_FILE => $fp,
CURLOPT_FOLLOWLOCATION => true,
CURLOPT_CONNECTTIMEOUT => 10,
CURLOPT_TIMEOUT => 30,
]);
curl_multi_add_handle($mh, $ch);
$handles[] = ['ch' => $ch, 'fp' => $fp];
}
$running = null;
do {
curl_multi_exec($mh, $running);
curl_multi_select($mh, 0.2);
} while ($running);
foreach ($handles as $h) {
curl_multi_remove_handle($mh, $h['ch']);
fclose($h['fp']);
curl_close($h['ch']);
}
curl_multi_close($mh);
}
function ftpUpload(string $ftpUrl, string $localFile, bool $ascii = false): bool {
$fp = fopen($localFile, 'rb');
if (!$fp) return false;
$ch = curl_init($ftpUrl); // ftp://user:pass@host:21/path/file
curl_setopt_array($ch, [
CURLOPT_UPLOAD => true,
CURLOPT_INFILE => $fp,
CURLOPT_INFILESIZE => filesize($localFile),
CURLOPT_FTPASCII => $ascii ? 1 : 0,
CURLOPT_RETURNTRANSFER => true,
]);
$result = curl_exec($ch);
$errno = curl_errno($ch);
fclose($fp);
curl_close($ch);
return $errno === 0;
}
function postJson(string $url, array $payload, array $headers = []): ?array {
$json = json_encode($payload, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
$defaultHeaders = [
'Content-Type: application/json',
'Content-Length: ' . strlen($json),
'Accept: application/json',
];
$ch = curl_init($url);
curl_setopt_array($ch, [
CURLOPT_CUSTOMREQUEST => 'POST',
CURLOPT_POSTFIELDS => $json,
CURLOPT_RETURNTRANSFER=> true,
CURLOPT_HTTPHEADER => array_merge($defaultHeaders, $headers),
CURLOPT_CONNECTTIMEOUT=> 5,
CURLOPT_TIMEOUT => 20,
]);
$resp = curl_exec($ch);
$errno = curl_errno($ch);
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);
if ($errno !== 0 || $httpCode >= 400) {
error_log("API Error: HTTP $httpCode, cURL $errno - $url");
return null;
}
return json_decode($resp, true);
}
// 使用
$res = postJson('https://api.example.com/users', [
'name' => 'Hagrid',
'age' => 36,
], ['Authorization: Bearer ' . $token]);
function downloadResume(string $url, string $destFile): bool {
$fileSize = is_file($destFile) ? filesize($destFile) : 0;
$fp = fopen($destFile, 'ab'); // 追加模式
$ch = curl_init($url);
curl_setopt_array($ch, [
CURLOPT_FILE => $fp,
CURLOPT_FOLLOWLOCATION => true,
CURLOPT_RANGE => $fileSize . '-', // 關鍵:從已下載位置續傳
CURLOPT_CONNECTTIMEOUT => 15,
CURLOPT_TIMEOUT => 0, // 不限制總時間
CURLOPT_LOW_SPEED_LIMIT=> 1024, // 低速限制
CURLOPT_LOW_SPEED_TIME => 30,
]);
$result = curl_exec($ch);
$errno = curl_errno($ch);
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
fclose($fp);
curl_close($ch);
// 206 Partial Content 或 200 OK(不支援 Range 時重新下載)
return $errno === 0 && in_array($httpCode, [200, 206]);
}
function loginAndFetch(string $loginUrl, string $targetUrl, array $formData): ?string {
$cookieFile = sys_get_temp_dir() . '/cookies_' . md5($loginUrl) . '.txt';
// 1. 登錄
$ch = curl_init($loginUrl);
curl_setopt_array($ch, [
CURLOPT_POST => true,
CURLOPT_POSTFIELDS => http_build_query($formData),
CURLOPT_COOKIEJAR => $cookieFile, // 保存 Cookie
CURLOPT_COOKIEFILE => $cookieFile, // 讀取 Cookie
CURLOPT_RETURNTRANSFER => true,
CURLOPT_FOLLOWLOCATION => true,
]);
curl_exec($ch);
curl_close($ch);
// 2. 訪問受保護頁面
$ch = curl_init($targetUrl);
curl_setopt_array($ch, [
CURLOPT_COOKIEFILE => $cookieFile,
CURLOPT_RETURNTRANSFER => true,
]);
$html = curl_exec($ch);
curl_close($ch);
return $html;
}
function requestViaProxy(string $url, string $proxy, ?string $auth = null): ?string {
$ch = curl_init($url);
$opts = [
CURLOPT_PROXY => $proxy, // 1.2.3.4:8080 或 socks5://1.2.3.4:1080
CURLOPT_PROXYTYPE => strpos($proxy, 'socks5') === 0 ? CURLPROXY_SOCKS5 : CURLPROXY_HTTP,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_FOLLOWLOCATION => true,
];
if ($auth) {
$opts[CURLOPT_PROXYUSERPWD] = $auth; // user:pass
}
curl_setopt_array($ch, $opts);
$data = curl_exec($ch);
$errno = curl_errno($ch);
curl_close($ch);
return $errno === 0 ? $data : null;
}
function uploadFile(string $url, string $fieldName, string $filePath, array $extraFields = []): ?array {
if (!function_exists('curl_file_create')) { // PHP 5.5-
$post[$fieldName] = '@' . realpath($filePath);
} else {
$post[$fieldName] = curl_file_create($filePath);
}
$post = array_merge($post, $extraFields);
$ch = curl_init($url);
curl_setopt_array($ch, [
CURLOPT_POST => true,
CURLOPT_POSTFIELDS => $post,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_FOLLOWLOCATION => true,
]);
$resp = curl_exec($ch);
$errno = curl_errno($ch);
curl_close($ch);
return $errno === 0 ? json_decode($resp, true) : null;
}
function concurrentRequests(array $urls, int $maxConcurrent = 10, callable $onComplete): void {
$mh = curl_multi_init();
$queue = $urls;
$active = [];
// 初始填充
while (count($active) < $maxConcurrent && $queue) {
$url = array_shift($queue);
$ch = curl_init($url);
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_FOLLOWLOCATION => true,
CURLOPT_TIMEOUT => 30,
]);
curl_multi_add_handle($mh, $ch);
$active[(int)$ch] = ['url' => $url, 'ch' => $ch];
}
do {
$mrc = curl_multi_exec($mh, $running);
if ($mrc !== CURLM_OK) break;
// 檢查完成的請求
while ($info = curl_multi_info_read($mh)) {
$ch = $info['handle'];
$meta = $active[(int)$ch];
$response = curl_multi_getcontent($ch);
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
$errno = curl_errno($ch);
$onComplete($meta['url'], $response, $httpCode, $errno);
curl_multi_remove_handle($mh, $ch);
curl_close($ch);
unset($active[(int)$ch]);
// 补充新請求
if ($queue) {
$url = array_shift($queue);
$ch = curl_init($url);
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_FOLLOWLOCATION => true,
CURLOPT_TIMEOUT => 30,
]);
curl_multi_add_handle($mh, $ch);
$active[(int)$ch] = ['url' => $url, 'ch' => $ch];
}
}
if ($running) curl_multi_select($mh, 0.1);
} while ($running || $active);
curl_multi_close($mh);
}
// 使用
concurrentRequests($urlList, 20, function($url, $body, $code, $err) {
if ($err === 0 && $code === 200) {
// 解析 $body 入庫
} else {
error_log("Fail: $url HTTP $code cURL $err");
}
});
| 選項 | 推薦值 | 說明 |
|---|---|---|
| CURLOPT_SSL_VERIFYPEER | true | 必須驗證證書,配合 CURLOPT_CAINFO 指定 cacert.pem |
| CURLOPT_CONNECTTIMEOUT | 3-10s | 防止 DNS/TCP 握手阻塞 |
| CURLOPT_TIMEOUT | 15-60s | 總超時,防慢速攻擊 |
| CURLOPT_USERAGENT | 自定義標識 | 便於對方日誌排查,避免被 WAF 攔截 |
| CURLOPT_FOLLOWLOCATION | true | 自動跟隨 3xx,注意 open_basedir/safe_mode 限制 |
| CURLOPT_MAXREDIRS | 5-10 | 防重定向循環 |
| CURLOPT_LOW_SPEED_LIMIT/TIME | 1024/30 | 斷開極慢連線,釋放資源 |
掌握上述 10 大場景模板,即可應對絕大多數 PHP 網絡編程需求。建議將核心邏輯封裝為 HttpClient 服務類,統一異常處理、重試策略、日誌記錄、指標上報(Prometheus),並配合 Guzzle 或 Symfony HttpClient 等現代庫在新項目中落地,享受 PSR-18/PSR-7 標準帶來的可攜性與可測試性。