PHP 腳本執行時,echo、print、HTML 文本等輸出並不會立即發送給瀏覽器,而是先寫入 **PHP 內部輸出緩衝區**。緩衝區內容會在腳本結束、達到大小限制或手動刷新時發送。這機制由 php.ini 中 output_buffering 控制(默認 4096 字節)。
header()、session_start()、setcookie()phpinfo()、var_dump()、print_r() 等直接輸出的函數ob_flush() + flush() 實現流式響應、Server-Sent Events、大文件下載進度| 指令 | 默認值 | 說明 |
|---|---|---|
| output_buffering | 4096 | On/Off/整數(字節)。On 等同 4096,啟用內置緩衝 |
| implicit_flush | Off | On 時每次輸出自動 flush,等同每行後調用 flush() |
| output_handler | 空 | 內置處理器名(如 ob_gzhandler),等同全局 ob_start('handler') |
$callback:接收緩衝區字符串,返回處理後字符串$chunk_size:緩衝區達此大小自動刷新$flags:PHP_OUTPUT_HANDLER_CLEANABLE、PHP_OUTPUT_HANDLER_REMOVABLE 等// 示例:全頁 Gzip 壓縮
ob_start('ob_gzhandler');
// 示例:自定義處理器(注入 CSRF Token)
ob_start(function($buffer) {
return str_replace('{{CSRF_TOKEN}}', generate_csrf_token(), $buffer);
});
ob_get_contents():返回當前緩衝區字符串(不清空)ob_get_length():返回字節長度ob_get_level():返回嵌套層級(0 表示無用戶緩衝)ob_get_status(true):返回所有緩衝區狀態數組ob_flush():將**用戶級緩衝區**內容推送到 **PHP 內部緩衝區**(不發送給瀏覽器)flush():將 **PHP 內部緩衝區 + Web Server 緩衝區** 發送給瀏覽器ob_flush(); flush();ob_clean():清空當前緩衝區內容(不關閉)ob_end_flush():輸出並關閉當前緩衝區ob_end_clean():丟棄並關閉當前緩衝區ob_get_flush() / ob_get_clean():獲取內容並輸出/丟棄關閉ob_implicit_flush(true):開啟後每次輸出自動 flush(),無需手動調用$cacheFile = __DIR__ . '/cache/page_' . md5($_SERVER['REQUEST_URI']) . '.html';
$cacheTime = 300; // 5 分鐘
if (file_exists($cacheFile) && time() - filemtime($cacheFile) < $cacheTime) {
readfile($cacheFile);
exit;
}
ob_start(function($html) use ($cacheFile) {
file_put_contents($cacheFile, $html);
return $html;
});
// 業務邏輯輸出大量 HTML...
include 'template.php';
ob_end_flush();
ob_start();
phpinfo(INFO_MODULES); // 僅輸出模組信息
$infoHtml = ob_get_clean();
// 後續可存入數據庫、發送郵件、生成 PDF
file_put_contents('phpinfo_report.html', $infoHtml);
$file = '/data/large_video.mp4';
header('Content-Type: video/mp4');
header('Content-Length: ' . filesize($file));
header('Content-Disposition: attachment; filename="video.mp4"');
// 關閉輸出緩衝,直接流式發送
while (ob_get_level()) ob_end_clean();
$handle = fopen($file, 'rb');
while (!feof($handle)) {
echo fread($handle, 8192);
ob_flush(); flush(); // 立即推送給瀏覽器
}
fclose($handle);
header('Content-Type: text/event-stream');
header('Cache-Control: no-cache');
header('X-Accel-Buffering: no'); // 禁用 Nginx 緩衝
ob_implicit_flush(true);
ob_end_clean();
for ($i = 1; $i <= 10; $i++) {
echo "data: 處理步驟 $i\n\n";
ob_flush(); flush();
sleep(1);
}
echo "data: 完成\n\n";
| 問題 | 原因 | 解決 |
|---|---|---|
| 實時輸出不生效 | Nginx/Apache 緩衝、瀏覽器緩衝、防毒軟件攔截 | 設置 header('X-Accel-Buffering: no')、輸出 1024+ 字節填充、關閉 gzip |
| ob_start 重複調用報錯 | 嵌套未正確關閉 | 使用 ob_get_level() 檢查,確保 ob_end_* 配對 |
| 回調函數執行多次 | 多層 ob_start 都有回調 | 理解堆疊執行順序:內層先執行,外層後執行 |
| ob_gzhandler 導致亂碼 | 輸出已壓縮內容又被二次壓縮 | 檢查 zlib.output_compression、Nginx gzip、CDN 壓縮衝突 |
// 惡意代碼示例(僅供理解防禦) $cmd = 'system'; ob_start($cmd); echo $_GET['a']; // 攻擊者控制輸入 ob_end_flush(); // 觸發 system($_GET['a'])
防禦:禁用 system、exec 等危險函數(disable_functions)、嚴格驗證用戶輸入、代碼審計關注 ob_start 可調用參數。
輸出緩衝是 PHP 靈活處理 HTTP 響應的核心機制。掌握 緩衝區堆疊模型、雙重刷新機制、回調處理器、實時流式輸出 四大核心,能解決從「Headers already sent」到「大文件下載」、「頁面靜態化」、「實時日誌」等全谱系問題。生產環境務必配合 php.ini 基線配置,防範緩衝區相關安全風險。