Professional Documents
Culture Documents
1 de 22
http://www.php-fig.org/psr/psr-7/meta/
PHP-FIG
Home
Recommendations (PSRs)
Members
Bylaws
FAQs
Get Involved
PSR-7: HTTP Message Meta Document
1. Summary
The purpose of this proposal is to provide a set of common interfaces for
HTTP messages as described in RFC 7230 and RFC 7231, and URIs as described
in RFC 3986 (in the context of HTTP messages).
RFC 7230: http://www.ietf.org/rfc/rfc7230.txt
RFC 7231: http://www.ietf.org/rfc/rfc7231.txt
RFC 3986: http://www.ietf.org/rfc/rfc3986.txt
All HTTP messages consist of the HTTP protocol version being used, headers,
and a message body. A Request builds on the message to include the HTTP
method used to make the request, and the URI to which the request is made.
A Response includes the HTTP status code and reason phrase.
In PHP, HTTP messages are used in two contexts:
To send an HTTP request, via the ext/curl extension, PHP's native stream
layer, etc., and process the received HTTP response. In other words, HTTP
messages are used when using PHP as an HTTP client.
To process an incoming HTTP request to the server, and return an HTTP
response to the client making the request. PHP can use HTTP messages
when used as a server-side application to fulfill HTTP requests.
This proposal presents an API for fully describing all parts of the various HTTP
2 de 22
http://www.php-fig.org/psr/psr-7/meta/
3 de 22
http://www.php-fig.org/psr/psr-7/meta/
developers.
On the response side of the equation, PHP was originally developed as a
templating language, and allows intermixing HTML and PHP; any HTML
portions of a file are immediately flushed to the output buffer. Modern
applications and frameworks eschew this practice, as it can lead to issues
with regards to emitting a status line and/or response headers; they tend to
aggregate all headers and content, and emit them at once when all other
application processing is complete. Special care needs to be paid to ensure
that error reporting and other actions that send content to the output buffer
do not flush the output buffer.
3. Why Bother?
HTTP messages are used in a wide number of PHP projects -- both clients and
servers. In each case, we observe one or more of the following patterns or
situations:
1. Projects use PHP's superglobals directly.
2. Projects will create implementations from scratch.
3. Projects may require a specific HTTP client/server library that provides
HTTP message implementations.
4. Projects may create adapters for common HTTP message
4 de 22
http://www.php-fig.org/psr/psr-7/meta/
implementations.
As examples:
1. Just about any application that began development before the rise of
frameworks, which includes a number of very popular CMS, forum, and
shopping cart systems, have historically used superglobals.
2. Frameworks such as Symfony and Zend Framework each define HTTP
components that form the basis of their MVC layers; even small, singlepurpose libraries such as oauth2-server-php provide and require their
own HTTP request/response implementations. Guzzle, Buzz, and other
HTTP client implementations each create their own HTTP message
implementations as well.
3. Projects such as Silex, Stack, and Drupal 8 have hard dependencies on
Symfony's HTTP kernel. Any SDK built on Guzzle has a hard requirement
on Guzzle's HTTP message implementations.
4. Projects such as Geocoder create redundant adapters for common
libraries.
Direct usage of superglobals has a number of concerns. First, these are
mutable, which makes it possible for libraries and code to alter the values,
and thus alter state for the application. Additionally, superglobals make unit
and integration testing difficult and brittle, leading to code quality
degradation.
In the current ecosystem of frameworks that implement HTTP message
abstractions, the net result is that projects are not capable of interoperability
or cross-pollination. In order to consume code targeting one framework from
another, the first order of business is building a bridge layer between the
HTTP message implementations. On the client-side, if a particular library
does not have an adapter you can utilize, you need to bridge the
5 de 22
http://www.php-fig.org/psr/psr-7/meta/
4. Scope
4.1 Goals
Provide the interfaces needed for describing HTTP messages.
Focus on practical applications and usability.
Define the interfaces to model all elements of the HTTP message and URI
specifications.
6 de 22
http://www.php-fig.org/psr/psr-7/meta/
Ensure that the API does not impose arbitrary limits on HTTP messages.
For example, some HTTP message bodies can be too large to store in
memory, so we must account for this.
Provide useful abstractions both for handling incoming requests for
server-side applications and for sending outgoing requests in HTTP
clients.
4.2 Non-Goals
This proposal does not expect all HTTP client libraries or server-side
frameworks to change their interfaces to conform. It is strictly meant for
interoperability.
While everyone's perception of what is and is not an implementation
detail varies, this proposal should not impose implementation details. As
RFCs 7230, 7231, and 3986 do not force any particular implementation,
there will be a certain amount of invention needed to describe HTTP
message interfaces in PHP.
5. Design Decisions
Message design
The MessageInterface provides accessors for the elements common to all
HTTP messages, whether they are for requests or responses. These elements
include:
HTTP protocol version (e.g., "1.0", "1.1")
HTTP headers
HTTP message body
More specific interfaces are used to describe requests and responses, and
7 de 22
http://www.php-fig.org/psr/psr-7/meta/
more specifically the context of each (client- vs. server-side). These divisions
are partly inspired by existing PHP usage, but also by other languages such as
Ruby's Rack, Python's WSGI, Go's http package, Node's http module, etc.
Why does the request interface have methods for dealing with the
request-target AND compose a URI?
RFC 7230 details the request line as containing a "request-target". Of the four
forms of request-target, only one is a URI compliant with RFC 3986; the most
common form used is origin-form, which represents the URI without the
scheme or authority information. Moreover, since all forms are valid for
19/06/2016 06:44 p.m.
8 de 22
http://www.php-fig.org/psr/psr-7/meta/
target, allowing users to create requests that use one of the other valid
request-target forms.
The URI is kept as a discrete member of the request for a variety of reasons.
For both clients and servers, knowledge of the absolute URI is typically
required. In the case of clients, the URI, and specifically the scheme and
authority details, is needed in order to make the actual TCP connection. For
server-side applications, the full URI is often required in order to validate the
request or to route to an appropriate handler.
9 de 22
http://www.php-fig.org/psr/psr-7/meta/
=> 'application/json',
]);;
$request = $baseRequest->withUri($uri->withPath('/user'))->withMethod('GET');
$response = $client->send($request);
10 de 22
http://www.php-fig.org/psr/psr-7/meta/
$response = $client->send($request)
11 de 22
http://www.php-fig.org/psr/psr-7/meta/
12 de 22
http://www.php-fig.org/psr/psr-7/meta/
This design decision was made so that developers can send and receive
(and/or receive and send) HTTP messages that contain more data than can
practically be stored in memory while still allowing the convenience of
interacting with message bodies as a string. While PHP provides a stream
abstraction by way of stream wrappers, stream resources can be
cumbersome to work with: stream resources can only be cast to a string
using stream_get_contents() or manually reading the remainder of a string.
Adding custom behavior to a stream as it is consumed or populated requires
registering a stream filter; however, stream filters can only be added to a
stream after the filter is registered with PHP (i.e., there is no stream filter
autoloading mechanism).
The use of a well- defined stream interface allows for the potential of flexible
stream decorators that can be added to a request or response pre-flight to
enable things like encryption, compression, ensuring that the number of
bytes downloaded reflects the number of bytes reported in the ContentLength of a response, etc. Decorating streams is a well-established pattern in
the Java and Node communities that allows for very flexible streams.
The majority of the StreamInterface API is based on Python's io module,
which provides a practical and consumable API. Instead of implementing
stream capabilities using something like a WritableStreamInterface and
ReadableStreamInterface , the capabilities of a stream are provided by
13 de 22
http://www.php-fig.org/psr/psr-7/meta/
readfile($filename);
Note that the above omits sending appropriate Content-Type and ContentLength headers; the developer would need to emit these prior to calling the
above code.
The equivalent using HTTP messages would be to use a StreamInterface
implementation that accepts a filename and/or stream resource, and to
provide this to the response instance. A complete example, including setting
appropriate headers:
= new Stream($filename);
$finfo
= new finfo(FILEINFO_MIME);
$response = $response
->withHeader('Content-Type', $finfo->file($filename))
->withHeader('Content-Length', (string) filesize($filename))
->withBody($stream);
14 de 22
http://www.php-fig.org/psr/psr-7/meta/
optimization or when emitting large data sets. If it needs to be done and you
still wish to work in an HTTP message paradigm, one approach would be to
use a callback-based StreamInterface implementation, per this example.
Wrap any code emitting output directly in a callback, pass that to an
appropriate StreamInterface implementation, and provide it to the message
body:
15 de 22
http://www.php-fig.org/psr/psr-7/meta/
has been updated, any instance that wraps that stream will also be updated
-- making immutability impossible to enforce.
Our recommendation is that implementations use read-only streams for
server-side requests and client-side responses.
Access to the parsed body (i.e., data deserialized from the incoming
request body; in PHP, this is typically the result of POST requests using
application/x-www-form-urlencoded content types, and encapsulated in the
$_POST superglobal, but for non-POST, non-form-encoded data, could be
an array or an object).
Access to uploaded files (encapsulated in PHP via the $_FILES
superglobal).
Access to cookie values (encapsulated in PHP via the $_COOKIE
superglobal).
16 de 22
http://www.php-fig.org/psr/psr-7/meta/
Access to attributes derived from the request (usually, but not limited to,
those matched against the URL path).
Uniform access to these parameters increases the viability of interoperability
between frameworks and libraries, as they can now assume that if a request
implements ServerRequestInterface , they can get at these values. It also
solves problems within the PHP language itself:
Until 5.6.0, php://input was read-once; as such, instantiating multiple
request instances from multiple frameworks/libraries could lead to
inconsistent state, as the first to access php://input would be the only
one to receive the data.
Unit testing against superglobals (e.g., $_GET , $_FILES , etc.) is difficult
and typically brittle. Encapsulating them inside the
ServerRequestInterface implementation eases testing considerations.
$foo = isset($request->getBodyParams()['foo'])
? $request->getBodyParams()['foo']
: null;
17 de 22
http://www.php-fig.org/psr/psr-7/meta/
The argument for using "parsed body" was made by examining the domain. A
message body can contain literally anything. While traditional web
applications use forms and submit data using POST, this is a use case that is
quickly being challenged in current web development trends, which are often
API centric, and thus use alternate request methods (notably PUT and
PATCH), as well as non-form-encoded content (generally JSON or XML) that
can be coerced to arrays in many cases, but in many cases also cannot or
should not.
If forcing the property representing the parsed body to be only an array,
developers then need a shared convention about where to put the results of
parsing the body. These might include:
A special key under the body parameters, such as __parsed__ .
A special named attribute, such as __body__ .
The end result is that a developer now has to look in multiple locations:
$data = $request->getBodyParams();
if (isset($data['__parsed__']) && is_object($data['__parsed__'])) {
$data = $data['__parsed__'];
}
// or:
$data = $request->getBodyParams();
if ($request->hasAttribute('__body__')) {
$data = $request->getAttribute('__body__');
}
18 de 22
http://www.php-fig.org/psr/psr-7/meta/
$data = $request->getParsedBody();
if (! $data instanceof \stdClass) {
// raise an exception!
}
// otherwise, we have what we expected
19 de 22
http://www.php-fig.org/psr/psr-7/meta/
20 de 22
http://www.php-fig.org/psr/psr-7/meta/
6. People
6.1 Editor(s)
21 de 22
http://www.php-fig.org/psr/psr-7/meta/
6.2 Sponsors
Paul M. Jones
Beau Simensen (coordinator)
6.3 Contributors
Michael Dowling
Larry Garfield
Evert Pot
Tobias Schultze
Bernhard Schussek
Anton Serdyuk
Phil Sturgeon
Chris Wilkinson
Additional Info:
PSR-7: HTTP message interfaces
PSR-7: HTTP Message Meta Document
Follow us on Twitter
22 de 22
http://www.php-fig.org/psr/psr-7/meta/