diff --git a/src/main/php/web/Response.class.php b/src/main/php/web/Response.class.php index 0acc48ce..2b0ec4bd 100755 --- a/src/main/php/web/Response.class.php +++ b/src/main/php/web/Response.class.php @@ -12,6 +12,7 @@ */ class Response { private $output; + private $flushing= []; private $flushed= false; private $status= 200; private $message= 'OK'; @@ -111,6 +112,9 @@ public function headers() { /** @param web.io.Output $output */ private function begin($output) { + foreach ($this->flushing as $function) { + $function($this); + } $output->begin($this->status, $this->message, $this->cookies ? array_merge($this->headers, ['Set-Cookie' => array_map(function($c) { return $c->header(); }, $this->cookies)]) : $this->headers @@ -118,19 +122,29 @@ private function begin($output) { $this->flushed= true; } + /** + * Passes a function to call before flushing the response + * + * @param function(self): void $function + * @return self + */ + public function flushing(callable $function) { + $this->flushing[]= $function; + return $this; + } + /** * Flushes response * - * @param web.io.Output $output * @return void * @throws lang.IllegalStateException */ - public function flush($output= null) { + public function flush() { if ($this->flushed) { throw new IllegalStateException('Response already flushed'); } - $this->begin($output ?: $this->output); + $this->begin($this->output); } /** @@ -157,8 +171,13 @@ public function end() { * * @param int $size If omitted, uses chunked transfer encoding * @return io.streams.OutputStream + * @throws lang.IllegalStateException */ public function stream($size= null) { + if ($this->flushed) { + throw new IllegalStateException('Response already flushed'); + } + if (null === $size) { $output= $this->output->stream(); } else { @@ -166,7 +185,7 @@ public function stream($size= null) { $output= $this->output; } - $this->flush($output); + $this->begin($output); return $output; } diff --git a/src/test/php/web/unittest/ResponseTest.class.php b/src/test/php/web/unittest/ResponseTest.class.php index 13b9b4d3..2eb8e032 100755 --- a/src/test/php/web/unittest/ResponseTest.class.php +++ b/src/test/php/web/unittest/ResponseTest.class.php @@ -309,10 +309,59 @@ public function flushed() { Assert::true($res->flushed()); } + #[Test] + public function ended() { + $res= new Response(new TestOutput()); + Assert::false($res->flushed()); + $res->end(); + Assert::true($res->flushed()); + } + #[Test, Expect(IllegalStateException::class)] public function flush_twice() { $res= new Response(new TestOutput()); $res->flush(); $res->flush(); } + + #[Test] + public function flushing_explicitely() { + $executed= 0; + $res= (new Response(new TestOutput()))->flushing(function() use(&$executed) { + $executed++; + }); + $res->flush(); + Assert::equals(1, $executed); + } + + #[Test] + public function flushing_implicitely() { + $executed= 0; + $res= (new Response(new TestOutput()))->flushing(function() use(&$executed) { + $executed++; + }); + $res->send('Test', 'text/plain'); + Assert::equals(1, $executed); + } + + #[Test] + public function flushing_on_end() { + $executed= 0; + $res= (new Response(new TestOutput()))->flushing(function() use(&$executed) { + $executed++; + }); + $res->end(); + Assert::equals(1, $executed); + } + + #[Test] + public function multiple_flushing_functions() { + $executed= ['one' => 0, 'two' => 0]; + $res= (new Response(new TestOutput())) + ->flushing(function() use(&$executed) { $executed['one']++; }) + ->flushing(function() use(&$executed) { $executed['two']++; }) + ; + $res->flush(); + Assert::equals(['one' => 1, 'two' => 1], $executed); + } } \ No newline at end of file