Requests and input
The HTTP Component provides PSR-7 request implementations for both generic requests and server-side requests. All standard PSR-7 methods are available, including getMethod(), getUri(), getHeaders(), getBody(), and their immutable with* counterparts.
Creating requests
Use Request when its global/input convenience helpers are useful:
<?php
declare(strict_types=1);
use Qubus\Http\Request;
$request = new Request(
uri: 'https://api.example.com/search?q=http&page=2',
method: 'GET',
headers: [
'Accept' => 'application/json',
'User-Agent' => 'ExampleClient/1.0',
],
);
When the URI or method is omitted, Request derives it from $_SERVER. Applications that construct requests explicitly should provide both values to avoid coupling the result to process globals.
Use the PSR-17 factory when framework code should depend on standard factory interfaces:
<?php
use Psr\Http\Message\RequestFactoryInterface;
use Psr\Http\Message\ServerRequestFactoryInterface;
use Qubus\Http\Factories\Psr17Factory;
$factory = new Psr17Factory();
assert($factory instanceof RequestFactoryInterface);
assert($factory instanceof ServerRequestFactoryInterface);
$request = $factory->createRequest('POST', 'https://example.com/orders');
$serverRequest = $factory->createServerRequest(
'GET',
'https://example.com/users/42',
['REMOTE_ADDR' => '203.0.113.10'],
);
ServerRequest accepts the complete PSR-7 server-request state in its constructor and adds a small get() shortcut for array parsed bodies:
<?php
use Qubus\Http\ServerRequest;
$request = new ServerRequest(
serverParams: ['REMOTE_ADDR' => '203.0.113.10'],
uri: 'https://example.com/profile',
method: 'POST',
headers: ['Content-Type' => 'application/json'],
cookies: ['theme' => 'dark'],
queryParams: ['preview' => '1'],
parsedBody: ['display_name' => 'Ada'],
);
$name = $request->get('display_name', 'Anonymous');
Use ServerRequestFactory::fromGlobals() when you need Diactoros' standard conversion from PHP superglobals:
Working with PSR-7 requests
Every operation that changes PSR-7 state returns a new request:
<?php
$authenticated = $request
->withMethod('PUT')
->withHeader('Authorization', 'Bearer example-token')
->withAttribute('user_id', 42);
Attributes are application metadata. Headers are HTTP protocol data. Keep authenticated identities, route results, and service objects in attributes rather than inventing transport headers.
URI helpers
Url implements UriInterface and adds query, relative/absolute URL, and inspection helpers:
<?php
use Qubus\Http\Url;
$url = new Url('https://alice:secret@example.com:8443/items?sort=name&page=2#results');
$url->getScheme(); // https
$url->getAuthority(); // alice:secret@example.com:8443
$url->getHost(); // example.com
$url->getPort(); // 8443
$url->getPath(); // /items
$url->getParams(); // ['sort' => 'name', 'page' => '2']
$url->getParam('page'); // '2'
$url->getFragment(); // results
$url->getRelativeUrl(); // /items?sort=name&page=2#results
$url->getAbsoluteUrl(); // full URL
$url->isSecure(); // true
$url->isRelative(); // false
URI mutations are immutable:
<?php
$pageThree = $url
->mergeParams(['page' => 3, 'filter' => 'active'])
->removeParam('sort')
->withFragment('top');
$withoutQuery = $pageThree->withParams([]);
$pathOnly = $pageThree->getRelativeUrl(includeParams: false);
withParams() replaces the query; mergeParams() overlays new values; removeParam() and removeParams() delete selected keys. Nested arrays use PHP's normal http_build_query() representation.
<?php
$query = Url::arrayToParams([
'filter' => ['status' => 'open'],
'page' => 1,
]);
// filter%5Bstatus%5D=open&page=1
getOriginalUrl() and getOriginalPath() describe the string supplied to the constructor. They intentionally do not change after immutable URI mutations.
Convenience request metadata
The concrete Request exposes normalized accessors:
<?php
$request->getHost();
$request->getScheme();
$request->getContentType();
$request->getUserAgent();
$request->getReferer();
$request->getServerName();
$request->getServerAddress();
$request->getServer('remote-addr');
$request->hasServer('http-authorization');
Server names are normalized to lowercase with underscores replaced by hyphens, so HTTP_USER_AGENT is available as http-user-agent.
Common request checks include:
<?php
if ($request->isPost() && $request->isAjax()) {
// Handle an XMLHttpRequest POST.
}
$request->isGet();
$request->isPut();
$request->isPatch();
$request->isDelete();
$request->isHead();
$request->isOptions();
$request->isConnect();
$request->isTrace();
$request->isPostBack(); // POST, PUT, PATCH, or DELETE
For HTML forms, a POST field named _method can override the request method to another supported method:
Note
Only an actual POST request is eligible for the override.
Client addresses and proxies
By default, use the connection address because forwarded headers are supplied by clients and can be spoofed:
Trust forwarded headers only after your web server has removed untrusted values and only when the application is reachable through a trusted proxy:
<?php
$address = $request->getClientAddress(trustForwardedHeader: true);
$secure = $request->isSecure(trustForwardedHeader: true);
getIp(safeMode: true) similarly limits lookup to REMOTE_ADDR. The default legacy behavior also considers common forwarding headers; security-sensitive applications should use safe mode unless the proxy boundary is controlled.
Authentication headers
<?php
$basic = $request->getBasicAuth();
if ($basic !== null) {
$username = $basic['username'];
$password = $basic['password'];
}
$digestFields = $request->getDigestAuth();
These helpers parse credentials; they do not authenticate or authorize them. Avoid logging credentials or complete authorization headers.
Parsed input
Request::handler() combines query parameters, form fields, JSON bodies, and PHP upload data. It recognizes JSON from Content-Type: application/json and also accepts JSON object/array bodies for compatibility.
<?php
use Qubus\Http\Factories\Psr17Factory;
use Qubus\Http\Request;
$body = new Psr17Factory()->createStream(
'{"name":"Ada","roles":["editor"]}',
);
$request = new Request(
uri: 'https://example.com/search?page=2',
method: 'POST',
body: $body,
headers: ['Content-Type' => 'application/json; charset=utf-8'],
);
$input = $request->handler();
$page = $input->value('page', 1, Request::REQUEST_TYPE_GET);
$name = $input->value('name', 'Anonymous', Request::REQUEST_TYPE_POST);
$roles = $input->value('roles', [], Request::REQUEST_TYPE_POST);
The main operations are:
| Method | Result |
|---|---|
get($key, $default) | Query Input, nested input array, or default. |
post($key, $default) | Body Input, nested input array, or default. |
file($key, $default) | Uploaded File, nested upload array, or default. |
find($key, ...$methods) | Search selected sources or query/body/files in that order. |
value($key, $default, ...$methods) | Return an unwrapped scalar/array value. |
exists($keyOrKeys, ...$methods) | Test one key or require every supplied key. |
all($filter) | Return raw merged input, optionally limited to named keys. |
<?php
if ($input->exists(['email', 'password'], Request::REQUEST_TYPE_POST)) {
$credentials = $input->all(['email', 'password']);
}
$item = $input->post('email');
if ($item instanceof \Qubus\Http\Input\Input) {
$label = $item->getName();
$value = $item->getValue();
}
Input implements ArrayAccess, IteratorAggregate, and string conversion, which is useful for nested form fields. Prefer value() when you need plain application data.
Uploaded files
The legacy input handler wraps $_FILES entries in Qubus\Http\Input\File:
<?php
use Qubus\Http\Input\File;
$upload = $request->handler()->file('avatar');
if ($upload instanceof File && !$upload->hasError()) {
$clientFilename = $upload->getFilename();
$clientMime = $upload->getType();
$size = $upload->getSize();
$contents = $upload->getContents();
$upload->move('/srv/app/uploads/generated-safe-name.bin');
}
The filename and MIME type originate from the client. Generate destination names yourself, enforce size limits, and inspect file contents with a trusted server-side mechanism before accepting an upload. For portable framework code, prefer the PSR-7 getUploadedFiles() API on ServerRequestInterface.
Content negotiation
<?php
if ($request->isFormatAccepted('application/json')) {
// JSON is present in Accept.
}
$formats = $request->getAcceptFormats();
These are convenient substring/list helpers rather than a full RFC quality and specificity negotiator. Applications requiring weighted negotiation should use a dedicated negotiator.