Newsgroups: php.internals Path: news.php.net Xref: news.php.net php.internals:114371 Return-Path: Delivered-To: mailing list internals@lists.php.net Received: (qmail 13400 invoked from network); 11 May 2021 09:49:10 -0000 Received: from unknown (HELO php-smtp4.php.net) (45.112.84.5) by pb1.pair.com with SMTP; 11 May 2021 09:49:10 -0000 Received: from php-smtp4.php.net (localhost [127.0.0.1]) by php-smtp4.php.net (Postfix) with ESMTP id 62AFD1804D8 for ; Tue, 11 May 2021 02:56:55 -0700 (PDT) X-Spam-Checker-Version: SpamAssassin 3.4.2 (2018-09-13) on php-smtp4.php.net X-Spam-Level: X-Spam-Status: No, score=-0.2 required=5.0 tests=BAYES_20,DKIM_SIGNED, DKIM_VALID,DKIM_VALID_AU,DKIM_VALID_EF,FREEMAIL_FROM,HTML_MESSAGE, RCVD_IN_DNSWL_NONE,RCVD_IN_MSPIKE_H2,SPF_HELO_NONE,SPF_PASS autolearn=no autolearn_force=no version=3.4.2 X-Spam-Virus: No X-Envelope-From: Received: from mail-lj1-f177.google.com (mail-lj1-f177.google.com [209.85.208.177]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange ECDHE (P-256) server-signature RSA-PSS (4096 bits) server-digest SHA256) (No client certificate requested) by php-smtp4.php.net (Postfix) with ESMTPS for ; Tue, 11 May 2021 02:56:54 -0700 (PDT) Received: by mail-lj1-f177.google.com with SMTP id p12so24399887ljg.1 for ; Tue, 11 May 2021 02:56:54 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025; h=mime-version:references:in-reply-to:from:date:message-id:subject:to :cc; bh=7IC8DAkHoeRgO0pCxaOhwW4Hgv8O9nZCFtKXk34pFDY=; b=DqUyEsjzOzsa8Lp77Qkhose5pn0CxlNSJQbjwjs8p8uLfIG045hTYeL6md2+QXtRxq c1RqI+NfWAOT+91mNl/cEbSk8lqCYGk7sFVug9iMmP0PpUjxYGOXngXRHDahAoWN/M2o lB8QytFVKWJR5nLm25QPs/hLTeq4Iltc7UESlWKB9MIOVnw7ykkU5B40Wdk3Ufu4aaGz 08Uc+2hULWEHK98CkebZ3X7ZGqUCL7rzyY/Gbqp3+79uuu7FeNBap26bumknKT9g0r9r CwprLrUoSLL/VlVycqLf5ubHve3RMaWp8T315ZAhPxY1Qa610ORZvqIBD+gGHh1ZRr+Y 1avQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:mime-version:references:in-reply-to:from:date :message-id:subject:to:cc; bh=7IC8DAkHoeRgO0pCxaOhwW4Hgv8O9nZCFtKXk34pFDY=; b=asea4cEX6VlwMO8uQ7LsYqo96Rn55nDAToJw9PuihBzhghzEd0Rkwk21KPwk1+vqqd qastpBWE/QmmuUkwASB5ww+eup2WsHdzsouAQcpoERJhS3+qR9edAr0fB+SqpzHWKzbX RvMPVI4RN2A5oii3GkzhZt04f9OaDie5l+V7iIO1/E561MfHjsWK+Z8N8Y0UDNXZdjAL O6zb9khs9iE5/xEDeLSzs2noXst2F5ZklXFlHrL2+UNoEz83N04Of75qmblSjohqbtm+ 4tGEa6tB+ydshTgLEEb5qw2GlzBTqUzIU5sTEBU1EtctInoLFqFkya+7/1GqT3zZHEvP Id9w== X-Gm-Message-State: AOAM530HA8sUnRkjy0RzzAkBHezpvq8hW5jqSG3EnJTLpEYFKq090toQ i1dk5V4kauKuPQmJHQtDpRsZpaDPhwUXbFP+XNM= X-Google-Smtp-Source: ABdhPJzHqFvXhHdGSrXNLGDZuIgllP8efO76QAPCmFihUciZPKe0Di76FxBHMdMyB/svaOIA6sm3je3DFf7FydQoQU8= X-Received: by 2002:a2e:8e3a:: with SMTP id r26mr23620987ljk.244.1620727012155; Tue, 11 May 2021 02:56:52 -0700 (PDT) MIME-Version: 1.0 References: In-Reply-To: Date: Tue, 11 May 2021 11:56:36 +0200 Message-ID: To: =?UTF-8?Q?Alexandru_P=C4=83tr=C4=83nescu?= Cc: PHP internals Content-Type: multipart/alternative; boundary="000000000000d6635805c20ae951" Subject: Re: [PHP-DEV] [RFC] New in initializers From: nikita.ppv@gmail.com (Nikita Popov) --000000000000d6635805c20ae951 Content-Type: text/plain; charset="UTF-8" Content-Transfer-Encoding: quoted-printable On Fri, Mar 19, 2021 at 2:02 PM Nikita Popov wrote: > On Fri, Mar 19, 2021 at 12:57 PM Nikita Popov > wrote: > >> On Fri, Mar 19, 2021 at 12:22 PM Nikita Popov >> wrote: >> >>> On Wed, Mar 3, 2021 at 7:04 PM Alexandru P=C4=83tr=C4=83nescu >>> wrote: >>> >>>> >>>> On Wed, Mar 3, 2021 at 5:49 PM Nikita Popov >>>> wrote: >>>> >>>>> On Wed, Mar 3, 2021 at 4:28 PM Alexandru P=C4=83tr=C4=83nescu < >>>>> drealecs@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 fiv= e >>>>> 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 aft= er >>>>> 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 cl= ass >>>>> constants and static properties are evaluated at the same time, on fi= rst >>>>> "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. T= his >>>>> would be a (minor) backwards-compatibility break, because invalid >>>>> constant/property declarations would error out immediately, even if t= hey >>>>> don't get used. However, I do think that this would be the most predi= ctable >>>>> behavior once potentially side-effecting expressions are involved (we >>>>> already support side-effecting expressions right now, but less explic= itly). >>>>> >>>>> >>>> Yes, this is what I was thinking about, to have a clear stated order o= f >>>> 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 doe= s >>>> the declaration >>>> - class constants and static property, when class is declared, in orde= r >>>> 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 declare= d >>>> parameter/arguments. >>>> >>>> That sounds good to me. >>>> Thanks! >>>> Alex >>>> >>> >>> I've updated the RFC (and implementation) to evaluate class constants >>> and static properties at time of class declaration. As such, everything >>> should have a well-defined evaluation order now. >>> >>> However, this also means that this RFC now also contains a >>> backwards-compatibility break: Anything used inside class constant / st= atic >>> property initializers needs to actually be available at the time the cl= ass >>> is declared. You can't first declare the class, then declare some >>> additional constants it uses, and then use it. >>> >> >> Another complication here is preloading. The current semantics of >> evaluation on first use work well there, because class loading (during >> preloading) is decoupled from evaluation (during request). Now, we can't >> evaluate initializers during "opcache_compile_file" style preloading, so >> we'd have to delay this to the start of the request. And then we'd have = to >> evaluate initializers for all preloaded classes, regardless of whether t= hey >> will be used in this particular request or not. Also opens up the questi= on >> of the order in which the classes should be evaluated. >> >> I initially liked the idea of evaluating everything at the time of class >> declaration, but now get the impression that this causes more problems t= han >> it solves, and we should go back to the previous lazy evaluation approac= h. >> Ultimately, my view here is that side-effecting constructors are a terri= ble >> idea, and if you use them then you should also carefully manage those >> side-effects yourself. >> > > Just to throw it out there, another option would be to not support "new" > for static properties and class constants, where the semantics are less > clear cut than in all the other cases. For me personally the important us= e > cases here are initializers for non-static properties and parameters, as > well as attribute arguments, and those all have unambiguous semantics. Of > course, there is also a lot of value in having consistent support. > An issue for using new in (non-static) properties is that these objects are going to get created even if the constructor doesn't run. This could be a problem for things like unserialize() and anything that implements related functionality in userland (e.g. Symfony's VarCloner). In particular, this means that the default is *always* going to be constructed and populated, even if you're bypassing the constructor. Of course, this also happens with other property defaults, but in this case object construction may be more expensive (and possibly side-effecting). That's not great, and there wouldn't really be any way to avoid that. The alternative would be to instead only initialize such properties in the constructor, which comes with its own caveats: We'd have to make all classes actually have a constructor, and you'd have to make sure to call parent::__construct() even if there is no explicit parent constructor, otherwise you might be missing some property initializations. This also causes something of an asymmetry between "simple" default values (initialized on object creation) and "complex" defaults (initialized during constructor call). TBH, I'm not quite sure what to do with this RFC. I'm considering whether it may make sense to cut it down to the bare minimum, which is support for new inside parameter defaults and attribute arguments (and possibly global constants and static variables -- I don't think those have any issues, but they're also not terribly useful). Parameter defaults happen to also cover the common case of property defaults, through constructor promotion. This would still work fine, because the default is on the parameter, not on the property: class Test { public function __construct( private Logger $logger =3D new NullLogger, ) {} } The main argument against this is that it will result in some places using "constant expressions" being more powerful, and some being less powerful. Regards, Nikita --000000000000d6635805c20ae951--