DEV Community

spO0q
spO0q

Posted on • Edited on

PHP: The first rule of the output buffering is you do not talk about the output buffering

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
Enter fullscreen mode Exit fullscreen mode

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();
}
Enter fullscreen mode Exit fullscreen mode

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)

Collapse
 
digital_pig profile image
Digital Pig

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!