PHP 擁有多層緩衝:PHP 內部 output_buffering → Web Server (Apache/Nginx) 緩衝 → TCP 發送緩衝 → 瀏覽器渲染緩衝。理解三函數作用層級,才能實現即時輸出(如進度條、長任務日誌)。
| 函數 | 作用層級 | 行為 | 典型用途 |
|---|---|---|---|
| flush() | PHP → SAPI → Web Server → TCP | 強制將所有上層緩衝推送至瀏覽器 | 即時輸出、長連線、SSE |
| ob_flush() | PHP 內部 output_buffer | 僅刷新 PHP 內部緩衝到 SAPI 層 | 配合 flush() 使用 |
| ob_end_flush() | PHP 內部 output_buffer | 刷新並關閉/銷毀當前緩衝區 | 結束緩衝、輸出並清理 |
ob_flush():緩衝區保留,後續 echo 仍進入同一緩衝區,可多次呼叫。ob_end_flush():緩衝區銷毀,後續 echo 直接進入上層(SAPI)或新開啟的緩衝區,不可重複呼叫(除非重新 ob_start())。<?php
// 1. 關閉 PHP 內建 output_buffering(或在 php.ini 設為 Off)
// 2. 關閉 Web Server 緩衝(Nginx: proxy_buffering off; Apache: 無直接關閉)
// 3. 發送足夠資料觸發瀏覽器渲染(通常 > 256 bytes 或 1KB)
// 防止瀏覽器緩衝:先輸出 1KB 空白
echo str_repeat(' ', 1024);
for ($i = 1; $i <= 10; $i++) {
echo "步驟 {$i} 完成";
// 關鍵順序:先 ob_flush 再 flush
ob_flush(); // 將 PHP 緩衝推到 SAPI
flush(); // 強制 SAPI/Web Server 發送到瀏覽器
sleep(1); // 模擬耗時任務
}
echo "全部完成!";
ob_end_flush(); // 結束腳本前清理
| 現象 | 原因 | 解決 |
|---|---|---|
| 一次性全部輸出 | php.ini output_buffering=4096 | php.ini 設 Off、或腳本開頭 @ini_set('output_buffering', '0')(需 PHP 5.4+) |
| Nginx 緩衝響應 | 預設 proxy_buffering on | Nginx location 加 proxy_buffering off; 或 X-Accel-Buffering: no header |
| Apache mod_gzip/deflate 壓縮 | 壓縮需等內容完整 | SetEnv no-gzip 1 或 apache_setenv('no-gzip', 1) |
| 瀏覽器不顯示 | 瀏覽器渲染緩衝(需一定量資料) | 先輸出 1-2KB 填充、或用 ` ` 區塊觸發渲染 |
| SSL/TLS 加密 | 加密記錄層緩衝 | 通常無解,接受輕微延遲 |
<?php
ob_start(); // Level 1
echo "A";
ob_start(); // Level 2
echo "B";
ob_end_flush(); // 輸出 B,關閉 Level 2
echo "C";
ob_end_flush(); // 輸出 A C,關閉 Level 1
// 輸出順序:B A C
ob_get_clean():取得緩衝內容並刪除緩衝區(常用於模板引擎捕獲輸出)。ob_end_clean():刪除緩衝區並捨棄內容。掌握緩衝層級與函數配合,能實現進度條、即時日誌、流式下載、Server-Sent Events 等進階功能。