php.net |  support |  documentation |  report a bug |  advanced search |  search howto |  statistics |  random bug |  login
Doc Bug #76563 ob_get_clean ignores return value of ob_start callback
Submitted: 2018-07-02 15:14 UTC Modified: 2024-01-07 14:04 UTC
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: girgias (profile)
Status: Closed Package: Output Control
PHP Version: 7.2.7 OS:
Private report: No CVE-ID: None
Welcome back! If you're the original bug submitter, here's where you can edit the bug or add additional notes.
If you forgot your password, you can retrieve your password here.
Password:
Status:
Package:
Bug Type:
Summary:
From: roland at nextendweb dot com
New email:
PHP Version: OS:

 

 [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

Pull Requests

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.
 [2021-11-08 18:46 UTC] cmb@php.net
-Summary: ob_get_clean does not call the ob_start callback +Summary: ob_get_clean ignores return value of ob_start callback -Status: Open +Status: Verified -Type: Bug +Type: Documentation Problem
 [2021-11-08 18:46 UTC] cmb@php.net
> 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.

Not really[1].  Actually, the handler *is* called, and the
respective string is returned, but it is not used, since
ob_get_clean() is supposed to discard this output.  See also bug
#44150 which is the reason for the currrent documentation.

Obviously, the documentation needs further clarification, that
ob_clean(), ob_end_clean() and ob_get_clean() call the handler,
but for all practical purposes ignore its return value.

> But there are developer who are not aware and use ob_get_clean()
> and that can mess thing up.

We cannot prevent misuse of features, but only improve the
documentation. And of course, everybody can spread the word…

[1] <https://3v4l.org/g0m0k>
 [2024-01-07 14:04 UTC] girgias@php.net
-Status: Verified +Status: Closed -Assigned To: +Assigned To: girgias
 
PHP Copyright © 2001-2024 The PHP Group
All rights reserved.
Last updated: Sun Dec 22 11:01:30 2024 UTC