file_get_contents() 讀取遠程 URL 時預設無超時控制,極易導致腳本卡死。本文釐清 set_time_limit 的誤區,給出「流上下文設置超時」與「失敗重試」兩種生產級方案。
set_time_limit()僅限制**腳本最大執行時間**,不影響流層面的網絡等待。若網絡不通,腳本仍會在流層阻塞,直到 PHP 內部預設超時(通常 60 秒)或系統 TCP 超時。
<?php
$url = 'https://api.example.com/data';
$opts = [
'http' => [
'method' => 'GET',
'timeout' => 10, // 連接+讀取總超時(秒)
'header' => "User-Agent: MyApp/1.0\r\nAccept: application/json\r\n",
],
'ssl' => [ // HTTPS 時常需放寬驗證(僅開發/受信環境)
'verify_peer' => false,
'verify_peer_name' => false,
],
];
$ctx = stream_context_create($opts);
$response = @file_get_contents($url, false, $ctx);
if ($response === false) {
$err = error_get_last();
throw new RuntimeException("請求失敗:{$err['message']}");
}
$data = json_decode($response, true);
關鍵參數:timeout 單位為秒,包含 DNS 解析、TCP 握手、SSL 握手、數據傳輸全過程。若需分別控制連接/讀取超時,請改用 cURL。
網絡抖動、目標服務短暫不可用屬常態,單次失敗不應直接拋錯。
function fetchWithRetry(string $url, int $maxAttempts = 3, int $baseDelayMs = 200): string
{
$opts = [
'http' => ['method' => 'GET', 'timeout' => 8],
'ssl' => ['verify_peer' => false, 'verify_peer_name' => false],
];
$ctx = stream_context_create($opts);
for ($attempt = 1; $attempt <= $maxAttempts; $attempt++) {
$response = @file_get_contents($url, false, $ctx);
if ($response !== false) {
return $response;
}
if ($attempt === $maxAttempts) break;
// 指數退避 + 抖動
$delay = $baseDelayMs * (1 << ($attempt - 1)) + mt_rand(0, 100);
usleep($delay * 1000);
}
throw new RuntimeException("{$maxAttempts} 次重試均失敗:{$url}");
}
| 場景 | 建議 |
|---|---|
| 需精確控制連接/讀取超時 | cURL CURLOPT_CONNECTTIMEOUT / CURLOPT_TIMEOUT |
| 需處理 Cookie、重定向策略、POST 大文件 | cURL |
| 並發請求 | cURL 多句柄 curl_multi_* |
| 單次簡單 GET,代碼極簡優先 | file_get_contents + Context |
timeout(建議 5-15 秒)合理配置超時與重試,是構建健壯對外 HTTP 客戶端的基石。