The last 72 hours have been interesting to say the least.My rebuttal to Anthony’s post have lead to some very interesting conversations. Among these is Andrew Carter’s post entitled PSR-7 Objects Are Not Immutable in which he details how an exception handler middleware can generate a bad response body, if the body was already written to.
This is because the PSR-7 StreamInterface is not immutable. As per the specification:
Unlike the request and response interfaces, StreamInterface does not model immutability. In situations where an actual PHP stream is wrapped, immutability is impossible to enforce, as any code that interacts with the resource can potentially change its state (including cursor position, contents, and more).
The Dependency Problem
If we look at the code in question we see that Expressive writes directly to the body, rather than replacing it, effectively:
Herein lies our dependency problem: there is no way to create a new stream instance unless we rely on a concrete implementation . The
StreamInterface is mutable and has no method to create a new instance from some content. The
MessageInterface::withBody() method requires that we pass it a stream. If we rely only on
psr/http-message and allow the user to choose the implementation, we have no option other than to write directly to the existing stream.
Andrew states that this is a problem with PSR-7 and I don’t necessarily disagree. The technical problem with streams is impossible to solve, which is why the FIG ended up with one mutable interface when all others are immutable. We cannot solve this problem directly, because that’s simply the way PHP works. We can’t rely on
StreamInterface::rewind() because the stream might not be seekable.
The Factory Solution
Anthony was entirely right when he suggested that a factory would solve the "need a clean response" problem. Andrew is highlighting something that I have wrestled with and was unable to solve. I think the correct solution is to define a factory interface for PSR-7 objects. Packages that implement PSR-7 could also implement the factory and then we could do the correct thing in our code:
$body = $this->factory->createStream($content); $response = $response->withBody($body);
Problems with Factories
I still stand by my earlier statement:
The best dependency inversion we can have is not needing a container or factories.
In relation to factories, which are usually injected into the constructor, the problem is that we cannot enforce constructor arguments via an interface. Constructor signatures can be modified by extension without runtime errors. And to a lesser extent, return type enforcement is not possible before PHP 7.
While we have to acknowledge these problems, I still believe that factory is extremely useful and would be beneficial.
Not only would a factory solve the partial body problem, it would provide a better focal point for continued debate on PSR-7 middleware. Having a factory would make writing middleware more robust regardless of the interface that becomes standard.