Na navigaci | Klávesové zkratky

Everything About Output Buffering in PHP

What you can't learn from the documentation + patch for security hole + advice on how to speed up server response.

Output buffering allows you to have output of PHP (primarily generated by echo) stored into an memory (ie. buffer) instead of immediately transmitted to the browser or terminal. Which is useful to a variety of tasks:

Preventing output:

ob_start();  // turns on output buffering
$foo->bar();  // all output goes only to buffer
ob_end_clean();  // clears buffer and closes buffering

Capturing the output to a variable:

ob_start();  // turns on output buffering
$foo->render();  // output goes only to buffer
$output = ob_get_contents();  // stores buffer contents to the variable
ob_end_clean();  // clears buffer and closes buffering

Pair ob_get_contents() and ob_end_clean() can be replaced by a single function ob_get_clean(), from whose name disappeared end although it actually turns off output buffering:

$output = ob_get_clean();  // stores buffer content into variable and turns off buffering

In the above examples, the received buffer was not sent to the output. If you want to send it, use ob_end_flush() instead of ob_end_clean(). To obtain the contents of the buffer, send it to the output and turn off buffering, there is again single function (including missing end in the name): ob_get_flush().

Buffer can be emptied at any time without having to turn it off, using ob_clean() (deletes it) or ob_flush() (sends it to output):

ob_start();  // turns on output buffering
$foo->bar();  // all output goes only to buffer
ob_clean();  // delete the contents of the buffer, but remains buffering active
$foo->render(); // output goes to buffer
ob_flush(); // send buffer output
$none = ob_get_contents();  // buffer content is now an empty string
ob_end_clean();  // turn off output buffering

Into buffer is also sent output written to php://output, while the buffers can be avoided by writing to` php://stdout` (or STDOUT), which is available only under the CLI, ie. when running scripts from command line.

Nesting

Buffers can be nested, so while one buffer is active, another ob_start() activates a new buffer. So ob_end_flush() and ob_flush() are not really sending the buffer to the output, but to the parent buffer. And only when there is no parent buffer, contents is sent to browser or terminal.

It is therefore important to turn off buffering, even if the occurs an exception:

ob_start();
try {
	$foo->render();
} finally {  finally exists since PHP 5.5
	ob_end_clean(); // or ob_end_flush()
}

Buffer size (chunk size)

Buffer may also improve server performace when PHP will not send each echo to the browser, but will send bigger chunks of data, for example, 4KB. Just call at the beginning of the script:

ob_start(null, 4096);

Once the buffer size exceeds 4096 bytes, PHP automatically executes flush, ie. the buffer is emptied and sent out. The same can be achieved by setting directive output_buffering, which is ignored in CLI.

Be careful, if you start buffering without the chunk size (ie. a simple ob_start()) it will cause that the page will not be sent continuously, but once at the end of the script, so the server will respond very sluggishly!

HTTP headers

Output buffering does not affect the HTTP headers, they are processed in different way. However, due to buffering you can send the headers even after the output was sent, because it is still in the buffer. However, you should not rely on this side effect, because there is no certainty when the output exceeds the buffer size.

Security hole

When the PHP script ends all pending buffers will write its contents to output. This can be considered annoying security hole. If you are preparing in buffer sensitive data that is not intended for output and error occurs, PHP writes it to output. The solution is to use a custom handler:

ob_start(function () { return ''; });

Custorm handlers

You can set own custom handler, ie. a function that processes the contents of the buffer before sending it out:

ob_start(
	function ($buffer, $phase) { return strtoupper($buffer); }
);
echo 'Hello';
ob_end_flush(); // outputs HELLO

Also ob_clean() and ob_end_clean() invoke handler, but will drop the output. The handler can determine which function is called and respond to it via second parameter $phase, which is a bit mask (since PHP 5.4)

  • PHP_OUTPUT_HANDLER_START when buffer is turned on
  • PHP_OUTPUT_HANDLER_FINAL when buffer is turned off
  • PHP_OUTPUT_HANDLER_FLUSH when calling ob_flush() (but not ob_end_flush() or ob_get_flush())
  • PHP_OUTPUT_HANDLER_CLEAN when calling ob_clean(), ob_end_clean() and ob_get_clean()
  • PHP_OUTPUT_HANDLER_WRITE automaticflush

Phases start, final and flush (resp. clean) can occur simultaneously. It can be distinguish using binary operator &:

if ($phase & PHP_OUTPUT_HANDLER_START) { ... }
if ($phase & PHP_OUTPUT_HANDLER_FLUSH) { ... }
elseif ($phase & PHP_OUTPUT_HANDLER_CLEAN) { ... }
if ($phase & PHP_OUTPUT_HANDLER_FINAL) { ... }

Phase PHP_OUTPUT_HANDLER_WRITE occurs only when the buffer size had been exceeded. It is an automatic flush. Just be careful, constant PHP_OUTPUT_HANDLER_WRITE has a value of 0, so you cannot use binary operator, but:

if ($phase === PHP_OUTPUT_HANDLER_WRITE) { .... }

Handler may not support all operations. The ob_start() can specify supported operations by bit mask in third parameter:

  • PHP_OUTPUT_HANDLER_CLEANABLE – you can call the function ob_clean() and related
  • PHP_OUTPUT_HANDLER_FLUSHABLE – you can call the ob_flush()
  • PHP_OUTPUT_HANDLER_REMOVABLE – buffer can be turned off
  • PHP_OUTPUT_HANDLER_STDFLAGS – is a combination of all three flags, the default behavior

These flags can be used without own handler. For example, if I want to capture the output to a variable, omitting PHP_OUTPUT_HANDLER_FLUSHABLE flag tells that buffer cannot be (perhaps mistakenly) send to output via ob_flush(). However, you can do so via ob_end_flush() or ob_get_flush(), so it loses some sense.

Similarly, the absence of flag PHP_OUTPUT_HANDLER_CLEANABLE avoid erasing the buffer, but again, it does not work.

And finally, the absence of PHP_OUTPUT_HANDLER_REMOVABLE means that buffer cannot be turned off by user. An example of a handler that is appropriate to be unremovable, is ob_gzhandler that compresses the output and thus reduces the traffic and increases the speed of data transmission. Once this buffer opens, it sends a HTTP header Content-Encoding: gzip and all other output must be compressed. So buffer must not be removed.

Proper usage is:

ob_start(
	'ob_gzhandler',
	16000, // to send data continuously
	PHP_OUTPUT_HANDLER_FLUSHABLE // but not removable and cleanable
);

Output compression can be also activate by directive zlib.output_compression, which turns on buffering with a different handler (I don't know how they differs), but unfortunately as removable buffer. Because it is appropriate to compress all text files, not only PHP output, it is better to activate compression on HTTP server.

I don't speak English well, so I welcome all corrections.

Komentáře

  1. Kamil #1

    avatar

    Hey,! THANKS i had problem with file_get_contents and empty return with big file jpg and know i get why after migration i forgot increast buffer size, or just setting like your idea!

    před 5 lety
  2. akmal #2

    avatar

    nice article, finally i started using this function in my projects

    před 5 lety

Tento článek byl uzavřen. Už není možné k němu přidávat komentáře.


phpFashion © 2004, 2024 David Grudl | o blogu

Ukázky zdrojových kódů smíte používat s uvedením autora a URL tohoto webu bez dalších omezení.