rfc:immutability
Differences
This shows you the differences between two versions of the page.
Both sides previous revisionPrevious revisionNext revision | Previous revisionNext revisionBoth sides next revision | ||
rfc:immutability [2016/08/16 20:01] – added Uri model brzuchal | rfc:immutability [2017/09/22 13:28] – external edit 127.0.0.1 | ||
---|---|---|---|
Line 9: | Line 9: | ||
===== Introduction ===== | ===== Introduction ===== | ||
- | This RFC proposes introduction of immutable classes and properties. Currently | + | This RFC proposes |
+ | If this mechanism is introduced, developers can be sure that they are programming with no side effects, meaning that state of the object can not be changed without developers being made aware. This is especially useful when dealing with concurrency, | ||
**Pros** | **Pros** | ||
- Immutability guaranteed by language instead of user-land implementation. | - Immutability guaranteed by language instead of user-land implementation. | ||
+ | - Programming with no side effects. | ||
- Safe for concurrency. | - Safe for concurrency. | ||
- Value objects, DTO's etc. can be easily created. | - Value objects, DTO's etc. can be easily created. | ||
- | - Properties can be public which removes need for getters without allowing state modification. | + | - Properties can be public which removes |
- (Please point it out more advantages) | - (Please point it out more advantages) | ||
**Cons** | **Cons** | ||
- | - Cloning has to be disabled for immutable objects | ||
- (Please point it out more disadvantages) | - (Please point it out more disadvantages) | ||
- | **Before** | + | ===== Proposal ===== |
- | <code php> | + | |
- | <?php | + | |
- | class Email { | + | ==== Immutable Class ==== |
- | private $_email; | + | |
- | public function __construct ($email) { | + | A class defined as immutable will imply immutability across all of its properties by default. After the object is constructed, |
- | // validation | + | |
- | $this-> | ||
- | } | ||
- | |||
- | public function getValue() { | ||
- | | ||
- | } | ||
- | } | ||
- | </ | ||
- | |||
- | **After** | ||
<code php> | <code php> | ||
- | <?php | ||
immutable class Email { | immutable class Email { | ||
Line 51: | Line 37: | ||
public function __construct ($email) { | public function __construct ($email) { | ||
- | // validation | ||
- | |||
$this-> | $this-> | ||
} | } | ||
} | } | ||
+ | |||
+ | $email = new Email(" | ||
+ | $email-> | ||
+ | |||
</ | </ | ||
- | ===== Proposal ===== | + | Changes to inheritance are made to add constraints when extending an immutable |
- | + | ||
- | Class defined as immutable | + | |
<code php> | <code php> | ||
+ | immutable class Foo{} | ||
+ | class Bar extends Foo{} // Will result in Fatal Error | ||
+ | </ | ||
- | immutable class Email { | ||
- | public $email; | ||
- | | + | ==== Immutable Properties ==== |
- | // validation | + | |
+ | Classes have the ability to enforce immutability to only a subset of properties if needed, in that case, immutability will be implied only on properties that are declared as immutable. | ||
+ | |||
+ | <code php> | ||
+ | class User { | ||
+ | private $id; | ||
+ | | ||
+ | public function __construct ($id, $email) { | ||
+ | $this-> | ||
$this-> | $this-> | ||
} | } | ||
} | } | ||
- | |||
- | $email = new Email(" | ||
- | $email-> | ||
- | |||
</ | </ | ||
- | Regular classes can define immutability per property. | ||
+ | If an immutable property contains an object, to preserve immutability, | ||
<code php> | <code php> | ||
- | class Email { | + | immutable |
+ | |||
+ | class User { | ||
public immutable $email; | public immutable $email; | ||
- | public function __construct ($email) { | + | public function __construct (Email $email) { |
- | // validation | + | |
$this-> | $this-> | ||
} | } | ||
} | } | ||
+ | </ | ||
- | $email = new Email(" | + | Resources are not allowed to be assigned to immutable properties because of fact that resources, by nature, are not immutable. |
- | $email->email = " | + | <code php> |
+ | class File { | ||
+ | public immutable | ||
+ | |||
+ | public function __construct | ||
+ | $this->handle | ||
+ | } | ||
+ | } | ||
+ | |||
+ | $file = new File(fopen(' | ||
</ | </ | ||
- | Any class that extends | + | If an immutable |
+ | Since the focus of this RFC is immutable objects, having static properties in immutable classes will result in a compile error. | ||
- | <code php> | ||
- | immutable class Foo{} | ||
- | class Bar extends Foo{} // Will result in Fatal Error | ||
- | </ | ||
+ | ==== References ==== | ||
+ | |||
+ | Assigning by references to immutable properties will result in an error, otherwise the object loses control over properties, and immutability can be broken. | ||
- | It will not be possible to assign value by reference to immutable property. | ||
<code php> | <code php> | ||
immutable class Email { | immutable class Email { | ||
Line 121: | Line 121: | ||
</ | </ | ||
- | If immutable property contains object, object must also be an instance of immutable class. | ||
- | <code php> | + | ===== Examples ===== |
+ | Notice in above example, changing getters and setters methods to public properties is optional. They simply don't need to be protected anymore, in fact, immutable class objects are deeply frozen with exceptions on write. | ||
- | class Bar{} | + | Every example shows where internal object state is important. Any references to objects passed into an immutable class constructor cannot be references |
- | + | ||
- | immutable class Foo { | + | |
- | public $bar; | + | |
- | + | ||
- | public function __construct (Bar $bar) { | + | |
- | // validation | + | |
- | + | ||
- | $this-> | + | |
- | } | + | |
- | } | + | |
- | + | ||
- | $foo = new Foo(new Bar()); // Will result in a error because Bar is not instance of immutable class | + | |
- | </ | + | |
- | + | ||
- | + | ||
- | ===== Examples ===== | + | |
- | Notice in above examples removing getters and setting properties to public is optional. They simply doesn' | + | |
---- | ---- | ||
Line 149: | Line 132: | ||
==== Money ==== | ==== Money ==== | ||
- | Money Pattern, defined by Martin Fowler and published in Patterns of Enterprise Application Architecture, | + | Money Pattern, defined by Martin Fowler and published in Patterns of Enterprise Application Architecture, |
<code php> | <code php> | ||
Line 283: | Line 266: | ||
</ | </ | ||
- | There is no need for getters because internally immutable object | + | There is no need for getters because |
- | ==== URI ==== | ||
- | Uri's domain is like Money complex ValueObjects and can be presented for eg. in this way. | ||
- | |||
- | <code php> | ||
- | class Scheme { | ||
- | |||
- | private $protocol; | ||
- | |||
- | private function __construct(string $protocol) { | ||
- | $this-> | ||
- | } | ||
- | |||
- | public function getProtocol() | ||
- | { | ||
- | return $this-> | ||
- | } | ||
- | |||
- | public static function HTTP() { | ||
- | return new Scheme(' | ||
- | } | ||
- | } | ||
- | |||
- | class UserInfo { | ||
- | |||
- | private $username; | ||
- | private $password; | ||
- | |||
- | public function __construct(string $username, string $password) | ||
- | { | ||
- | $this-> | ||
- | $this-> | ||
- | } | ||
- | |||
- | public function getUsername() : string | ||
- | { | ||
- | return $this-> | ||
- | } | ||
- | |||
- | public function getPassword() : string | ||
- | { | ||
- | return $this-> | ||
- | } | ||
- | } | ||
- | |||
- | class Authority { | ||
- | |||
- | private $host; | ||
- | private $port; | ||
- | private $userInfo; | ||
- | |||
- | public function __construct(string $host, int $port = 80, UserInfo $userInfo = null) | ||
- | { | ||
- | $this-> | ||
- | $this-> | ||
- | $this-> | ||
- | } | ||
- | |||
- | public function getHost() : string | ||
- | { | ||
- | return $this-> | ||
- | } | ||
- | |||
- | public function getPort() : int | ||
- | { | ||
- | return $this-> | ||
- | } | ||
- | |||
- | public function getUserInfo() : UserInfo | ||
- | { | ||
- | return $this-> | ||
- | } | ||
- | } | ||
- | |||
- | class Path { | ||
- | |||
- | private $path; | ||
- | |||
- | public function __construct(string $path) | ||
- | { | ||
- | $this-> | ||
- | } | ||
- | |||
- | public function getPath() : string | ||
- | { | ||
- | return $this-> | ||
- | } | ||
- | } | ||
- | |||
- | class Uri { | ||
- | |||
- | private $scheme; | ||
- | private $authority; | ||
- | private $path; | ||
- | |||
- | public function __construct(Scheme $scheme, Authority $authority, Path $path) | ||
- | { | ||
- | $this-> | ||
- | $this-> | ||
- | $this-> | ||
- | } | ||
- | |||
- | public function getScheme() : Scheme | ||
- | { | ||
- | return $this-> | ||
- | } | ||
- | |||
- | public function getAuthority() : Authority | ||
- | { | ||
- | return $this-> | ||
- | } | ||
- | |||
- | public function getPath() : Path | ||
- | { | ||
- | return $this-> | ||
- | } | ||
- | } | ||
- | |||
- | $phpNetUri = new Uri(Scheme:: | ||
- | </ | ||
- | |||
- | After refactoring to immutable class implementation the model is getting simplier: | ||
- | |||
- | <code php> | ||
- | immutable class Scheme { | ||
- | |||
- | /** @var string */ | ||
- | public $protocol; | ||
- | |||
- | private function __construct(string $protocol) { | ||
- | $this-> | ||
- | } | ||
- | | ||
- | public static function HTTP() { | ||
- | return new Scheme(' | ||
- | } | ||
- | |||
- | public static function HTTPS() { | ||
- | return new Scheme(' | ||
- | } | ||
- | } | ||
- | |||
- | immutable class UserInfo { | ||
- | |||
- | /** @var string */ | ||
- | public $username; | ||
- | /** @var string */ | ||
- | public $password; | ||
- | |||
- | public function __construct(string $username, string $password) | ||
- | { | ||
- | $this-> | ||
- | $this-> | ||
- | } | ||
- | } | ||
- | |||
- | immutable class Authority { | ||
- | |||
- | /** @var string */ | ||
- | public $host; | ||
- | /** @var int */ | ||
- | public $port; | ||
- | /** @var UserInfo */ | ||
- | public $userInfo; | ||
- | |||
- | public function __construct(string $host, int $port = 80, UserInfo $userInfo = null) | ||
- | { | ||
- | $this-> | ||
- | $this-> | ||
- | $this-> | ||
- | } | ||
- | } | ||
- | |||
- | immutable class Path { | ||
- | |||
- | /** @var string */ | ||
- | public $path; | ||
- | |||
- | public function __construct(string $path) | ||
- | { | ||
- | $this-> | ||
- | } | ||
- | } | ||
- | |||
- | immutable class Uri { | ||
- | |||
- | /** @var Scheme */ | ||
- | public $scheme; | ||
- | /** @var Authority */ | ||
- | public $authority; | ||
- | /** @var Path */ | ||
- | public $path; | ||
- | |||
- | public function __construct(Scheme $scheme, Authority $authority, Path $path) | ||
- | { | ||
- | $this-> | ||
- | $this-> | ||
- | $this-> | ||
- | } | ||
- | | ||
- | public function changeScheme(Scheme $scheme) : Uri { | ||
- | return new self($scheme, | ||
- | } | ||
- | } | ||
- | |||
- | $http = new Uri(Scheme:: | ||
- | $https = $http-> | ||
- | </ | ||
===== Backward Incompatible Changes ===== | ===== Backward Incompatible Changes ===== | ||
- | No backward | + | No backwardly |
Line 509: | Line 285: | ||
==== To Existing Extensions ==== | ==== To Existing Extensions ==== | ||
- | - Reflection. | + | - Reflection |
==== To Opcache ==== | ==== To Opcache ==== | ||
Line 538: | Line 314: | ||
===== Patches and Tests ===== | ===== Patches and Tests ===== | ||
- | + | [[https:// | |
Line 549: | Line 324: | ||
===== References ===== | ===== References ===== | ||
- | |||
===== Rejected Features ===== | ===== Rejected Features ===== | ||
+ | - Immutable interfaces |
rfc/immutability.txt · Last modified: 2018/02/20 11:19 by marijic.silvio