PHP provides built-in helpers to manipulate the output buffering, but it can be tricky to debug.
What do you mean by "OB"?
I'll use "OB" for "Output Buffering" in this post.
Why use buffers anyway?
The OB can be convenient for various usages, like aggregating into a string different sources that output HTML in different ways, which removes the hassle of concatenating everything manually.
If you need to capture/inspect/filter the output generated by a third-party code you don't control, the OB may also help. Some frameworks use it to handle includes and other templates.
Another common trick consists of reusing the same output for different purposes, like displaying the data and sending it through other channels (e.g., emails).
In my experience, problems usually occur with nested buffers or when buffers overrun, which is hard to predict.
"Headers already sent"
You may be already familiar with the "cannot modify headers, [...] headers already sent" error, which occurs when headers are sent to the browser after the script has begun outputting data.
PHP sends the headers to the server first because of the structure of HTTP responses:
HTTP/1.1 200 OK
Vary: Accept-Encoding
Content-Type: text/html; charset utf-8
<html>
.... etc
As a workaround, PHP developers often use the OB to capture the data and send it in one piece to the client (e.g., the browser) afterwards. This trick is heavily used to set headers but also cookies.
Memory is running out?
Memory leaks sometimes happen with ob_start()
, as memory usage and latency can increase if you manipulate/dump/log large the objects.
Besides, when the OB is on, built-in functions like readfile()
won't present memory issues, which makes debugging harder.
Explicit vs. implicit buffering
Unless you disable it in your server configuration (e.g., php_value output_buffering off
), the OB is on. It's called implicit buffering.
Using ob_start()
while another ob_start()
is still active will start a new level.
About nested buffers
The OB is stackable, which means:
-
ob_start()
adds an empty buffer on top the stack -
ob_get_contents()
returns the content of the active buffer -
ob_get_clean()
removes the active buffer and returns it
As a result, you might not get what you want because the parent buffer is still active:
output is being filtered sequentially through each of them in nesting order
That's why you find the following helper in some try
/catch
blocks:
try {
// some code
} catch(Exception $e) {
// some code
ob_end_clean();
}
IMHO, having multiple levels of buffering is not a common need and should be used with caution.
It does not mean it's pure evil, but it can impact error processing and trigger unexpected behaviors if you don't pay attention enough.
PHP outputs everything when the script ends
Handling sensitive data in the OB can be insecure as PHP writes pending buffers to the output automatically at the end of the script, which may appear convenient but could lead to nasty surprises.
It's probably a bad idea to put such data in the OB, but some developers may want to use it to obscure specific errors.
The buffer size is limited
The buffer must not exceed the OB limit (4096 bytes by default). Otherwise, it would be flushed unexpectedly, and you may lose control.
You cannot use the OB in the OB display handlers
The ob_start()
function accepts callbacks, also called handlers, as its first argument.
This callback is called when the OB is flushed. If you use ob_start()
inside handlers, you will get this error:
Cannot use output buffering in output buffering display handlers
Be careful with those callbacks where the Laws of Physics may not apply.
Wrap up
While the OB is convenient to set cookies and headers quickly, it should not become a general rule, as there are probably better designs.
While the OB may seem appropriate for various usages, it can be tricky when you start nesting things.
Such middle layers can be powerful to capture data but only in very specific cases.
Top comments (1)
I love OB! Many developers don't know that you can load a template/view file using OB and everything is just rendered perfectly when you : ob_get_clean();
Just don't forget to start fresh, or any of the stuff we're hiding in a specific environment will pollute your buffer...nobody appreciates a polluted buffer!
Thanks for the many great posts!