diff --git a/src/Emitter/SapiEmitterTrait.php b/src/Emitter/SapiEmitterTrait.php index 9a4a7be..38735b5 100644 --- a/src/Emitter/SapiEmitterTrait.php +++ b/src/Emitter/SapiEmitterTrait.php @@ -31,8 +31,10 @@ trait SapiEmitterTrait */ private function assertNoPreviousOutput() { - if (headers_sent()) { - throw EmitterException::forHeadersSent(); + $file = null; + $line = null; + if (headers_sent($file, $line)) { + throw EmitterException::forHeadersSent($file, $line); } if (ob_get_level() > 0 && ob_get_length() > 0) { diff --git a/src/Exception/EmitterException.php b/src/Exception/EmitterException.php index a00f278..2e2bf07 100644 --- a/src/Exception/EmitterException.php +++ b/src/Exception/EmitterException.php @@ -13,9 +13,15 @@ class EmitterException extends RuntimeException implements ExceptionInterface { - public static function forHeadersSent() : self + public static function forHeadersSent(string $file, int $line) : self { - return new self('Unable to emit response; headers already sent'); + return new self( + sprintf( + 'Unable to emit response; headers already sent, output started at %s:%d', + $file, + $line + ) + ); } public static function forOutputSent() : self diff --git a/test/Emitter/SapiStreamEmitterTest.php b/test/Emitter/SapiStreamEmitterTest.php index cb10f5c..01fb41b 100644 --- a/test/Emitter/SapiStreamEmitterTest.php +++ b/test/Emitter/SapiStreamEmitterTest.php @@ -58,6 +58,48 @@ public function testEmitCallbackStreamResponse() $this->assertSame('it works', ob_get_clean()); } + public function testAssertNoPreviousOutput() + { + $stream = new CallbackStream(function () { + return 'it works'; + }); + $response = (new Response()) + ->withStatus(200) + ->withBody($stream); + ob_start(); + echo 'Unexpected Output'; + try { + $this->emitter->emit($response); + } catch (\Throwable $e) { + $this->assertEquals( + 'Output has been emitted previously; cannot emit response', + $e->getMessage() + ); + } finally { + ob_end_clean(); + } + } + + public function testAssertNoPreviousOutputNoBuffer() + { + $stream = new CallbackStream(function () { + return 'it works'; + }); + $response = (new Response()) + ->withStatus(200) + ->withBody($stream); + echo 'Unexpected Output'; + try { + $this->emitter->emit($response); + } catch (\Throwable $e) { + $this->assertEquals($e->getMessage(),sprintf( + 'Unable to emit response; headers already sent, output started at %s:%d', + __FILE__, + __LINE__ - 5 + )); + } + } + public function testDoesNotInjectContentLengthHeaderIfStreamSizeIsUnknown() { $stream = $this->prophesize(StreamInterface::class);