If output_buffering = Off then ob_end_clean fails to delete buffer

mpare - December 31, 2007 - 22:15
Project:Drupal
Version:6.x-dev
Component:file system
Category:bug report
Priority:normal
Assigned:Unassigned
Status:patch (reviewed & tested by the community)
Description

If output_buffering is turned off in php.ini and after a call to file_transfer in file.inc line 794 is called the following notice will be returned.

"notice: ob_end_clean() [ref.outcontrol]: failed to delete buffer. No buffer to delete. in .../includes/file.inc on line 795."

One possible solution, as seen in obendclean_sol2.patch, would be to suppress the error by prefixing ob_end_clean() with an '@'.

<?php
 
@ob_end_clean();
?>

Another possible solution, as seen in obendclean_sol1.patch, would be to perform a check first.

<?php
 
if (ob_get_length() > 0) {
   
ob_end_clean();
  }
?>

I checked against Drupal 5 versions of file.inc and it appears to have ob_end_clean() called at the beginning of file_transfer() as well. The once difference that I could think of that would make this issue appear now is the recent changes to common.inc involving E_ALL.

I don't know if this is something that we even want to change but users who meet the above conditions may be subjected to such notices, for example, every time a new imagecache image is generated.

I've attached two patches one for each method discussed above.

Thanks,
-mpare

AttachmentSize
obendclean_sol1.patch664 bytes
obendclean_sol2.patch628 bytes

#1

bdragon - December 31, 2007 - 22:26

Just chiming in as I was discussing this issue with mpare.

I believe the intention of the line was to make sure that output would be sent to the client rather than any output buffer that may be open...

Hmm, it should *probabaly* be closing as many buffers as needed, right?

<?php
while (ob_get_length()) {
 
ob_end_clean();
}
?>

The ob_end_clean() line was in the original version of file.inc. I'm guessing that the reason it's popping up now is that in the past, nobody had A) E_ALL enabled, B) output_buffering off, and C) filed an issue.

#2

drewish - April 16, 2008 - 06:56
Version:6.x-dev» 7.x-dev

it looks like it's used a few places in core now:
includes/batch.inc: ob_end_clean();
includes/common.inc: ob_end_clean();
includes/common.inc: ob_end_flush();
includes/file.inc: ob_end_clean();
includes/theme.inc: ob_end_clean(); // End buffering and discard
modules/system/system.module: ob_end_clean();
modules/system/system.module.orig: ob_end_clean();

#3

mfb - October 9, 2008 - 22:06

This should be the one place in core where ob_end_clean() is called without ob_start(). Here's another proposed fix -- use ob_get_level() instead of ob_get_length().

AttachmentSize
d7-file-ob.patch696 bytes

#4

earnie - October 10, 2008 - 00:17
Status:patch (code needs review)» patch (reviewed & tested by the community)

Indeed ob_get_level is the correct function for this filter.

Returns the level of nested output buffering handlers or zero if output buffering is not active.

#5

drewish - October 10, 2008 - 00:32

+1

i don't see any easy way to unit test this since it's just a warning message so i manually verified it. added a file, enabling private file transfers, downloaded it to get the warning, then patch and downloaded again and had no warning.

#6

webchick - October 11, 2008 - 20:41
Version:7.x-dev» 6.x-dev

I checked over the other instances of ob_end* and it appears they indeed all call start/get_contents ahead of time.

Committed to HEAD. Thanks! Moving back for consideration for 6.x.

 
 

Drupal is a registered trademark of Dries Buytaert.