Newsgroups: php.internals Path: news.php.net Xref: news.php.net php.internals:123083 X-Original-To: internals@lists.php.net Delivered-To: internals@lists.php.net Received: from php-smtp4.php.net (php-smtp4.php.net [45.112.84.5]) by qa.php.net (Postfix) with ESMTPS id B2B191A009C for ; Wed, 10 Apr 2024 08:25:30 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=php.net; s=mail; t=1712737562; bh=t9YgXB++kx7oUYFxuc2MvdKSy4esS1oLfJWzOOa/lZo=; h=References:In-Reply-To:From:Date:Subject:To:Cc:From; b=iRUi7ePgHU+N5fD+bDwXxyfNGAeV2PXkd6eF8QPE4W6Yzcclcz5aPxGDCEpAsV/MX JJVRKFRRWm6EgplLIgep7UjPypJQ3akiyyBtICBWx6it15J+JPNbr8o/r8s3tzJh9Z 4K6rTe60ZBv1ovJ9y/gh//LraUQl7SHP2uMQ8wQhe9m8ik8Jo0zo2fqTKVTSyKgNdu EoRLex3npz0/RkrKwoZ6nldo1hEyKyGlrWJ+/Fbbnqw3Zsy2ovHBMhtVoJaYoFkQEV jMcSPYQiKiDzc7+ImfWjJv0uhtXy8ZewE+QfOS5ZIUycNh6VPdn9R00mAE2kxseaos +HVnuqw09wObg== Received: from php-smtp4.php.net (localhost [127.0.0.1]) by php-smtp4.php.net (Postfix) with ESMTP id 7937D18082F for ; Wed, 10 Apr 2024 08:26:01 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 4.0.0 (2022-12-13) on php-smtp4.php.net X-Spam-Level: X-Spam-Status: No, score=-2.1 required=5.0 tests=BAYES_00,DKIM_SIGNED, DKIM_VALID,DKIM_VALID_AU,DKIM_VALID_EF,DMARC_PASS,FREEMAIL_FROM, HTML_MESSAGE,RCVD_IN_DNSWL_NONE,RCVD_IN_MSPIKE_H2,SPF_HELO_NONE, SPF_PASS,T_SCC_BODY_TEXT_LINE autolearn=no autolearn_force=no version=4.0.0 X-Spam-Virus: No X-Envelope-From: Received: from mail-ed1-f51.google.com (mail-ed1-f51.google.com [209.85.208.51]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits) server-digest SHA256) (No client certificate requested) by php-smtp4.php.net (Postfix) with ESMTPS for ; Wed, 10 Apr 2024 08:26:01 +0000 (UTC) Received: by mail-ed1-f51.google.com with SMTP id 4fb4d7f45d1cf-56e1f3462caso6791784a12.3 for ; Wed, 10 Apr 2024 01:25:28 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1712737527; x=1713342327; darn=lists.php.net; h=cc:to:subject:message-id:date:from:in-reply-to:references :mime-version:from:to:cc:subject:date:message-id:reply-to; bh=zH32jdWWWRV63XUXWEFe4ClMTqoZ75+1UadnQCrDnjA=; b=frmhY2TwG0S4wl7L1T9jiUltva9qVqCnp2NHOC0pPqKpgp18jMZZIo6XJ0Ovkkcvyw Vhq8ts2pNqdJSldnUpitVSNgYp40VMh+VJsC/8H2BqmdqWdoypaLP7budgRQ1po+5dAl PBbiohdrsn4lErglXND7jkdYofjNAuF1B1SlJJpiJ4y34/l/yq7ZU+Ix9mSSDPiWPjDC Q8aAsbD16tTX/6xYlubSSRn2dFPhKoSIVe7WVfCxgW0PLZvBrfSee/5FcplMoCSPMrv8 FRW4XfhKjhHbq6Nr70UPcROG39zbKc4kwhwhKaHGY52Mdv8B4VWNhvzvZtWLBT77wf2m yA1Q== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1712737527; x=1713342327; h=cc:to:subject:message-id:date:from:in-reply-to:references :mime-version:x-gm-message-state:from:to:cc:subject:date:message-id :reply-to; bh=zH32jdWWWRV63XUXWEFe4ClMTqoZ75+1UadnQCrDnjA=; b=W7iLhG3q4FjvB/EzgzIDS+5/HvZB/uAOBgWTEKnajxdDcHvzB9Lo7iTCMgQiVh0N/D xJQ0CZTg0EvCxdxy67Li6M78c9+tkKe37PSw5SscZzcH20CP5r9MZn6N9BM/JF6e3olJ pI4MX9Rp5D4ptM4iQ4lhFOWaUuuPxFGFG+mUPMGxQl0+Mb/dBuFEFgYC4S3kPGpoa1nR SMGIK9m82l6wjB8CVfKNf+MLs2faDhUU/75wzXK5DfcZBMtPbpoFWNPr0iBihIzEcYuu JYrc2PjqS90YXMAmZMP/ntKWzuu91Yzli1kyAaFci+nsROtjJhMVBoy+8f7gQpwbfvTt 7t8w== X-Gm-Message-State: AOJu0YxVCJL53AtkHyzQ7VPmg7BIWi2tGlNRpjePNBc5IHV95ZH67yVq tkx986Cq7pyX+tjwF84lfDPKon9YN1vQ1A7lG/Npz7fzmMqxdIAzLhGY7VxJWLxvo2wXckNyPE4 GbFYzsGSZqaa3IXiQUHIusrdfzzY= X-Google-Smtp-Source: AGHT+IHkjvi+dWQNO9+Z59DZ5DdbbSO9sRTHmxr5e+zrkSo2lV2CGwG6qTc6/gOIOBrzxrmHB5A/cW/TO6AvArVNdMU= X-Received: by 2002:a50:d51e:0:b0:567:45e2:c4db with SMTP id u30-20020a50d51e000000b0056745e2c4dbmr1132576edi.39.1712737527061; Wed, 10 Apr 2024 01:25:27 -0700 (PDT) Precedence: bulk list-help: list-post: List-Id: internals.lists.php.net MIME-Version: 1.0 References: <66154AA0.1040905@adviesenzo.nl> <66160A1D.4060409@adviesenzo.nl> In-Reply-To: <66160A1D.4060409@adviesenzo.nl> Date: Wed, 10 Apr 2024 10:25:01 +0200 Message-ID: Subject: Re: [PHP-DEV] [RFC][Vote announcement] Property hooks To: Juliette Reinders Folmer Cc: internals@lists.php.net Content-Type: multipart/alternative; boundary="000000000000e4e90e0615b9c7db" From: kjarli@gmail.com (Lynn) --000000000000e4e90e0615b9c7db Content-Type: text/plain; charset="UTF-8" Content-Transfer-Encoding: quoted-printable On Wed, Apr 10, 2024 at 5:47=E2=80=AFAM Juliette Reinders Folmer < php-internals_nospam@adviesenzo.nl> wrote: > On 9-4-2024 16:03, Juliette Reinders Folmer wrote: > > On 8-4-2024 23:39, Ilija Tovilo wrote: > > Hi everyone > > Heads-up: Larry and I would like to start the vote of the property > hooks RFC tomorrow:https://wiki.php.net/rfc/property-hooks > > We have worked long and hard on this RFC, and hope that we have found > some middle-ground that works for the majority. One last concern we > have not officially clarified on the list: > https://externals.io/message/122445#122667 > > I personally do not feel strongly about whether asymmetric types make it = into the initial implementation. Larry does, however, and I think it is not= fair to exclude them without providing any concrete reasons not to. [snip] > > My concern is more about the external impact of what is effectively a cha= nge to the type system of the language: [snip] will tools like PhpStan and = Psalm require complex changes to analyse code using such properties? > > In particular, this paragraph is referencing the ability to widen the > accepted $value parameter type of the set hook, described at the > bottom of https://wiki.php.net/rfc/property-hooks#set. I have talked > to Ond=C5=99ej Mirtes, the maintainer of PHPStan, and he confirmed that > this should not be complex to implement in PHPStan. In fact, PHPStan > already offers the @property-read and @property-write class > annotations, which can be used to describe "virtual" properties > handled within __get/__set, already providing asymmetric types of > sorts. Hence, this concern should be a non-issue. > > Thank you to everybody who has contributed to the discussion! > > Ilija > > > > Ilija, > > Heads-up: I'm still writing up an opinion and intend to send it to the > list before end of day (CET). I know I'm late to the party, but I've been > having trouble finding the words to express myself properly regarding thi= s > RFC (and have been struggling to find the right words for months). > > Smile, > Juliette > > > Later than intended, but here goes.... > > If there is one RFC which has been giving me nightmares since I first > heard of it, it's this one. > > I realize it is late in the discussion period to speak up, but for months > I've been trying to find the words to express my concerns in a polite and > constructive way and have failed. > > I am going to try now anyway (before it is too late), so please bear with > me. Also, as I'm not a C-developer, please forgive me if I get the > internals wrong. I'm writing this from a PHP-dev/user perspective, with m= y > perspective being heavily influenced by my role as maintainer of > PHP_CodeSniffer. > > --- > TL;DR: this RFC tries to do too much in one go and introduces a huge > amount of cognitive complexity with all the exceptions and the difference= s > in behaviour between virtual and backed properties. This cognitive > complexity is so high that I expect that the feature will catch most > developers out a lot of the time. > --- > > I can definitely see the use case and desirability of the property hooks > functionality proposed in the RFC and compared to the initial RFC I read > last year, the current RFC is, IMO, much improved. > Huge kudos to Ilija and Larry for all the work they have put in to this! > > I applaud the intention of this RFC to make it easier to avoid the magic > __get()/__set() et al methods. What I have a problem with is the > implementation details. > > Over the last few years, we've seen a movement to get rid of more and mor= e > of the _surprising_ behaviour of PHP, with subtle exceptions being > deprecated and slated for removal and (most) new syntaxes trying to use t= he > principle of least surprise by design. > > This RFC, in my view, is in stark contrast to this as it introduces a > plethora of exceptions and subtle different behaviour in a way that will > catch developers out for years to come. > > At this moment (excluding property hooks), from a user perspective, there > are three different function syntaxes in PHP: named functions (and > methods), anonymous functions and arrow functions. > > The semantics of these are very similar with subtle differences: > * Can be static or non-static. > * Take parameters, which can be typed, references, variadic, optional etc= . > * Can have a return type. > * Can return by reference. > * Have a function "body". > > The differences between the current syntaxes - from a user perspective - > are as follows: > =3D Named functions: > * When declared in a class, can have visibility attached, can be abstract= , > can be final. > * When declared in an interface or declared as abstract, will not have a > function "body". > > =3D Anonymous functions: > * Can import plain variables from outside its scope with a `use()` clause= . > * Are declared as an expression (can be assigned to a variable etc). > > =3D Arrow functions: > * Have access to variables in the same scope. > * Are declared as an expression. > * Body of the function starts with a =3D> instead of being enclosed in > curlies and can end on a range of characters. > * Can only take one statement in the body. > * Automagically returns. > > The property hooks RFC introduces a fourth flavour of function syntax. An= d > not just one syntax, but five and the differences in the semantics of the > function syntaxes are significant. > > The differences in semantics I see for "full" property hook functions > compared to other function syntaxes are as follows: > * `get` cannot take parameters (and doesn't have the parentheses typicall= y > expected for a function declaration). > * `get` cannot take return type (inherits this from the property, which i= s > logically sound, but does create a syntactic difference). > * `set` can take one (typed or untyped) parameter. > * `set` cannot take return type (silently set to void, which is logically > sound, but does create a syntactic difference). > * `set` magically creates a $value variable for the "new" value, though > that variable _may_ have an arbitrary different name depending on whether > or not the parameter was explicitly specified. > * Has a function body. > > Then there are multiple short hand syntaxes, which each have yet more > syntactic differences: > * The arrow function variant for `get` with all the above differences + > the differences inherent to arrow functions, combined, with the exception > of the access to variables in the same scope and a more clearly defined e= nd > of the expression. > * The implicit "set" parameter, which does not have a explicit parameter, > does not have parentheses and has the magically created $value variable. > * The arrow function variant for `set` with all the above differences + > the differences inherent to arrow functions with the above mentioned > exceptions + the implicit assignment, which breaks the expected behaviour > of arrow functions by assigning the result of the expression instead of > returning it (totally understandable, but still a difference). > * The abstract/interface variants, which don't have a function body. > > Next there are the differences in semantics which are now being introduce= d > for properties: > * Aside from the hooks themselves.... > * Properties _may_ or _may not_ have a default value anymore, depending o= n > whether the hooks cause it to be a backed or a virtual property. > * Properties with hooks can _be_ readonly, but cannot be declared as > readonly. > > And then there are the new features for properties (not just hooked ones)= : > * Properties can now be declared as final. > * Properties can now be declared as abstract, but only with explicit hook > requirements, otherwise the abstract keyword is not allowed. > * Properties can now be declared on interfaces, but only with explicit > hook requirements, otherwise they are not allowed. > * Abstract/Interface properties don't comply with the same > covariance/contravariance rules as other properties > This complexity describes what I've been unconsciously worried about though I really like having the proposed feature in PHP. I'm not sure if there's a solution to reduce the complexity to a point where glancing at something will tell you how it works when you don't know exactly how all variants work. There are already a lot of gotches with something as simple as adding a `string|null` to a property without setting its default value to `null`. It's one of those things you won't find out until you run that code because technically speaking it's not incorrect. I worry that a bunch of those gotchas will bite me in the behind because I don't fully understand how it works and mistakes are easily made. It also adds a lot of complexity when reviewing someone else's code where bugs might slip through. > And last but not least, there is the ability to declare property hooks in > constructor property promotion ... where we can now basically have a > function declaration within the signature of a method declaration.... wit= h > all five possible syntax types. > This is what I've been afraid of. Constructor method signatures are already exploding and I hope that one day we'll revise this notation and head towards `__construct($this->theProperty)` instead. One could argue that property promotion is fully optional, except that in its most basic design it does save a lot of lines of code and is a net-gain in terms of maintainability and readability in its minimum form for properly written code. The benefits fall apart the moment you don't have static code analysis available, or if the class is big/old and it just takes time for the tool to be done. If I forget to add the visibility keyword in the constructor I have to rely on my IDE telling me that the argument is unused, which results in extra overhead of trying to find out why it's telling me it's unused, just to realize I forgot to add `public` or `private`. Some of my worries would be removed if hooks are not allowed in property promotion scenarios, but this doesn't solve the complexity mentioned for the hooks themselves. --000000000000e4e90e0615b9c7db Content-Type: text/html; charset="UTF-8" Content-Transfer-Encoding: quoted-printable


