rfc:new_in_initializers
Differences
This shows you the differences between two versions of the page.
Both sides previous revisionPrevious revision | Next revisionBoth sides next revision | ||
rfc:new_in_initializers [2021/03/19 11:14] – nikic | rfc:new_in_initializers [2021/06/15 14:57] – nikic | ||
---|---|---|---|
Line 8: | Line 8: | ||
===== Introduction ===== | ===== Introduction ===== | ||
- | This RFC proposes to allow use of '' | + | This RFC proposes to allow use of '' |
Currently, code such as the following is not permitted: | Currently, code such as the following is not permitted: | ||
Line 36: | Line 36: | ||
This makes the actual default value less obvious (from an API contract perspective), | This makes the actual default value less obvious (from an API contract perspective), | ||
- | This RFC proposes to relax this restriction and allow the use of '' | + | This RFC proposes to relax this restriction and allow the use of '' |
===== Proposal ===== | ===== Proposal ===== | ||
- | '' | + | '' |
<PHP> | <PHP> | ||
Line 64: | Line 64: | ||
</ | </ | ||
- | Affected positions | + | New expressions |
<PHP> | <PHP> | ||
Line 70: | Line 70: | ||
const C = new Foo; | const C = new Foo; | ||
+ | |||
+ | function test($param = new Foo) {} | ||
# | # | ||
class Test { | class Test { | ||
- | | + | |
- | public | + | public $prop = new Foo, |
- | | + | |
} | } | ||
- | |||
- | function test($param = new Foo) {} | ||
</ | </ | ||
+ | |||
+ | ==== Unsupported positions ==== | ||
+ | |||
+ | New expressions continue to not be supported in (static and non-static) property initializers and class constant initializers. The reasons for this are twofold: | ||
+ | |||
+ | For non-static property initializers, | ||
+ | |||
+ | Performing the initialization by injecting code in the constructor avoids the issue, but requires that constructor to actually be called. In particular, this would necessitate generating constructors for classes that do not explicitly declare them, and the disciplined invocation of such constructors from potential parent constructors. The third option would be to introduce an additional initialization phase between creation and construction. | ||
+ | |||
+ | For static property initializers and class constant initializers a different evaluation order issue arises. Currently, these initializers are evaluated lazily the first time a class is used in a certain way. Once initializers can contain potentially side-effecting expressions, | ||
+ | |||
+ | The discussion has not yielded a satisfactory solution for these concerns, as such support in these contexts is delayed until such a time as a consensus on the preferred behavior can be reached. | ||
==== Order of evaluation ==== | ==== Order of evaluation ==== | ||
- | Initializer expressions could always contain side-effects through autoloaders or error handlers. However, support for '' | + | Initializer expressions could always contain side-effects through autoloaders or error handlers. However, support for '' |
* Static variable initializers are evaluated when control flow reaches the static variable declaration. | * Static variable initializers are evaluated when control flow reaches the static variable declaration. | ||
Line 89: | Line 101: | ||
* Attribute arguments are evaluated from left to right on every call of '' | * Attribute arguments are evaluated from left to right on every call of '' | ||
* Parameter default values are evaluated from left to right on every call to the function where the parameter is not explicitly passed. | * Parameter default values are evaluated from left to right on every call to the function where the parameter is not explicitly passed. | ||
- | * Property default values are evaluated in order of declaration (with parent properties before properties declared in the class) when the object is instantiated. This happens before the constructor is invoked. If an exception is thrown during evaluation, the object destructor will not be invoked. | ||
- | * When a class is declared, all class constants and then all static properties are evaluated (constants and properties from parents are already evaluated at this point). If an exception is thrown during evaluation, then subsequent uses of the class will also throw an '' | ||
- | * As an exception, static properties in traits (class constants are not supported in the first place) are not evaluated at declaration time. Evaluation is delayed to the first direct access of a static property on the trait. Static properties in traits should only ever be accessed through a using class. Due to an implementation bug, it is currently possible to access static properties directly on traits, however this possibility will be removed in the future. At that point, static properties in traits will never be evaluated. | ||
- | ==== Interaction with reflection ==== | + | Additionally, initializers can be accessed through Reflection, in which case the following evaluation semantics apply: |
- | + | ||
- | Initializers, or values based on initializers, can be accessed through Reflection in various ways. This section specifies how the different methods behave: | + | |
* '' | * '' | ||
* '' | * '' | ||
* '' | * '' | ||
- | * '' | ||
- | * '' | ||
* '' | * '' | ||
- | * '' | ||
- | |||
- | ==== Recursion protection ==== | ||
- | |||
- | If the evaluation of an object property default value results in recursion, an '' | ||
- | |||
- | <PHP> | ||
- | class Test { | ||
- | public $test = new Test; | ||
- | } | ||
- | |||
- | new Test; | ||
- | // Error: Trying to recursively instantiate Test while evaluating default value for Test::$test | ||
- | </ | ||
- | |||
- | ==== Trait property compatibility ==== | ||
- | |||
- | When two traits declaring the same property are used in a class, a compatibility check is performed, which requires that both use the same initializer. Consider the following example: | ||
- | |||
- | <PHP> | ||
- | trait T1 { | ||
- | public $prop = new A; | ||
- | } | ||
- | trait T2 { | ||
- | public $prop = new A; | ||
- | } | ||
- | |||
- | class B { | ||
- | use T1, T2; | ||
- | } | ||
- | </ | ||
- | |||
- | These properties are not compatible, because trait compatibility is determined using identity comparison ('' | ||
- | |||
- | Initializer expressions are separated into two categories: Non-dynamic (all existing expression types) and dynamic (containing '' | ||
==== Nested attributes ==== | ==== Nested attributes ==== | ||
Line 149: | Line 119: | ||
===== Backward Incompatible Changes ===== | ===== Backward Incompatible Changes ===== | ||
- | This RFC introduces a well-defined order for the evaluation of initializers, which differs from the order used in previous PHP versions. In particular, class constant | + | This RFC does not introduce any backwards-incompatible changes, and also should not break any major assumptions. The only case where something genuinely new is possible are nested attributes. |
- | + | ||
- | The following code will no longer work: | + | |
- | + | ||
- | < | + | |
- | class X { | + | |
- | const C = Y; | + | |
- | } | + | |
- | define(' | + | |
- | var_dump(X:: | + | |
- | </ | + | |
- | + | ||
- | Instead, it should be written as: | + | |
- | + | ||
- | < | + | |
- | define(' | + | |
- | class X { | + | |
- | const C = Y; | + | |
- | } | + | |
- | var_dump(X:: | + | |
- | </ | + | |
- | + | ||
- | For the same reason, classes that have unresolved initializers will not be early-bound. That is, in the above example the declaration of class '' | + | |
===== Future Scope ===== | ===== Future Scope ===== |
rfc/new_in_initializers.txt · Last modified: 2021/07/14 07:19 by nikic