request-interop/interface

Interoperable server request interfaces for PHP.

1.x-dev 2025-06-09 18:21 UTC

This package is auto-updated.

Last update: 2025-06-09 18:22:02 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 and refines the common practices of over a dozen different userland 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:

Request-Interop also defines a marker interface, RequestThrowable, for marking an Exception as request-related.

Finally, Request-Interop defines a RequestTypeAliases interface with PHPStan types to aid static analysis.

Notes:

RequestStruct

The RequestStruct interface represents copies of the PHP superglobals (or their equivalents) and values derived from them. It defines these properties:

  • StringableStream $body { get; }

    • Corresponds to the raw request content.
    • The encapsulated resource SHOULD be php://input.
  • cookies_array $cookies { get; }

    • Corresponds to a copy of the $_COOKIES superglobal array or its equivalent.
  • files_array $files { get; }

    • Corresponds to a copy of the $_FILES superglobal array or its equivalent.
  • headers_array $headers { get; }

    • Corresponds to an array of the request headers.
    • The values SHOULD be derived from the $_SERVER superglobal array or its equivalent.
    • Each array key MUST be the header field name in lower-kebab-case.
  • input_array $input { get; }

    • Corresponds to an array of the request body values.
    • The values SHOULD be a copy of the $_POST superglobal array or its equivalent.
    • The values MAY be derived from a parsed or decoded representation of the request body.
  • method_string $method { get; }

    • Corresponds to the request method.
    • The value SHOULD be derived from the $_SERVER superglobal array or its equivalent.
  • query_array $query { get; }

    • Corresponds to an array of the request query values.
    • The values SHOULD be a copy of the $_GET superglobal array or its equivalent.
  • server_array $server { get; }

    • Corresponds to a copy of the $_SERVER superglobal array or its equivalent.
  • uploads_array $uploads { get; }

    • An array of [UploadStruct][] instances.
    • The values SHOULD be derived from the $_FILES superglobal array or its equivalent.
  • RequestUrlStruct $uri { get; }

    • Corresponds to the requested URI.
    • The values SHOULD be derived from the $_SERVER superglobal array or its equivalent.

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 interfaces defines property hooks for get but not set. The interface only guarantees readability; writability is outside the scope of this package.

  • There is no requirement to keep $query and $uri->queryParams in sync. Though they may originate from the same source, their values might diverge from each other.

  • The $body property is a Stream-Interop StringableStream. This affords idempotent reading from the raw content of a RequestStruct.

  • The $uploads property is an Upload-Interop [uploads_array][].

RequestStructFactory

The RequestStructFactory affords one creation method.

  • newRequest() returns a new RequestStruct instance:

    public function newRequest(
        ?StringableStream $body = null,
        ?cookies_array $cookies = null,
        ?files_array $files = null,
        ?headers_array $headers = null,
        ?input_array $input = null,
        ?method_string $method = null,
        ?query_array $query = null,
        ?server_array $server = null,
        ?uploads_array $uploads = null,
        ?RequestUrlStruct $uri = null,
    ) : Request;

Notes:

  • All newRequest() arguments are optional. The arguments are intended to override whatever defaults the implementation may provide; i.e., providing no arguments SHOULD return the default implementation RequestStruct, such as one created from the superglobals.

RequestThrowable

The RequestThrowable interface extends Throwable to mark an Exception as request-related. It adds no new class members.

RequestTypeAliases

The RequestTypeAliases interface provides these custom PHPStan types to aid static analysis:

  • cookies_array: array<string, string>

  • headers_array: array<lowercase-string, string>

  • input_array: array<array-key, null|scalar|input_array> recursively up to 16 dimensions.

  • method_string: uppercase-string

  • query_array: array<array-key, string|query_array> recursively up to 16 dimensions.

  • server_array: array<string, string>

Notes:

  • The method_string is not a Method interface. Usually the reason for a Method interface is to define is(string $method) : bool to make sure the comparison values use matching cases. However, the custom method_string type is uppercase-string, which means static analysis should catch mismatched casing.

  • The query_array type allows only string, while input_array allows any scalar. The query_array values correspond to $_GET, which is composed only of strings. However, input_array corresponds to any parsed or decoded form of the request content body; different parsing strategies, such as json_decode(), may return various scalar types.

  • The server_array type is array<string, string> and not array<uppercase-string, string>. Some servers add $_SERVER keys in mixed case. For example, Microsoft IIS adds IIS_WasUrlRewritten.

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

What userland projects were used as reference points for Request-Interop?

The pre-PSR-7 versions of Aura, Cake, Code Igniter, Horde, Joomla, Klein, Lithium, MediaWiki, Nette, Phalcon, Symfony, Yaf, Yii, and Zend. See this project comparison for more information.

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 URL, and the uploads properties remain.