request-interop / interface
Interoperable server request interfaces for PHP.
Installs: 45
Dependents: 1
Suggesters: 0
Security: 0
Stars: 6
Watchers: 3
Forks: 3
Open Issues: 0
pkg:composer/request-interop/interface
Requires
- php: >=8.4
- stream-interop/interface: 1.x@dev
- upload-interop/interface: 1.x@dev
- uri-interop/interface: 1.x@dev
Requires (Dev)
- pds/skeleton: ^1.0
- phpstan/phpstan: ^2.0
This package is auto-updated.
Last update: 2025-12-04 15:47:13 UTC
README
This package provides a standard set of interoperable interfaces for encapsulating readable server-side request values in PHP 8.4 or later, in order to reduce the global mutable state problems that exist with PHP superglobals. It reflects, refines, and reconciles the common practices identified within several pre-existing projects.
The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this document are to be interpreted as described in BCP 14 (RFC 2119, RFC 8174).
Interfaces
Request-Interop defines the following interfaces:
- RequestStruct represents the current request values and input stream.
- RequestStructFactory affords creating a new RequestStruct instance for the current request.
- RequestThrowable extends Throwable to mark an Exception as request-related.
- RequestTypeAliases provides custom PHPStan types to aid static analysis.
RequestStruct
The RequestStruct interface represents the current request values and input stream.
-
Directives:
- Implementations MUST retain their properties in such way that they remain independent of the superglobal arrays.
-
Notes:
-
The interface defines readable properties, not getter methods. PHP superglobals are presented as variables and not as functions; using properties instead of methods maintains symmetry with the language. In addition, using things like array access and null-coalesce against a property looks more idiomatic in PHP than with a getter method; it is the difference between
$request->query['foo'] ?? 'bar'and$request->getQuery()['foo'] ?? 'bar'or$request->query->get('foo', 'bar'). -
The interface defines property hooks for
getbut notset. The interface only guarantees readability; writability is outside the scope of this package. -
The properties and the superglobals should be decoupled from each other. For example, this means that change to
$_GETshould not result in a corresponding change to$query. This is to keep the request object free from global mutable state.
-
RequestStruct Properties
-
public request_body_array $body { get; }
-
Corresponds to a parsed array of the request body content.
-
Directives:
- Implementations SHOULD populate the property value from a copy of
the
$_POSTsuperglobal array but MAY use some other data source, such as a parsed or decoded representation of the request body.
- Implementations SHOULD populate the property value from a copy of
the
-
-
public StreamInterop\Interface\StringableStream $bodyStream { get; }
-
Corresponds to a stream of the unparsed body content.
-
Directives:
- Implementations SHOULD use
php://inputas the encapsulated resource but MAY use some other data source.
- Implementations SHOULD use
-
Notes:
- This property is a Stream-Interop StringableStream.
Although most of the researched projects use a
stringproper for the raw body content, some use a resource. A StringableStream allows for treating the content as a either a string or a resource stream.
- This property is a Stream-Interop StringableStream.
Although most of the researched projects use a
-
-
public request_cookies_array $cookies { get; }
-
Corresponds to an array of the request cookie values.
-
Directives:
- Implementations SHOULD populate the property value from a copy of
the
$_COOKIEsuperglobal array but MAY use some other data source.
- Implementations SHOULD populate the property value from a copy of
the
-
-
public request_headers_array $headers { get; }
-
Corresponds to an array of the request headers.
-
Directives:
-
Implementations SHOULD derive the property value from the
$serverarray but MAY use some other data source. -
Implementations MUST normalize each header field array key to
lower-kebab-case.
-
-
-
public request_method_string $method { get; }
-
Corresponds to the request method.
-
Directives:
- Implementations SHOULD derive the property value from the
$serverarray'REQUEST_METHOD'value but MAY use some other data source.
- Implementations SHOULD derive the property value from the
-
-
public request_query_array $query { get; }
-
Corresponds to an array of the request query values.
-
Directives:
- Implementations SHOULD populate the property value from a copy of
the
$_GETsuperglobal array but MAY use some other data source.
- Implementations SHOULD populate the property value from a copy of
the
-
Notes:
- There is no requirement to keep
$queryand$uri->queryParamsin sync. Though they may originate from the same source, their values might diverge from each other.
- There is no requirement to keep
-
-
public request_server_array $server { get; }
-
Corresponds to an array of server and execution environment values.
-
Directives:
- Implementations SHOULD populate the property value from a copy of
the
$_SERVERsuperglobal array but MAY use some other data source.
- Implementations SHOULD populate the property value from a copy of
the
-
-
public upload_structs_array $uploads { get; }
-
An array of UploadStruct instances corresponding to the uploaded files in the request.
-
Directives:
- Implementations SHOULD derive the property value from the
$_FILESsuperglobal array but MAY use some other data source.
- Implementations SHOULD derive the property value from the
-
Notes:
- This property is an Upload-Interop
upload_structs_array. Thus,$uploadstakes the place of a$_FILESsuperglobal equivalent.
- This property is an Upload-Interop
-
-
public UriInterop\Interface\UriStruct $uri { get; }
-
Corresponds to the requested URI.
-
Directives:
- Implementations SHOULD derive the property value from the
$serverarray but MAY use some other data source.
- Implementations SHOULD derive the property value from the
-
Notes:
- This property is a Uri-Interop UriStruct. Although
most of the researched projects use a
stringfor the request URI, some use an object. A UriStruct allows for treating the URI as either an object or a string.
- This property is a Uri-Interop UriStruct. Although
most of the researched projects use a
-
RequestStructFactory
The RequestStructFactory interface affords creating a new RequestStruct instance for the current request.
RequestStructFactory Methods
-
public function newRequest() : RequestStruct;
-
Creates a new RequestStruct instance representing the current request.
-
Directives:
-
Implementations SHOULD create the new RequestStruct from the superglobals and
php://inputof the current request, but MAY use some other data source. -
Implementations SHOULD catch all Throwables encountered during new RequestStruct creation.
-
Implementations SHOULD provide defaults for missing or invalid values discovered during RequestStruct creation.
-
Inplementations MAY throw a RequestThrowable on failure to create a new RequestStruct.
-
-
Notes:
-
This method should always succeed. All of the researched implementations always return a new instance, even if they have to provide default values for missing or invalid request elements.
However, Response-Interop recognizes that there may be times where catastrophic failure is appropriate, thus the allowance for throwing a RequestThrowable.
Consumers should consider failure to create a request object as deserving a 4xx ("Client Error") or 5xx ("Server Error") HTTP response code.
-
-
RequestThrowable
The RequestThrowable interface extends Throwable to mark an Exception as request-related. It adds no class members.
RequestTypeAliases
The RequestTypeAliases interface provides custom PHPStan types to aid static analysis.
-
request_cookies_array: array<string, string>- An
arrayrepresenting$_COOKIEdata.
- An
-
request_headers_array: array<lowercase-string, string>- An
arrayconsisting of a header field name string inlower-kebab-caseand the corresponding header field value string.
- An
-
request_body_array: array<array-key, null|scalar|request_body_array>- An
arrayrepresenting$_POSTdata (or other data parsed or decoded from the request body) up to 16 dimensions.
- An
-
request_method_string: uppercase-string- A
stringrepresenting the HTTP request method.
- A
-
request_query_array: array<array-key, string|request_query_array>- An
arrayrepresenting$_GETdata up to 16 dimensions.
- An
-
request_server_array: array<string, string>- The
request_server_arraytype isarray<string, string>and notarray<uppercase-string, string>. Some servers add$_SERVERkeys in mixed case; for example, Microsoft IIS addsIIS_WasUrlRewritten.
- The
-
Notes:
-
The
request_query_arraytype allows onlystring, whilerequest_body_arrayallows anyscalar. Therequest_query_arrayvalues correspond to$_GET, which is composed only of strings. However,request_body_arraycorresponds to any parsed or decoded form of the request content body; different parsing strategies, such asjson_decode(), may return various scalar types. -
The
*_[00-0F]types are to enable limited recursion. PHPStan does not handle recursive type aliases, sorequest_body_arrayandrequest_query_arraycannot ever refer back to themselves. As a result, those type aliases refer to the*_[00-0F]types to enable recursion to 16 dimensions. Consumers need not use these recursion-enabling type aliases.
-
Implementations
Implementations advertised as readonly or immutable MUST be deeply readonly or immutable. With the exception of StringableStream implementations meeting the specified readonly or immutable conditions, they MUST NOT encapsulate any references, resources, mutable objects, objects or arrays encapsulating references or resources or mutable objects, and so on.
Implementations MAY define additional class members not specified in these interfaces; implementations advertised as readonly or immutable MUST make those additional class members deeply readonly or immutable.
Notes:
-
Reflection does not invalidate advertisements of readonly or immutable implementations. The ability of a consumer to use Reflection to mutate an implementation advertised as readonly or immutable does not constitute a failure to comply with Request-Interop.
-
Reference implementations may be found at https://github.com/request-interop/impl.
Q & A
Why is RequestStruct not identical to a client-side request interface?
None of the researched projects model their request objects that way.
A more general answer is from Fowler in Patterns of Enterprise Application Architecture (2003, p 21):
... I think there is a good distinction to be made between an interface that you provide as a service to others and your use of someone else's service. ... I find it beneficial to think about these differently because the difference in clients alters the way you think about the service.
Request-Interop attempts to model an interface that uses a request received from an external source, not one that provides a request for sending.
How is Request-Interop different from PSR-7 ServerRequestInterface?
In short:
-
ServerRequestInterface attempts to model the incoming HTTP request message, plus application-specific context, with shallow and inconsistent immutability requirements.
-
Request-Interop attempts to model the PHP superglobals, provides no space for application context, and requires readonly or immutable implementations to be deeply so.
A longer answer is at README-PSR-7.md.
How is Request-Interop different from the Server-Side Request and Response Objects RFC?
This package is an intellectual descendant of that RFC, similar in form but much reduced in scope: only the superglobal-equivalent arrays, the method string, the URI, and the uploads properties remain.
Why is there a separate RequestStructFactory ?
Of the 16 researched projects, only 3 provide a separate factory class. The
remainder provide either a static factory method on the request object itself,
or use only new for creating a request object.
However, the Request-Interop request interface is modeled as a struct, meaning it can have no methods, only properties.
As such, even though the use of a factory class is decidedly the minority position, Request-Interop asserts that it is the more suitable choice here.
Further, Request-Interop opines that a separate factory interface better separates the concern of creating or building the RequestStruct using the superglobals and related environment elements.