Lightweight object-oriented HTTP foundation for PHP.

Installs: 109

Dependents: 3

Suggesters: 0

Security: 0

Stars: 0

Watchers: 0

Forks: 0

Open Issues: 0

pkg:composer/codemonster-ru/http

v2.0.0 2025-12-24 16:16 UTC

This package is auto-updated.

Last update: 2025-12-24 16:37:01 UTC


README

Latest Version on Packagist Total Downloads License Tests

Lightweight object-oriented HTTP foundation for PHP.

Table of Contents

Design Goals

  • Simple, small HTTP layer for frameworks or microservices.
  • Predictable, test-friendly API with immutable helpers.
  • Safe defaults for headers, cookies, and proxy handling.

Installation

composer require codemonster-ru/http

Usage

Handling HTTP Requests

use Codemonster\Http\Request;

// Capture the current request from PHP globals
$request = Request::capture();

echo $request->method();      // GET
echo $request->uri();         // /hello
echo $request->query('id');   // 42
echo $request->input('name'); // Vasya

You can also create a request manually (useful for tests or CLI):

$request = new Request(
    'POST',
    '/api/user',
    ['page' => 2],
    ['name' => 'John'],
    ['Accept' => 'application/json']
);

if ($request->wantsJson()) {
    // Return JSON
}

Request data helpers:

// input() excludes files, files() returns normalized uploads
$name = $request->input('name');
$files = $request->files();

// all() merges query + body + files
$all = $request->all();

// headers/server access
$headers = $request->headers();
$server = $request->server();

// full URL with query string
$url = $request->fullUrlWithQuery();

// immutable request changes
$request2 = $request
    ->withHeader('X-Trace', '1')
    ->withoutHeader('X-Deprecated')
    ->withQuery(['page' => 2])
    ->withInput(['name' => 'Vasya']);

// files with nested keys
$avatarName = $request->files('attachments.docs.a.name');

Trusted proxies:

// Trust proxy IPs (exact or CIDR) for ip() and isSecure()
Request::setTrustedProxies(['192.168.1.0/24', '2001:db8::/32']);

$ip = $request->ip();
$secure = $request->isSecure();
$ua = $request->userAgent();

Security note:

Only set trusted proxies if you control them. Otherwise, X-Forwarded-* headers can be spoofed and ip()/isSecure() will be incorrect.

Method override:

// For POST forms, override method via header or _method field
$_SERVER['HTTP_X_HTTP_METHOD_OVERRIDE'] = 'PATCH';
$_POST['_method'] = 'PUT';

Middleware-style response sending:

$request = Request::capture();
$response = $next($request);

// Respects HEAD automatically
$response->sendFor($request);

Sending HTTP Responses

use Codemonster\Http\Response;

// Basic response
$response = new Response('Hello world', 200);
$response->send();

// JSON response with custom json_encode flags
return Response::json(['ok' => true], 200, [], JSON_UNESCAPED_SLASHES)->send();

// Redirect
return Response::redirect('/login');

// HEAD response (skip body)
$request = Request::capture();
(new Response('Hello'))->sendFor($request);

// Or send HEAD explicitly
(new Response('Hello'))->sendHead();

// Cookies (immutable)
$response = (new Response('OK'))
    ->withCookie('session', 'abc', ['path' => '/', 'httponly' => true])
    ->send();

// Cookie with options
(new Response('OK'))
    ->cookie('remember', '1', ['max_age' => 3600, 'samesite' => 'lax'])
    ->send();

// Cookies (mutable)
(new Response('OK'))
    ->cookie('session', 'abc')
    ->send();

// Multiple Set-Cookie via header array
(new Response('OK'))
    ->header('Set-Cookie', ['a=1', 'b=2'])
    ->send();

// SameSite=None forces Secure
(new Response('OK'))
    ->cookie('session', 'abc', ['samesite' => 'none'])
    ->send();

// Custom headers
$response = (new Response('Created', 201))
    ->header('X-Custom', '1')
    ->type('text/plain')
    ->send();

// Immutable response changes
$response = (new Response('OK', 200))
    ->withStatus(201)
    ->withHeader('X-Trace', '1')
    ->withType('text/plain')
    ->withHeaders(['X-Foo' => 'bar'])
    ->withoutHeader('X-Deprecated')
    ->withoutHeaders();

Convenience helpers:

// Filter input data
$only = $request->only(['name', 'email']);
$except = $request->except(['password']);

// Dot-notation for nested data
$onlyNested = $request->only(['user.name']);
$exceptNested = $request->except(['user.password']);

// Dot-notation for input/query
$name = $request->input('user.name');
$page = $request->query('meta.page');

// Dot-notation for files
$fileName = $request->files('attachments.docs.a.name');

API Reference (Highlights)

Request:

  • Request::capture()
  • method(), uri(), fullUrl(), fullUrlWithQuery()
  • query(), input(), files(), all(), body()
  • headers(), server(), header()
  • only(), except() (dot-notation supported)
  • ip(), isSecure(), userAgent()
  • withHeader(), withoutHeader(), withQuery(), withInput()
  • setTrustedProxies() / getTrustedProxies()

Response:

  • send(), sendFor(), sendHead()
  • json(), redirect(), empty(), type()
  • header(), withHeader(), withHeaders(), withoutHeader(), withoutHeaders()
  • withStatus(), withType()
  • withCookie(), withoutCookie(), cookie()

Edge Cases

// 204/304 responses never send a body
(new Response('Will not be sent', 204))->send();

// If headers were already sent, an exception is thrown
// (guarded by headers_sent()).

Testing

You can run tests with the command:

composer test

Author

Kirill Kolesnikov

License

MIT