php.net |  support |  documentation |  report a bug |  advanced search |  search howto |  statistics |  random bug |  login
Bug #76563 ob_get_clean does not call the ob_start callback
Submitted: 2018-07-02 15:14 UTC Modified: -
Votes:5
Avg. Score:4.6 ± 0.5
Reproduced:4 of 4 (100.0%)
Same Version:2 (50.0%)
Same OS:2 (50.0%)
From: roland at nextendweb dot com Assigned:
Status: Open Package: Output Control
PHP Version: 7.2.7 OS:
Private report: No CVE-ID: None
Have you experienced this issue?
Rate the importance of this bug to you:

 [2018-07-02 15:14 UTC] 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>


Patches

Add a Patch

Pull Requests

Add a Pull Request

History

AllCommentsChangesGit/SVN commitsRelated reports
 [2019-05-29 14:27 UTC] 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..
 [2019-11-28 02:04 UTC] 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-11-28 06:54 UTC] roland at nextendweb dot com
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.
 
PHP Copyright © 2001-2020 The PHP Group
All rights reserved.
Last updated: Thu Feb 20 09:01:25 2020 UTC