You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Output buffer manipulation in tests causes incorrect capture, hangs, and silent failures #5851
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
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
when a test (or any function called from the test) register a non-removable ob callback, this leads to complete failure/unexpected behavior
non-removable ob callbacks cannot currently be tested
if stopping output buffering fails, it will try again creating the same notices twice
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.
Activity
sebastianbergmann commentedon Jun 3, 2024
What are non-removable output buffering handlers?
kkmuffme commentedon Jun 3, 2024
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();mvorisek commentedon Jun 7, 2024
Can you show a small repro on https://3v4l.org/?
kkmuffme commentedon Jun 7, 2024
https://3v4l.org/kPQXO
See the PR, there are unit tests for that case already
https://github.com/sebastianbergmann/phpunit/pull/5852/files#diff-198b09778388878e8068a870068387c10c3f9db2a6ed610c24daccda3b669fa3R115
SirLouen commentedon May 22, 2025
Hey @mvorisek @sebastianbergmann any news regarding this topic?
[-]Various output buffer issues[/-][+]Output buffer manipulation in tests causes incorrect capture, hangs, and silent failures[/+]sebastianbergmann commentedon Apr 12, 2026
ob_clean()/ob_flush()no longer corrupt output capture. Previously, callingob_flush()inside a test would send the buffered output to stdout and PHPUnit would then capture an empty string — makingexpectOutputString()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 byob_end_clean(). Previously, PHPUnit's cleanup loop would callob_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.
Closes sebastianbergmann#5851