PSR-7 Objects Could Be Immutable

I've been thinking a lot about immutable objects lately. Yegor Bugayenko claims that Objects Should Be Immutable and PSR-7: HTTP message interfaces are designed to be immutable.

Messages are values where the identity is the aggregate of all parts of the message; a change to any aspect of the message is essentially a new message. This is the very definition of a value object. The practice by which changes result in a new instance is termed immutability and is a feature designed to ensure the integrity of a given value.

There has been some discussion around the mutability of streams in PSR-7.

Andrew Carter wrote a post discussing why PSR-7 Objects Are Not Immutable saying:

You shouldn’t expect to see the message that was written to the response actually rendered. The code we have written explicitly avoided returning the response by throwing the exception. However, if you disable the Whoops error handler and use the default templated error handler you will find that your message still appears at the top of the error page that is generated by the framework.

Paul M. Jones mentions the immutability of streams in Avoiding Quasi-Immutable Objects in PHP.

If a stream or similar resource has been opened in a writable (or appendable) mode, and is used as an immutable property, it should be obvious that object immutability is not preserved.

This makes sense. If you write to a stream, then rewind it and write it, the value has changed.

I don't think this makes the class wrapping the stream mutable.

Yegor talks about this idea in Gradients of Immutability.

His argument would be that the object isn't a constant, but it is immutable.

The object isn't representative of the content of the stream, but of the reference to the stream.

It's like if you had an object that interacted with a file.

class File
{
    private $filename;

    public function __construct($filename)
    {
        $this->filename = $filename;
    }

    public function read()
    {
        return file_get_contents($this->filename);
    }

    public function write($data)
    {
        file_put_contents($this->filename, $data);
    }
}

$f = new File(__DIR__ . '/test.txt');
$f->write('hoopla');

$g = clone $f;
$g->write('ballyhoo');

// Prints 'ballyhoo'
echo $f->read();

You might argue that a File object isn't immutable because if it was $f->read() should have returned "hoopla". But I disagree.

The File object is wrapping a file name, a pointer to a file in the file system. The object isn't constant, but it is immutable because it's data is the file name and not the contents of the file.

I'd be breaking immutability if I added a setFilename method that allowed you to change what file the object was pointing to.

Zend Diactoros

The PSR-7 implementation that I typically use is Zend Diactoros because it's what used by Radar.

I was hoping to dig in and show how it actually is immutable, but I can't. Because it isn't. :-(

It could have been, but the Stream::attach method makes it mutable.

The strange thing is that it implements Psr\Http\Message\StreamInterface which does not define an attach method.

So Zend\Diactoros\Stream chooses to define additional methods that are not part of the PSR-7 interface, and in doing so, breaks immutability.

Guzzle PSR-7

Looking at other implementations, it would appear Guzzle PSR-7 streams are immutable.