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 onPHP_OUTPUT_HANDLER_FINAL
when buffer is turned offPHP_OUTPUT_HANDLER_FLUSH
when callingob_flush()
(but notob_end_flush()
orob_get_flush()
)PHP_OUTPUT_HANDLER_CLEAN
when callingob_clean()
,ob_end_clean()
andob_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 functionob_clean()
and relatedPHP_OUTPUT_HANDLER_FLUSHABLE
– you can call theob_flush()
PHP_OUTPUT_HANDLER_REMOVABLE
– buffer can be turned offPHP_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
Kamil http://www #1
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!
akmal https://mirakmalsulton.github.io/notes/2019/08/27/php-ob_sta #2
nice article, finally i started using this function in my projects
Tento článek byl uzavřen. Už není možné k němu přidávat komentáře.