ob_start() 是 PHP 輸出控制系列函數的核心,許多開發者誤以為它是「加速頁面輸出」的工具,實際上它的本質是**攔截並控制輸出流**。理解其機制,能解決「Header 已發送無法 setcookie」、「捕獲 phpinfo 輸出」、「頁面靜態化」、「GZIP 壓縮輸出」等經典問題。
echo、print、HTML 直接輸出等產生內容ob_end_flush()/ob_get_clean() 時,緩衝區內容才被送往 SAPI<?php
ob_start(); // 開啟緩衝,所有輸出暫存
echo '頁面內容...'; // 實際未輸出到瀏覽器
setcookie('token', 'abc'); // 此時仍可發送 Header
session_start(); // 同理
ob_end_flush(); // 統一發送
?>
⚠️ **最佳實踐**:調整代碼結構,確保 Header/cookie/session 在**任何輸出前**執行,而非依賴 ob_start 掩蓋架構問題。
<?php ob_start(); phpinfo(); // 直接輸出大量 HTML $info = ob_get_clean(); // 獲取並清空緩衝區 // 現可對 $info 進行字串替換、存檔、局部輸出 echo '<div class="phpinfo-container">' . $info . '</div>'; ?>
<?php
// 開啟 GZIP 壓縮輸出(需 zlib 擴充模組)
ob_start('ob_gzhandler');
echo '大量 HTML 內容...'; // 自動壓縮後發送給支援 gzip 的瀏覽器
?>
進階:自定義回調函數實現繁簡轉換、敏感詞過濾、模板變量替換:
<?php
function postProcess($buffer) {
$buffer = str_replace('{SITE_NAME}', '我的網站', $buffer);
$buffer = preg_replace_callback('/\{DATE:(.*?)\}/', fn($m) => date($m[1]), $buffer);
return $buffer;
}
ob_start('postProcess');
// 模板輸出...
?>
<?php
$cacheFile = '/var/www/cache/page_' . md5($_SERVER['REQUEST_URI']) . '.html';
$cacheTime = 3600; // 1 小時
if (file_exists($cacheFile) && time() - filemtime($cacheFile) < $cacheTime) {
readfile($cacheFile); // 直接輸出靜態檔
exit;
}
ob_start();
// 複雜業務邏輯、資料庫查詢、模板渲染...
$content = ob_get_clean();
file_put_contents($cacheFile, $content); // 存檔
echo $content; // 輸出
?>
| 函數 | 作用 | 返回值 |
|---|---|---|
ob_start(callable $callback=null) | 開啟輸出緩衝,可選回調處理輸出 | bool |
ob_get_contents() | 獲取緩衝區內容(不清空) | string|false |
ob_get_clean() | 獲取內容並**刪除緩衝區**(關閉緩衝) | string|false |
ob_get_flush() | 獲取內容、發送輸出、並刪除緩衝區 | string|false |
ob_end_flush() | 發送緩衝區內容並關閉緩衝 | bool |
ob_end_clean() | 丟棄緩衝區內容並關閉緩衝 | bool |
ob_flush() | 發送緩衝區內容(保留緩衝區) | bool |
ob_clean() | 清空緩衝區內容(保留緩衝區) | bool |
ob_get_level() | 獲取當前緩衝嵌套層級 | int |
ob_list_handlers() | 列出當前活躍的回調處理器 | array |
PHP 支援多層 ob_start() 嵌套,每層獨立緩衝區:
<?php ob_start(); // Level 1 echo 'A'; ob_start(); // Level 2 echo 'B'; $level2 = ob_get_clean(); // 獲取 B,關閉 Level 2 echo 'C'; $level1 = ob_get_clean(); // 獲取 A + C,關閉 Level 1 echo $level1 . '|' . $level2; // 輸出:AC|B ?>
實戰中常見於框架:核心佈局佔 Level 1,局部區塊(如 Widget)各自開啟 Level 2 獨立渲染快取。
| 誤區 | 真相 |
|---|---|
| ob_start 能加速頁面載入 | **錯誤**。ob_start 增加記憶體複製與回調開銷,僅在「需整體處理輸出」(GZIP、靜態化、替換)時有價值 |
| 所有頁面開頭加 ob_start 總是好的 | **錯誤**。會增加 CPU/記憶體壓力,且掩蓋「過早輸出」的架構異味 |
| ob_gzhandler 通用無副作用 | **需注意**:需 zlib、瀏覽器支援 gzip、某些代理/CDN 可能干擾、二進制檔案(圖片/PDF)不應壓縮 |
| ob_get_contents() 後自動發送 | **錯誤**。必須配合 ob_end_flush()/ob_flush() 才會發送 |
gzip on、Apache mod_deflate —— 性能更高、不佔用 PHP 進程ob_start() 是 PHP 早期為彌補「單線程、單輸出流」架構而設計的強大工具。掌握其「攔截→處理→釋放」三階段模型,能解決一類「輸出時機控制」問題。但在現代框架與 Web Server 優化下,**業務代碼應盡量避免直接操作 ob_***,改用框架提供的 Response、View、Cache 抽象層,才能寫出可維護、高效能的應用。