In May, 2015 PSR-7 was officially accepted. Despite that it’s quite a lot of time since then, the discussion on some of its aspects is still vivid. There are few implementations already available – maybe you are already familiar with some of them, but have you ever wondered about how HTTP messages are supposed to be processed according to PSR-7 recommendation? Instead of describing the standard here, I’d like to point out some details which caused most of a fuss.

OK, but what’s PSR-7?

Most of PHP developers have heard about PSRs from 0 to 4. Framework Interoperability Group, which is responsible for issuing such recommendations, accepted PSR-7 before version 6. At this moment even PSR-5 is a draft. If you’re interested in why those recommendations did not get consecutive numbers, look here. PSR-7 is about standardizing HTTP message interfaces. Furthermore, citing Matthew Weier O’Phinney, Principal Engineer and Project Lead of Zend Framework, who was also the editor of this recommendation:

I’ve been working with the Framework Interoperability Group (FIG) since September to help finalize a set of standard HTTP message interfaces so that we can create an ecosystem in which PHP developers can create re-usable middleware that they can share.

Discussion over middleware pattern usage in php frameworks have never been that inherent, of course due to the architecture recommended by PSR-7. People tend to talk about “PSR-7 middleware”, by which they mean middleware components designed to work with the PSR-7 Request/Response interfaces. At the time when PSR-7 was issued, there was no official recommendation for middleware interface though. Because of that, most of the modern frameworks defined their own variation of such an interface, like this one for example: https://github.com/phly/conduit/blob/master/src/MiddlewareInterface.php. Currently PSR-15 is officially accepted, it defines interfaces for HTTP request handlers as well as for middleware components. But in fact, nowadays vast majority of PHP frameworks utilize Model-View-Controller pattern. Does it mean, that we should ditch Symfony and Zend in favour of StackPHP and Slim Framework? Not really – just two weeks after PSR-7 became official, SensioLabs released Symfon’y PSR-7 bridge, and Zend came out with Diactoros, which previously was phly/http (started when PSR-7 was in draft phase). For now, I’ll focus on Symfony, which utilizes Zend’s Diactoros to convert HttpFoundation’s Request into PSR-7 ResponseMessage. It’s possible to utilize PSR-7 messages in Symfony’s Controllers since version 2.3 (with updated SensioFrameworkExtraBundle), and that’s how it looks:

 

It’s possible now to make use of some middleware, which operates on PSR-7 message interfaces. You should have a good reason for using PSR7/Response instead of HttpFoundation’s one though, sticking to latter is still recommended.

Immutable messages

PSR-7 defines HttpMessageInterface as immutable, meaning that changing the object, creates a new one – leaving the old one unchanged.

For example, one has to be aware of the difference between:

And

Because in the first case the original object is being modified, which is in contrary to PSR-7 specification. The second one returns a copy of the message with a new header – leaving the original one intact, but since the $response message is immutable, we end up with another copy of the message in both cases.

What if the message body is really big? Adding a header to a Response containing already hundreds of megabytes of some CSV data, leaves user with two huge objects. And something like that:

Leaves user with three objects. That makes most people concerned about possible performance issues. Nevertheless, most current implementations utilize objects cloning and it performs really well. All that thanks to the garbage collector. In the PHP documentation we read:

PHP is a dynamic, loosely typed language, that uses copy-on-write and reference counting.

Those optimizations provided by the Zend engine allow to drastically reduce CPU and memory usage. Typical implementations of MessageInterface store headers within arrays, make it possible to take advantages of “copy-on-write” mechanisms. Additionally, message body isn’t contained directly within MessageInterface – it’s only a reference to a stream wrapper. One can think of an implementation which clones message’s body, but it’s rather uncommon and shouldn’t be considered as bespoken recommendation’s flaw. Moreover, chaining methods like above end up with two references with no pointers to them, so they are tagged for removal by garbage collector.

Even if this design stands in contrary to one’s imagination of Request/Response object, which is expected to be a single instance during the life-cycle of a single isolated HTTP request, it is to ensure the integrity of Request/Response messages. If performance is not an issue, immutability shouldn’t be concerned as a design flaw. If we think about HttpMessage as a set of properties, changing any of those properties should end up with a new, different message. Therefore, if you would like to follow PSR-7 recommendations in your application, performance shouldn’t be an issue, but processing HTTP messages will require wrapping them or using references in methods definitions to keep requests and responses immutable throughout the whole application lifecycle.

StreamInterface

Another heavily discussed issue is related to StreamInterface, which is returned by MessageInterface::getBody. PSR-7, meta document states that:

the interface is intended to wrap a PHP stream or similar. A write operation therefore will proxy to writing to the stream. Even if we made StreamInterface immutable, once the stream has been updated, any instance that wraps that stream will also be updated — making immutability impossible to enforce.

This means, that the whole MessageInterface isn’t in fact immutable. Wrapping PHP stream resource with StreamInterface and proxying its read() to stream’s one will end with that:

We, of course, can imagine some implementations, where Message takes care of cloning streams when it’s cloned or mutated. But most likely that would have big performance impact, moreover, having multiple PSR-7 implementations, different on some aspects, violates Liskov substitution principle, which is not what we want. For many developers this seems to be the most intriguing part of the recommendation – mutable streams can alter message’s state.

Approach recommended by PSR-7 is to use read-only streams for HTTP messages, and:

when in doubt, create a new stream instance and attach it to a message to enforce state.

If you’re interested in why streams won over iterators and strings, read this article.

I hope that this brief summary of the standard recommendation casts some light on its quirks and will help you understand better its most discussed aspects. Eventually, it looks like the immutability in conjunction with streams isn’t impossible to achieve, but requires changing mindset a bit. Even though middleware and immutable objects weren’t commonly used at the time when the proposal was formed and the decision to define PSR-7 the way it is caused mixed reactions amongst developers, it’s done – standardization is required to help building consistent ecosystem.

 

PHP Developer

Software engineer at Codete with over 10 years of experience in professional software development. Dedicated fan of PHP ecosystem and clean code.