Skip to content

Calling ob_clean() after ob_start('gz_handler') does not set Content-Encoding #7953

Closed

Description

@timint

Description

The following code:

<?php

  ob_start('ob_gzhandler');
  ob_clean();
  
  echo 'Hello World';

Resulted in this output:

��� �H���W��/�I�V��J�

But I expected this output instead:

Hello World

There is no mention anywhere in the documentation that clearing the output buffer resets the Content-Encoding HTTP Header. If this is by design it should be stated, but it feels like an error.

PHP Version

PHP 8.0.10

Operating System

Windows 11 x64

Activity

  1. damianwadley commented on Jan 17, 2022

    @damianwadley
    Member

    ob_gzhandler isn't some special string that output buffering recognizes - it's an actual function - so it's not output buffering itself that is at fault. It's like you wrote your own callback that added a response header the first time it was called - that header will still be there after output buffering ends.

    I think it would be possible for ob_gzhandler to detect the PHP_OUTPUT_HANDLER_CLEAN flag and to unset the header and reset back to the initial state. (Equally possible in userland.) Or to remove the header automatically if there's been no output, but that would mean no more effectively-empty responses with just the compression container and instead actually-empty responses with no content at all, but any good client should be totally okay with that.

  2. timint commented on Jan 17, 2022

    @timint
    Author

    Yes, I'm a PHP developer since 15 years back. I know it's a callback function. What I don't get is why ob_clean() unsets the header when stackoverflow and PHP documentations is full of comments that ob_clean() does not affect headers.

    Do you not see a problem with this?

  3. damianwadley commented on Jan 17, 2022

    @damianwadley
    Member

    Hmm, looks like I remembered ob_gzhandler backwards: it is a special string after all. It's an alias that goes through slightly different handling in that it will initialize a context, which ob_gzhandler the function does too but in a slightly different way. Thus why ob_start("ob_gzhandler") and ob_start(fn($buffer, $phase) => ob_gzhandler($buffer, $phase)); aren't the same.

    Looking into it more, it's not that the headers are removed but that they aren't added at all. I'm also seeing odd behavior with a flush + clean in that the special "ob_gzhandler" issues a "Failed to delete buffer" notice while the wrapped ob_gzhandler() call does not.

    So it seems like there's some iffy behavior with how it handles the CLEAN phases in general.

  4. changed the title [-]Calling ob_clean() after ob_start('gz_handler') unsets Content-Encoding[/-] [+]Calling ob_clean() after ob_start('gz_handler') does not set Content-Encoding[/+] on Jan 17, 2022
  5. added a commit that references this issue on Jan 17, 2022
    ee2077f
  6. self-assigned this
    on Jan 17, 2022
  7. timint commented on Jan 17, 2022

    @timint
    Author

    Well done 👍

  8. cmb69 commented on Jan 17, 2022

    @cmb69
    Member

    I'm also seeing odd behavior with a flush + clean in that the special "ob_gzhandler" issues a "Failed to delete buffer" notice while the wrapped ob_gzhandler() call does not.

    The special ob_gzhandler makes the handler immutable (i.e. uncleanable and unremoveable) after the headers have been sent. The wrapped ob_gzhandler doesn't do that. I'm not sure whether uncleanability is required, but at least the handler must be unremoveable, or otherwise output after removing the handler causes issues:

    ob_start(fn($buffer, $phase) => ob_gzhandler($buffer, $phase));
    echo "Hello";
    ob_flush();
    ob_end_clean();
    echo "world";

    Note to self: check ob_iconv_handler regarding these issues.

  9. cmb69 commented on Jan 20, 2022

    @cmb69
    Member

    check ob_iconv_handler regarding these issues.

    Indeed, it has basically the same issue, namely that the Content-Type header is not properly set, if ob_clean() is called right after ob_start().

    The "wrapper" issue isn't a thing, though, since ob_iconv_handler() isn't a real function, but only an ob handler alias.

  10. timint commented on Jan 20, 2022

    @timint
    Author

    I just want to point out ob_clean() doesn't need to be right after. Any use of ob_clean() long after ob_start('gzhandler') should cause the same problem.

    <?php
    
      ob_start('ob_gzhandler');
    
      ...
    
      echo 'Say something regretful';
      ob_clean();
      
      echo 'Hello World';
    

    Result:

     ��� �H���W��/�I�V��J�
    
  11. cmb69 commented on Jan 20, 2022

    @cmb69
    Member

    I just want to point out ob_clean() doesn't need to be right after. Any use of ob_clean() long after ob_start('gzhandler') should cause the same problem.

    Ah, right! More correctly, it is about calling ob_clean() before output has been flushed.

  12. timint commented on Jan 20, 2022

    @timint
    Author

    Correct, that is how I stumbled upon this.

  13. added 2 commits that reference this issue on Feb 3, 2022
    8009b21
    d67698a
  14. added a commit that references this issue on Apr 12, 2022
  15. added a commit that references this issue on Apr 25, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions