Output Buffering(輸出緩衝)是 PHP 4 引入的核心機制,解決 HTTP Header 必須先於 Body 發送的協議限制,同時賦予開發者強大的輸出控制能力。本文從 HTTP 協議基礎切入,詳細剖析緩衝運作原理、核心函式與進階實戰場景。
HTTP 回應由 Header 區塊 與 Body 區塊 組成,中間以空行(CRLF)分隔:
HTTP/1.1 200 OK
Date: Sat, 02 Sep 2000 21:40:08 GMT
Server: Apache/1.3.11 PHP/4.0.2
Content-Type: text/html; charset=UTF-8
Content-Length: 1234
<html><body>Hello, world!</body></html>
鐵律:Header 必須完整發送並以空行結束,之後才能發送 Body。一旦發送任何 Body 內容(含空白字元),Header 列表即被鎖定,後續 header()、setcookie()、session_start() 將失效並拋出警告。
ob_start() 或 output_buffering 開啟後,PHP 內核建立動態擴展的內存緩衝區。echo、HTML、print 等)寫入緩衝區,不經過 SAPI 發送給 Web Server。header()、setcookie() 只更新內部 Header 列表,不發送。ob_start(),形成緩衝棧,ob_end_flush()/ob_end_clean() 對應最內層。| 函式 | 功能 | 返回值 | 典型用途 |
|---|---|---|---|
ob_start(callable $callback = null, int $chunk_size = 0, int $flags = PHP_OUTPUT_HANDLER_STDFLAGS) | 開啟輸出緩衝,可指定回調處理輸出內容 | bool | 腳本入口、模板引擎捕獲輸出、GZIP 壓縮 |
ob_end_flush() | 發送緩衝內容並關閉緩衝 | bool | 手動提前輸出、長連線串流 |
ob_end_clean() | 丟棄緩衝內容並關閉緩衝 | bool | 錯誤處理、條件輸出取消 |
ob_get_contents() | 取得當前緩衝內容字串(不清空) | string|false | 模板渲染捕獲、快取生成、日誌記錄 |
ob_get_clean() | 取得內容並關閉緩衝(= ob_get_contents() + ob_end_clean()) | string | 最常用模式:捕獲輸出後決定是否輸出 |
ob_flush() | 發送緩衝內容但保持緩衝開啟 | bool | 長執行腳本分塊輸出、Comet/Server-Sent Events |
ob_get_level() | 取得當前緩衝巢狀層級 | int | 除錯、條件判斷是否在緩衝中 |
ob_get_status(bool $full_status = false) | 取得所有緩衝層狀態陣列 | array | 深度除錯、中介軟體檢查 |
<?php
function renderTemplate(string $template, array $data): string {
ob_start();
extract($data);
include $template; // 模板內 echo 輸出
return ob_get_clean(); // 取得渲染結果字串
}
// 生成靜態快取
$html = renderTemplate('views/article.php', ['title' => '標題', 'content' => '內容']);
file_put_contents('cache/article.html', $html);
echo $html; // 直接輸出
?>
<?php
ob_start(); // 全域緩衝
try {
// 業務邏輯
require 'controller.php';
} catch (Throwable $e) {
// 丟棄已輸出的錯誤內容,輸出友善錯誤頁
ob_end_clean();
http_response_code(500);
include 'views/500.php';
exit;
}
// 正常結束自動 flush
?>
<?php
ob_start();
echo "<table><thead><tr><th>ID</th><th>Data</th></tr></thead><tbody>";
foreach ($hugeDataset as $row) {
echo "<tr><td>{$row['id']}</td><td>{$row['data']}</td></tr>";
if (ob_get_length() > 8192) { // 每 8KB flush 一次
ob_flush();
flush(); // 強制 SAPI 發送到 Web Server
}
}
echo "</tbody></table>";
ob_end_flush();
?>
<?php
function compressHandler(string $buffer): string {
// 移除多餘空白、註解(簡易示範)
$buffer = preg_replace('/\s+/', ' ', $buffer);
return $buffer;
}
ob_start('compressHandler', 4096, PHP_OUTPUT_HANDLER_REMOVABLE);
// 輸出將自動經過 compressHandler 處理
echo " Hello World ";
ob_end_flush(); // 輸出:"Hello World"
?>
output_buffering 與 implicit_flushoutput_buffering = 4096:每個腳本隱含 ob_start(),緩衝區大小 4KB。On 表示無限制(不建議)。implicit_flush = Off:預設關閉。開啟後每次輸出自動呼叫 flush(),極大降低效能,僅除錯用。memory_limit),大量輸出務必分塊 ob_flush() + flush()。ob_start()。output_buffering = 4096,兼顧 Header 靈活性與記憶體安全。精通 Output Buffering,即掌握 PHP 輸出層的終極控制權,從解決 Header 錯誤到構建高效模板、串流輸出、快取層,皆建立於此核心機制之上。