=
On Wed, Apr 10, 2024 at 5:47=E2=80=AF= AM Juliette Reinders Folmer <php-internals_nospam@adviesenzo.nl> wrote:
=20 =20 =20
On 9-4-2024 16:03, Juliette Reinders Folmer wrote:
=20
On 8-4-2024 23:39, Ilija Tovilo wrote:
Hi everyone

Heads-up: Larry and I would like to start the vote of the property
hooks RFC tomorrow:
https=
://wiki.php.net/rfc/property-hooks

We have worked long and hard on this RFC, and hope that we have found
some middle-ground that works for the majority. One last concern we
have not officially clarified on the list:

ht=
tps://externals.io/message/122445#122667

I personally do not feel strongly about whether asymmetric=
 types make it into the initial implementation. Larry does, however, and I =
think it is not fair to exclude them without providing any concrete reasons=
 not to. [snip]
My concern is more about the external impact of what is effe=
ctively a change to the type system of the language: [snip] will tools like=
 PhpStan and Psalm require complex changes to analyse code using such prope=
rties?
In particular, this paragraph is referencing the ability to wi=
den the
accepted $value parameter type of the set hook, described at the
bottom of https://wiki.php.net/rfc/property-hooks#set. I have talked
to Ond=C5=99ej Mirtes, the maintainer of PHPStan, and he confirmed that
this should not be complex to implement in PHPStan. In fact, PHPStan
already offers the @property-read and @property-write class
annotations, which can be used to describe "virtual" properties
handled within __get/__set, already providing asymmetric types of
sorts. Hence, this concern should be a non-issue.

