Newsgroups: php.internals Path: news.php.net Xref: news.php.net php.internals:123928 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 566CA1A009C for ; Thu, 27 Jun 2024 07:26:01 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=php.net; s=mail; t=1719473239; bh=q6lyXb0zgKbl5/OF42cPwWTdxtIq3XHIP7ySsxf/+/I=; h=From:Subject:Date:In-Reply-To:Cc:To:References:From; b=A0+9ukE5EVA0SnZ2yaxGa1R4R2/8aaYGCk5sfbd6X4sThb8P9kIDN3mEMDQKY30T9 R1FafbX3QC/CxrWnqW2GX1pHBbaowbKiLpLThJmbB1dsgBc6867LaJHgOmdIXyeuMF NRqCH7ORYWci/JTNeKozwn8AaFjZhAsnz5VV4h6z+m/49pYASS9R/y843gXw/fv0G7 dj9Uqd4w9/y7/Iuoh8/1Tu/Gs9AffVhiuRF5dKk8FC9dSzNJAOqWoJcBESXOkZ+CIV zIZ8UQC7ZFn4ikXR0B2RgveYSXJ1POBwdlDgZs3qohNH1jshBXlqDBDHWINPGcPWBx VH1vpElYrC5eQ== Received: from php-smtp4.php.net (localhost [127.0.0.1]) by php-smtp4.php.net (Postfix) with ESMTP id CC7B21806A2 for ; Thu, 27 Jun 2024 07:27:17 +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=0.8 required=5.0 tests=BAYES_50,DMARC_MISSING, HTML_MESSAGE,SPF_HELO_NONE,SPF_PASS,T_SCC_BODY_TEXT_LINE autolearn=no autolearn_force=no version=4.0.0 X-Spam-Virus: Error (Cannot connect to unix socket '/var/run/clamav/clamd.ctl': connect: Connection refused) X-Envelope-From: Received: from mail1.25mail.st (mail1.25mail.st [206.123.115.54]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by php-smtp4.php.net (Postfix) with ESMTPS for ; Thu, 27 Jun 2024 07:27:17 +0000 (UTC) Received: from smtpclient.apple (unknown [49.48.245.197]) by mail1.25mail.st (Postfix) with ESMTPSA id 5718160401; Thu, 27 Jun 2024 07:25:51 +0000 (UTC) Message-ID: <20070836-3F80-4CCE-A565-32C9720EDCDB@koalephant.com> Content-Type: multipart/alternative; boundary="Apple-Mail=_83B7CDE2-BA09-4D15-ABEB-647E60C4C774" Precedence: bulk list-help: list-post: List-Id: internals.lists.php.net Mime-Version: 1.0 (Mac OS X Mail 16.0 \(3774.600.62\)) Subject: Re: [PHP-DEV] [RFC] Static class Date: Thu, 27 Jun 2024 14:25:38 +0700 In-Reply-To: <94425545-7066-4D2B-BAA2-65CDE7117E56@newclarity.net> Cc: Claude Pache , php internals To: Mike Schinkel References: <88D83E92-94BE-4548-B398-8F5C74765FFD@gmail.com> <882BD9E0-42E9-4C84-A144-7C1DFC4CE5EB@newclarity.net> <662F259B-D125-48EA-8AA0-1DC8BECA4C09@newclarity.net> <97BA9681-FA0A-4929-A6F4-67DF4820ADBF@koalephant.com> <94425545-7066-4D2B-BAA2-65CDE7117E56@newclarity.net> X-Mailer: Apple Mail (2.3774.600.62) From: php-lists@koalephant.com (Stephen Reay) --Apple-Mail=_83B7CDE2-BA09-4D15-ABEB-647E60C4C774 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset=utf-8 > On 27 Jun 2024, at 13:11, Mike Schinkel wrote: >=20 >=20 >> On Jun 27, 2024, at 12:09 AM, Stephen Reay > wrote: >>=20 >> Hi Mike, >>=20 >> To answer your question: I believe `abstract static` should be = allowed, because the "objection" mis-characterises a particular aspect = of them as an unintended consequence, when there's evidence to show = that's not that case. >>=20 >> Claude essentially dismisses the use of abstract static methods: >>=20 >>>> only consequences of their intended meaning on non-static class >>=20 >> In v5.2 a strict standards notice was added regarding the use of = abstract static methods in classes. No notice was ever shown when = they're used in interfaces. In v7 this notice was removed (via = https://wiki.php.net/rfc/reclassify_e_strict#abstract_static_methods) = because, as Nikita noted at the time: >=20 > Thank you for elaborating.=20 >=20 > We are on the same page here as I too think `abstract static` should = be allowed when declaring a class >=20 >>> We currently allow the use of abstract static functions in = interfaces, as such it is inconsistent to not allow them as abstract = methods. By using late static binding a method in the abstract class can = reasonably depend on the existence of a static method in a superclass.=20= >>=20 >>=20 >> That to me says this is an intended feature, and not an unintended = consequence. >=20 > Before I address this and your other comments, please understand that = I do not see this as a huge issue either way, but I do want my argument = to be understood. If my argument on this does not win the day, I will = not lament it in any way. >=20 > As an aside, I had never seen interfaces used that way and found it = surprising. Obviously I missed when Nikita made that claim. =20 >=20 > As for "an intended feature, and not an unintended consequence," when = I was in university they drilled the idea of a sunken cost into me to = the point I am a true believer. IOW, make the best decision moving = forward vs. doubling down on bad decisions.=20 >=20 > Note that I am not saying Nikita's was a bad decision (or a good one) = just that the existence of a prior decision should not color making the = best decision moving forward. >=20 I understand that, but I also think there is much value in consistency. = If static methods (and abstract static methods) are supported, they = should be supported consistently. There have been numerous RFCs passed = that bring consistency (and thus remove developer surprise) to the = language, even on features that some voters dislike so much, they'd = rather the feature is intentionally difficult to use, to discourage use = by other people (remember the trait constants RFC?) > I am curious, do you know of real-world userland code that is actually = configured as your example that would be negatively affected by = disallowing static methods calls? >=20 Not in that specific case, no. I've seen some that uses `abstract class` = as a poor mans `static class` (just to prevent instantiation) but not = that uses `abstract` to allow omitting an upstream method. >> On Jun 26, 2024, at 4:26 AM, Stephen Reay > wrote: >=20 >>=20 >=20 >> This is an example of code that works today (and all the way back to = 5.0): https://3v4l.org/4EKo2 >> The class hierarchy embody the type of classes this RFC is about: = only static members, no instantiation.=20 >=20 >>=20 >=20 >> The *implemented methods* can be called statically, regardless of = whether the class they're implemented in is abstract or not. The = *abstract methods* cannot be called directly. >=20 > Understood, but I do not see how interfaces or instantiation or = abstract method are relevant to the discussion. They all seem orthogonal = to me. A class cannot contain abstract methods if it is not also marked as = abstract. That's the whole point. If you prevent a static class from = also being abstract, you prevent that class from partially implementing = an interface (or parent class with abstract methods). >=20 >> So these classes would be a candidate for the `static` class keyword = (or Attribute) - except they can't, if calls to implemented methods on = abstract classes are disallowed. Because the Base::a() method has been = publicly callable, for potentially as long as 20 years = next month. >=20 > While it may be true that it has been that way, disallowing `static = methods` from being called using `abstract static` classes would not be = a BC break because it was previously impossible to declare a class as = `static` let alone `abstract static`. Right so I'm not actually saying this is a BC break itself - I'm saying = the authors of the library/code then can't adopt the `static` keyword, = which embodies their intent fully, because if they do so *their* code = has a BC break (code that was previously callable, it not any more).=20 >=20 > Further, there are often new features with constraints that result in = developer not being "to do what they have been able to do for 20 years" = because, looking forward, those constraints make more sense than not = having those constraints.=20 >=20 > Take a look at `readonly` properties. They must be typed, but I could = have used the same argument against that saying "We've always been able = to have untyped properties so readonly should not have to be typed."[1] = I'd say requiring them to be typed was a win, though, regardless of past = property capabilities. I don't think the *reason* something has limitations should be ignored. = It's not like Nikita said "we should disallow untyped properties because = otherwise we have to allow them forever". It was specifically to prevent = confusion due to the implicit null default on untyped properties. Also, = this restriction was quite easy to work around, thanks to the `mixed` = type introduced in 8.0. >=20 > I'm not claiming necessarily that disallowing calling `static` methods = on a `abstract static` class is a best practice, but I am saying that = "we've been able to do it with similar syntax for a long time" is not a = particularly compelling argument if disallowing is a better approach. >=20 > As another aside, assuming everyone agrees on what a best practice is = for a given case, I doubt anyone would object to a constraint that = forced developers to follow that best practice vs. allowing them to = write less ideal code. >=20 >> My point here is that if someone wants to prohibit calling public = static methods on abstract classes *with* the static keyword, that's = going be inconsistent with how it's worked for the last 20 years (i.e. = on classes that were 'static' in intent but not syntactically), >=20 > Back to sunken cost, does it make more sense to only give developers = who want to disallow using static methods for their `abstract static` = classes access to `@internal docblock` =E2=80=94 which I argued against = elsewhere in this thread =E2=80=94 and let Hyrum's Law take over, OR do = we enable them to disallow calling those `static` methods from the = `abstract` class itself? >=20 > More importantly, IF we do not immediately disallow calling `static` = methods on `abstract static` classes then we will never be able to = disallow in the future, because of BC. But on the flip side we could = later open them up for calling if we found the limitation to be = problematic. >=20 >> or if it applies the change everywhere it's going to be a BC break. >=20 > And to be clear, I am against BC breaks in almost all cases. So = bringing that up between us is moot. >=20 > All that said, if you tell me "I don't care about closing the door on = being able to disallow developers from calling static methods on = `abstract static` methods because prior to `abstract static` classes = `static` methods could be called on `abstract` classes in the past" I = will respect that, and as I said above, it won't bother me. =20 >=20 Don't care is the wrong term I think. As I've said, numerous times, I'm = concerned about consistency first and foremost. If the voting powers = that be deem calling a static method on an abstract class (regardless of = the class's other modifers/keywords) is too much of a pearl clutching = moment, and it's deprecated, I'll probably ask pretty loudly "why, what = actual problem does this prevent" but I'll appreciate that it's being = consistent, despite the BC break. > My only reason for persisting is to ensure that the argument I was = making was understood before it was dismissed. >=20 I think I understand your view: you're not as concerned with consistency = if the alternative is something "better".=20 > -Mike >=20 > [1] Ignoring there were technical issues with allowing untyped to be = readonly, which could have been gotten around somehow if the consensus = was "They must be able to untyped." >=20 Cheers Stephen=20 --Apple-Mail=_83B7CDE2-BA09-4D15-ABEB-647E60C4C774 Content-Transfer-Encoding: quoted-printable Content-Type: text/html; charset=utf-8

On 27 Jun 2024, at 13:11, Mike Schinkel = <mike@newclarity.net> wrote:


On Jun 27, = 2024, at 12:09 AM, Stephen Reay <php-lists@koalephant.com> = wrote:

Hi = Mike,

To answer your question: I believe `abstract static` should be = allowed, because the "objection" mis-characterises a particular aspect = of them as an unintended consequence, when there's evidence to show = that's not that case.

Claude essentially = dismisses the use of abstract static = methods:

only = consequences of their intended meaning on non-static = class

In v5.2 a = strict standards notice was added regarding the use of abstract static = methods in classes. No notice was ever shown when they're used in = interfaces.  In v7 this notice was removed (via https://wiki.php.net/rfc/reclassify_e_strict#abstract_static_methods) because, as Nikita noted at the = time:

Thank you for = elaborating. 

Before I = address this and your other comments, please understand that I do not = see this as a huge issue either way, but I do want my argument to be = understood. If my argument on this does not win the day, I will not = lament it in any way.



I am = curious, do you know of real-world userland code that is actually = configured as your example that would be negatively affected by = disallowing static methods = calls?


No= t in that specific case, no. I've seen some that uses `abstract class` = as a poor mans `static class` (just to prevent instantiation) but not = that uses `abstract` to allow omitting an upstream = method.


This is = an example of code that works today (and all the way back to = 5.0): https://3v4l.org/4EKo2
The class hierarchy embody the type = of classes this RFC is about: only static members, no = instantiation. 
<= div style=3D"overflow-wrap: break-word; -webkit-nbsp-mode: space; = line-break: after-white-space;">

The = *implemented methods* can be called statically, regardless of whether = the class they're implemented in is abstract or not. The *abstract = methods* cannot be called = directly.

Understood, but I do not see how interfaces or instantiation or = abstract method are relevant to the discussion. They all seem orthogonal = to me.

A class cannot = contain abstract methods if it is not also marked as abstract. That's = the whole point. If you prevent a static class from also being abstract, = you prevent that class from partially implementing an interface (or = parent class with abstract methods).


So these = classes would be a candidate for the `static` class keyword (or = Attribute) - except they can't, if calls to implemented methods on = abstract classes are disallowed. Because the Base::a() method has been = publicly callable, for potentially as long as <checks notes> =  20 years next = month.

While it may be true = that it has been that way, disallowing `static methods` from being = called using `abstract static` classes would not be a BC break because = it was previously impossible to declare a class as `static` let alone = `abstract = static`.

Right so I'm = not actually saying this is a BC break itself - I'm saying the authors = of the library/code then can't adopt the `static` keyword, which = embodies their intent fully, because if they do so *their* code has a BC = break (code that was previously callable, it not any = more). 


Further, there are often new = features with constraints that result in developer not being "to do = what they have been able to do for 20 years" because, looking = forward, those constraints make more sense than not having those = constraints. 

Take a look at `readonly` = properties. They must be typed, but I could have used the same argument = against that saying "We've always been able to have untyped = properties so readonly should not have to be typed."[1] I'd say requiring them to be typed was a = win, though, regardless of past = property capabilities.

=
I don't think the *reason* something has limitations should = be ignored. It's not like Nikita said "we should disallow untyped = properties because otherwise we have to allow them forever". It was = specifically to prevent confusion due to the implicit null default on = untyped properties.  Also, this restriction was quite easy to work = around, thanks to the `mixed` type introduced in = 8.0.


I'm not claiming necessarily = that disallowing calling `static` methods on a `abstract static` class = is a best practice, but I am saying that "we've been able to = do it with similar syntax for a long time" is not a particularly = compelling argument if disallowing is a better = approach.

As another aside, assuming everyone agrees on what a best practice = is for a given case, I doubt anyone would object to a = constraint that forced developers to follow that best practice vs. = allowing them to write less ideal = code.

My point here is that if someone = wants to prohibit calling public static methods on abstract classes = *with* the static keyword, that's going be inconsistent with how it's = worked for the last 20 years (i.e. on classes that were 'static' in = intent but not = syntactically),

Back = to sunken cost, does it make more sense to only give developers who want = to disallow using static methods for their `abstract = static` classes access to `@internal docblock` =E2=80=94 which I = argued against elsewhere in this thread =E2=80=94 and let Hyrum's Law = take over, OR do we enable them to disallow calling those `static` = methods from the `abstract` class itself?

More = importantly, IF we do not immediately disallow calling `static` methods = on `abstract static` classes then we will never be able to = disallow in the future, because of BC. But on the flip side we could = later open them up for calling if we found the limitation to be = problematic.

 or if it applies the change = everywhere it's going to be a BC = break.

And to be clear, I = am against BC breaks in almost all cases. So bringing that up between us = is moot.

All that said, if you tell me "I = don't care about closing the door on being able to disallow developers = from calling static methods on `abstract static` methods because prior = to `abstract static` classes `static` methods could be called on = `abstract` classes in the past" I will respect that, and as I = said above, it won't bother me. =  


Do= n't care is the wrong term I think. As I've said, numerous times, I'm = concerned about consistency first and foremost. If the voting powers = that be deem calling a static method on an abstract class (regardless of = the class's other modifers/keywords) is too much of a pearl clutching = moment, and it's deprecated, I'll probably ask pretty loudly "why, what = actual problem does this prevent" but I'll appreciate that it's being = consistent, despite the BC break.


My only = reason for persisting is to ensure that the argument I was making was = understood before it was = dismissed.


I think I understand your view: you're not as concerned with = consistency if the alternative is something = "better". 

-Mike

[1] =  Ignoring there were technical issues with allowing untyped to = be readonly, which could have been gotten around somehow if the = consensus was "They must be able to = untyped."



Cheers

Steph= en 

= --Apple-Mail=_83B7CDE2-BA09-4D15-ABEB-647E60C4C774--