Immutable objects in PHP?

Is it good to create objects that cannot be modified in PHP?

For example, a date object that has setter methods, but will always return a new instance of the object (with a modified date).

Will these objects get confused for other people who use the class because in PHP you usually expect the object to change?

Example

$obj = new Object(2); $x = $obj->add(5); // 7 $y = $obj->add(2); // 4 
+12
immutability php class
source share
6 answers

An immutable object cannot be modified after its initial creation, so using setter methods does not make sense, since it contradicts this basic principle.

You can implement some workarounds to mimic immutability in PHP by manipulating the visibility of a class member and overriding the __set () magic method, but its not guaranteed immutable, since immutability is not a language feature. I believe that someone once wrote an extension to provide an immutable value type in PHP, although you could use Google to do this.

+5
source share

Immutable objects do not have customization methods. Period.

Everyone will expect that the setXyz() method will have a return type of void (or return nothing in freely typed languages). If you add setter methods to your immutable object, this will confuse the hell of people and lead to ugly errors.

+24
source share

In my opinion, objects should be immutable for value objects. In addition, it does not have great advantages if you do not share your object with your entire application.

There are some incorrect answers here; an immutable object may have setters . Here is some implementation of immutable objects in PHP.

Example # 1.

 class ImmutableValueObject { private $val1; private $val2; public function __construct($val1, $val2) { $this->val1 = $val1; $this->val2 = $val2; } public function getVal1() { return $this->val1; } public function getVal2() { return $this->val2; } } 

As you can see after creating the instance, you cannot change any value.

Example 2: with setters :

 class ImmutableValueObject { private $val1; private $val2; public function __construct($val1, $val2) { $this->val1 = $val1; $this->val2 = $val2; } public function getVal1() { return $this->val1; } public function withVal1($val1) { $copy = clone $this; $copy->val1 = $val1; return $copy; // here the trick: you return a new instance! } public function getVal2() { return $this->val2; } public function withVal2($val2) { $copy = clone $this; $copy->val2 = $val2; return $copy; } } 

There are several implementation options, and this is by no means an exclusive list. And remember, with Reflection there is always a way around this in PHP, so the end result is that everything is in your head!

It is also often good practice to put immutable objects as final objects.

EDIT:

  • changed setX for w thanks
  • added a comment about the finale
+9
source share

I made a small line by avoiding using Reflection to facilitate the implementation of immutability: https://github.com/jclaveau/php-immutable-trait

Obviously, since this is not a language function, it will not cause mutations in a magical way, but it will facilitate the code of mutators that must clone the current instance before use. In relation to the example of Massimiliano this will give

 class ImmutableValueObject { use JClaveau\Traits\Immutable; private $val1; private $val2; public function __construct($val1, $val2) { $this->val1 = $val1; $this->val2 = $val2; } public function getVal1() { return $this->val1; } public function withVal1($val1) { // Just add these lines at the really beginning of methods supporting // immutability ("setters" mostly) if ($this->callOnCloneIfImmutable($result)) return $result; // Write your method body as if you weren't in an Immutable class $this->val1 = $val1; return $this; } public function getVal2() { return $this->val2; } public function withVal2($val2) { if ($this->callOnCloneIfImmutable($result)) return $result; $this->val2 = $val2; return $this; } } 
  • You can see that here you are not returning $ copy, but $ this, as noted by Konstantin K.
  • In native PHP https://secure.php.net/manual/en/class.datetimeimmutable.php there are mutators that will return new instances with the modification applied. So copy inline sentences that immutable objects shouldn't have mutators doesn't seem super interesting.
  • The practice of using "withXXX" instead of "setXXX" is very interesting, thanks for the suggestion! I personally used "getsXXX" to configure instance volatility (optional API in the SwitchableMutability trait).

Hope this can help some people here!

PS: suggestions for this little function are really welcome :): https://github.com/jclaveau/php-immutable-trait/issues

0
source share

You can get its values ​​from an immutable object, but you cannot change them. Here you can see an example of an immutable class:

 <?php declare(strict_types=1); final class Immutable { /** @var string */ private $value; public static function withValue(string $value): self { return new self($value); } public function __construct(string $value) { $this->value = $value; } public function value(): string { return $this->value; } } // Example of usage: $immutable = Immutable::withValue("my value"); $immutable->value(); 
0
source share

If you need setters for a class and an object, that’s fine, we do it all the time, since we need to set the object data. Just don't call it immutable.

Many things in the development world are subjective - our approaches, methodology, etc. - but "immutable" is a pretty solid definition:

"Unchanging":
- Does not change over time or cannot be changed.

If you want an immutable object, this means that it cannot be changed after the instance is created. This is good for things like data from the database, which should remain unchanged throughout the cycle.

If you need to call an object and set or modify data after its creation, this is not an immutable object.

Would you take two wheels off a car and call it a motorcycle?

There is some talk about methods for a "immutable" class that is named without the word "set", but that does not stop their functionality, which is the method that sets the data. You can name it thisDoesNotSetAnything(int $id) and allow the transfer of data in which the object changes. It will be a setter, and therefore the object is mutable.

0
source share

All Articles