Click the Castor logo or press Ctrl Alt T to change theme.
# Response writer `Castor\Net\Http\ResponseWriter` is the server-side abstraction for emitting a response to a client. Instead of building a complete `Response` object in memory, a handler can set headers, write the status line, and stream body bytes. ```php <?php use Castor\Io\Writer; use Castor\Net\Http\Headers; use Castor\Net\Http\Status\Code; interface ResponseWriter extends Writer { public function headers(): Headers; public function writeHeaders(Code|int $status = Code::OK): void; } ``` Because it extends `Castor\Io\Writer`, body output is written with `write(string $bytes): int`. ## Writing a response ```php <?php use Castor\Net\Http\Status\Code; $writer->headers()->set('Content-Type', 'application/json'); $writer->writeHeaders(Code::OK); $writer->write('{"ok": true}'); ``` The usual order is: 1. Mutate headers through `headers()`. 2. Call `writeHeaders()` once with a status. 3. Stream body bytes with `write()`. Calling `writeHeaders` more than once throws `HeadersAlreadySent`. ## Implicit 200 OK Writers may send headers automatically when the first body bytes are written. The built-in recorder does this: ```php $writer->headers()->set('Content-Type', 'text/plain'); $writer->write('Hello'); // sends 200 OK if no status has been sent yet ``` If your handler needs a non-200 status, call `writeHeaders` before writing the body. ## Header mutability after sending Before headers are sent, `headers()` returns the mutable response header collection. After the status line has been written, a writer may protect the sent headers from late mutation. `ResponseWriter\Recorder` returns a clone after headers have been sent, emulating real server behavior where already-sent headers cannot be changed. ```php $writer->headers()->set('X-Before', 'yes'); $writer->writeHeaders(); $writer->headers()->set('X-After', 'no-effect-on-recorder-response'); ``` Treat headers as finalized immediately after `writeHeaders` or the first body write. ## Testing with Recorder `Castor\Net\Http\ResponseWriter\Recorder` is a test implementation of `ResponseWriter`: ```php <?php use Castor\Net\Http\ResponseWriter\Recorder; use Castor\Net\Http\Status\Code; $writer = Recorder::create(); $writer->headers()->set('Content-Type', 'text/plain'); $writer->writeHeaders(Code::Created); $writer->write('created'); $response = $writer->response(); $response->status->code; // 201 $response->headers->get('Content-Type'); // "text/plain" ``` The recorder stores the body in an in-memory `Castor\Io\Stream\Buffer`. When you call `response()`, it rewinds seekable bodies to the beginning and returns a normal `Response` object. ## Recorder failure modes `response()` requires headers to have been sent: ```php $writer = Recorder::create(); $writer->response(); // LogicException ``` Write headers explicitly or write at least one byte first: ```php $writer->writeHeaders(); $response = $writer->response(); ``` Calling `writeHeaders` twice throws `HeadersAlreadySent`: ```php $writer->writeHeaders(Code::OK); $writer->writeHeaders(Code::Created); // HeadersAlreadySent ``` ## ResponseWriter vs Response Use `ResponseWriter` when implementing server-side handlers and middleware. Use `Response` when you already have a complete message, when testing with a recorder, or when serializing a response as a value.
Castor ecosystem