PHP 內建的 get_headers() 函數雖然方便,但缺乏超時控制參數,在網路環境不佳時常導致程式卡住超過 20 秒。本文分享一個基於 cURL 實現的替代方案,支援自訂超時時間,並能解析 HTTP 標頭為結構化陣列。
在處理 Gravatar 頭像載入優化時,發現 get_headers() 呼叫極其緩慢,甚至綁定 IP 後仍需 20 秒以上。查閱 PHP 官方文件,該函數簽名為 array get_headers(string $url[, int $format = 0]),完全沒有超時參數。深入原始碼發現其底層依賴 php_stream,且涉及 zend_parse_parameters 等內部函數,無法直接在使用者層擴展。
採用 cURL 的 CURLOPT_HEADER 和 CURLOPT_NOBODY 選項僅抓取標頭,搭配 CURLOPT_TIMEOUT 實現超時控制。核心程式碼如下:
function get_url_headers($url, $timeout = 10) {
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_HEADER, true);
curl_setopt($ch, CURLOPT_NOBODY, true);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_TIMEOUT, $timeout);
$data = curl_exec($ch);
$data = preg_split('/\n/', $data);
$data = array_filter(array_map(function($line) {
$line = trim($line);
if ($line) {
$parts = preg_split('/:\s/', $line, 2);
switch (count($parts)) {
case 2: return [$parts[0] => $parts[1]];
case 1: return $parts;
default: return null;
}
}
}, $data));
sort($data);
foreach ($data as $key => $value) {
$itemKey = array_keys($value)[0];
if (is_int($itemKey)) {
$data[$key] = $value[$itemKey];
} elseif (is_string($itemKey)) {
$data[$itemKey] = $value[$itemKey];
unset($data[$key]);
}
}
return $data;
}
原版 get_headers() 回傳結果包含重複標頭(如多組 Date、Server),且耗時不可控。cURL 版本瞬間回傳,結構更乾淨,僅保留首組標頭值,適合高併發場景。實測在 Gravatar 請求中,cURL 版本穩定在 200ms 內完成,而原版頻繁超時。
CURLOPT_SSL_VERIFYPEER