8000 GitHub - koriym/interface: Interoperable server request interfaces for PHP.
[go: up one dir, main page]
More Web Proxy on the site http://driver.im/
Skip to content

Interoperable server request interfaces for PHP.

License

Notifications You must be signed in to change notification settings

koriym/interface

 
 

Repository files navigation

RequestInterop Interface Package

This package provides 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

This package defines the following interfaces:

  • Request to represent the incoming request.
  • Upload to represent an uploaded file.
  • Url to represent the request URL.
  • Body to represent mutable request or upload body content.
  • Factory to create instances of the above.

Notes:

  • 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 usually 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 define property hooks for get but not set. The interfaces only guarantee readability; writability is 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, usually derived from $_SERVER or its equivalent. Each array key MUST be the header field name in lower-kebab-case.

  • InputArray $input { get; } corresponds to an array of the request body values, usually a copy of the $_POST superglobal array or its equivalent (such as a parsed or decoded representation of the request body).

  • MethodString $method { get; } corresponds to the request method, usually derived from $_SERVER or its equivalent.

  • QueryArray $query { get; } corresponds to an array of the request query values, usually 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; } is an array of Upload instances, usually 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, usually derived from $_SERVER or its equivalent.

It also provides these custom PHPStan types to aid static analysis:

  • CookiesArray: array<string, string>

  • FilesArray: mixed[] -- Implementations MUST honor this mixed[] type as the recursive pseudo-type array<array-key, FilesArrayGroup|FilesArrayItem|FilesArray>.

  • FilesArrayGroup:

    array{
        tmp_name:string[],
        error:int[],
        name?:string[],
        full_path?:string[],
        type?:string[],
        size?:int[],
    }
    
  • FilesArrayItem:

    array{
        tmp_name:string,
        error:int,
        name?:string,
        full_path?:string,
        type?:string,
        size?:int,
    }
    
  • HeadersArray: array<lowercase-string, string>

  • InputArray: mixed[] -- Implementations MUST honor this mixed[] type as the recursive pseudo-type <array-key, null|scalar|InputArray>.

  • MethodString: uppercase-string

  • QueryArray: mixed[] -- Implementations MUST honor this mixed[] type as the recursive pseudo-type <array-key, string|QueryArray>.

  • ServerArray: array<string, string>

  • UploadsArray: mixed[] -- Implementations MUST honor this mixed[] type as the recursive pseudo-type array<array-key, Upload|UploadsArray>.

Notes:

  • The $method property is a string and 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 MethodString type is uppercase-string, which means static analysis should catch mismatched casing.

  • The FilesArray, InputArray, QueryArray, and UploadsArray types are mixed[] only because they are recursive. Currently, static analysis tools such as PHPStan cannot process recursive types. Implementations MUST honor these mixed[] types as the more strict, but not analyzable, recursive pseudo-type provided with their respective type descriptions.

  • The QueryArray type allows only string, while InputArray allows any scalar. The QueryArray values correspond to $_GET, which is composed only of strings. However, InputArray 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 ServerArray 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.

Url

The Url interface represents the URL of the request. It defines these properties and methods:

  • ?string $scheme { get; } corresponds to the scheme key from parse_url().

  • ?string $host { get; } corresponds to the host key from parse_url().

  • ?int $port { get; } corresponds to the port key from parse_url().

  • ?string $user { get; } corresponds to the user key from parse_url().

  • ?string $pass { get; } corresponds to the pass key from parse_url().

  • ?string $path { get; } corresponds to the path key from parse_url().

  • ?string $query { get; } corresponds to the query key from parse_url().

  • ?string $fragment { get; } corresponds to the fragment key from parse_url().

  • __toString() : string returns the full URL as a string.

It also provides this custom PHPStan type to aid static analysis:

  • UrlArray:

    array{
        scheme:?string,
        user:?string,
        pass:?string,
        host:?string,
        port:?int,
        path:?string,
        query:?string,
        fragment:?string
    }
    

Notes:

  • This is a Url interface, not a Uri interface. This is because the protocol (i.e., the $scheme) 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."

  • The Url properties, and the UrlArray elements, are taken from the parse_url() array structure.

