Everything About Output Buffering in PHP
And what you won't read in the documentation, including a security patch and advice on speeding up server response without slowing it down.
Output buffering allows the output of a PHP script (primarily from the
echo
function) to be stored in memory (i.e., a buffer) instead of
being sent immediately to the browser or terminal. This is useful for various
purposes.
Preventing Output to the Screen:
ob_start(); // enables output buffering
$foo->bar(); // all output goes only to the buffer
ob_end_clean(); // clears the buffer and ends buffering
Capturing Output into a Variable:
ob_start(); // enables output buffering
$foo->render(); // output goes only to the buffer
$output = ob_get_contents(); // saves the buffer content into a variable
ob_end_clean(); // clears the buffer and ends buffering
The pair ob_get_contents()
and ob_end_clean()
can be replaced by a single function ob_get_clean()
,
which removes end
from the name but indeed turns off output
buffering:
$output = ob_get_clean(); // saves the buffer content into variable and disables buffering
In the given examples, the buffer content did not reach the output at all. If
you want to send it to the output instead, you should use ob_end_flush()
instead of ob_end_clean()
. To simultaneously get the buffer
content, send it to the output, and end buffering, there is also a shortcut: ob_get_flush()
.
You can empty the buffer at any time without ending it using ob_clean()
(clears it) or ob_flush()
(sends it to the output):
ob_start(); // enables output buffering
$foo->bar(); // all output goes only to the buffer
ob_clean(); // clears the buffer content, but buffering remains active
$foo->render(); // output still goes to the buffer
ob_flush(); // sends the buffer to the output
$none = ob_get_contents(); // the buffer content is now an empty string
ob_end_clean(); // disables output buffering
Output written to php://output
is also sent to the buffer, while
buffers can be bypassed by writing to php://stdout
(or
STDOUT
), which is available only under CLI, i.e., when running
scripts from the command line.
Nesting
Buffers can be nested, so while one buffer is active, calling ob_start()
activates a new buffer. Thus, ob_end_flush()
and
ob_flush()
send the buffer content not to the output but to the
parent buffer. Only when there is no parent buffer does the content get sent to
the actual output, i.e., the browser or terminal.
Therefore, it is important to end buffering, even if an exception occurs during the process:
ob_start();
try {
$foo->render();
} finally { // finally available from PHP 5.5
ob_end_clean(); // or ob_end_flush()
}
Buffer Size
The buffer can also “speed up page generation (I haven't measured this,
but it sounds logical)” by not sending every single echo
to the
browser, but a larger amount of data (e.g., 4kB). Just call at the beginning of
the script:
ob_start(null, 4096);
When the buffer size exceeds 4096 bytes (the so-called
chunk size
), a flush
is performed automatically, i.e.,
the buffer is emptied and sent out. The same can be achieved by setting the output_buffering
directive. It is ignored in CLI mode.
But beware, starting buffering without specifying the size, i.e.,
simply with ob_start()
, will cause the page not to be sent
gradually but only after it is fully rendered, making the server appear
very slow!
HTTP Headers
Output buffering has no effect on sending HTTP headers, which are processed by a different path. However, thanks to buffering, headers can be sent even after some output has been printed, as it is still held in the buffer. This is a side effect you shouldn't rely on, as there is no certainty when the output will exceed the buffer size and be sent.
Security Hole
When the script ends, all unclosed buffers are outputted. This can be considered an unpleasant security hole if, for example, you prepare sensitive data in the buffer not intended for output and an error occurs. The solution is to use a custom handler:
ob_start(function () { return ''; });
Handlers
You can attach a custom handler to output buffering, i.e., a function that processes the buffer content before sending it out:
ob_start(
function ($buffer, $phase) { return strtoupper($buffer); }
);
echo 'Hello';
ob_end_flush(); // 'HELLO' is sent to the output
Functions ob_clean()
or ob_end_clean()
will call
the handler but discard the output without sending it out. The handler can
detect which function is called and respond accordingly. The second parameter
$phase
is a bitmask (from PHP 5.4):
PHP_OUTPUT_HANDLER_START
when the buffer is openedPHP_OUTPUT_HANDLER_FINAL
when the buffer is closedPHP_OUTPUT_HANDLER_FLUSH
whenob_flush()
is called (but notob_end_flush()
orob_get_flush()
)PHP_OUTPUT_HANDLER_CLEAN
whenob_clean()
,ob_end_clean()
, andob_get_clean()
are calledPHP_OUTPUT_HANDLER_WRITE
when an automatic flush occurs
The start, final, and flush (or clean) phases can occur simultaneously,
distinguished by the 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) { ... }
The PHP_OUTPUT_HANDLER_WRITE
phase occurs only if the buffer has
a size (chunk size
) and that size was exceeded. This is the
mentioned automatic flush. Note, the constant
PHP_OUTPUT_HANDLER_WRITE
has a value of 0, so you can't use a bit
test, but:
if ($phase === PHP_OUTPUT_HANDLER_WRITE) { ... }
A handler doesn't have to support all operations. When activating with
ob_start()
, you can specify the bitmask of supported operations as
the third parameter:
PHP_OUTPUT_HANDLER_CLEANABLE
– allows callingob_clean()
and related functionsPHP_OUTPUT_HANDLER_FLUSHABLE
– allows callingob_flush()
PHP_OUTPUT_HANDLER_REMOVABLE
– buffer can be endedPHP_OUTPUT_HANDLER_STDFLAGS
– combines all three flags, the default behavior
This applies even to buffering without a custom handler. For example, if
I want to capture the output into a variable, I don't set the
PHP_OUTPUT_HANDLER_FLUSHABLE
flag, preventing the buffer from being
(accidentally) sent to the output with ob_flush()
. However, it can
still be done with ob_end_flush()
or ob_get_flush()
,
which somewhat defeats the purpose.
Similarly, not setting the PHP_OUTPUT_HANDLER_CLEANABLE
flag
should prevent the buffer from being cleared, but again it doesn't work.
Finally, not setting PHP_OUTPUT_HANDLER_REMOVABLE
makes the
buffer user-undeletable; it turns off only when the script ends. An example of a
handler that should be set this way is ob_gzhandler
,
which compresses output, thus reducing volume and increasing data transfer
speed. Once this buffer is opened, it sends the HTTP header
Content-Encoding: gzip
, and all subsequent output must be
compressed. Removing the buffer would break the page.
The correct usage is:
ob_start(
'ob_gzhandler',
16000, // without chunk size, the server would not send data gradually
PHP_OUTPUT_HANDLER_FLUSHABLE // but not removable or cleanable
);
You can also enable output compression by setting the zlib.output_compression
directive, which turns on buffering with a different handler (not sure how it
differs specifically), but it lacks the flag to be non-removable. Since
it's good to compress the transfer of all text files, not just PHP-generated
pages, it's better to activate compression directly on the HTTP
server side.
Comments
Kamil #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 #2
nice article, finally i started using this function in my projects
This article has been closed. It is no longer possible to add comments.