Bug #76563 [Com]: ob_get_clean does not call the ob_start callback

From: Date: Thu, 28 Nov 2019 06:54:31 +0000
Subject: Bug #76563 [Com]: ob_get_clean does not call the ob_start callback
References: 1  Groups: php.bugs 
Request: Send a blank email to php-bugs+get-223910@lists.php.net to get a copy of this message
Edit report at https://bugs.php.net/bug.php?id=76563&edit=1

 ID:                 76563
 Comment by:         roland at nextendweb dot com
 Reported by:        roland at nextendweb dot com
 Summary:            ob_get_clean does not call the ob_start callback
 Status:             Open
 Type:               Bug
 Package:            Output Control
 PHP Version:        7.2.7
 Block user comment: N
 Private report:     N

 New Comment:

Thanks for your detailed explanation. 

Well, the problem come from extendable platforms for example from the WordPress ecosystem. People
can write their own plugins and those plugins might conflict. As a plugin developer you want to make
sure that you get the output to parse when "your" output buffer closed or the content
used. To be sure you need to create an output buffer with a callback functions. But there are
developer who are not aware and use ob_get_clean() and that can mess thing up. 

For me: Hard to understand based on the documentation of the output buffer what is going on and why
and it is  not so easy to debug output buffers. 

For other developer: There is no sign that he is doing something what he shouldn't.

For users of the end product: He will experience that something is not working and won't be
able to debug this as output buffer functions related to each other might be in very different
places.


Previous Comments:
------------------------------------------------------------------------
[2019-11-28 02:04:49] a at b dot c dot de

First of all, for reference, here is the test script from the original bug report:

<?php
function parse_output( $out ) {

	return 'Parsed: ' . $out.'<hr>';
}

ob_start( 'parse_output');
echo "asd";

$content = ob_get_clean();
echo $content;
?>

And

<?php
function parse_output( $out ) {

	return 'Parsed: ' . $out.'<hr>';
}

ob_start( 'parse_output');
echo "asd";

$content = ob_get_contents();
ob_clean();
echo $content;
?>


TL/DR; while the documentation says that the callback is used by ob_flush(), ob_clean() and similar,
the fact is the callback is only used when flushing the buffer to output. Neither discarding the
buffer's contents (ob_clean() ob_end_clean(), ob_get_clean()) nor retrieving the current buffer
contents (ob_get_contents(), ob_get_clean()) invoke the output callback.

Whether the buffer-cleaning functions _should_ invoke the output callback (keeping in mind that in
themselves they do not generate output), or whether the documentation is at fault, is the question.

Demonstrations:

The first thing to notice about the second script is that it never turns output buffering off: it
only clears the buffer's contents. There is therefore an implicit ob_flush() at the very end
that passes the buffer through the callback. Here are some variations, starting with the original
two

1:
$content = ob_get_clean();
echo strlen($content);
Output: 3
[No flushing of the output buffer occurs: its contents are retrieved, and then discarded from the
buffer, and buffering is turned off. The subsequent echo then proceeds as normally.]

2:
$content = ob_get_contents();
ob_clean();
echo strlen($content);
Output: Parsed: 3<hr>
[The contents of the buffer are retrieved and then discarded from the buffer. The buffer remains
active, however, and the subsequent echo is buffered. When the script ends, the still-active output
buffer is implicitly flushed and that flushed content is passed through the callback.]

3:
$content = ob_get_contents();
ob_end_clean();
echo strlen($content);
[ob_get_clean() should be equivalent to this. The behaviour is the same as case 1]

4:
$content = ob_get_contents();
ob_flush();
echo strlen($content);
Output: Parsed: asd<hr>Parsed: 3<hr>
[So let's flush explicitly. We retrieved the contents of the buffer ("asd") and put
it in $content. We then flushed the buffer and in doing so passed it through the callback. But we
didn't turn off buffering, so when we echoed the length of $content, it got buffered, and the
implicit flush at the end saw _that_ passed through the callback. 

5: 
$content = ob_get_contents();
ob_end_flush();
echo strlen($content);
Output: Parsed: asd<hr>3
[So let's turn off buffering before the end. Since we turned buffering off, the second echo
wasn't buffered and didn't need flushing and didn't go through the callback].

------------------------------------------------------------------------
[2019-05-29 14:27:48] calvet dot thomas at gmail dot com

Just experienced the same behavior. That looks like a bug as well to me. If you ob_end_flush()
before the ob_end_clean(), it just works..

------------------------------------------------------------------------
[2018-07-02 15:14:13] roland at nextendweb dot com

Description:
------------
If you use ob_start with callback, the callback does not run, if you use ob_get_clean() on the same
output buffer. The callback get skipped, which gives unexpected results. I think it is a bug.

Test script:
---------------
Expected output:
Parsed: asd<hr>


#1:
ob_get_clean:
http://sandbox.onlinephpfunctions.com/code/9444bc54b4311c456f7e0f26d936c55b7114417a
Output:
asd

#2 ob_get_contents + ob_clean
http://sandbox.onlinephpfunctions.com/code/f4aa458142dfdb7867d662983504689bbba191b8
Output:
Parsed: asd<hr>

Expected result:
----------------
Output:
Parsed: asd<hr>



------------------------------------------------------------------------



--
Edit this bug report at https://bugs.php.net/bug.php?id=76563&edit=1


Thread (4 messages)

« previous php.bugs (#223910) next »