Upload

The Upload interface represents a single uploaded file. It defines these properties and methods:

  • string $tmpName { get; } corresponds to the 'tmp_name' key in a FilesArrayItem (usually from $_FILES).

  • int $error { get; } corresponds to the 'error' key in a FilesArrayItem (usually from $_FILES).

  • ?string $name { get; } corresponds to the 'name' key in a FilesArrayItem (usually from $_FILES).

  • ?string $fullPath { get; } corresponds to the 'full_path' key in a FilesArrayItem (usually from $_FILES).

  • ?string $type { get; } corresponds to the 'type' key in a FilesArrayItem (usually from $_FILES).

  • ?int $size { get; } corresponds to the 'size' key in a FilesArrayItem (usually from $_FILES).

  • move(string $to) : bool moves the uploaded file to another location, usually via move_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 to php://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 aid static analysis:

  • BodyResource: resource of type (stream)

Implementations of Body MUST NOT be advertised as readonly or immutable. Thus, 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.

Factory

The Factory interface defines the following methods.

  • newRequest() returns a new Request instance:

    /**
     * @param ?CookiesArray $cookies
     * @param ?FilesArray $files
     * @param ?HeadersArray $headers
     * @param ?InputArray $input
     * @param ?InputArray $input
     * @param ?MethodString $method
     * @param ?QueryArray $query
     * @param ?ServerArray $server
     * @param ?UploadsArray $uploads
     * @param ?BodyResource $body
     * @return Request|(Request&Body)
     */
    public function newRequest(
        ?array $cookies = null,
        ?array $files = null,
        ?array $headers = null,
        ?array $input = null,
        ?string $method = null,
        ?array $query = null,
        ?array $server = null,
        ?array $uploads = null,
        ?Url $url = null,
        mixed $body = null,
    ) : Request;
  • newUpload() returns a new Upload instance:

    /**
     * @param ?BodyResource $body
     * @return Upload|(Upload&Body)
     */
    public function newUpload(
        string $tmpName,
        int $error,
        ?string $name = null,
        ?string $fullPath = null,
        ?string $type = null,
        ?int $size = null,
        mixed $body = null,
    ) : Upload;
  • newUrl() returns a new Url instance:

    public function newUrl(
        ?string $scheme = null,
        ?string $host = null,
        ?int $port = null,
        ?string $user = null,
        ?string $pass = null,
        ?string $path = null,
        ?string $query = null,
        ?string $fragment = null,
    ) : Url;
  • newBody() returns a new independent Body instance:

    /**
     * @param BodyResource $body
     */
    public function newBody(mixed $body) : Body;

    Implementations otherwise advertised as readonly or immutable SHOULD throw a BadMethodCallException for this method, but MAY return an independent Body implementation advertised as mutable.

Notes:

  • All newRequest() and newUrl() 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 object, such as one created from the superglobals.

  • The first two newUpload() arguments are required. An Upload MUST have at least a $tmpName and an $error code; all other values are optional.

  • The newBody() method MUST NOT return an implementation advertised as readonly or immutable. Whereas readonly or immutable implementations of Request and Upload are not allowed to implement Body, a separate Body implementation is allowed, so long as it is advertised as mutable. Thus, factories for otherwise readonly or immutable implementations are allowed to return an independent mutable Body implementation.

  • The newBody() method $body parameter is not nullable. An independent Body implementation is not expected to have a default resource to draw from.

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 additional properties and methods not defined in these interfaces; implementations advertised as readonly or immutable MUST make those additional elements 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 RequestInterop.

  • Reference implementations may be found at https://github.com/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 ServerRequestInterface?

In short:

  • ServerRequestInterface attempts to model the incoming HTTP request message, plus application-specific context, with shallow and inconsistent immutability requirements.

  • RequestInterop attempts to model the PHP superglobals, provides no space for application context, and requires that readonly or immutable implementations to be deeply so.

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.)


About

Interoperable server request interfaces for PHP.

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages

  • PHP 100.0%
0