{
    "href": "/post/2016/09/06/avoiding-quasi-immutable-objects-in-php/",
    "relId": "2016/09/06/avoiding-quasi-immutable-objects-in-php",
    "title": "Avoiding Quasi-Immutable Objects in PHP",
    "author": "pmjones",
    "markup": "html",
    "tags": [
        {
            "href": "/tag/php/",
            "relId": "php",
            "title": "PHP",
            "author": null,
            "created": null,
            "updated": [],
            "markup": "markdown"
        },
        {
            "href": "/tag/programming/",
            "relId": "programming",
            "title": "Programming",
            "author": null,
            "created": null,
            "updated": [],
            "markup": "markdown"
        }
    ],
    "created": "2016-09-06 11:30:07 UTC",
    "updated": [
        "2016-09-06 11:30:07 UTC"
    ],
    "html": "<p>tl;dr: Immutability in PHP is most practical when the object properties are scalars or nulls. Using streams, objects, or arrays as properties makes it very difficult, sometimes impossible, to preserve immutablity.</p>\n<hr>\n<p>One of the tactics in <a href=\"http://dddcommunity.org/learning-ddd/what_is_ddd/\">Domain Driven Design</a> is to use <a href=\"http://martinfowler.com/bliki/ValueObject.html\">Value Objects</a>. A Value Object has no identifier attached to it; only the combination of the values of its properties gives it any identification. If you change any of the properties in any way, the modification must return an entirely new instance of the Value Object.</p>\n<p>This kind of behavior means the Value Object is \u201cimmutable.\u201d That is, the particular instance is not allowed to change, though you can get back a new instance with modified values. The code for an immutable object looks something like this:</p>\n<pre><code>&lt;?php\nclass ImmutableFoo\n{\n    protected $bar;\n\n    public function __construct($bar)\n    {\n        $this-&gt;bar = $bar;\n    }\n\n    public function getBar()\n    {\n        return $this-&gt;bar;\n    }\n\n    public function withBar($newBar)\n    {\n        $clone = clone $this;\n        $clone-&gt;bar = $newBar;\n        return $clone;\n    }\n}\n?&gt;\n</code></pre>\n<p>(Note how <code>$bar</code> is accessible only through a method, not as a public property.)</p>\n<p>When you create an <code>ImmutableFoo</code> instance, you cannot change the value of <code>$bar</code> after instantiation. Instead, you can only get back a new instance with the new value of <code>$bar</code> by calling <code>withBar()</code>:</p>\n<pre><code>&lt;?php\n$foo = new ImmutableFoo('a');\n$newFoo = $foo-&gt;withBar('b');\n\necho $foo-&gt;getBar(); // 'a'\necho $newFoo-&gt;getBar(); // 'b'\nvar_dump($foo === $newFoo); // (bool) false\n?&gt;\n</code></pre>\n<p>With this approach, you are guaranteed that one place in the code cannot change the <code>$foo</code> object at a distance from any other place in the code. Anything that ever gets that instance of <code>$foo</code> knows that its properties will always be the same no matter what.</p>\n<p>The immutability approach can be powerful in Domain Driven Design and elsewhere. It works very easily in PHP with <a href=\"http://php.net/is_scalar\">scalar values</a> and nulls. That\u2019s because PHP returns those by copy, not by reference.</p>\n<p>However, enforcing immutability in PHP is difficult when the immutable object properties are non-scalar (i.e., when they are streams, objects, or arrays). With non-scalars, your object might <em>seem</em> immutable at first, but mutablity reveals itself later. These objects will be \u201cquasi-\u201c, not truly, immutable.</p>\n<h3 id=\"streams-as-immutable-properties\">\n<a name=\"user-content-streams-as-immutable-properties\" href=\"#streams-as-immutable-properties\" class=\"headeranchor-link\" aria-hidden=\"true\"><span class=\"headeranchor\"></span></a>Streams as Immutable Properties</h3>\n<p>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. For example:</p>\n<pre><code>&lt;?php\nfile_put_contents('/tmp/bar.txt', 'baz');\n\n$foo = new ImmutableFoo(fopen('/tmp/bar.txt', 'w+'));\n$bar = $foo-&gt;getBar();\nfpassthru($bar); // 'baz'\n\nrewind($bar);\nfwrite($bar, 'dib');\nrewind($bar);\n\nfpassthru($foo-&gt;getBar()); // 'dib'\n?&gt;\n</code></pre>\n<p>As you can see, the effective property value has changed, meaning immutability has been compromised.</p>\n<p>One way around this might be to make sure that immutable objects themselves check that stream resources are always-and-only in read-only mode. However, even that is not a certain solution, because the resource pointer might be moved by reading operations in different parts of the application code. In turn, that means reading from the stream may yield different results at different times, making the value appear mutable.</p>\n<p>As such, it appears that only \u201cread-only\u201d streams can be used as immutable properties, and then only if the immutable object restores the stream, its pointers, and all of its meta-data to their initial state every time the stream is accessed.</p>\n<h3 id=\"objects-as-immutable-properties\">\n<a name=\"user-content-objects-as-immutable-properties\" href=\"#objects-as-immutable-properties\" class=\"headeranchor-link\" aria-hidden=\"true\"><span class=\"headeranchor\"></span></a>Objects as Immutable Properties</h3>\n<p>Because PHP returns objects as references, rather than as copies, using an object as a property value compromises the immutability of the parent object. For example:</p>\n<pre><code>$foo = new ImmutableFoo((object) ['baz' =&gt; 'dib']);\n$bar = $foo-&gt;getBar();\necho $bar-&gt;baz; // 'dib'\n\n$bar-&gt;baz = 'zim';\necho $foo-&gt;getBar()-&gt;baz; // 'zim'\n</code></pre>\n<p>As you can see, the value of <code>$bar</code> has changed in the <code>$foo</code> instance. Any other code using <code>$foo</code> will see those changes as well. This means immutability has not been preserved.</p>\n<p>One way around this is to make sure that all objects used as immutable properties are themselves immutable.</p>\n<p>Another way around this is to make sure that getter methods clone any object properties they return. However, it will have to be a recursively deep clone, covering all of the cloned object\u2019s properties (and all of <em>their</em> properties, etc.). That\u2019s to make sure that all object properties down the line are also cloned; otherwise, immutability is again compromised at some point.</p>\n<h3 id=\"arrays-as-immutable-properties\">\n<a name=\"user-content-arrays-as-immutable-properties\" href=\"#arrays-as-immutable-properties\" class=\"headeranchor-link\" aria-hidden=\"true\"><span class=\"headeranchor\"></span></a>Arrays as Immutable Properties</h3>\n<p>Unlike objects, PHP returns arrays as copies by default. However, if an immutable object property is an array, mutable objects in that array compromise the parent object\u2019s immutability. For example:</p>\n<pre><code>$foo = new ImmutableFoo([\n    0 =&gt; (object) ['baz' =&gt; 'dib'],\n]);\n\n$bar = $foo-&gt;getBar();\necho $bar[0]-&gt;baz;\n\n$bar[0]-&gt;baz = 'zim';\necho $foo-&gt;getBar()[0]-&gt;baz; // 'zim'\n</code></pre>\n<p>Because the array holds an object, and because PHP returns objects by reference, the contents of the array have now changed. That means <code>$foo</code> has effectively changed as well. Again, immutability has not been preserved.</p>\n<p>Likewise, if the array holds a reference to a stream resource, we see the problems described about streams above.</p>\n<p>The only way around this is for the immutable object to recursively scan through array properties to make sure that they contain only immutable values. This is probably not practical in most situations, which means that arrays are probably not suitable as immutable values.</p>\n<h3 id=\"settable-undefined-public-properties\">\n<a name=\"user-content-settable-undefined-public-properties\" href=\"#settable-undefined-public-properties\" class=\"headeranchor-link\" aria-hidden=\"true\"><span class=\"headeranchor\"></span></a>Settable Undefined Public Properties</h3>\n<p>Finally, PHP allows you to set values on undefined properties, as if they were public. This means it is possible to add mutable properties to an immutable object:</p>\n<pre><code>$foo = new ImmutableFoo('bar');\n\n// there is no $zim property, so PHP\n// creates it as if it were public\n\n$foo-&gt;zim = 'gir';\necho $foo-&gt;zim; // 'gir'\n\n$foo-&gt;zim = 'irk';\necho $foo-&gt;zim; // 'irk'\n</code></pre>\n<p>Immutability of the object is once again compromised. The only way around this is to impelement <code>__set()</code> to prevent setting of undefined properties.</p>\n<p>Further, it might be wise to implement <code>__unset()</code> to warn that properties cannot be unset.</p>\n<h3 id=\"conclusion\">\n<a name=\"user-content-conclusion\" href=\"#conclusion\" class=\"headeranchor-link\" aria-hidden=\"true\"><span class=\"headeranchor\"></span></a>Conclusion</h3>\n<p>If you want to build a truly immutable object in PHP, it appears the best approach is the following:</p>\n<ul>\n<li>Default to using only scalars and nulls as properties.</li>\n<li>Avoid streams as properties; if a property must be a stream, make sure that it is read-only, and that its state is restored each time it is used.</li>\n<li>Avoid objects as properties; if a property must be an object, make sure that object is itself immutable.</li>\n<li>Especially avoid arrays as properties; use only with extreme caution and care.</li>\n<li>Implement <code>__set()</code> to disallow setting of undefined properties.</li>\n<li>Possibly implement <code>__unset()</code> to warn that the object is immutable.</li>\n</ul>\n<p>Overall, it seems like immutability is easiest with only scalars and nulls. Anything else, and you have a lot more opportunity for error.</p>\n<p>Remember, though, there\u2019s nothing wrong with fully- or partially-mutable objects, as long as they are advertised as such. What you want to avoid are quasi-immutable objects: ones that advertise, but do not deliver, true immutability.</p>\n<p>(For some further reading, check out <a href=\"http://programmers.stackexchange.com/questions/68058/at-what-point-do-immutable-classes-become-a-burden\">At What Point Do Immutable Classes Become A Burden?</a>)</p>\n<p><strong>Update 1</strong>: </p>\n<ul>\n<li>\n<a href=\"http://docs.oracle.com/javase/tutorial/essential/concurrency/imstrat.html\">A strategy for defining immutable objects</a> (Java, but might be useful in PHP too).</li>\n<li><a href=\"http://www.javaranch.com/journal/2003/04/immutable.htm\">A template for immutable classes</a></li>\n</ul>\n<p><strong>Update 2</strong>: </p>\n<ul>\n<li><a href=\"https://ragazzo.github.io/immutability/oop/2016/05/03/immutability.html\">\"If you store collection inside immutable object, it should be immutable too.\"</a></li>\n<li><a href=\"https://www.infoq.com/news/2015/06/CSharp-7-immutable\">\"An immutable object can only reference other immutable objects.\"</a></li>\n</ul>\n<p><strong>UPDATE 3:</strong> Beware <a href=\"https://christopherdavis.me/blog/leaking-mutability.html\">Leaking Mutability</a>.</p>\n<hr>\n<p class=\"reddit-links\">Read the Reddit discussion about this post <a href=\"https://www.reddit.com/r/PHP/comments/51ergo/avoiding_quasiimmutable_objects_in_php/\">here</a>.</p>\n"
}
