Output buffer manipulation in tests causes incorrect capture, hangs, and silent failures · Issue #5851 · sebastianbergmann/phpunit · GitHub
Skip to content

Output buffer manipulation in tests causes incorrect capture, hangs, and silent failures #5851

Closed

Description

@kkmuffme
Q A
PHPUnit version 11.1.3
PHP version 8.2.x, 8.3.x
Installation Method Composer / PHAR

Summary

  1. when a test (or any function called from the test) flushes or cleans the output buffer, this incorrectly changes the output making tests pass that fail with expectOutputString and/or would cause a PHP notice when the code is actually executed
  2. if a test ends but starts a new output buffer, no error is reported, since only the ob level is compared instead of comparing the output
  3. when a test (or any function called from the test) register a non-removable ob callback, this leads to complete failure/unexpected behavior
  4. non-removable ob callbacks cannot currently be tested
  5. if stopping output buffering fails, it will try again creating the same notices twice

I will provide a PR.

Activity

  1. sebastianbergmann commented on Jun 3, 2024

    @sebastianbergmann
    Owner

    What are non-removable output buffering handlers?

  2. kkmuffme commented on Jun 3, 2024

    @kkmuffme
    Author

    e.g.

    • ob_start('my_cb', 0, 0);
    • ob_start('my_cb', 0, PHP_OUTPUT_HANDLER_FLUSHABLE);
    • ob_start(null, 0, PHP_OUTPUT_HANDLER_CLEANABLE);

    These cannot be removed/terminated with e.g. ob_end_flush();

  3. mvorisek commented on Jun 7, 2024

    @mvorisek
    Contributor

    Can you show a small repro on https://3v4l.org/?

  4. kkmuffme commented on Jun 7, 2024

    @kkmuffme
    Author
  5. SirLouen commented on May 22, 2025

    @SirLouen

    Hey @mvorisek @sebastianbergmann any news regarding this topic?

  6. changed the title [-]Various output buffer issues[/-] [+]Output buffer manipulation in tests causes incorrect capture, hangs, and silent failures[/+] on Apr 12, 2026
  7. sebastianbergmann commented on Apr 12, 2026

    @sebastianbergmann
    Owner

    ob_clean() / ob_flush() no longer corrupt output capture. Previously, calling ob_flush() inside a test would send the buffered output to stdout and PHPUnit would then capture an empty string — making expectOutputString() pass when it shouldn't, or fail when it shouldn't. Similarly, ob_clean() could discard output that PHPUnit was tracking. Now PHPUnit uses a phase-aware output buffer callback that preserves the correct captured output regardless of flush/clean operations within the test.

    Buffer substitution is now detected. If test code closes PHPUnit's output buffer and opens a new one (leaving ob_get_level() unchanged), PHPUnit previously had no way to notice. It now tracks whether its own buffer was destroyed and flags the test as risky.

    Non-removable output buffers no longer cause hangs or duplicate notices. Buffers started with ob_start($cb, 0, 0) or similar flag combinations cannot be removed by ob_end_clean(). Previously, PHPUnit's cleanup loop would call ob_end_clean() repeatedly on these buffers, never succeeding, producing duplicate PHP notices in an infinite loop. The loop now checks the return value and breaks on failure.

    I do not feel comfortable implementing these fixes for PHPUnit 12.5 and PHPUnit 13.1 considering how much related functionality such as error handling has diverged on the main branch for PHPUnit 13.2. Due to this work on PHPUnit 13.2, the main branch also has better test coverage for these related areas. Therefore these fixes were implemented only on the main branch for now and may or may not be backported to PHPUnit 12.5 and PHPUnit 13.1.

  8. added a commit that references this issue on Apr 12, 2026
    ea51aa4
  9. locked as resolved and limited conversation to collaborators on May 13, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    type/bugSomething is broken

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions