rfc:object-initializer
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:object-initializer [2019/09/05 10:26] – Added future scope additional info brzuchal | rfc:object-initializer [2019/10/07 11:10] – brzuchal | ||
---|---|---|---|
Line 2: | Line 2: | ||
* Version: 1.0 | * Version: 1.0 | ||
* Date: 2019-09-03 | * Date: 2019-09-03 | ||
- | * Author: Michał Brzuchalski <michal.brzuchalski@gmail.com> | + | * Author: Michał Brzuchalski <brzuchal@php.net> |
- | * Status: | + | * Status: |
* First Published at: http:// | * First Published at: http:// | ||
- | |||
- | |||
===== Introduction ===== | ===== Introduction ===== | ||
- | PHP doesn' | + | PHP doesn' |
- Instantiate an object. | - Instantiate an object. | ||
Line 15: | Line 13: | ||
- Pass created a fully initialized object. | - Pass created a fully initialized object. | ||
- | That's where object initializer optimization can benefit with single expression statement and it can be used to initialize any kind of object // | + | In current PHP implementation with Typed Properties, it is possible to instantiate class which doesn' |
+ | properties in an uninitialized state. | ||
+ | |||
+ | That's where object initializer optimization can benefit with single expression statement and it can be used to initialize any kind of object // | ||
+ | required properties are properly initialized, | ||
+ | |||
+ | The initializer block can use any properties and variables available in the containing scope, but one has to be wary of the fact that initializers are run after constructors. | ||
===== Proposal ===== | ===== Proposal ===== | ||
- | Object initializers allow to assign | + | Object initializers allow assigning |
- | + | ||
- | The following example shows how to use an object initializer with a named type, Car and how to invoke the parameterless constructor. | + | |
<code php> | <code php> | ||
Line 29: | Line 31: | ||
public $id; | public $id; | ||
public $name; | public $name; | ||
+ | private DateTimeImmutable $createdAt; | ||
+ | | ||
+ | public function __construct() | ||
+ | { | ||
+ | $this-> | ||
+ | } | ||
} | } | ||
- | |||
- | $customer = new Customer { | ||
- | id = 123, | ||
- | name = "John Doe", | ||
- | }; | ||
class Car | class Car | ||
Line 41: | Line 44: | ||
public string $vin; | public string $vin; | ||
} | } | ||
+ | </ | ||
+ | |||
+ | The following example shows how to use an object initializer with a '' | ||
+ | |||
+ | <code php> | ||
+ | <?php | ||
+ | |||
+ | $customer = new Customer { | ||
+ | id = 123, | ||
+ | name = "John Doe", | ||
+ | }; | ||
$car = new Car { | $car = new Car { | ||
Line 48: | Line 62: | ||
</ | </ | ||
- | The object initializers syntax allows to create an instance, and after that, it assigns the newly created | + | > **Note!** If in current scope there are constants with exactly the same name as property names used in the above example, they do not impact |
- | Above example is an equivalent to the previous one. | + | The following |
<code php> | <code php> | ||
Line 65: | Line 79: | ||
The main difference is that object initializers allow creating a new object, with its assigned properties in a single expression. For eg. factory methods where normally a significant amount of argument has default values or simple Data Transfer Objects could benefit. | The main difference is that object initializers allow creating a new object, with its assigned properties in a single expression. For eg. factory methods where normally a significant amount of argument has default values or simple Data Transfer Objects could benefit. | ||
+ | |||
+ | > **Note!** Currently, language allows instantiating object and initializing only a subset of typed non-nullable properties without a default value. These rules apply to object initializer the same way, meaning the creation of properly initialized object state is in authors responsibility, | ||
+ | |||
+ | ==== Restrictions ==== | ||
+ | Using Object Initializer enforce that if a class is instantiated with the Object Initializer, | ||
+ | This helps to avoid bugs where a property is added to the class but forgot to be assigned it a value in all cases where the class is instantiated and initialized. | ||
+ | |||
+ | The object initializers syntax allows to create an instance, and after that, it assigns the newly created object, with its assigned properties, to the variable in the assignment. | ||
+ | |||
+ | <code php> | ||
+ | <?php | ||
+ | |||
+ | $customer = new Customer { | ||
+ | name = "John Doe", | ||
+ | }; // throws RuntimeException: | ||
+ | |||
+ | $car = new Car { | ||
+ | yearOfProduction = 2019, | ||
+ | }; // throws RuntimeException: | ||
+ | </ | ||
+ | |||
+ | |||
+ | ==== Constructors ==== | ||
+ | Due to the fact that initializer block purpose is a simplification of instantiating and initializing object properties, constructors are called before initialization takes apart. Constructors allow initializing default values // | ||
+ | |||
+ | Due to the fact that objects in PHP simply have constructor directly declared in class definition or indirectly through the defaulting constructor, | ||
+ | |||
+ | > **Note!** Object instantiation allows only constructors without required arguments to be used. Any class which requires passing arguments to constructor cannot be used in combination with object initializer. | ||
+ | |||
+ | |||
+ | <code php> | ||
+ | <?php | ||
+ | |||
+ | class Customer | ||
+ | { | ||
+ | public int $id; | ||
+ | public string $name; | ||
+ | private DateTimeImmutable $createdAt; | ||
+ | | ||
+ | public function __construct() | ||
+ | { | ||
+ | $this-> | ||
+ | } | ||
+ | } | ||
+ | |||
+ | $customer = new Customer { | ||
+ | id = 123, | ||
+ | name = "John Doe", | ||
+ | }; | ||
+ | </ | ||
+ | |||
+ | |||
+ | |||
+ | > **Note!** Classes without constructor desired to mimick " | ||
+ | |||
+ | > **Note!** If a class needs validation upon to validate its invariants a proper validation logic needs to be called after initialization. To combine it with object initializer and keep the validation process encapsulated, | ||
==== Lexical scope ==== | ==== Lexical scope ==== | ||
Line 74: | Line 144: | ||
Initializer block allows assigning values to properties accessible from the current class context. This means if used to initialize object properties from inside the same class like for eg. using named static constructor all standard visibility rules apply as it is just a simplification of object creation and assigning values statements. | Initializer block allows assigning values to properties accessible from the current class context. This means if used to initialize object properties from inside the same class like for eg. using named static constructor all standard visibility rules apply as it is just a simplification of object creation and assigning values statements. | ||
- | Above example shows the correct behaviour of visibility rules. | + | The following |
<code php> | <code php> | ||
Line 96: | Line 166: | ||
==== Magic methods ==== | ==== Magic methods ==== | ||
- | An object initializer is just a simplification of instantiating | + | |
+ | Due to lacks of property accessors magic methods like set() are often used to keep invariants | ||
+ | and restrict property values to valid ones. This leads potentially to more issues than benefit but at current implementation, | ||
+ | |||
+ | An object initializer is just a simplification of instantiating | ||
+ | that's why all rules regarding | ||
Using an object initializer combined with magic set method call might be used if an additional validation is required. | Using an object initializer combined with magic set method call might be used if an additional validation is required. | ||
Line 105: | Line 180: | ||
class EmailAddress | class EmailAddress | ||
{ | { | ||
- | | + | |
public ?string $name = null; | public ?string $name = null; | ||
| | ||
Line 129: | Line 204: | ||
Initializer block stands instead of constructor arguments which means in case of anonymous classes that place is right before the class definition. | Initializer block stands instead of constructor arguments which means in case of anonymous classes that place is right before the class definition. | ||
- | Above example shows how to use object initializer with anonymous classes. | + | The following |
<code php> | <code php> | ||
Line 148: | Line 223: | ||
Dynamic properties can be set by their name or by a variable which holds the name of property. | Dynamic properties can be set by their name or by a variable which holds the name of property. | ||
- | Above example shows how to use object initializer with dynamic properties. | + | The following |
<code php> | <code php> | ||
Line 160: | Line 235: | ||
</ | </ | ||
+ | ===== Backward Incompatible Changes ===== | ||
+ | This proposal changes the meaning of the above example which uses a curly brace to fetch array offset //(which is already deprecated since PHP 7.4)// which is very rare usage. | ||
+ | <code php> | ||
+ | <?php | ||
- | ===== Backward Incompatible Changes | + | class Foo {} |
- | None. | + | $foo = [123 => Foo:: |
+ | new $foo { | ||
+ | $var = 123 | ||
+ | }; | ||
+ | </ | ||
+ | |||
+ | ===== Reflection | ||
+ | Both '' | ||
+ | |||
+ | <code php> | ||
+ | <?php | ||
+ | |||
+ | $reflectionClass = new ReflectionClass(Customer:: | ||
+ | $customer = $reflectionClass-> | ||
+ | // equivalent to | ||
+ | $customer = new Customer {name = ' | ||
+ | </ | ||
===== Proposed PHP Version(s) ===== | ===== Proposed PHP Version(s) ===== | ||
Line 180: | Line 275: | ||
==== New Constants ==== | ==== New Constants ==== | ||
None. | None. | ||
- | |||
- | |||
- | ===== Open Issues ===== | ||
- | Calling ctor before or after initializer or using a ctor at all is not resolved yet. | ||
- | Calling zero args ctor might be an option. | ||
- | |||
===== Future Scope ===== | ===== Future Scope ===== | ||
Line 217: | Line 306: | ||
The vote is a straight Yes/No vote for accepting the RFC and merging the patch. | The vote is a straight Yes/No vote for accepting the RFC and merging the patch. | ||
+ | |||
+ | <doodle title=" | ||
+ | * Yes | ||
+ | * No | ||
+ | </ | ||
+ | . | ||
+ | <doodle title=" | ||
+ | * = | ||
+ | * => | ||
+ | </ | ||
+ | |||
===== Patches and Tests ===== | ===== Patches and Tests ===== |
rfc/object-initializer.txt · Last modified: 2019/10/24 07:36 by brzuchal