I have an output buffer with callback function. When cleaning the buffer the callback function is executed, however, the string returned isn't being altered.
I'm using following code:
<?php
ob_start('callback');
print 'some text';
error_log(ob_get_clean());
function callback($content) {
error_log('callback');
return $content . ' altered';
}
Output:
callback
some text
What I want:
callback
some text altered
What am I missing? I'm using PHP 5.3.10 in CLI.
Edit: the callback is being executed.
From the PHP manual:
The function will be called when the output buffer is flushed (sent) or cleaned (with ob_flush(), ob_clean() or similar function) or when the output buffer is flushed to the browser at the end of the request.
I'm not sure if this is a bug or a feature. Looking at the PHP source code, I found that the return value of ob_get_clean
is filled before the callback is invoked.
I see at least two workarounds. The first is to manually invoke the callback on the output string yourself. I think this needs no example.
The second is to exploit the possibility to stack output buffering. Since flushing successfully uses the callback, you can wrap the output code inside an additional output buffer and get the modified contents.
ob_start();
function callback($input) { return $input . " altered"; }
ob_start('callback');
echo "foo";
ob_end_flush();
$content = ob_get_clean();
ob_end_clean();
echo $content . "\n"; // prints "foo altered\n"
See the source code for ob_get_clean
(main/output.c) if you're curious. You can get the source code at the PHP website. Here are some pointers.
/* {{{ proto bool ob_get_clean(void)
Get current buffer contents and delete current output buffer */
PHP_FUNCTION(ob_get_clean)
{
if (zend_parse_parameters_none() == FAILURE) {
return;
}
// THIS CALL FILLS THE RETURN VALUE
if (php_ob_get_buffer(return_value TSRMLS_CC) == FAILURE) {
RETURN_FALSE;
}
if (!OG(ob_nesting_level)) {
php_error_docref("ref.outcontrol" TSRMLS_CC, E_NOTICE, "failed to delete buffer. No buffer to delete");
zval_dtor(return_value);
RETURN_FALSE;
}
if (OG(ob_nesting_level) && !OG(active_ob_buffer).status && !OG(active_ob_buffer).erase) {
php_error_docref("ref.outcontrol" TSRMLS_CC, E_NOTICE, "failed to delete buffer %s", OG(active_ob_buffer).handler_name);
zval_dtor(return_value);
RETURN_FALSE;
}
// THIS CALL KILLS THE CURRENT BUFFER AND EXECUTES THE CALLBACK
php_end_ob_buffer(0, 0 TSRMLS_CC);
}
/* }}} */
php_end_ob_buffer
takes the contents of the OB buffer and applies the callback to it. If the first parameter is true, it passes the contents to the next output buffering handler. In this case it's false, so the content is lost even though it executed the callback.