HTTP Responses
Introduction
Slenix's Response class provides a clean, fluent API for building and sending HTTP responses. Every route handler receives a $response object as its second argument. You may use it to send JSON, HTML, plain text, files, redirects, or any other HTTP response.
use Slenix\Http\Routing\Router;
Router::get('/greet', function ($request, $response) {
$response->json(['message' => 'Hello, World!']);
});All response methods that configure the response (status codes, headers, cookies, cache) return $this, allowing them to be chained fluently:
$response
->status(201)
->withHeader('X-Custom', 'value')
->json(['id' => $newUser->id]);Setting the Status Code
Use the status() method to set the HTTP status code. It accepts any valid code between 100 and 599:
$response->status(200); // OK
$response->status(201); // Created
$response->status(404); // Not Found
$response->status(422); // Unprocessable Entity
$response->status(500); // Internal Server ErrorThe status code is sent immediately via http_response_code(). Calling status() on an already-sent response has no effect.
JSON Responses
The json() method sets the Content-Type to application/json, encodes the given data, and sends the response:
$response->json(['name' => 'Cláudio', 'role' => 'admin']);
// With a custom status code
$response->json(['error' => 'Not found'], 404);
// With custom json_encode flags
$response->json($data, 200, JSON_PRETTY_PRINT);Standardised Success Response
$response->success($user, 'User created successfully.', 201);
// Sends:
// {
// "success": true,
// "message": "User created successfully.",
// "data": { ... }
// }Standardised Error Response
$response->error('Validation failed.', 422, [
'email' => 'The email field is required.',
]);
// Sends:
// {
// "success": false,
// "error": true,
// "message": "Validation failed.",
// "status_code": 422,
// "status_text": "Unprocessable Entity",
// "details": { "email": "..." }
// }HTML Responses
$response->html('<h1>Hello, World!</h1>');
// With a custom status code
$response->html('<h1>Not Found</h1>', 404);Rendering a View
$response->render('users.profile', ['user' => $user]);
// With a status code
$response->render('errors.404', [], 404);When the global view() helper is available (registered by the application kernel), render() delegates to it automatically.
Plain Text Responses
$response->write('Hello, plain text!');
$response->write('Error occurred', 500);XML Responses
$xml = '<?xml version="1.0"?><user><name>Cláudio</name></user>';
$response->xml($xml);
// With a SimpleXMLElement
$element = new SimpleXMLElement('<user><name>Cláudio</name></user>');
$response->xml($element, 201);Redirects
// Temporary redirect (302)
$response->redirect('/dashboard');
// Permanent redirect (301)
$response->redirect('/new-url', 301);
// Redirect back to the previous page
$response->redirectBack();
// Redirect back with a fallback URL
$response->redirectBack('/home');Supported redirect status codes: 301, 302, 303, 307, 308. Passing any other code throws an InvalidArgumentException.
All redirect URLs are automatically sanitized against header injection attacks (newlines and null bytes are stripped).
File Downloads
The download() method streams a file to the client in 8 KB chunks, keeping memory usage flat regardless of file size:
$response->download('/storage/reports/q3.pdf');
// With a custom file name
$response->download('/storage/reports/q3.pdf', 'Q3-Report-2025.pdf');
// With a custom MIME type
$response->download('/storage/data.csv', 'export.csv', 'text/csv');
// Inline (display in browser instead of downloading)
$response->download('/storage/invoice.pdf', 'Invoice.pdf', null, true);If the file does not exist or cannot be read, a 404 error response is sent automatically.
Headers
// Set a single header
$response->withHeader('X-Request-ID', 'abc-123');
// Set multiple headers at once
$response->withHeaders([
'X-Request-ID' => 'abc-123',
'X-Version' => '1.0',
]);
// Remove a header
$response->withoutHeader('X-Powered-By');
// Read a header that was set
$value = $response->getHeader('X-Request-ID');
// Get all set headers
$all = $response->getHeaders();All header names and values are automatically sanitized against header injection.
Cookies
// Basic cookie
$response->withCookie('user_token', $token, time() + 86400);
// With options
$response->withCookie('theme', 'dark', 0, [
'path' => '/',
'domain' => '.example.com',
'secure' => true,
'httponly' => true,
'samesite' => 'Lax',
]);
// Maximum security cookie (secure, httponly, SameSite=Strict)
$response->withSecureCookie('session_token', $token, time() + 3600);
// Delete a cookie
$response->withoutCookie('user_token');
// Read all cookies set on this response
$cookies = $response->getCookies();Cache Control
// Cache for 1 hour (public — can be cached by CDN and browser)
$response->withCache(3600);
// Cache for 5 minutes, private (browser only, not CDN)
$response->withCache(300, false);
// Disable caching entirely
$response->withoutCache();withCache() sets the Cache-Control, Expires, and Last-Modified headers automatically.
CORS Headers
// Allow all origins with default settings
$response->withCors();
// Custom CORS configuration
$response->withCors([
'origin' => 'https://app.example.com',
'methods' => 'GET, POST, PUT, DELETE',
'headers' => 'Content-Type, Authorization',
'credentials' => true,
'max_age' => 86400,
]);Chaining Example
Methods that configure the response return $this, allowing you to chain multiple calls before sending:
$response
->status(201)
->withHeader('X-Resource-ID', (string) $user->id)
->withCors(['origin' => 'https://app.example.com'])
->withoutCache()
->json([
'success' => true,
'data' => $user,
]);Sending the Response Manually
In most cases you call a terminal method (json, html, redirect, etc.) which internally calls send(). If you need to build a response object and send it later, you can do so manually:
$res = Response::make('Hello!', 200);
$res->withHeader('X-Custom', 'yes');
$res->send();send() outputs the body and calls exit. Calling it on an already-sent response is a no-op.
Inspecting the Response
$code = $response->getStatusCode(); // 200
$text = $response->getStatusText(); // 'OK'
$body = $response->getBody(); // raw content
$sent = $response->isSent(); // boolMethod Reference
| Method | Returns | Description |
|---|---|---|
status(code) | self | Set HTTP status code |
json(data, status, flags) | void | Send JSON response |
success(data, message, status) | void | Standardised success JSON |
error(message, status, details) | void | Standardised error JSON |
html(html, status) | void | Send HTML response |
write(text, status) | void | Send plain text response |
xml(xml, status) | void | Send XML response |
render(template, data, status) | void | Render a view template |
redirect(url, status) | void | Redirect to URL |
redirectBack(fallback, status) | void | Redirect to previous page |
download(path, name, type, inline) | void | Stream a file download |
withHeader(name, value) | self | Set a response header |
withHeaders(array) | self | Set multiple headers |
withoutHeader(name) | self | Remove a header |
getHeader(name, default) | mixed | Read a set header |
getHeaders() | array | All set headers |
withContentType(type, charset) | self | Set Content-Type |
withCookie(name, value, expire, opts) | self | Set a cookie |
withSecureCookie(name, value, expire, opts) | self | Set a secure cookie |
withoutCookie(name, opts) | self | Delete a cookie |
getCookies() | array | All cookies set |
withCache(maxAge, public) | self | Set cache headers |
withoutCache() | self | Disable caching |
withCors(options) | self | Set CORS headers |
send(content, status) | void | Send the full response |
sendHeaders() | self | Send headers only |
getStatusCode() | int | Current status code |
getStatusText() | string | Status text for current code |
getBody() | mixed | Raw response body |
isSent() | bool | Whether response was sent |
Response::make(content, status) | self | Static factory |