Output Buffering (ob_start / ob_flush)
debt(d7/e3/b3/t5)
Closest to 'only careful code review or runtime testing' (d7). The detection_hints note automated=no and phpstan as the only listed tool, but phpstan does not natively catch missing ob_start() or unmatched ob_end_clean() calls reliably. The 'headers already sent' error is a runtime signal only seen when the code executes, and the memory-leak from nested buffers is silent in production until the script degrades — matching d7.
Closest to 'simple parameterised fix' (e3). The quick_fix describes adding ob_start() at the top of templates and pairing it with ob_end_clean()/ob_end_flush(). This is a small, localised fix in one file or component, not a one-liner swap (e1) but not a multi-file refactor either — e3 is accurate.
Closest to 'localised tax' (b3). The applies_to covers web and CLI contexts broadly, but the actual buffering calls are typically confined to specific entry points or template layers. The choice doesn't reshape the whole codebase, but it does impose a persistent maintenance awareness in those zones (ensure buffers are flushed/cleaned), making b3 the best fit.
Closest to 'notable trap / documented gotcha' (t5). The misconception field explicitly states developers think output buffering is only for capturing template output, missing its header-deferral, compression, and response-interception roles. The SAPI-dependent implicit buffering behaviour (PHP-FPM vs others) is an additional documented gotcha that many developers learn the hard way — solidly t5.
Also Known As
TL;DR
Explanation
PHP's output buffering (ob_start(), ob_get_clean(), ob_flush()) intercepts all echo/print output into an internal buffer. This enables: modifying output after generation (adding compression, injecting content), sending headers after output has started (workaround for 'headers already sent' errors — though proper code shouldn't need this), capturing template output as a string, and implementing full-page caching. Nested buffers are supported. ob_gzhandler compresses output — but prefer server-level compression. Forgetting to flush or clean a buffer on exceptions is a common source of partially-delivered or corrupted responses.
Common Misconception
Why It Matters
Common Mistakes
- Calling header() after output has been sent — 'headers already sent' error caused by missing ob_start().
- Not calling ob_end_clean() or ob_end_flush() — nested buffers cause memory leaks in long-running scripts.
- Relying on output buffering to fix poorly structured code instead of fixing the output order.
- Not knowing that PHP-FPM and some SAPIs have implicit output buffering — behaviour differs across environments.
Code Examples
// Headers already sent — ob_start() would fix this:
echo 'Some content'; // Output sent
header('Location: /dashboard'); // Warning: headers already sent
// Fix:
ob_start();
echo 'Some content';
header('Location: /dashboard');
ob_end_clean();
exit;
// ob_start() captures all output until ob_end_flush()/ob_get_clean()
ob_start();
require 'template.php'; // outputs HTML
$html = ob_get_clean(); // capture and stop buffering
// Send custom headers before any output — ob_start() buys time
ob_start();
// ... lots of processing that might echo ...
header('X-Processing-Time: ' . $elapsed); // still works — nothing sent yet
ob_end_flush();
// Template rendering pattern
function render(string $template, array $vars): string {
extract($vars, EXTR_SKIP);
ob_start();
require "views/{$template}.php";
return ob_get_clean();
}
$html = render('email/welcome', ['user' => $user]);