Thank you to everybody who has contributed to the discussion!

Ilija


Ilija,

Heads-up: I'm still writing up an opinion and intend to send it t= o the list before end of day (CET). I know I'm late to the party, but I've been having trouble finding the words to express myself properly regarding this RFC (and have been struggling to find the right words for months).

Smile,
Juliette

Later than intended, but here goes....

If there is one RFC which has been giving me nightmares since I first heard of it, it's this one.

I realize it is late in the discussion period to speak up, but for months I've been trying to find the words to express my concerns in a polite and constructive way and have failed.

I am going to try now anyway (before it is too late), so please bear with me. Also, as I'm not a C-developer, please forgive me if I get the internals wrong. I'm writing this from a PHP-dev/user perspective, with my perspective being heavily influenced by my role as maintainer of PHP_CodeSniffer.

---
TL;DR: this RFC tries to do too much in one go and introduces a huge amount of cognitive complexity with all the exceptions and the differences in behaviour between virtual and backed properties. This cognitive complexity is so high that I expect that the feature will catch most developers out a lot of the time.
---

I can definitely see the use case and desirability of the property hooks functionality proposed in the RFC and compared to the initial RFC I read last year, the current RFC is, IMO, much improved.
Huge kudos to Ilija and Larry for all the work they have put in to this!

