On Wed, Mar 3, 2021 at 7:04 PM Alexandru Pătrănescu <dreal...@gmail.com>
wrote:

>
> On Wed, Mar 3, 2021 at 5:49 PM Nikita Popov <nikita....@gmail.com> wrote:
>
>> On Wed, Mar 3, 2021 at 4:28 PM Alexandru Pătrănescu <dreal...@gmail.com>
>> wrote:
>>
>>> Hi,
>>>
>>> This looks very nice and I'm interested in further steps where not only
>>> new can be used :).
>>>
>>> The only thing I think it would be good to improve is to have a
>>> deterministic order for running initialization.
>>> Yes, this can be done at a later point, I guess. But maybe there is
>>> already an order of initialization right now and people would start
>>> replying on it and it would be good to mention it.
>>> Or maybe I didn't understand what this refers to: "this is not
>>> guaranteed behavior, and code should not rely on a specific point of
>>> evaluation."
>>>
>>
>> Which particular cases would you like to see specified? There are five
>> cases that have clearly defined behavior, and that I could explicitly
>> specify if desired:
>>
>>  * Non-class constants: Are evaluated immediately when declared (i.e.
>> when control flow reaches the declaration).
>>  * Attribute arguments: Are evaluated in the order of the arguments.
>>  * Parameter defaults: Are evaluated in the order of the parameters.
>>  * Non-static property defaults: Are evaluated in order of declaration,
>> with parent properties first. The constructor is run after defaults are
>> evaluated.
>>  * Static variables: Are evaluated immediately when declared (i.e. when
>> control flow reaches the declaration).
>>
>> And then there are the two problematic cases: Class constants and static
>> properties. Currently, PHP evaluates these semi-lazily. All class constants
>> and static properties are evaluated at the same time, on first "use" of the
>> class. I would consider this to be something of an implementation detail.
>> That's what I meant by that sentence.
>>
>> Now, if we allow "new" expressions, then I could see an argument in favor
>> of requiring class constant and static property initializers to be
>> evaluated eagerly, i.e. directly after the class has been declared. This
>> would be a (minor) backwards-compatibility break, because invalid
>> constant/property declarations would error out immediately, even if they
>> don't get used. However, I do think that this would be the most predictable
>> behavior once potentially side-effecting expressions are involved (we
>> already support side-effecting expressions right now, but less explicitly).
>>
>>
> Yes, this is what I was thinking about, to have a clear stated order of
> initialization.
> Yes, I agree that class constants and static properties should be eagerly
> declared when class is declared.
>
> So the order would be:
> - constants and static variables, when reaching the statement that does
> the declaration
> - class constants and static property, when class is declared, in order of
> their declaration in the class
> - instance property, when class is instantiated, in order of their
> declaration in the class, before construct
> - parameter defaults and attribute arguments defaults, when
> function/method/attribute construct is called, in order of the declared
> parameter/arguments.
>

If we wanted to eagerly evaluate class constants and static properties when
the class is declared, I wonder what the expected behavior would be if
evaluation fails. If you have something like

try {
    class A {
        const B = new Foo; // Let's assume this throws an Error.
        const C = 1;
    }
} catch (Error) {}
var_dump(A::C);

then what would happen? Some initial thoughts would be:

1. If evaluation fails, don't declare the class. This is problematic
because we may want to use the class while evaluating, e.g. for a
"self::FOO" default. So I don't think registering the class only after
evaluation is an option. We can also not un-register the class on failure
(technically impossible).

2. If evaluation fails, set the remaining values to UNDEF. For static
properties this will act like an uninitialized typed property, for
constants we'd have to add an error condition for this ("Class constant not
available due to initialization failure").

Also, an unrelated realization that I had is that our trait property
compatibility check currently performs evaluation when checking whether two
properties are the same -- but this seems problematic if the default value
uses "new".

Regards,
Nikita

Reply via email to