PHP Output Control Functions: Everything You Need to Know
Learn PHP output buffering and the ob_* output control functions: ob_start, ob_get_clean, ob_end_flush, callbacks, and common gotchas, with runnable examples.
Normally, every echo, print, or piece of HTML in a PHP script is sent to the browser the moment it runs. Output buffering lets you intercept that output and hold it in memory (a buffer) instead, so you can inspect, edit, discard, or send it later. PHP's output control functions are the built-in tools that manage this buffer.
This chapter explains what output buffering is, why it solves real problems, and how each ob_* function works — with runnable examples.
Why output buffering matters
Output buffering is more than a curiosity. It solves a handful of everyday PHP problems:
- Avoid "headers already sent" errors. In PHP you must call
header()andsetcookie()before any output reaches the browser. Buffer the output and you can still send headers after your templates have run, because nothing has actually been flushed yet. See headers_sent(). - Capture output as a string. Render a template or include a file and grab its result as a variable instead of printing it — the foundation of most simple templating engines.
- Post-process the whole page. Minify HTML, replace placeholders, or compress output (gzip) in one place before it's sent.
- Discard unwanted output. Throw away noise from a third-party library or a debug statement you don't want the user to see.
The output control functions
Here are the functions you will use most. They all operate on the buffer started by ob_start().
| Function | What it does |
|---|---|
ob_start() | Starts a new output buffer. Capturing begins from this point. |
ob_get_contents() | Returns the buffer's current contents without stopping buffering. |
ob_get_length() | Returns the number of bytes currently in the buffer. |
ob_get_level() | Returns the nesting level (how many buffers are stacked). |
ob_clean() | Empties the buffer but keeps buffering on. |
ob_get_clean() | Returns the contents and turns the buffer off — a common combo. |
ob_end_clean() | Discards the buffer and turns buffering off (returns nothing). |
ob_flush() / ob_end_flush() | Sends the buffer to the browser; ob_end_flush() also stops buffering. |
Capture output as a string
The most common use of buffering is grabbing whatever a block of code prints and storing it in a variable. ob_get_clean() returns the buffer and stops buffering in one call:
Nothing reaches the browser until the final echo, which prints HELLO, WORLD!. We captured the text, transformed it, and only then sent it out. This is exactly how a tiny template function works:
<?php
function renderTemplate(string $name): string {
ob_start();
echo "Hello, $name!";
return ob_get_clean(); // contents + stop buffering
}
echo renderTemplate("Ada");This prints Hello, Ada!. In a real project the buffered block would be a full HTML template with embedded <?= $name ?> tags.
Flush the buffer to the browser
When you only want to delay output (not capture it), use ob_end_flush() to send everything at once:
While the buffer is open you can also inspect it with ob_get_length() and ob_get_level():
<?php
ob_start();
echo "buffered text";
echo "\nLevel: " . ob_get_level(); // 1 — one buffer is active
echo "\nLength: " . ob_get_length(); // bytes captured so far
ob_end_flush();ob_get_level() returns 1 because a single buffer is active; if you call ob_start() again inside it, the level becomes 2 (buffers nest like a stack).
Transform output with a callback
ob_start() accepts a callback that receives the entire buffer and returns the modified version. This is how output minifiers and find-and-replace filters work:
<?php
ob_start(function (string $buffer): string {
return str_replace("cat", "dog", $buffer);
});
echo "I have a cat.";
ob_end_flush();The callback runs when the buffer is flushed, so the page prints I have a dog.. The same pattern powers ob_gzhandler, PHP's built-in callback for gzip-compressing output.
Gotchas
- Always close what you open. Every
ob_start()should be matched by a flush or clean call. An unbalanced buffer leaves output stuck in memory and never reaches the user. - Buffers nest. Each
ob_start()adds a level.ob_get_clean()only closes the innermost one; useob_get_level()in a loop if you need to unwind several. ob_get_contents()does not stop buffering — only*_cleanand*_end_*functions do. Mixing them up is a frequent source of duplicated output.- A buffer has a finite size. By default PHP flushes automatically once the buffer reaches
output_bufferingbytes (php.ini), so don't rely on it to hold an unlimited amount of data.
Conclusion
Output buffering gives you control over when and how your script's output is sent. Use it to capture rendered content as a string, to set headers after generating a page, to discard unwanted output, or to post-process the entire response in one place. Once you understand the start/get/clean/flush cycle, the ob_* family is small and predictable.
Related reading: echo and print, PHP header(), PHP Sessions, and PHP Cookies.