I applaud the intention of this RFC to make it easier to avoid the magic __get()/__set() et al methods. What I have a problem with is the implementation details.

Over the last few years, we've seen a movement to get rid of more and more of the _surprising_ behaviour of PHP, with subtle exceptions being deprecated and slated for removal and (most) new syntaxes trying to use the principle of least surprise by design.

This RFC, in my view, is in stark contrast to this as it introduces a plethora of exceptions and subtle different behaviour in a way that will catch developers out for years to come.

At this moment (excluding property hooks), from a user perspective, there are three different function syntaxes in PHP: named functions (and methods), anonymous functions and arrow functions.

The semantics of these are very similar with subtle differences:
* Can be static or non-static.
* Take parameters, which can be typed, references, variadic, optional etc.
* Can have a return type.
* Can return by reference.
* Have a function "body".

The differences between the current syntaxes - from a user perspective - are as follows:
=3D Named functions:
* When declared in a class, can have visibility attached, can be abstract, can be final.
* When declared in an interface or declared as abstract, will not have a function "body".

=3D Anonymous functions:
* Can import plain variables from outside its scope with a `use()` clause.
* Are declared as an expression (can be assigned to a variable etc).
=3D Arrow functions:
* Have access to variables in the same scope.
* Are declared as an expression.
* Body of the function starts with a =3D> instead of being enclosed in curlies and can end on a range of characters.
* Can only take one statement in the body.
* Automagically returns.

