When I chose to write about output buffering for this year's PHP Advent, my depth of knowledge on the subject was very limited. I picked a topic that I could learn well, and then explain thoroughly without writing an entire book. It's a feature that will likely be new to beginners, but which even intermediate and advanced users may not have used much. Output buffering has simple, practical applications, and it can also play a roll in more complicated systems. It is one of those tools that you might not realize you need if you don't know that it exists, and it is my pleasure to introduce you to it.
PHP's output buffering trinity
A typical PHP installation actually has three different layers of output buffering. The layer closest to the client is controlled by the output_buffering directive in php.ini. This setting can be set to On, Off, or an integer that represents the number of bytes at which PHP should flush the buffer. The purpose of this buffer is to control how much data is sent to the browser at a time. The options are fairly self-explanatory; Off sends the data immediately, and On collects the entire output of the script and sends it all at once. I'll call this layer the output buffering layer.
I call the next layer the flush layer. It is another control that can simply be turned On or Off using the implicit_flush directive in php.ini. When implicit_flush is on, every output operation flushes immediately to the output buffering layer; otherwise, you have to call flush() to manually flush this buffer. By default, implicit_flush is disabled, except when using the CLI SAPI. This is a sensible default, because the constant flushing can generate a lot of overhead, particularly when output_buffering is disabled. If the purpose of the output buffering layer is to control how much data is output, this layer's purpose is to control when data is output.
The last layer is the userspace output buffer, which is controlled by the various ob_* functions. It provides far greater control than the other layers, as well as greater flexibility. While this layer can be used to control how much data is sent and when the sending occurs, those are just two of its many tricks. The true purpose of this layer is to provide control over which data is output. I call this layer the ob layer, and it is the primary focus of this article.
The ob layer
Let's start with a simple example:
The above example is very simple, and it should be fairly obvious what is going on. First, we create an output buffer by calling ob_start(). From this point on, anything we output will be stored in this buffer. When we call ob_flush(), the contents of the buffer created by ob_start() are flushed to the next output buffer layer, which should be the flush layer, in this case. It's that simple to create an output buffer.
One use of output buffers that you are sure to hear about is the ability to send a header after you output something. Since output is held in a buffer, you can still send headers and avoid the infamous aoheaders already senta warning. This is particularly useful if you need to use a function that writes directly to the output, but you aren't quite ready for it to do so. Some people argue that buffering output so that you can send headers later adds to the complexity of the code. Regardless of whether you agree, this feature only scratches the surface of output buffer utility. Let's take a look at another example:\n" . $output . "\n"; }; ob_start('output_handler'); echo "This output just got handled.\n"; ob_end_flush(); echo "Some text outside of the buffer.\n";
You'll notice two important additions to this code. First, ob_start() takes a callback or closure as its first argument. (I highly recommend using a string callback, which I'll explain later.) The function referenced by that argument should take the content of the output buffer as its first argument, and it should return a string containing the processed output. The second thing you ought to notice is the call to ob_end_flush(). This will flush the current buffer and close it, so that future output does not use it. If you ran this code, you would see that only the content from the first echo is wrapped in the tags:This output just got handled
Truncated by Planet PHP, read more at the original (another 5613 bytes)