HTTP Responses
ZephyrPHP provides a comprehensive Response class for creating HTTP responses, from simple text to JSON APIs, file downloads, and streaming.
Creating Responses
Create responses in multiple ways:
use ZephyrPHP\Core\Http\Response;
// Basic response
$response = new Response('Hello, World!');
// With status code
$response = new Response('Not Found', 404);
// With headers
$response = new Response('Hello', 200, [
'X-Custom-Header' => 'value'
]);
// Using static factory
$response = Response::make('Hello, World!', 200);
// Using fluent builder
$response = Response::create('Content')
->status(200)
->header('X-Custom', 'value')
->build();
Sending Responses
// Send response
$response->send();
// Send and terminate script
$response->sendAndExit();
Content Types
JSON Responses
// Basic JSON
return Response::json([
'name' => 'John',
'email' => 'john@example.com'
]);
// With status code
return Response::json(['error' => 'Not found'], 404);
// Pretty printed JSON (debugging)
return Response::jsonPretty($data);
// JSONP response
return Response::jsonp($data, 'callback');
HTML & Text Responses
// HTML response
return Response::html('<h1>Hello World</h1>');
// Plain text
return Response::text('Hello World');
// XML response
return Response::xml('<root><item>data</item></root>');
HTTP Status Helpers
Success Responses
// 201 Created
return Response::created($user);
// With location header
return Response::created($user, '/api/users/' . $user->id);
// 202 Accepted
return Response::accepted($data);
// 204 No Content
return Response::noContent();
Error Responses
// 400 Bad Request
return Response::badRequest('Invalid input');
// 401 Unauthorized
return Response::unauthorized('Please login');
// 403 Forbidden
return Response::forbidden('Access denied');
// 404 Not Found
return Response::notFound('User not found');
// 405 Method Not Allowed
return Response::methodNotAllowed(['GET', 'POST'], 'Method not allowed');
// 409 Conflict
return Response::conflict('Resource already exists');
// 410 Gone
return Response::gone('Resource no longer available');
// 422 Validation Error
return Response::validationError([
'email' => ['The email has already been taken.'],
'password' => ['The password must be at least 8 characters.']
]);
// With custom message
return Response::validationError($errors, 'Please fix the errors below');
// 429 Too Many Requests
return Response::tooManyRequests(60, 'Rate limit exceeded');
// 500 Server Error
return Response::serverError('Something went wrong');
// 503 Service Unavailable
return Response::serviceUnavailable(300, 'Maintenance in progress');
// Generic error
return Response::error('Custom error', 400);
JSON API Helpers
Standardized API response format:
// Success response
return Response::success($data, 'Operation completed');
/*
{
"success": true,
"message": "Operation completed",
"data": { ... }
}
*/
// Failure response
return Response::fail($errors, 'Operation failed');
/*
{
"success": false,
"message": "Operation failed",
"errors": { ... }
}
*/
// Paginated response
return Response::paginated(
$users, // data array
$totalCount, // total records
$perPage, // items per page
$currentPage // current page number
);
/*
{
"data": [...],
"meta": {
"total": 100,
"per_page": 15,
"current_page": 1,
"last_page": 7,
"from": 1,
"to": 15
}
}
*/
Redirects
// Basic redirect
$response = Response::redirectTo('/dashboard');
$response->send();
// Or direct redirect (terminates script)
(new Response())->redirect('/dashboard');
// Permanent redirect (301)
return Response::permanentRedirect('/new-url');
// Temporary redirect (307)
return Response::temporaryRedirect('/temp-url');
// Redirect to named route
return Response::route('users.show', ['id' => $user->id]);
// External redirect
return Response::away('https://external-site.com');
// Redirect back
(new Response())->back();
// Back with fallback
(new Response())->back('/home');
Headers
$response = new Response('Hello');
// Set single header
$response->setHeader('X-Custom-Header', 'value');
$response->header('X-Custom-Header', 'value'); // alias
// Set multiple headers
$response->withHeaders([
'X-Custom-One' => 'value1',
'X-Custom-Two' => 'value2'
]);
// Check header exists
if ($response->hasHeader('Content-Type')) { }
// Get header value
$contentType = $response->getHeader('Content-Type');
// Get all headers
$headers = $response->getHeaders();
// Remove header
$response->removeHeader('X-Custom-Header');
Cookies
$response = new Response('Hello');
// Set a cookie
$response->cookie('name', 'value');
// With expiration (in minutes)
$response->cookie('remember', 'token', 60 * 24 * 30); // 30 days
// With all options
$response->cookie(
'session_id', // name
'abc123', // value
60, // minutes
'/', // path
'example.com', // domain
true, // secure (HTTPS only)
true, // httpOnly
'Strict' // sameSite
);
// Forever cookie (5 years)
$response->cookieForever('preferences', 'value');
// Delete a cookie
$response->withoutCookie('name');
// Clear all cookies
$response->clearCookies();
File Downloads
$response = new Response();
// Download a file
$response->download('/path/to/file.pdf');
// With custom filename
$response->download('/path/to/file.pdf', 'invoice.pdf');
// With custom headers
$response->download('/path/to/file.pdf', 'report.pdf', [
'X-Download-Options' => 'noopen'
]);
// Stream download (for generated content)
$response->streamDownload(function() {
echo generateCsvContent();
}, 'export.csv');
File Display
$response = new Response();
// Display file inline (in browser)
$response->file('/path/to/image.jpg');
// With custom filename
$response->file('/path/to/document.pdf', 'preview.pdf');
Streaming Responses
$response = new Response();
$response->stream(function() {
echo "data: Starting...\n\n";
flush();
for ($i = 0; $i < 100; $i++) {
echo "data: Progress $i%\n\n";
flush();
usleep(50000);
}
echo "data: Done!\n\n";
});
Caching
$response = new Response('Content');
// Set cache duration (seconds)
$response->cache(3600); // 1 hour public cache
$response->cache(3600, false); // 1 hour private cache
// Prevent caching
$response->noCache();
// Set ETag
$response->etag(md5($content));
$response->etag($hash, weak: true); // Weak ETag
// Set Last-Modified
$response->lastModified(new DateTime('2024-01-15 10:30:00'));
// Check if modified (for 304 responses)
if ($response->isNotModified($request)) {
return $response->setStatusCode(304)->setContent('');
}
CORS Support
$response = Response::json($data);
// Add CORS headers
$response->withCors(
origins: '*', // or ['https://example.com']
methods: ['GET', 'POST', 'PUT', 'DELETE'],
headers: ['Content-Type', 'Authorization'],
credentials: false,
maxAge: 86400
);
// Handle preflight request
return Response::corsPreflightResponse(
origins: 'https://myapp.com',
methods: ['GET', 'POST'],
headers: ['Content-Type', 'X-Custom-Header'],
credentials: true
);
Security Headers
$response = new Response($content);
// Add common security headers
$response->withSecurityHeaders();
/*
Adds:
- X-Content-Type-Options: nosniff
- X-Frame-Options: SAMEORIGIN
- X-XSS-Protection: 1; mode=block
- Referrer-Policy: strict-origin-when-cross-origin
*/
// Content Security Policy
$response->contentSecurityPolicy([
'default-src' => ["'self'"],
'script-src' => ["'self'", 'https://cdn.example.com'],
'style-src' => ["'self'", "'unsafe-inline'"],
'img-src' => ["'self'", 'data:', 'https:'],
]);
// HTTP Strict Transport Security (HSTS)
$response->hsts(
maxAge: 31536000, // 1 year
includeSubdomains: true,
preload: true
);
Status Checks
$response = new Response('', 200);
// Status category checks
$response->isInformational(); // 1xx
$response->isSuccessful(); // 2xx
$response->isRedirection(); // 3xx
$response->isClientError(); // 4xx
$response->isServerError(); // 5xx
// Specific status checks
$response->isOk(); // 200
$response->isForbidden(); // 403
$response->isNotFound(); // 404
$response->isEmpty(); // 204 or 304
// Get status info
$code = $response->getStatusCode();
$text = $response->getStatusText();
Response Builder
Fluent interface for building responses:
$response = Response::create()
->content('Hello World')
->status(200)
->header('X-Custom', 'value')
->headers([
'X-Another' => 'value2',
'X-Third' => 'value3'
])
->cookie('session', 'abc123', 60)
->build();
// Build and send
Response::create()
->content($html)
->status(200)
->send();
Complete API Example
app/Controllers/Api/UserController.php
<?php
namespace App\Controllers\Api;
use ZephyrPHP\Core\Controllers\Controller;
use ZephyrPHP\Core\Http\Response;
use App\Models\User;
class UserController extends Controller
{
public function index()
{
$page = $this->request->integer('page', 1);
$perPage = $this->request->integer('per_page', 15);
$users = User::query()
->limit($perPage)
->offset(($page - 1) * $perPage)
->all();
$total = User::query()->count();
return Response::paginated($users, $total, $perPage, $page)
->withCors()
->cache(300);
}
public function store()
{
if (!$this->request->filled(['name', 'email'])) {
return Response::validationError([
'name' => ['Name is required'],
'email' => ['Email is required']
]);
}
$user = new User();
$user->fill($this->request->only(['name', 'email']));
$user->save();
return Response::created($user, '/api/users/' . $user->id);
}
public function show(int $id)
{
$user = User::find($id);
if (!$user) {
return Response::notFound('User not found');
}
return Response::success($user);
}
public function destroy(int $id)
{
$user = User::find($id);
if (!$user) {
return Response::notFound('User not found');
}
$user->delete();
return Response::noContent();
}
}
Status Codes Reference
| Code | Status | Description |
|---|---|---|
| 200 | OK | Request successful |
| 201 | Created | Resource created |
| 202 | Accepted | Request accepted for processing |
| 204 | No Content | Success with no body |
| 301 | Moved Permanently | Permanent redirect |
| 302 | Found | Temporary redirect |
| 304 | Not Modified | Use cached version |
| 400 | Bad Request | Invalid request |
| 401 | Unauthorized | Authentication required |
| 403 | Forbidden | Access denied |
| 404 | Not Found | Resource not found |
| 405 | Method Not Allowed | HTTP method not allowed |
| 422 | Unprocessable Entity | Validation error |
| 429 | Too Many Requests | Rate limit exceeded |
| 500 | Internal Server Error | Server error |
| 503 | Service Unavailable | Service temporarily unavailable |
Method Reference
| Method | Description |
|---|---|
json($data, $status) | Create JSON response |
success($data, $message) | JSON success response |
fail($errors, $message) | JSON failure response |
paginated(...) | Paginated JSON response |
created($data, $location) | 201 Created response |
noContent() | 204 No Content response |
validationError($errors) | 422 validation error |
redirectTo($url) | Redirect response |
download($path, $name) | File download response |
file($path) | Inline file response |
stream($callback) | Streaming response |
withCors(...) | Add CORS headers |
withSecurityHeaders() | Add security headers |
cache($seconds) | Set cache headers |
cookie(...) | Set response cookie |