執行耗時腳本(如數據遷移、報表生成、批量發送郵件)時,希望瀏覽器能實時看到進度輸出,而非等待腳本結束一次性顯示。
ob_get_level() / ob_start():開啟/檢查輸出緩衝;ob_flush():刷新 PHP 內部緩衝區到 Web 服務器;flush():刷新 Web 服務器緩衝區到瀏覽器(需服務器支持,如 Apache mod_php、Nginx fastcgi_buffering off);str_repeat(' ', 4096):填充足夠字節觸發瀏覽器渲染(部分瀏覽器需 > 1KB/8KB 才開始顯示)。\n";
// 實際業務邏輯
sleep(1);
}
echo "輸出完畢!";
第 1 次輸出 第 2 次輸出 ... 第 10 次輸出 輸出完畢!
瀏覽器每隔 1 秒顯示一行,而非等待 10 秒一次性顯示。
| 層級 | 配置/代碼 | 說明 |
|---|---|---|
| PHP | output_buffering = Off (php.ini) | 或運行時 while(ob_get_level()) ob_end_clean(); |
| PHP-FPM | fastcgi_buffering off; (Nginx) | 關閉 FastCGI 緩衝,直接傳輸 |
| Nginx | proxy_buffering off; (反代時) | 關閉代理緩衝 |
| Apache | SetOutputFilter DEFLATE 可能干擾 | 壓縮模塊會緩衝輸出,需排除該路徑 |
| 瀏覽器 | 需接收足夠字節(>1KB)才渲染 | 用 str_repeat(' ', 4096) 填充 |
class Progress {
public static function start() {
while (ob_get_level()) ob_end_clean();
echo str_repeat(' ', 4096);
ob_end_flush();
ob_implicit_flush(true);
}
public static function write($msg) {
echo $msg . "
\n";
}
public static function end($msg = '完成') {
echo $msg;
}
}
// 使用
Progress::start();
for ($i=1; $i<=5; $i++) {
Progress::write("處理項目 #{$i}");
sleep(1);
}
Progress::end("全部完成");
對於簡單 CLI 腳本或內部工具,ob_flush + flush 方案最輕量、零依賴。