Http Request
Purpose
Quantum\Http\Request is Quantum's mutable snapshot of the current HTTP request.
It combines server-derived metadata with helper methods for:
- request body values
- headers
- files
- query strings and URL segments
- matched-route metadata
- synthetic internal requests for testing-style flows
Method and URL state
The request object stores:
- method
- protocol
- host
- port
- URI
- raw query string
setMethod() validates against Request::METHODS and throws HttpException for unsupported methods.
Supported methods are currently limited to:
GETPOSTPUTPATCHDELETE
isMethod() compares case-insensitively, so callers can safely check isMethod('post') even if the stored value is uppercase.
Base URL and current URL
getBaseUrl(bool $withModulePrefix = false) first reads config()->get('app.base_url').
If that config value exists, it returns that base URL and optionally appends the matched route prefix.
If the config value is empty, it falls back to a host prefix built from protocol, host, and port.
Port handling is conservative:
- port
80is omitted forhttp - port
443is omitted forhttps - any other port is included
getCurrentUrl() always uses the current protocol/host/port snapshot plus the current URI and raw query string.
Body parameter access
The request body store lives in an internal array and is accessed with:
has($key)get($key, $default = null, $raw = false)set($key, $value)all()delete($key)
Sanitization contract
get() strips HTML tags by default.
- scalar values are passed through
strip_tags() - array values are transformed with
array_map('strip_tags', $value) - raw values are returned unchanged only when
$raw = true
That means get() is not a pure retrieval API. It performs output-oriented sanitization unless you opt out.
Reserved key guard
set() rejects the reserved key stored in Quantum\App\Enums\ReservedKeys::RENDERED_VIEW.
Trying to write that key raises InvalidArgumentException.
all() behavior
all() returns merged request parameters plus uploaded files.
If a key exists in both payload data and files, the later merge from the files array wins.
Input parsing and precedence
setRequestParams() rebuilds the request payload by merging five sources in order:
getParams()from$_GETpostParams()from$_POSTjsonPayloadParams()urlEncodedParams()- explicit parsed params passed into
setRequestParams()
This makes raw multipart parameters the highest-precedence source during normal bootstrap.
JSON payload parsing
JSON parsing runs only when both conditions match:
- method is
PUT,PATCH, orPOST - normalized content type is exactly
application/json
Invalid JSON quietly becomes an empty array because json_decode(..., true) ?: [] is used.
URL-encoded raw body parsing
URL-encoded raw body parsing uses the same method gate and requires content type application/x-www-form-urlencoded.
The implementation applies urldecode() before parse_str(). Callers get the parsed array only; there is no separate raw representation.
Multipart raw parsing
Raw multipart parsing runs only when both conditions match:
- method is
PUT,PATCH, orPOST - normalized content type is exactly
multipart/form-data
The parser extracts the multipart boundary from server()->contentType() and then processes each block as either:
- a normal parameter
- a file part
- an octet-stream field
Notable details from the parser:
- fields named like
tags[]are accumulated into arrays - file parts are written to temporary files under the system temp directory
- those temp files are registered for removal with
register_shutdown_function() - file parts with empty content are skipped
- array-like file names such as
photos[]orphotos[cover]become nested file collections
Uploaded files
setUploadedFiles() merges normalized $_FILES with parsed raw multipart files.
handleFiles() converts each entry into Quantum\Storage\UploadedFile objects.
It supports:
- single uploads -> one
UploadedFile - multi-file top-level arrays ->
array<UploadedFile>
hasFile($key) returns true only when the key exists and every uploaded file under that key has UPLOAD_ERR_OK.
getFile($key) throws FileUploadException::fileNotFound(...) when that check fails.
Header access
Request headers are stored with lowercase keys, but lookup is tolerant.
hasHeader() and getHeader() normalize the requested key into both hyphen and underscore forms, so these lookups are treated as equivalent:
Content-Typecontent-typeCONTENT_TYPE
Authorization helpers
getAuthorizationBearer() returns the token only when the Authorization header matches Bearer <token>.
getBasicAuthCredentials() prefers server variables PHP_AUTH_USER and PHP_AUTH_PW. If those are missing, it falls back to decoding a Basic <base64> authorization header.
Invalid or non-decodable Basic auth values return null.
AJAX and referrer helpers
isAjax() returns true when either of these is true:
- header
X-REQUESTED-WITHexists server()->ajax()returns true
getReferrer() simply proxies server()->referrer().
Query string helpers
The query helpers work on the raw stored query string, not on a decoded map.
getQueryParam($key):
- splits on
& - splits each pair on
= - returns the first matching raw value
- does not URL-decode the value
setQueryParam($key, $value) always appends a new key=value pair.
It does not replace existing keys and does not encode the value.
URI segments
getUri() stores the request URI without a leading slash.
getAllSegments() parses the URI path, trims outer slashes, and prepends a sentinel element:
['zero_segment', ...actualSegments]
That means segment access is effectively 1-based for real path parts.
getSegment(1) returns the first real path segment.
If the URI is null, getAllSegments() returns ['zero_segment'].
CSRF token lookup
getCsrfToken() checks the request payload first and then the header X-<TOKEN_KEY>.
The first match wins.
Route metadata access
The request can hold a Quantum\Router\MatchedRoute instance.
From that object, the package exposes read helpers for:
- current middlewares
- current module
- current controller
- current action
- route closure callback
- original route pattern
- compiled route pattern
- route parameters
- route cache settings
- route name
- route prefix
If no route has been set, these helpers return null or [] depending on the method.
Route collection lookups
findRouteByName($name, $module) and routeGroupExists($name, $module) only work when RouteCollection::class has already been registered in DI.
If not, they return null or false instead of constructing anything.
Both lookups are case-insensitive for the requested name and module.
Module namespace helper
getModuleBaseNamespace() hardcodes two environments:
- testing ->
Quantum\\Tests\\_root\\modules - everything else ->
Modules
Internal request creation
create($method, $url, $params = [], $headers = [], $files = []) rewrites the shared server state and rebuilds the request object from it.
It is intended for internal or testing-style request simulation.
The method performs these steps:
- parses the target URL
- flushes the shared server object
- writes request method, URI, scheme, host, port, and query into that server object
- infers content type from the presence of params or files
- writes custom headers as
HTTP_*server entries - flushes the request object's stored headers/body/files/URL pieces
- repopulates the request from the rewritten server state
- optionally overwrites params and files with the explicit arrays passed in
Content-type inference is simple:
- files present ->
multipart/form-data - params present ->
application/x-www-form-urlencoded - neither ->
text/html
Flush behavior
flush() clears:
- headers
- request payload
- files
- protocol
- host
- port
- URI
- query string
It is not a full reset helper. It does not clear the matched route property directly.