The property hooks RFC introduces a fourth flavour of function syntax. And not just one syntax, but five and the differences in the semantics of the function syntaxes are significant.

The differences in semantics I see for "full" property hook functions compared to other function syntaxes are as follows:
* `get` cannot take parameters (and doesn't have the parentheses typically expected for a function declaration).
* `get` cannot take return type (inherits this from the property, which is logically sound, but does create a syntactic difference).
* `set` can take one (typed or untyped) parameter.
* `set` cannot take return type (silently set to void, which is logically sound, but does create a syntactic difference).
* `set` magically creates a $value variable for the "new" val= ue, though that variable _may_ have an arbitrary different name depending on whether or not the parameter was explicitly specified.
* Has a function body.

Then there are multiple short hand syntaxes, which each have yet more syntactic differences:
* The arrow function variant for `get` with all the above differences + the differences inherent to arrow functions, combined, with the exception of the access to variables in the same scope and a more clearly defined end of the expression.
* The implicit "set" parameter, which does not have a explici= t parameter, does not have parentheses and has the magically created $value variable.
* The arrow function variant for `set` with all the above differences + the differences inherent to arrow functions with the above mentioned exceptions + the implicit assignment, which breaks the expected behaviour of arrow functions by assigning the result of the expression instead of returning it (totally understandable, but still a difference).
* The abstract/interface variants, which don't have a function body= .

Next there are the differences in semantics which are now being introduced for properties:
* Aside from the hooks themselves....
* Properties _may_ or _may not_ have a default value anymore, depending on whether the hooks cause it to be a backed or a virtual property.
* Properties with hooks can _be_ readonly, but cannot be declared as readonly.

And then there are the new features for properties (not just hooked ones):
* Properties can now be declared as final.
* Properties can now be declared as abstract, but only with explicit hook requirements, otherwise the abstract keyword is not allowed.
* Properties can now be declared on interfaces, but only with explicit hook requirements, otherwise they are not allowed.
* Abstract/Interface properties don't comply with the same covariance/contravariance rules as other properties

This complexity describes what I've been unconsc= iously worried about though I really like having the proposed feature in PH= P. I'm not sure if there's a solution to reduce the complexity to a= point where glancing at something will tell you how it works when you don&= #39;t know exactly how all variants work. There are already a lot of gotche= s with something as simple as adding a `string|null` to a property without = setting its default value to `null`. It's one of those things you won&#= 39;t find out until you run that code because technically speaking it's= not incorrect. I worry that a bunch of those gotchas will bite me in the b= ehind because I don't fully understand how it works and mistakes are ea= sily=C2=A0made. It also adds a lot of complexity when reviewing someone els= e's code where bugs=C2=A0might slip through.
=C2=A0
And last but not least, there is the ability to declare property hooks in constructor property promotion ... where we can now basically have a function declaration within the signature of a method declaration.... with all five possible syntax types.

This is what I've been afraid of. Constr= uctor method signatures are already exploding and I hope that one day we= 9;ll revise this notation and head towards `__construct($this->theProper= ty)` instead. One could argue that property promotion is fully optional, ex= cept that in its most basic design it does save a lot of lines of code and = is a net-gain in terms of maintainability and readability in its=C2=A0minim= um form for properly=C2=A0written code. The benefits fall apart the=C2=A0mo= ment you don't have static code analysis available,=C2=A0or if the clas= s is big/old and it just takes time for the tool to be done. If I forget to= add the visibility keyword in the constructor I have to rely on my IDE tel= ling me that the argument is unused, which results in extra overhead of try= ing to find out why it's telling me it's unused, just to realize I = forgot to add `public` or `private`.

Some of my wo= rries would be removed if hooks are not allowed in property promotion scena= rios, but this doesn't solve the complexity mentioned for the hooks the= mselves.
--000000000000e4e90e0615b9c7db--