delight-im/http
A lightweight, framework-agnostic HTTP utility library for PHP. It provides simple objects for requests, responses, URLs, and headers, plus helpers for methods, status codes, and content types, so you can read and build HTTP messages in any PHP application.
Install and locate the entry points
composer require delight-im/http
The package namespace is Delight\Http. After installation, open vendor/delight-im/http/src/ first — it's a small codebase (typically a Response class with static helpers and a ResponseCode constants class), and you'll have read all of it in a few minutes. There is almost no configuration, no service provider, and no framework coupling, so there's nothing to register.
Minimal first use case — replacing ad-hoc header() calls and magic numbers in a plain-PHP endpoint:
use Delight\Http\Response;
use Delight\Http\ResponseCode;
Response::setStatusCode(ResponseCode::OK);
Response::setHeader('Content-Type', 'application/json; charset=utf-8');
Response::setBody(json_encode(['ok' => true]));
Response::send(); // flush status + headers + body
Or the more common day-to-day use: just the constants, to stop scattering 200, 404, 422 through controllers.
if (!$user) {
Response::setStatusCode(ResponseCode::NOT_FOUND);
exit;
}
Where to look first if you're not on raw PHP: if you're in Laravel, Symfony, or Slim, you will mostly use this package as a readable constants library and delegate actual emission to the framework's response object (see patterns below). Don't fight the framework — use the constants and drop the emission helpers.
Pattern 1: Constants-only usage (recommended in frameworks). Use ResponseCode::* as named status codes and build the framework's native response:
// Laravel
return response()->json($data, ResponseCode::CREATED);
// Slim / PSR-7
return $response->withStatus(ResponseCode::NO_CONTENT);
This is the lowest-risk, highest-value use of the package: semantics with zero runtime coupling.
Pattern 2: Thin response wrapper in legacy / plain-PHP apps. Centralize emission behind one method so you can later swap in a PSR-7 implementation:
final class JsonResponse
{
public static function send(array $payload, int $status = ResponseCode::OK): void
{
Response::setStatusCode($status);
Response::setHeader('Content-Type', 'application/json; charset=utf-8');
Response::setBody(json_encode($payload, JSON_THROW_ON_ERROR));
Response::send();
}
}
Pattern 3: REST endpoint dispatch. Combine method checks with status constants:
switch ($_SERVER['REQUEST_METHOD']) {
case 'GET': /* ... */ break;
case 'POST': /* ... */ break;
default:
Response::setHeader('Allow', 'GET, POST');
Response::setStatusCode(ResponseCode::METHOD_NOT_ALLOWED);
Response::send();
}
Pattern 4: CORS / preflight handling — a natural fit because you need early header emission before routing logic:
Response::setHeader('Access-Control-Allow-Origin', $origin);
Response::setHeader('Access-Control-Allow-Methods', 'GET, POST, OPTIONS');
if ($_SERVER['REQUEST_METHOD'] === 'OPTIONS') {
Response::setStatusCode(ResponseCode::NO_CONTENT);
Response::send();
exit;
}
Pattern 5: Content negotiation. Pick the response type from Accept, then branch into the matching renderer, using the same status constants for both success and error paths.
Pattern 6: Middleware / error handler integration. In a Laravel render() method or a Slim error handler, convert exceptions into status codes via ResponseCode and let the framework emit them. This keeps your exception-to-status mapping in one place and readable:
$status = match (true) {
$e instanceof ValidationException => ResponseCode::UNPROCESSABLE_ENTITY,
$e instanceof NotFoundException => ResponseCode::NOT_FOUND,
default => ResponseCode::INTERNAL_SERVER_ERROR,
};
Workflow tip: add the package as a dev-only utility if you're only using the constants during prototyping, then remove it once you standardize on the framework's own constants class. There's no reason to carry two constant sets in production.
Response::send() (or whatever emits headers internally) will fail or silently no-op after echo, whitespace outside <?php, or a BOM. Wrap endpoint bodies in ob_start() while debugging, then flush.header() and http_response_code() are effectively no-ops. Don't write assertions against real headers — assert on your own return values or mock your wrapper class instead. This is a common source of "it works in the browser but fails in CI."Response holds static status/headers, tests can leak state between cases. Prefer the constants-only usage in tested code, or wrap the static API in an injectable adapter.composer.json and consider committing or forking it if you rely on it in production. The MIT license makes both trivial.Response or (better) compose it behind your own JsonResponse/ErrorResponse classes. Composition gives you a clean seam to swap in laminas-diactoros or Symfony Response later without touching call sites.ResponseCode constant you need exists in the installed version — this package predates several modern codes (e.g. 103 Early Hints, 425 Too Early). Fall back to an integer literal if missing, and note it in a comment.setStatusCode vs statusCode mismatches bite you. grep -r "function " vendor/delight-im/http/src is faster than reading docs that may not exist.send(). Ordering matters if send() also sets Content-Length; otherwise the header is computed from empty content and browsers will hang on Transfer-Encoding mismatch.X-Content-Type-Options, etc. still need an intentional, centralized policy — ideally one your wrapper enforces for every outgoing response.How can I help you explore Laravel packages today?