兩大 HTTP 客戶端函式各有優劣,理解差異並建立通用封裝能應對不同伺服器環境限制。
<?php
$content = file_get_contents('https://www.example.com');
echo $content;
<?php
$ch = curl_init();
curl_setopt_array($ch, [
CURLOPT_URL => 'https://www.example.com',
CURLOPT_RETURNTRANSFER => 1,
CURLOPT_CONNECTTIMEOUT => 5,
CURLOPT_TIMEOUT => 10,
]);
$content = curl_exec($ch);
curl_close($ch);
echo $content;
| 特性 | file_get_contents | cURL |
|---|---|---|
| 依賴設定 | 需 allow_url_fopen=On | 需 php_curl.dll 擴充 |
| HTTP 方法 | 僅 GET | GET/POST/PUT/DELETE/HEAD 等 |
| 標頭控制 | 僅透過 stream context | 完整標頭設定 (CURLOPT_HTTPHEADER) |
| Cookie 管理 | 困難 | 內建 CookieJar/CookieFile |
| 重定向控制 | stream context 參數 | CURLOPT_FOLLOWLOCATION/MAXREDIRS |
| 逾時控制 | stream context (PHP 5.2+) | 精確到毫秒 (CONNECTTIMEOUT_MS/TIMEOUT_MS) |
| SSL/憑證 | 有限支援 | 完整控制 (CAINFO, VERIFYPEER, VERIFYHOST) |
| Proxy 支援 | stream context | 完整 Proxy 認證、類型、通道 |
| 檔案上傳 | 不支援 | CURLOPT_POSTFIELDS + CURLFile |
| 並發請求 | 不支援 | curl_multi 非阻塞並發 |
| 進度回呼 | 不支援 | CURLOPT_PROGRESSFUNCTION |
| 效能 (單請求) | 略快 (內建) | 略慢 (進程間通訊) |
| 記憶體佔用 | 一次性載入 | 可串流寫入檔案 (CURLOPT_FILE) |
<?php
/**
* 智慧型 HTTP GET 獲取器
* 優先使用 file_get_contents (效能較好),失敗回退 cURL
* @param string $url
* @param array $options 額外選項
* @return string|false
*/
function vitaGetUrlContent($url, $options = []) {
$defaults = [
'timeout' => 10,
'connect_timeout' => 5,
'user_agent' => 'Mozilla/5.0 (compatible; PHP HTTP Client)',
'headers' => [],
'follow_location' => true,
'max_redirects' => 5,
'verify_ssl' => true,
];
$opts = array_merge($defaults, $options);
// 方案一:file_get_contents (需 allow_url_fopen)
if (ini_get('allow_url_fopen') && function_exists('file_get_contents')) {
$context = stream_context_create([
'http' => [
'method' => 'GET',
'timeout' => $opts['timeout'],
'header' => array_merge([
'User-Agent: ' . $opts['user_agent'],
], $opts['headers']),
'follow_location' => $opts['follow_location'] ? 1 : 0,
'max_redirects' => $opts['max_redirects'],
'verify_peer' => $opts['verify_ssl'],
'verify_peer_name' => $opts['verify_ssl'],
],
'ssl' => [
'verify_peer' => $opts['verify_ssl'],
'verify_peer_name' => $opts['verify_ssl'],
]
]);
$content = @file_get_contents($url, false, $context);
if ($content !== false) return $content;
// 失敗時不報錯,嘗試 cURL
}
// 方案二:cURL
if (function_exists('curl_init')) {
$ch = curl_init();
curl_setopt_array($ch, [
CURLOPT_URL => $url,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_TIMEOUT => $opts['timeout'],
CURLOPT_CONNECTTIMEOUT => $opts['connect_timeout'],
CURLOPT_USERAGENT => $opts['user_agent'],
CURLOPT_HTTPHEADER => $opts['headers'],
CURLOPT_FOLLOWLOCATION => $opts['follow_location'],
CURLOPT_MAXREDIRS => $opts['max_redirects'],
CURLOPT_SSL_VERIFYPEER => $opts['verify_ssl'],
CURLOPT_SSL_VERIFYHOST => $opts['verify_ssl'] ? 2 : 0,
]);
$content = curl_exec($ch);
$errno = curl_errno($ch);
curl_close($ch);
if ($errno === 0) return $content;
}
// 方案三:Socket (最後手段)
return @file($url) ? implode('', @file($url)) : false;
}
// 使用範例
$html = vitaGetUrlContent('https://api.example.com/data', [
'timeout' => 15,
'headers' => ['Accept: application/json', 'Authorization: Bearer token123'],
]);
if ($html === false) {
die('所有 HTTP 客戶端均失敗,請檢查網路與 PHP 配置');
}
function vitaPostUrl($url, $data, $options = []) {
$defaults = [
'timeout' => 30,
'content_type' => 'application/x-www-form-urlencoded', // 或 application/json
];
$opts = array_merge($defaults, $options);
$postFields = ($opts['content_type'] === 'application/json')
? json_encode($data)
: http_build_query($data);
// cURL 是 POST 的唯一可靠選擇
if (!function_exists('curl_init')) {
trigger_error('cURL extension required for POST requests', E_USER_WARNING);
return false;
}
$ch = curl_init();
curl_setopt_array($ch, [
CURLOPT_URL => $url,
CURLOPT_POST => true,
CURLOPT_POSTFIELDS => $postFields,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_TIMEOUT => $opts['timeout'],
CURLOPT_HTTPHEADER => array_merge([
'Content-Type: ' . $opts['content_type'],
'User-Agent: Mozilla/5.0 (compatible; PHP HTTP Client)',
], $options['headers'] ?? []),
CURLOPT_SSL_VERIFYPEER => $options['verify_ssl'] ?? true,
]);
$response = curl_exec($ch);
$errno = curl_errno($ch);
curl_close($ch);
return $errno === 0 ? $response : false;
}
function checkHttpClientAvailability() {
$info = [
'allow_url_fopen' => ini_get('allow_url_fopen') ? 'On' : 'Off',
'file_get_contents' => function_exists('file_get_contents') ? '可用' : '不可用',
'curl_extension' => extension_loaded('curl') ? '已載入' : '未載入',
'curl_version' => function_exists('curl_version') ? curl_version()['version'] : 'N/A',
'openssl' => extension_loaded('openssl') ? '已載入' : '未載入 (HTTPS 將失敗)',
];
return $info;
}
print_r(checkHttpClientAvailability());
需要 POST/上傳/並發/精細控制?
是 → 必須用 cURL
否 →
只是簡單 GET?
是 → allow_url_fopen=On? → 是 → file_get_contents (效能最佳)
否 → cURL
否 → cURL