Stop output buffer cleanup at buffers that cannot be removed
zlib.output_compression makes its buffer non-removable once it has sent compressed output, so ob_end_clean() raised a notice that replaced the original exception. Fixes #704.
This commit is contained in:
1 parent
a5b89f86ae
commit
13ce578de8
4 files changed
+73
-2
No files matched your search
+4
-2
@@ -425,8 +425,10 @@ final class Run implements RunInterface
|
|||||||
if ($this->writeToOutput()) {
|
if ($this->writeToOutput()) {
|
||||||
// @todo Might be able to clean this up a bit better
|
// @todo Might be able to clean this up a bit better
|
||||||
if ($willQuit) {
|
if ($willQuit) {
|
||||||
// Cleanup all other output buffers before sending our output:
|
// Cleanup all other output buffers before sending our output.
|
||||||
while ($this->system->getOutputBufferLevel() > 0) {
|
// Stop at a buffer that cannot be removed, e.g. zlib output
|
||||||
|
// compression once it has started sending compressed output:
|
||||||
|
while ($this->system->getOutputBufferLevel() > 0 && $this->system->isOutputBufferRemovable()) {
|
||||||
$this->system->endOutputBuffering();
|
$this->system->endOutputBuffering();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -85,6 +85,16 @@ class SystemFacade
|
|||||||
return ob_get_level();
|
return ob_get_level();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public function isOutputBufferRemovable()
|
||||||
|
{
|
||||||
|
$status = ob_get_status();
|
||||||
|
|
||||||
|
return isset($status['flags']) && ($status['flags'] & PHP_OUTPUT_HANDLER_REMOVABLE);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return bool
|
* @return bool
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -479,6 +479,46 @@ class RunTest extends TestCase
|
|||||||
$this->assertEquals("", ob_get_clean());
|
$this->assertEquals("", ob_get_clean());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @covers Whoops\Run::handleException
|
||||||
|
*/
|
||||||
|
public function testOutputBufferCleanupStopsAtNonRemovableBuffer()
|
||||||
|
{
|
||||||
|
// A removable buffer on top of one that cannot be removed, like
|
||||||
|
// zlib.output_compression once it has sent compressed output.
|
||||||
|
$removable = [false, true];
|
||||||
|
$test = $this;
|
||||||
|
|
||||||
|
$system = m::mock('Whoops\Util\SystemFacade')->makePartial();
|
||||||
|
$system->shouldReceive('stopExecution');
|
||||||
|
$system->shouldReceive('getOutputBufferLevel')
|
||||||
|
->andReturnUsing(function () use (&$removable) {
|
||||||
|
return count($removable);
|
||||||
|
});
|
||||||
|
$system->shouldReceive('isOutputBufferRemovable')
|
||||||
|
->andReturnUsing(function () use (&$removable) {
|
||||||
|
return end($removable);
|
||||||
|
});
|
||||||
|
$system->shouldReceive('endOutputBuffering')
|
||||||
|
->andReturnUsing(function () use ($test, &$removable) {
|
||||||
|
if (!array_pop($removable)) {
|
||||||
|
$test->fail('Tried to end an output buffer that cannot be removed');
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
|
||||||
|
$run = new Run($system);
|
||||||
|
$run->pushHandler(function () {
|
||||||
|
echo "hello there";
|
||||||
|
return Handler::QUIT;
|
||||||
|
});
|
||||||
|
|
||||||
|
ob_start();
|
||||||
|
$run->handleException(new RuntimeException());
|
||||||
|
$this->assertEquals("hello there", ob_get_clean());
|
||||||
|
$this->assertEquals([false], $removable);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @covers Whoops\Run::sendHttpCode
|
* @covers Whoops\Run::sendHttpCode
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -68,6 +68,20 @@ class SystemFacadeTest extends TestCase
|
|||||||
$this->facade->endOutputBuffering();
|
$this->facade->endOutputBuffering();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function test_it_reports_a_removable_output_buffer()
|
||||||
|
{
|
||||||
|
self::$runtime->shouldReceive('ob_get_status')->once()->andReturn(['flags' => PHP_OUTPUT_HANDLER_STDFLAGS]);
|
||||||
|
|
||||||
|
$this->assertTrue($this->facade->isOutputBufferRemovable());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function test_it_reports_a_non_removable_output_buffer()
|
||||||
|
{
|
||||||
|
self::$runtime->shouldReceive('ob_get_status')->once()->andReturn(['flags' => PHP_OUTPUT_HANDLER_STDFLAGS & ~PHP_OUTPUT_HANDLER_REMOVABLE]);
|
||||||
|
|
||||||
|
$this->assertFalse($this->facade->isOutputBufferRemovable());
|
||||||
|
}
|
||||||
|
|
||||||
public function test_it_delegates_flushing_the_current_buffer_to_the_native_implementation()
|
public function test_it_delegates_flushing_the_current_buffer_to_the_native_implementation()
|
||||||
{
|
{
|
||||||
self::$runtime->shouldReceive('flush')->once();
|
self::$runtime->shouldReceive('flush')->once();
|
||||||
@@ -161,6 +175,11 @@ function ob_end_clean()
|
|||||||
return SystemFacadeTest::delegate('ob_end_clean');
|
return SystemFacadeTest::delegate('ob_end_clean');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function ob_get_status()
|
||||||
|
{
|
||||||
|
return SystemFacadeTest::delegate('ob_get_status');
|
||||||
|
}
|
||||||
|
|
||||||
function flush()
|
function flush()
|
||||||
{
|
{
|
||||||
return SystemFacadeTest::delegate('flush');
|
return SystemFacadeTest::delegate('flush');
|
||||||
|
|||||||
Reference in new issue
Block a user