request-interop / interface
Interoperable server request interfaces for PHP.
Requires
- php: ^8.4
Requires (Dev)
- pds/skeleton: ^1.0
- phpstan/phpstan: ^2.0
This package is auto-updated.
Last update: 2024-12-21 03:57:22 UTC
README
This package provides interoperable interfaces for reading server-side request values, 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 RFC 2119.
Interfaces
The interfaces define 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 typically idiomatic in PHP than with a getter method; it is the difference between $request->query['foo'] ?? 'bar'
and $request->getQuery()['foo'] ?? 'bar'
.
The interfaces define property hooks for get
but not set
. The intent is to guarantee only the readability of implementations; guarantees of writability are outside the scope of this package.
Request
The Request interface represents copies of the PHP superglobals (or their equivalents) and values derived from them. It defines these properties:
-
CookiesArray $cookies { get; }
corresponds to a copy of the$_COOKIES
superglobal array or its equivalent. -
FilesArray $files { get; }
corresponds to a copy of the$_FILES
superglobal array or its equivalent. -
HeadersArray $headers { get; }
corresponds to an array of the request headers, typically derived from$_SERVER
or its equivalent. Each array key MUST be the header field name in lower-kebab-case. -
InputArray $input { get; }
corresponds to a parsed or decoded array representation of the request body, typically a copy of the$_POST
superglobal array or its equivalent. -
MethodString $method { get; }
corresponds to the request method, typically derived from$_SERVER
or its equivalent. -
QueryArray $query { get; }
corresponds to an array of the request query values, typically a copy of$_GET
or its equivalent. -
ServerArray $server { get; }
corresponds to a copy of the$_SERVER
superglobal array or its equivalent. -
UploadsArray $uploads { get; }
corresponds to an array of Upload instances, typically derived from$_FILES
or its equivalent. The$uploads
index structure MUST correspond to the structure in which the uploaded files were indexed. (Cf. README-UPLOADS.md). -
Url $url { get; }
is a Url instance corresponding to this request, typically using values derived from$_SERVER
or its equivalent.
It also provides these custom PHPStan types to enable better static analysis:
-
@phpstan-type CookiesArray array<string, string>
-
@phpstan-type FilesArray array<array-key, FilesArray|FilesArrayItem|FilesArrayGroup>
(to 16 levels deep) -
@phpstan-type HeadersArray array<lowercase-string, string>
-
@phpstan-type InputArray array<array-key, null|scalar|InputArray>
(to 16 levels deep) -
@phpstan-type MethodString uppercase-string
-
@phpstan-type QueryArray array<string, string|QueryArray>
(to 16 levels deep) -
@phpstan-type ServerArray array<string, string>
-
@phpstan-type UploadsArray array<array-key, Upload|UploadsArray>
(to 16 levels deep)
Notes:
-
The
$method
property is a string and not a Method interface. Usually the reason for a Method interface is to defineis(string $method) : bool
to make sure the comparison values use matching cases. However, the customMethodString
type isuppercase-string
, which means static analysis should catch mismatched casing. -
The
$query
property allows onlystring
, while$input
allows anyscalar
. The$query
property corresponds to$_GET
, which is composed only of strings. However,$input
corresponds to any parsed or decoded form of the request content body; different parsing strategies, such asjson_decode()
, may return various scalar types. -
ServerArray
is composed ofarray<string, string>
and notarray<uppercase-string, string>
. Some servers add$_SERVER
keys in mixed case. For example, Microsoft IIS addsIIS_WasUrlRewritten
.
Url
The Url interface represents the URL of the request. It defines these properties and methods:
-
?string $scheme { get; }
corresponds to thehost
key fromparse_url()
. -
?string $host { get; }
corresponds to thehost
key fromparse_url()
. -
?int $port { get; }
corresponds to theport
key fromparse_url()
. -
?string $user { get; }
corresponds to theuser
key fromparse_url()
. -
?string $pass { get; }
corresponds to thepass
key fromparse_url()
. -
?string $path { get; }
corresponds to thepath
key fromparse_url()
. -
?string $query { get; }
corresponds to thequery
key fromparse_url()
. -
?string $fragment { get; }
corresponds to thefragment
key fromparse_url()
. -
__toString() : string
returns the full URL as a string.
Notes:
- This is a Url interface, not a Uri interface. This is because the protocol is intended to be included in the properties. Cf. The Real Difference Between a URL and a URI: "A URL is a more specific version of a URI, so if the protocol is given or implied you should probably use URL."
Upload
The Upload interface represents a single uploaded file. It defines these properties and methods:
-
?string $name { get; }
corresponds to the'name'
key in a$_FILES
element (or its equivalent). -
?string $fullPath { get; }
corresponds to the'full_path'
key in a$_FILES
element (or its equivalent). -
?string $type { get; }
corresponds to the'type'
key in a$_FILES
element (or its equivalent). -
?string $tmpName { get; }
corresponds to the'tmp_name'
key in a$_FILES
element (or its equivalent). -
?int $size { get; }
corresponds to the'size'
key in a$_FILES
element (or its equivalent). -
?int $error { get; }
corresponds to the'error'
key in a$_FILES
element (or its equivalent). -
move(string $to) : bool
moves the uploaded file to another location, typically viamove_uploaded_file()
.
(Cf. https://www.php.net/manual/en/features.file-upload.post-method.php.)
Body
The Body interface represents the raw content of a Request or an Upload. It defines these properties and methods:
-
?BodyResource $body { get; }
is a stream resource of the raw content; for a Request, this SHOULD refer tophp://input
but MAY refer to some other stream, whereas for an Upload it SHOULD refer to the$tmpName
property but MAY refer to some other stream. -
__toString() : string
MUST return the entire$body
resource as a string.
It also provides this custom PHPStan type to enable better static analysis:
@phpstan-type BodyResource resource of type (stream)
Implementations of Body MUST NOT be advertised as readonly or immutable. As a consequence, any implementation of Request or Upload that also implements Body MUST NOT be advertised as readonly or immutable.
The Body interface MAY be implemented independently from a Request or Upload.
Notes:
-
The Body interface is separated from the other interfaces. Whereas readonly or immutable Request and Upload objects can be implemented easily, readonly and immutability on a stream resource is (practically speaking) so difficult to achieve as to be impossible. Thus, implementors who want a truly readonly or immutable Request or Upload can do so, though without access to the Body as a resource. Implementors who need access to a Body can implement it as part of a mutable Request or Upload. Alternatively, it can be an independent mutable Body alongside (but separate from) a readonly or immutable Request or Upload.
-
The
$body
resource might be manipulated externally. As with any stream resource, the state of the$body
resource is mutable. Consumers might modify it, close it, leave the pointer in an unexpected location, and so on. This is why Body implementations MUST NOT be advertised as readonly or immutable.
Implementations
Implementations advertised as readonly or immutable MUST be deeply readonly or immutable; they MUST NOT encapsulate any references, resources, mutable objects, objects or arrays encapsulating references or resources or mutable objects, and so on.
Implementations MAY contain properties and methods not defined in these interfaces.
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 RequstInterop.
-
Reference implementations are to be found at https://github.com/pmjones/request-interop-impl.
Q & A
What userland projects were used as reference points for RequestInterop?
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 RequestInterop different from PSR-7?
The short answer is that PSR-7 attempts to model HTTP messages, whereas RequestInterop attempts to model the PHP superglobals. A longer answer is at README-PSR-7.md.
How is RequestInterop 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 array properties remain. (Notably, the URL array is now a Url interface.)