PSR7 is an attempt to reinvent and harmonize Request
and Response
proposed by PHP Framework Interop Group.
PSR7 is not a common denominator of existing HTTP libraries.
HTTP is the heart of every web project out there and nearly every php-fig member voted in favor of this PHP Standard Recommendation.
At some point you inevitable stumble over PSR7 as nearly all major PHP projects will have some sort of PSR7 support.
Considering the long development time of PSR7 it’s unlikely. Furthermore projects need even more time to readopt and release new versions. It’s of good use to engage and learn about PSR7 now.
No, PSR7 is invasive. If you work with PSR7 compliant HTTP Messages
you need to know the philosophy and ideas behind PSR7 design decisions.
In the Meta Document.
Functions without side-effects: No observable interaction with calling functions or the outside world beside returning a value.
Immutability is not used as PHP does not offer this feature. Most implementations even modify objects after creation by accessing private properties.
No, suchlike characteristics can only be implemented and enforced by external supervisor (layer/runtime/…).
Separate specialized interfaces for write methods were scrapped on the way with somewhat absurd argument about too many interfaces.
By using mutator methods defined by PSR7.
Mutator method is a method changing data. e.g.:
->withHost($host)
->setName($name)
->replaceFooWithBar($foo, $bar)
No, there is no segregation between read and write in PSR7. You have to implement them even on readonly objects. In fact readonly objects are implicitly forbidden. You could do no-op or throw exceptions but this violates LSP.
Only methods without side-effects are defined by PSR7.
public function withHeader($name, $value);
public function withoutHeader($name);
Create new instance and reassign data from old object. Cloning old object is easy and efficient way to do so:
public function withHost($host)
{
$new = clone $this;
$new->host = $host;
return $new;
}
some alternatives:
$new = clone $this;
$new = new self(...)
$new = new static(...)
$new = new MyClass(...)
Shure, other PSR7 aware projects simply ignore those.
No, changes may not affect instance in caller. You should return
the newly created object.
You can implement additional mutators non-violating interface contract. e.g. ->setHost($host);
Change your code flow. PSR7 is invading you.
function changeFoo($request, $response) {
$request = $request->withUri(new Uri('https://example.org/'));
$response = $response->withStatus(200, 'OK');
// return changes to outer scope
return array($request, $response);
}
list ($req2, $res2) = changeFoo($req, $res);
// $req, $res are unchanged
// $req2, $res2 are fooChanged
Transform your objects on project boundaries to some PSR7 implementation and vice versa. Bridge/Wrapper/Proxy/you-name-it look here and here
No, streams are a special case breaking this PSR7 convention.
A stream is defined as “data elements made available over time”.
So by definition there is a side-effect over time even for readonly
streams while read in data.
Immutable objects cannot be changed after creation at no time, so streams cannot be immutable.
Official explanation: Why are streams mutable?
No, PSR7 does not specify any way to access header values from “Chunked Trailer Parts”.
no-op instance of StreamInterface (php://memory)
Objects without explicit identity. e.g.: 20 ikea coffee cups
No way to know.
Yes, “Uniform Resource Identifier” (URI) is defined by UriInterface.
PSR7 define HTTP Messages
as on the wire
, consequently omit common methods from point-of-view of a PSR7 consumer.
So there is no way to access the final URI of a redirected response.
Yes, always implement getUri()
on all message types. URIs are solid base to identify, validate and compare http resources.
All of HTTP is unthinkable without URIs.
not NULL, empty string
No. There is no guarantee any data is available.
not NULL, empty string
NULL (not integer!)
empty string (because of bugs in arbitrary frontcontrollers losing context)
Fill in sane defaults for missing values in constructor.
default scheme: http
default host: 0.0.0.0
default port: 80
default path: /
empty string as default for all other parts
minimum valid URI:
"http://0.0.0.0/" === (new Uri())->__toString()
Yes. PSR7 does not define any methods to do so, however. Implement those methods in Uri definition:
public function originForm(); // absolute-path [ "?" query ]
public function absoluteForm(); // alias for ->__toString()
public function authorityForm(); // alias for ->getAuthority()
public function asteriskForm(); // always: *
usage:
$request = $request->withRequestTarget($uri->originForm())
For implementation details see: 5.3. Request Target
127.0.0.1
[::1]
localhost
a
foo.bar
Buzzword for “passing messages around” in contrast to “calling methods”.
There are way more definitions nor specific nor related to PSR7: MOM, 1, 2, EDA
Onion layers are not Middleware.
No, faking global state by “attributes” property is as evil as real global state. Everyone can change this attributes at any time and place. It’s abusing HTTP Request as a trashcan. Define your dependencies explicit in method signature.
Yes. Here is dependency graph:
UriInterface
methods: 16
dependencies: -
StreamInterface
methods: 15
dependencies: -
MessageInterface
methods: 11
dependencies: StreamInterface
ResponseInterface
methods: 3
dependencies: MessageInterface, StreamInterface, UriInterface
RequestInterface
methods: 6
dependencies: MessageInterface, StreamInterface, UriInterface
ServerRequestInterface
methods: 13
dependencies: MessageInterface, RequestInterface, StreamInterface, UriInterface
UploadedFileInterface
methods: 6
dependencies: StreamInterface
interface MessageInterface
* public function getProtocolVersion();
* public function withProtocolVersion($version);
* public function getHeaders();
* public function hasHeader($name);
* public function getHeader($name);
* public function getHeaderLine($name);
* public function withHeader($name, $value);
* public function withAddedHeader($name, $value);
* public function withoutHeader($name);
* public function withBody(StreamInterface $body);
* public function getBody();
interface RequestInterface extends MessageInterface
* public function getRequestTarget();
* public function withRequestTarget($requestTarget);
* public function getMethod();
* public function withMethod($method);
* public function getUri();
* public function withUri(UriInterface $uri, $preserveHost = false);
interface ServerRequestInterface extends RequestInterface
* public function getServerParams();
* public function getCookieParams();
* public function withCookieParams(array $cookies);
* public function getQueryParams();
* public function withQueryParams(array $query);
* public function getUploadedFiles();
* public function withUploadedFiles(array $uploadedFiles);
* public function getParsedBody();
* public function withParsedBody($data);
* public function getAttributes();
* public function getAttribute($name, $default = null);
* public function withAttribute($name, $value);
* public function withoutAttribute($name);
interface ResponseInterface extends MessageInterface
* public function getStatusCode();
* public function withStatus($code, $reasonPhrase = '');
* public function getReasonPhrase();
interface StreamInterface
* public function __toString();
* public function close();
* public function detach();
* public function getSize();
* public function tell();
* public function eof();
* public function isSeekable();
* public function seek($offset, $whence = SEEK_SET);
* public function rewind();
* public function isWritable();
* public function write($string);
* public function isReadable();
* public function read($length);
* public function getContents();
* public function getMetadata($key = null);
interface UriInterface
* public function getScheme();
* public function getAuthority();
* public function getUserInfo();
* public function getHost();
* public function getPort();
* public function getPath();
* public function getQuery();
* public function getFragment();
* public function withScheme($scheme);
* public function withUserInfo($user, $password = null);
* public function withHost($host);
* public function withPort($port);
* public function withPath($path);
* public function withQuery($query);
* public function withFragment($fragment);
* public function __toString();
interface UploadedFileInterface
* public function getStream();
* public function moveTo($targetPath);
* public function getSize();
* public function getError();
* public function getClientFilename();
* public function getClientMediaType();
Immutability, it turns out, has costs. High costs. (for PHP’s garbage collector)