An immutable object is an object whose state cannot be modified after it is created [Wikipedia]. Key advantage: if an object is known to be immutable it can be referenced rather than copied, saving memory.
In this example class A is immutable and can be safely referenced.


final class A {
    const NUMBER = 4;
    public function getNumber() {
        return self::NUMBER;
    }
}

$a = new A;
$b = &$a;
echo $b->getNumber();  // prints 4

PHP does not have inherent support for immutable objects. They can be simulated by using constants or private properties. Adoption of a final class or final methods guards against extending classes. Setters should obviously not be present in an immutable class but so not forget to override the magic __set() method.

In this example class A is again immutable even when extended:


class A {
    protected $var = 10;
    public function getVar() {
        return $this->var;
    }

    final public function __set($key, $value) {        
    }
}

class B extends A {

    public function __set($key, $value) {
        $this->$key = $value;
    }
}

$a = new B;
$ref = &$a;
$ref->var = 100;
echo $ref->getVar();  
// Fatal error: Cannot override final method A::__set() 

Wojciech Sznapka recently demonstrated how to copy a seemingly immutable class using cloning, demonstrated here in a variation:


final class A {
    private $var = 10;
    public function getVar() {
        return $this->var;
    }

    public function __set($key, $value){
        throw new Exception('I am immutable');
    }

    public function getClone($var) {
        $clone = clone $this;
        $clone->var = $var;
        return $clone;
    }
}

$a = new A;
$b = $a->getClone(100);
echo $b->getVar();  // prints 100
echo $a->getVar();  // prints 10

Florian Wolters observes that even if a property is private, immutability is still not guaranteed when the property is also another object as demonstrated here in a variation :


final class A {
    private $obj;

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

    public function getObj() {
        return $this->obj;
    }

}

$obj = new stdClass();
$obj->var = 10;
$a = new A($obj);
echo $a->getObj()->var;  // prints 10

$obj->var = 100;
echo $a->getObj()->var; // prints 100

The solution to the problem in this example is cloning the object when it arrives in the constructor as in


final class A {
    private $obj;

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

    public function getObj() {
        return $this->obj;
    }

}

$obj = new stdClass();
$obj->var = 10;
$a = new A($obj);
echo $a->getObj()->var;  // prints 10

$obj->var = 100;
echo $a->getObj()->var; // prints 10

The PHP-Component-Core-Immutable project launched by Wolters provides an interface that does not do anything except identifying a class as immutable. It also comes with a trait that takes care of avoiding setting of inaccessible properties and direct calling of the constructor. And speaking of Github, it hosts several other immutable PHP projects such as Period for handling date ranges.

PHP itself has also discovered actual immutable objects. Compare the DateTime object:


$dateTime = new DateTime();
$dateTime->setTimezone(new DateTimeZone('Europe/Amsterdam'));
$zone = $dateTime->getTimezone();
echo $zone->getName();  // prints Europe/Amsterdam

with the DateTimeImmutable object available in PHP 5.5 (use http://phiddle.net/ for fiddling around with PHP 5.6)


$dateTime = new DateTimeImmutable();
$dateTime->setTimezone(new DateTimeZone('Europe/Amsterdam'));
$zone = $dateTime->getTimezone();
echo $zone->getName();   // prints UTC

which does not allow messing around with time zones.

Are immutable objects otherwise a solution looking for a problem? Maybe so. They appear to be relevant when it comes to so-called value objects. More on that a later time.

Links:
* http://stackoverflow.com/questions/14427050/immutable-objects-in-php
* http://phiddle.net/
* http://bradley-holt.com/2010/09/immutable-value-objects-in-php/
* http://blog.sznapka.pl/immutable-value-objects-in-php/
* http://blog.florianwolters.de/educational/2013/03/07/Pattern-Immutable-Object/
* http://derickrethans.nl/immutable-datetime.html

Advertisements