輸出緩衝區 讓 PHP 腳本先將輸出存入記憶體,再一次性或分階段送給瀏覽器,解決「header 已送出無法 setcookie」、捕獲不可控函式輸出、頁面壓縮、靜態頁生成等需求。
核心原理
- 開啟
ob_start() 後,echo/print 寫入 OB 緩衝區,header() 直接寫入 SAPI 緩衝區。
- 腳本結束或呼叫
ob_end_flush() 時,OB 內容移至 SAPI 緩衝區,隨後送往瀏覽器。
- 可多層巢狀
ob_start(),ob_get_level() 取得層數。
常用函式速查表
| 函式 | 行為 | 回傳 |
ob_start(cb?) | 開啟緩衝,可指定回調處理內容 | bool |
ob_get_contents() | 取得目前緩衝內容 | string |
ob_get_clean() | 取得內容並關閉、丟棄緩衝 | string |
ob_end_flush() | 輸出內容並關閉緩衝 | bool |
ob_end_clean() | 丟棄內容並關閉緩衝 | bool |
ob_flush() | 輸出內容但保持緩衝開啟 | bool |
flush() | 將 SAPI/伺服器緩衝推送給瀏覽器 | void |
四大經典應用場景
1. 解決「Header already sent」
<?php
ob_start();
echo '頁面內容...';
header('Location: /login.php'); // 正常運作
ob_end_flush();
?>
2. 捕獲 phpinfo()、var_dump() 等無法直接賦值的輸出
<?php
ob_start();
phpinfo();
$info = ob_get_clean();
file_put_contents('phpinfo.html', $info);
?>
3. GZIP 壓縮輸出 (需 zlib)
<?php
// 簡單啟用
ob_start('ob_gzhandler');
// 或手動判斷瀏覽器支援
if (str_contains($_SERVER['HTTP_ACCEPT_ENCODING'] ?? '', 'gzip')) {
ob_start('ob_gzhandler');
} else {
ob_start();
}
?>
4. 靜態頁生成 / 全頁快取
<?php
ob_start();
// ... 複雜邏輯產生頁面 ...
$html = ob_get_contents();
file_put_contents('cache/page.html', $html);
ob_end_flush(); // 仍正常輸出給使用者
?>
⚠️ 常見坑與最佳實踐
- 順序:
ob_flush() → flush() (先刷 PHP 緩衝,再推 Web Server 緩衝)。
- 預設緩衝:
php.ini output_buffering=4096 導致小輸出不即時,長輪詢/串流需 ob_implicit_flush(true) 或手動 ob_flush/flush。
- 記憶體:大頁面整頁緩衝會佔用大量記憶體,串流輸出改用
ob_flush/flush 分塊送出。
- 錯誤處理:
ob_end_clean() 在例外發生時清理殘留緩衝,避免雙重輸出。
- 不要濫用:純 API 回傳 JSON 無需 OB,直接
header('Content-Type: application/json'); echo json_encode($data);。
php.ini 相關設定