coroq/http-responder

Simple PSR-7 HTTP response factory with semantic method names

Installs: 4

Dependents: 0

Suggesters: 0

Security: 0

Stars: 0

Watchers: 0

Forks: 0

Open Issues: 0

pkg:composer/coroq/http-responder

v1.0.0 2025-11-24 18:23 UTC

This package is auto-updated.

Last update: 2025-11-25 12:55:20 UTC


README

PSR-7 HTTP response factory with semantic method names.

Requirements

  • PHP ^8.0
  • PSR-7 HTTP Message implementation (e.g., nyholm/psr7, guzzlehttp/psr7)
  • PSR-17 HTTP Factory implementation (e.g., nyholm/psr7, laminas/laminas-diactoros)

Installation

composer require coroq/http-responder

Quick Start

use Coroq\HttpResponder\Responder;
use Coroq\HttpResponder\JsonResponder;

// Inject PSR-17 ResponseFactoryInterface
$responder = new Responder($responseFactory);

// Returns PSR-7 ResponseInterface with status 200 and HTML body
return $responder->ok('<h1>Hello World</h1>');

// For JSON responses, use JsonResponder
$responder = new JsonResponder($responseFactory);

// Returns PSR-7 ResponseInterface with status 200, Content-Type: application/json,
// and body: {"status":"success"}
return $responder->ok(['status' => 'success']);

Concepts

Two independent classes for different response types:

  • Responder - HTML, text, file downloads
  • JsonResponder - JSON responses with automatic encoding

Method names map directly to HTTP status codes. No need to remember numbers.

Both classes return PSR-7 ResponseInterface, allowing standard PSR-7 chaining.

Who Is This For?

For developers building PSR-7/PSR-15 applications who want readable response creation without framework coupling. Good fit for Mezzio, Slim, or custom middleware stacks mixing HTML and JSON responses.

Not for pure REST APIs needing comprehensive status code coverage, or Laravel/Symfony projects with built-in response helpers.

Usage

Basic Responses

$responder = new Responder($responseFactory);

// 200 OK
return $responder->ok('Hello World');
return $responder->ok($streamInterface);
return $responder->ok(); // empty body

// 401 Unauthorized
return $responder->unauthorized('Login required');

// 403 Forbidden
return $responder->forbidden();

// 404 Not Found
return $responder->notFound('<h1>Not Found</h1>');

// 500 Internal Server Error
return $responder->internalServerError();

// 503 Service Unavailable
return $responder->serviceUnavailable('Under maintenance');

// Custom status
return $responder->response(418);

File Downloads

// With filename
return $responder->okDownload(
  $fileContent,
  'application/pdf',
  'invoice.pdf'
);

// UTF-8 filenames work
return $responder->okDownload(
  $content,
  'text/plain',
  'ファイル.txt'
);

// Without filename
return $responder->okDownload($stream, 'image/png');

Redirects

// 302 Found (temporary redirect)
return $responder->found('/login');

// With query parameters
return $responder->found('/search', ['q' => 'test', 'page' => '2']);

// With fragment
return $responder->found('/page', [], 'section-3');

// Both
return $responder->found('/article', ['id' => '123'], 'comments');

// 301 Moved Permanently
return $responder->movedPermanently('/new-url');
return $responder->movedPermanently('/new', ['ref' => 'old']);

JSON Responses

$responder = new JsonResponder($responseFactory);

// 200 OK
return $responder->ok(['user' => 'john', 'status' => 'active']);

// 201 Created
return $responder->created(['id' => 123, 'created_at' => '2024-01-01']);

// 400 Bad Request
return $responder->badRequest(['error' => 'Invalid email format']);

// 401 Unauthorized
return $responder->unauthorized(['error' => 'Token expired']);

// 403 Forbidden
return $responder->forbidden(['error' => 'Insufficient permissions']);

// 404 Not Found
return $responder->notFound(['error' => 'User not found']);

// 500 Internal Server Error
return $responder->internalServerError(['error' => 'Database connection failed']);

// 503 Service Unavailable
return $responder->serviceUnavailable(['error' => 'Maintenance mode']);

// Custom status
return $responder->response(418, ['message' => "I'm a teapot"]);

JSON Redirects

return $responder->found('/dashboard');
return $responder->movedPermanently('/new-api', [], 'v2');

JSON Encoding Options

// Default flags: JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES
$responder = new JsonResponder($responseFactory);
return $responder->ok(['path' => '/foo/bar', 'name' => '日本語']);
// Output: {"path":"/foo/bar","name":"日本語"}

// Override flags for one response
return $responder->withEncodingOptions(JSON_PRETTY_PRINT)->ok($data);
// Returns new instance, original unchanged

// Configure at construction
$responder = new JsonResponder($responseFactory, JSON_PRETTY_PRINT);

// Custom encoder
$encoder = fn($data) => json_encode($data, JSON_THROW_ON_ERROR);
$responder = new JsonResponder($responseFactory, 0, $encoder);