[RFC] Static class

php.internals

Bilge

2 years ago
Hi Internals! I am pleased to present my first RFC: Static class <https://wiki.php.net/rfc/static_class>. This work is based on the previous discussion thread on this list of the same name, and hopefully captured all the relevant details, notwithstanding anything unanticipated that may present itself during the implementation. Let me know if you feel anything important has been missed. I am aware it omits to mention specifics about messages so emitted when runtime or compile-time errors occur, but I anticipate this can be hashed out in the PR, unless consensus indicates otherwise. I am aware this idea is not supported by everyone, but there seemed to be enough positive voices for it to be worth a shot. I'd like to get a better idea of where people might stand when it comes down to a vote, now there is a formal RFC, so we know whether it's worth completing the implementation, although any sentiments so proffered are of course not a commitment to vote any particular way and nobody should feel compelled to speak to that unless comfortable. Looking forward to feedback! Cheers, Bilge

Larry Garfield

2 years ago
On Sun, Jun 23, 2024, at 6:10 PM, Bilge wrote:
> Hi Internals! > > I am pleased to present my first RFC: Static class > <https://wiki.php.net/rfc/static_class>. > > This work is based on the previous discussion thread on this list of the > same name, and hopefully captured all the relevant details, > notwithstanding anything unanticipated that may present itself during > the implementation. Let me know if you feel anything important has been > missed. I am aware it omits to mention specifics about messages so > emitted when runtime or compile-time errors occur, but I anticipate this > can be hashed out in the PR, unless consensus indicates otherwise. > > I am aware this idea is not supported by everyone, but there seemed to > be enough positive voices for it to be worth a shot. I'd like to get a > better idea of where people might stand when it comes down to a vote, > now there is a formal RFC, so we know whether it's worth completing the > implementation, although any sentiments so proffered are of course not a > commitment to vote any particular way and nobody should feel compelled > to speak to that unless comfortable. Looking forward to feedback! > > Cheers, > Bilge
On the point of getting feedback on where people will vote now there's an RFC, I can only say with all honesty "Good luck." :-) This is by far the biggest challenge of making an RFC: the lack of a meaningful sense of how something will be received before the actual vote. Welcome to the club. For the record, as previously stated, I will be voting No on this RFC. --Larry Garfield

Bilge

2 years ago
On 24/06/2024 00:29, Larry Garfield wrote:
> On the point of getting feedback on where people will vote now there's an RFC, I can only say with all honesty "Good luck." :-) This is by far the biggest challenge of making an RFC: the lack of a meaningful sense of how something will be received before the actual vote. Welcome to the club. > > For the record, as previously stated, I will be voting No on this RFC. > > --Larry Garfield
Thanks, Larry. I appreciate that, and on that point, I appreciate you being clear where you stand. Cheers, Bilge.

Derick Rethans

2 years ago
On Sun, 23 Jun 2024, Larry Garfield wrote:
> On Sun, Jun 23, 2024, at 6:10 PM, Bilge wrote: > > Hi Internals! > > > > I am pleased to present my first RFC: Static class > > <https://wiki.php.net/rfc/static_class>. > > > > This work is based on the previous discussion thread on this list of the > > same name, and hopefully captured all the relevant details, > > notwithstanding anything unanticipated that may present itself during > > the implementation. Let me know if you feel anything important has been > > missed. I am aware it omits to mention specifics about messages so > > emitted when runtime or compile-time errors occur, but I anticipate this > > can be hashed out in the PR, unless consensus indicates otherwise. > > > > I am aware this idea is not supported by everyone, but there seemed to > > be enough positive voices for it to be worth a shot. I'd like to get a > > better idea of where people might stand when it comes down to a vote, > > now there is a formal RFC, so we know whether it's worth completing the > > implementation, although any sentiments so proffered are of course not a > > commitment to vote any particular way and nobody should feel compelled > > to speak to that unless comfortable. Looking forward to feedback!
<snip>
> > For the record, as previously stated, I will be voting No on this RFC.
Having read this thread, and the previous one from half a year ago, I will do so too. In short, we shouldn't be encouraging static classes as a bag of static functions, that ought to be just namespaced functions. cheers, Derick

Chuck Adams

2 years ago

Derick Rethans

2 years ago
On Tue, 25 Jun 2024, Chuck Adams wrote:
> > On Jun 25, 2024, at 9:17 AM, Derick Rethans <derick@php.net> wrote: > > > > Having read this thread, and the previous one from half a year ago, > > I will do so too. In short, we shouldn't be encouraging static > > classes as a bag of static functions, that ought to be just > > namespaced functions. > > Which brings us back to the age-old problem that functions can’t be > autoloaded. Me, I want first-class modules, but until we have those, I > have to settle for classes in the meantime. Scala/Kotlin-like > “companion objects” might be a good all-round substitute though.
There has been some work done on function autoloading too: https://wiki.php.net/rfc/core-autoloading cheers, Derick

Lanre

2 years ago
On Wed, Jun 26, 2024 at 5:52 PM Derick Rethans <derick@php.net> wrote:
> On Tue, 25 Jun 2024, Chuck Adams wrote: > > > > On Jun 25, 2024, at 9:17 AM, Derick Rethans <derick@php.net> wrote: > > > > > > Having read this thread, and the previous one from half a year ago, > > > I will do so too. In short, we shouldn't be encouraging static > > > classes as a bag of static functions, that ought to be just > > > namespaced functions. > > > > Which brings us back to the age-old problem that functions can’t be > > autoloaded. Me, I want first-class modules, but until we have those, I > > have to settle for classes in the meantime. Scala/Kotlin-like > > “companion objects” might be a good all-round substitute though. > > There has been some work done on function autoloading too: > https://wiki.php.net/rfc/core-autoloading > > cheers, > Derick > > My bad, I thought "top post" meant the same thing in mailing lists as it
does in forums. I am now aware of my mistake and it won't happen again. Can we address my actual points now? Lanre

Chuck Adams

2 years ago
> On Jun 26, 2024, at 5:49 PM, Derick Rethans <derick@php.net> wrote: > > There has been some work done on function autoloading too: > https://wiki.php.net/rfc/core-autoloading
Fantastic! I still want modules and/or companion objects of course... I wonder whether the autoloading machinery could be bent to such a purpose, or am I smoking the bad stuff again? —c

Lanre

2 years ago
In JavaScript/Typescript, we can declare functions that are local to the current file by not exporting them. In C/C++ this is achieved by simply writing the function in the implementation file as opposed to the header. However, achieving a similar level of privacy in PHP requires using private static methods, or else you incur the overhead of creating an object for stateless functions. How do you propose handling such cases using namespaced functions? Can you think of any scenarios where hiding non-API functions might be necessary? I'm curious why you keep returning to namespaced functions when they don't fulfill the same role. Cheers, Lanre On Tue, Jun 25, 2024 at 9:20 AM Derick Rethans <derick@php.net> wrote:

Larry Garfield

2 years ago
On Wed, Jun 26, 2024, at 6:05 PM, Lanre wrote:
> In JavaScript/Typescript, we can declare functions that are local to > the current file by not exporting them. In C/C++ this is achieved by > simply writing the function in the implementation file as opposed to > the header. However, achieving a similar level of privacy in PHP > requires using private static methods, or else you incur the overhead > of creating an object for stateless functions. How do you propose > handling such cases using namespaced functions? Can you think of any > scenarios where hiding non-API functions might be necessary? I'm > curious why you keep returning to namespaced functions when they don't > fulfill the same role. > > Cheers, > Lanre
Please don't top-post. PHP has no concept of packages at present, and thus no concept of package-level scope. A private static method is private not to a file or package, but to that class. (The class and file usually correlate in practice, but there's nothing in the language that mandates that.) I would love to have a concept of packages that we could use for scope, as most more recent languages have concluded that is the correct level at which to enforce visibility, NOT objects/classes. That's an entirely separate question, however. For marking a function as "package private", the same way you would for a package-private class today: @internal docblock. It's not ideal (the ideal would require packages), but it still communicates the intent, has worked for classes for 15 years at least, and signals "if you use this class/function/thing and it breaks later, that's on you, don't come crying to me." (ab)using static classes to emulate a pseudo-single-class-package is... abusing static. To be blunt, 90% of uses of static methods in PHP are a sign the developer doesn't actually understand OOP and is just writing procedural code with more steps. The other 10% are either named constructors or cases that can now be replaced with attributes. --Larry Garfield

Lanre

2 years ago
That isn't a top post, look again.And I am not referring to packages,rather to the ability to define functions that aren't exposed to the users of a library. C doesn't have packages either yet this is possible in it. And marking it as internal is not the same thing at all. I am not going to argue about why a developer would use static methods and whether that is actually a sign that the developer doesn't understand OOP. But I do want to get this straight. Instead of actually hiding the function, your idea is to mark it with @internal (via PHPDoc)? To you, that solves the problem? On Wed, Jun 26, 2024 at 1:46 PM Larry Garfield <larry@garfieldtech.com> wrote:

Derick Rethans

2 years ago
On Wed, 26 Jun 2024, Lanre wrote:
> That isn't a top post, look again.
It was. If people point out you're doing something against this list's guidelines, don't reply with "it wasn't" in a snarky way. cheers, Derick

Lanre

2 years ago
Also I specifically said "we can declare functions that are local to the current file by not exporting them.", Where did you get "package private" from? On Wed, Jun 26, 2024 at 1:46 PM Larry Garfield <larry@garfieldtech.com> wrote:

Mike Schinkel

2 years ago
Hi Larry,
> On Jun 26, 2024, at 2:21 PM, Larry Garfield <larry@garfieldtech.com <mailto:larry@garfieldtech.com>> wrote: > PHP has no concept of packages at present, and thus no concept of package-level scope.
Not entirely true. PHP has static classes, which are stand-ins for packages for as long as PHP does not inherently support the concept of packages. Arguing "that is abusing static" without offering an alternative is just Puritanistic logic that ignores pragmatic real world needs.
> For marking a function as "package private", the same way you would for a package-private class today: @internal docblock. It's not ideal (the ideal would require packages), but it still communicates the intent, has worked for classes for 15 years at least, and signals "if you use this class/function/thing and it breaks later, that's on you, don't come crying to me."
Not only is `@internal docblock` not ideal, it sets up those internal properties to be infected with usage as explained by Hyrum's Law[1]. Thus providing `@internal docblock` as if it were a viable alternative to package private is essentially a non-serious argument.
> PHP has no concept of packages at present, and thus no concept of package-level scope. A private static method is private not to a file or package, but to that class. (The class and file usually correlate in practice, but there's nothing in the language that mandates that.) I would love to have a concept of packages that we could use for scope, as most more recent languages have concluded that is the correct level at which to enforce visibility, NOT objects/classes. That's an entirely separate question, however.
If you really want people to stop "abusing static" then drive to address that entirely separate question. Otherwise let others who are more pragmatic give the language features that they need. As a quick aside, I would argue that PHP should look at its biggest warts — which to me include but are not limited to autoloading and namespaces — and deprecate them to be replaced with a proper package system that leverages a symbol compiler instead of autoloading and that ejects the god-awful choice of using the escape character for package name separation. But I digress.
> To be blunt, 90% of uses of static methods in PHP are a sign the developer doesn't actually understand OOP and is just writing procedural code with more steps. The other 10% are either named constructors or cases that can now be replaced with attributes.
Speaking of best practices, there is a growing contingent of software developers who feel that OOP should not be the future of software[2], e.g. that it was "a good idea at the time" but now no longer. My point here is that PHP should not let entrenched dogma drive language design but instead pragmatically evolve as other languages are also evolving. -Mike [1] https://www.laws-of-software.com/laws/hyrum/ <https://www.laws-of-software.com/laws/hyrum/> [2] https://news.ycombinator.com/item?id=8676872 <https://news.ycombinator.com/item?id=8676872>

Bilge

2 years ago
On 25/06/2024 16:17, Derick Rethans wrote:
> we shouldn't be encouraging static classes as > a bag of static functions, that ought to be just namespaced functions.
I understand this is the prevailing preference of a certain few, including (but not limited to) yourself and Larry. Nevertheless, classes as a bag of static functions is a phenomenon that exists and will continue to exist in PHP until such a time as `static` is completely removed from the language, so I don't really understand the avoidance of adding, in the interim, what seems like just a missing part of an existing feature. However, I still want to understand why a file of functions is strictly better than a class of functions. What are the benefits of a file over a class? For me, I dislike importing individual functions. It is often the case that, when such logic is so grouped in a static class, where we use one we may use several such functions. In such a case, we only need a single import statement instead of one for each function. This seems like a clear benefit. Even in the case that I only use one, I still prefer to import a class over a function, because it has better support in my editor and doesn't stick out like a sore thumb in the list of imports (why should it?). Cheers, Bilge

Bilge

2 years ago
On 25/06/2024 16:17, Derick Rethans wrote:
> we shouldn't be encouraging static classes as a bag of static functions, that ought to be just namespaced functions. > > cheers, > Derick
Can someone clue me in as to why grouping related functions in a file with nothing but a namespace is strictly better than those same related functions grouped as members of a class? It doesn't have to be Larry or Derick, because even though they have expressed this view, I am aware they are not the only ones whom hold it. Anyone who can shed some light on this perspective is welcome to comment. Cheers, Bilge

Lanre

2 years ago
On Thu, Jul 11, 2024 at 6:16 PM Bilge <bilge@scriptfusion.com> wrote:
> On 25/06/2024 16:17, Derick Rethans wrote: > > we shouldn't be encouraging static classes as a bag of static functions, that ought to be just namespaced functions. > > cheers, > Derick > > Can someone clue me in as to why grouping related functions in a file with > nothing but a namespace is strictly better than those same related > functions grouped as members of a class? It doesn't have to be Larry or > Derick, because even though they have expressed this view, I am aware they > are not the only ones whom hold it. Anyone who can shed some light on this > perspective is welcome to comment. > > Cheers, > Bilge >
It isn't implicitly better, but if PHP had proper support for autoloading functions, I would at least consider them equivalent when all the functions/methods are public. My primary language is C++, where the distinction is even blurrier since both static class members and namespace members are accessed with '::'. For example, X::Y() could be a namespaced function or a static method, and it doesn't actually matter which it is under the hood since they are functionally identical. In PHP, however, putting all your functions in a namespace is less performant than using static classes. For instance, if you have ten functions.php files each for a separate namespace, Composer will load all ten for every single request, regardless of whether they are used. Additionally, if you install a Composer package that registers some functions, those will also be loaded for every request. Cheers, Lanre

Mike Schinkel

2 years ago
> On Jul 11, 2024, at 8:14 PM, Bilge <bilge@scriptfusion.com> wrote: > > On 25/06/2024 16:17, Derick Rethans wrote: >> we shouldn't be encouraging static classes as >> a bag of static functions, that ought to be just namespaced functions. >> >> cheers, >> Derick >> > Can someone clue me in as to why grouping related functions in a file with nothing but a namespace is strictly better than those same related functions grouped as members of a class? It doesn't have to be Larry or Derick, because even though they have expressed this view, I am aware they are not the only ones whom hold it. Anyone who can shed some light on this perspective is welcome to comment.
I cannot answer your question as posed but I can give you a counter-reason, one supporting the use of static classes as a bag of static functions, at least with the feature set of PHP 8.4 and prior. If a developer needs to hide global state — and there are valid reasons for maintaining global state using static classes along with a related set of static functions. Minimally, when using streams with include*()/require*() e.g. include("myprotocol://${handle}") namespaces simply do not have the necessary visibility scoping features and thus are not an option. I am currently working on a PoC for packages in userland that uses static classes exactly in this way. I will hopefully be able to share working code for that soon. -Mike P.S. None of include*()/require*() accept a stream context AFAICT. If they did a developer could pass in a dependency object as a context, but currently (again, AFAICT) there is no way to associate a dependency object with an include*()/require*() call. The only approach I have been able to identify to allow access to retrieve and update a dependency object is to use a key into a array in a private static property with public static get/set methods. If context were added to include*()/require*() and/or visibility scoping where added to symbols within namespaces then this use-case would no longer be an argument for static classes. But unless and until then, static classes are the only way to go. #fwiw

Alexandru Pătrănescu

2 years ago
On Mon, Jun 24, 2024, 02:15 Bilge <bilge@scriptfusion.com> wrote:
> Hi Internals! > > I am pleased to present my first RFC: Static class > <https://wiki.php.net/rfc/static_class>. > >
Hi! and good luck with the RFC. While I don't use static classes, I can see how this can be used by others. I worked in big projects that used too many libraries, and when those libraries have functions.php file, they pile up and loading the composer autoloader can take a small dent on performance of each request. Having functions grouped by a class that can autoloader instead of a file is an advantage that should be spelled out as an use case. Feedback: 1. I agree that static classes may extend other non-static classes (that might have some static behavior). But I think that static classes should be extended only by other static classes, or at least for the first iteration of these feature. This should be documented more in the RFC, no matter how it ends up being. 1.1 I think using a static class as a type should be considered an error, as there will never be instances of it if we consider all child classes will also be static classes. Of course, this cannot be an error on php side given the class per file loading separation. 2. __set_state() is strongly linked to exporting a class instance as a valid php string, and is used to reconstruct the class instance upon execution. Considering we forbid instances, we should not support __set_state(). If you think otherwise, please share an example how it can be used. Regards, Alex

Bilge

2 years ago
On 24/06/2024 07:59, Alexandru Pătrănescu wrote:
> On Mon, Jun 24, 2024, 02:15 Bilge <bilge@scriptfusion.com> wrote: > > Hi Internals! > > I am pleased to present my first RFC: Static class > <https://wiki.php.net/rfc/static_class>. > > > Hi! and good luck with the RFC.
Hi Alex, and thanks for your excellent feedback!
> While I don't use static classes, I can see how this can be used by > others.
It is my sincere hope that everyone will vote with similar empathy.
> Feedback: > > 1. I agree that static classes may extend other non-static classes > (that might have some static behavior).
To be clear, they must /exclusively/ have static members, not just /some/.
> But I think that static classes should be extended only by other > static classes, or at least for the first iteration of these feature. > This should be documented more in the RFC, no matter how it ends up being.
This is an interesting debate, and something we may end up doing. I am currently undecided if it would be better to enforce full static class hierarchy, but I could definitely be so convinced.
> 1.1 I think using a static class as a type should be considered an > error, as there will never be instances of it if we consider all child > classes will also be static classes. > Of course, this cannot be an error on php side given the class per > file loading separation.
This is a very interesting point that I had not considered. I agree, it should be correct to forbid type hinting static classes. However, if I understood you correctly, we cannot actually implement that? My knowledge of engine internals is too weak to know the correct answer here. If it is possible, I think we should do it.
> 2. __set_state() is strongly linked to exporting a class instance as a > valid php string, and is used to reconstruct the class instance upon > execution. > Considering we forbid instances, we should not support __set_state(). > If you think otherwise, please share an example how it can be used.
Thank you for catching this. This is strictly a mistake on my part. I have never used, or even heard of, __set_state() in my life. I was merely perusing the magic methods to make sure I caught them all and it seemed, at a glance, this might apply to static classes, but as you point out, if this indeed requires an instance then it does not apply. I will remove this from the next version of the RFC, which I shall publish imminently. Many thanks, Bilge

Ayesh - PHP.Watch

2 years ago
> > This work is based on the previous discussion thread on this list of the > same name, and hopefully captured all the relevant details, > notwithstanding anything unanticipated that may present itself during > the implementation. Let me know if you feel anything important has been > missed. I am aware it omits to mention specifics about messages so > emitted when runtime or compile-time errors occur, but I anticipate this > can be hashed out in the PR, unless consensus indicates otherwise.
Hi Bilge, I was reading the mailing list discussions, and I am glad you created the RFC. Personally, I think this will be a useful feature. Just like we have `readonly` properties and `readonly` classes, I don't see why we can't have static methods. I also saw in the draft PR that it does not require a lot of changes to the engine to have this. I'd like to propose to add some more information and points addressed in the RFC. This is of course merely a suggestion, but it's something I was thinking when reading the RFC. - Why is it a class-level flag and not an attribute (similar to the `#[Override]` attribute in PHP 8.3) ? - Can a subclass extend a static parent class without using the `static` keyword in the subclass? This will avoid a lot of BC issues if a library decides to declare classes as static. - Are there any opt-out mechanisms for subclasses? - Can you mark interfaces, abstract classes, and Enums as static?

Tim Düsterhus

2 years ago
Hi On 6/24/24 09:53, Ayesh Karunaratne wrote:
> Personally, I think this will be a useful feature. Just like we have > `readonly` properties and `readonly` classes, I don't see why we can't > have static methods. I also saw in the draft PR that it does not > require a lot of changes to the engine to have this. > > I'd like to propose to add some more information and points addressed > in the RFC. This is of course merely a suggestion, but it's something > I was thinking when reading the RFC. > > - Why is it a class-level flag and not an attribute (similar to the > `#[Override]` attribute in PHP 8.3) ?
To quote myself from the #[\Override] RFC:
> This RFC proposes an attribute instead of a keyword, because contrary to other modifiers (e.g. visibility) that are part of the method signature, the attribute does not affect behavior or compatibility for users that further extend a given class and neither does it affect users that call the method. It is purely an assistance to the author of a given class. >
Marking a class as 'static' *does* affect how the class may be used and thus the static-ness is part of the public API. Therefore it should be a keyword. Everything else would also be inconsistent with final classes effectively making all members final and readonly classes making all members readonly.
> - Can a subclass extend a static parent class without using the > `static` keyword in the subclass? This will avoid a lot of BC issues > if a library decides to declare classes as static.
It should not. See: readonly classes. I also believe that static classes should be implictly final. Given that no objects of such a class can exist, they cannot be exchanged by a different class anyways and inheritance is not meant for code reuse. Best regards Tim Düsterhus

Lanre

2 years ago
On Thu, Jun 27, 2024 at 1:18 PM Tim Düsterhus <tim@bastelstu.be> wrote:
> It should not. See: readonly classes. I also believe that static classes > should be implictly final. Given that no objects of such a class can > exist, they cannot be exchanged by a different class anyways and > inheritance is not meant for code reuse. > >
I don't agree that they should be implicitly final, or that inheritance is not meant for code reuse. It is currently possible eg: https://gist.github.com/oplanre/6894f56fb61134ee5ea93cf262ba3662 and I don't see why that should change, unless you have any solid reasons as to why. Lanre

Mike Schinkel

2 years ago
> On Jun 27, 2024, at 2:11 PM, Tim Düsterhus <tim@bastelstu.be> wrote: > Marking a class as 'static' *does* affect how the class may be used and thus the static-ness is part of the public API. Therefore it should be a keyword. Everything else would also be inconsistent with final classes effectively making all members final and readonly classes making all members readonly.
Very insightful; that is a great rule of thumb.
>> I also believe that static classes should be implictly final. Given that no objects of such a class can exist, they cannot be exchanged by a different class anyways
I object here. There are functions in classes that I have built in the past that are static and also benefit from inheritance. Could they be called directly on the base class; yes? But that is less elegant and increases complexity with two levels of inheritance are needed. Specifically the functionality I am thinking of is the base class is generic functionality for factory functions and then child factory functions implement use-case specific logic for the type of the object produced. For example, each of these functions has a MakeNew(...) function, and children call self::MakeNew(...). Base -- Term -- User -- BasePost -- Post -- Page That is a subset of classes I used, and those classes were designed for use with WordPress. Further, these classes implemented "hooks" in WordPress. Using inheritance allowed all the hooks to be managed without repeating them in many places, being able to modify them for a class-specific need, and also without accidentally forgetting to add them.
>> and inheritance is not meant for code reuse.
Just because code reuse in inheritance can be problematic it does not have to be in all-cases. Moderation in all things. I used that approach for 10+ years and never once had any of the problems people claim about using inheritance for code reuse. This was likely because my needs were constrained by the use-case and by nature did not grow out of control with complexity. So I would really hate to finally get static classes but still not be able to use them as intended for select use-cases only because someone decided without knowing all applicable use-cases that they did not like the idea of people using inheritance. -Mike

Lynn

2 years ago
On Fri, Jun 28, 2024 at 2:48 AM Mike Schinkel <mike@newclarity.net> wrote:
> >> and inheritance is not meant for code reuse. > > Just because code reuse in inheritance can be problematic it does not have > to be in all-cases. Moderation in all things. I used that approach for 10+ > years and never once had any of the problems people claim about using > inheritance for code reuse. This was likely because my needs were > constrained by the use-case and by nature did not grow out of control with > complexity. >
My experience is the opposite. There are subtle bugs I keep running into with static function and properties causing unexpected behavior. I'm not against having static classes open by default for the sake of consistency, though my preference would be to avoid the headache altogether and just make them final by default so I won't ever have to deal with it. Would traits not solve the problem of horizontal reuse?

Lanre

2 years ago
On Fri, Jun 28, 2024 at 1:05 AM Lynn <kjarli@gmail.com> wrote:
> > > On Fri, Jun 28, 2024 at 2:48 AM Mike Schinkel <mike@newclarity.net> wrote: > >> >> and inheritance is not meant for code reuse. >> >> Just because code reuse in inheritance can be problematic it does not >> have to be in all-cases. Moderation in all things. I used that approach for >> 10+ years and never once had any of the problems people claim about using >> inheritance for code reuse. This was likely because my needs were >> constrained by the use-case and by nature did not grow out of control with >> complexity. >> > > My experience is the opposite. There are subtle bugs I keep running into > with static function and properties causing unexpected behavior. I'm not > against having static classes open by default for the sake of consistency, > though my preference would be to avoid the headache altogether and just > make them final by default so I won't ever have to deal with it. Would > traits not solve the problem of horizontal reuse? >
Sounds like a 'you' problem. Can you go into more details about said subtle bugs? With examples if possible? Lanre

Stephen Reay

2 years ago
> On 28 Jun 2024, at 14:05, Lynn <kjarli@gmail.com> wrote: > > > > On Fri, Jun 28, 2024 at 2:48 AM Mike Schinkel <mike@newclarity.net <mailto:mike@newclarity.net>> wrote: >> >> and inheritance is not meant for code reuse. >> >> Just because code reuse in inheritance can be problematic it does not have to be in all-cases. Moderation in all things. I used that approach for 10+ years and never once had any of the problems people claim about using inheritance for code reuse. This was likely because my needs were constrained by the use-case and by nature did not grow out of control with complexity. > > My experience is the opposite. There are subtle bugs I keep running into with static function and properties causing unexpected behavior. I'm not against having static classes open by default for the sake of consistency, though my preference would be to avoid the headache altogether and just make them final by default so I won't ever have to deal with it. Would traits not solve the problem of horizontal reuse?
Hi Lynn, I understand it's frustrating when there are bugs/unexpected behaviour, but wouldn't the (existing) *ability* to add the `final` keyword to a class solve those issues for you, without preventing others from using the capabilities of parent/child class relationships? Cheers Stephen

Lynn

2 years ago
On Fri, Jun 28, 2024 at 8:04 PM Stephen Reay <php-lists@koalephant.com> wrote:
> > > On 28 Jun 2024, at 14:05, Lynn <kjarli@gmail.com> wrote: > > > > On Fri, Jun 28, 2024 at 2:48 AM Mike Schinkel <mike@newclarity.net> wrote: > >> >> and inheritance is not meant for code reuse. >> >> Just because code reuse in inheritance can be problematic it does not >> have to be in all-cases. Moderation in all things. I used that approach for >> 10+ years and never once had any of the problems people claim about using >> inheritance for code reuse. This was likely because my needs were >> constrained by the use-case and by nature did not grow out of control with >> complexity. >> > > My experience is the opposite. There are subtle bugs I keep running into > with static function and properties causing unexpected behavior. I'm not > against having static classes open by default for the sake of consistency, > though my preference would be to avoid the headache altogether and just > make them final by default so I won't ever have to deal with it. Would > traits not solve the problem of horizontal reuse? > > > Hi Lynn, > > I understand it's frustrating when there are bugs/unexpected behaviour, > but wouldn't the (existing) *ability* to add the `final` keyword to a class > solve those issues for you, without preventing others from using the > capabilities of parent/child class relationships? > > > Cheers > > > Stephen >
The main issue is that it's easy to mess up by default and not messing up requires you to understand why. I prefer defensive programming, close it and only open when it's really required. If there's legit scenarios where you need inheritance in a static context the requirement could be an "open" keyword or removing it being final, we can't do it the other way around without massive BC break.

Bilge

2 years ago
Hi Ayesh, On 24/06/2024 08:53, Ayesh Karunaratne wrote:
> - Why is it a class-level flag and not an attribute (similar to the `#[Override]` attribute in PHP 8.3) ?
I believe Tim already answered this, and I understood his reasoning to be that keywords are used when they modify behaviour in a way that is meaningful for the consumer of that class. Or to put it another way, attributes are purely informational. For me personally, introducing an attribute never crossed my mind, and since we already have the keyword in the language performing a similar function in PHP, and other languages already, it seems nonsense to contemplate anything else.
> - Can a subclass extend a static parent class without using the > `static` keyword in the subclass? This will avoid a lot of BC issues > if a library decides to declare classes as static.
No, a static parent implies a static child, but implied or not, it must still be specified explicitly. I may need to clarify that in the RFC, so thanks for the question.
> - Are there any opt-out mechanisms for subclasses?
Not sure what you mean exactly, but if you mean, can a static subclass suddenly become non-static, the answer is no.Again thanks for the question, as this is also something I had no previously considered.
> - Can you mark interfaces, abstract classes, and Enums as static?
It seems others avoided addressing this question, and honestly I was not really sure where to go with this one. What would it mean for an enum to be static? I thought the whole point was we can type a group of values, but static classes have no meaning within the type system, so I cannot see the application. As for abstract classes, the current version of the RFC (1.2) now declares static mutually exclusive with abstract, so the answer there is also no. For interfaces, I haven't given this a great deal of thought. In principle I don't think a static interface is especially useful, but I could be wrong about this. Nevertheless, it is not something I intend for this RFC, since even just static classes already have their detractors, and expanding its scope any further is probably placing it further in danger of rejection. So for now, the answer is also no, but static interfaces seem like a reasonable extension to this work if there is interest in future. Cheers, Bilge

Tim Düsterhus

2 years ago
Hi On 6/29/24 17:56, Bilge wrote:
>> - Why is it a class-level flag and not an attribute (similar to the `#[Override]` attribute in PHP 8.3) ? > I believe Tim already answered this, and I understood his reasoning to > be that keywords are used when they modify behaviour in a way that is > meaningful for the consumer of that class. Or to put it another way, > attributes are purely informational. For me personally, introducing an
You understood that correctly. The forward compatibility argument brought forward by the other participants is not really useful either, because consider the following: #[StaticClass] class Foo { public static function myFunc() { } } class Bar extends Foo { public function myNonStaticFunc() { } } This would work fine in PHP 8.3, but break when upgrading to PHP 8.4! By using a keyword it is guaranteed that the code only runs on PHP versions that will reliably enforce all the constraints, preventing this gotcha during a PHP version upgrade. Best regards Tim Düsterhus

Claude Pache

2 years ago
> Le 24 juin 2024 à 01:10, Bilge <bilge@scriptfusion.com> a écrit : > > Hi Internals! > > I am pleased to present my first RFC: Static class <https://wiki.php.net/rfc/static_class>. > > This work is based on the previous discussion thread on this list of the same name, and hopefully captured all the relevant details, notwithstanding anything unanticipated that may present itself during the implementation. Let me know if you feel anything important has been missed. I am aware it omits to mention specifics about messages so emitted when runtime or compile-time errors occur, but I anticipate this can be hashed out in the PR, unless consensus indicates otherwise. > > I am aware this idea is not supported by everyone, but there seemed to be enough positive voices for it to be worth a shot. I'd like to get a better idea of where people might stand when it comes down to a vote, now there is a formal RFC, so we know whether it's worth completing the implementation, although any sentiments so proffered are of course not a commitment to vote any particular way and nobody should feel compelled to speak to that unless comfortable. Looking forward to feedback! > > Cheers, > Bilge
Hi, A general remark: I appreciate that a static class does not impose arbitrary restrictions (like: implicitly `final`) on the class beyond what is meaningful for staticness. On the other side, I don’t think that we should support markers that are technically possible, but semantically meaningless, like the `readonly` marker on class. ----- Some more specific remarks: * In the intro: “A static class is a class whose members (properties and methods) are all static”. One of the most important point is missing, namely: It is meaningless for a static class to have instances. * In the “Proposal” section, it should be stated explicitly that any attempt to construct an instance, not only with `new`, but also with any of the `ReflectionClass::newInstance*()` methods, or with `unserialize()`, or with whatever other mean, shall fail. ----- Should a static class be marked `readonly` or `abstract`? I think not, because those have no real semantic meaning for static class; their effects on static members are only consequences of their intended meaning on non-static class: * Unless/until the `readonly` marker may be applied to static properties, the only effect of such a keyword, is that it would prevent the creation of static properties. I don’t know if that restriction is useful, but in case it would be used for that purpose, it would be hijacking the `readonly` marker for a something it wasn’t intended for. * The main purpose of the `abstract` keyword is to prevent a class to be instantiated, which (in case of static class) is more semantically described by the `static` marker. Beyond that, it just allows to declare a method that, if implemented by a subclass, should have a compatible signature. Most notably, it does not prevent the other static members of the class to be used directly. ---- The RFC says that a static class may extend a class not explicitly marked as static, but with no instance member. This is not sound, because a class with no instance members is not necessarily static. The most obvious example is `stdClass` (which has no member at all, even if their instances may have properties). —Claude

Andreas Hennings

2 years ago
> I am pleased to present my first RFC: Static class
To recap my opinion from the other thread: I generally support the idea to have a label to slap on all-static classes, to make that contract more explicit. The goal is not to promote a specific use of the language, but to add clarity to what already exists. Since my last response on the other thread, I warmed up more to namespaced floating functions, thanks Larry :) At the same time I don't see static methods and all-static classes fully going away soon, so I still support them having a label. But...
> Why is it a class-level flag and not an attribute (similar to the `#[Override]` attribute in PHP 8.3) ?
I don't know that it needs to be a language keyword. An attribute would be ok for me, or even just a doc tag with phpstan. In fact maybe a feature request with phpstan could be a starting point.
> One question our razor cannot answer is whether static member declarations still require the static keyword in a static class. Marking a class readonly makes marking properties with the same optional, thus one might argue static should follow suit. However, I opine it should not. Unlike readonly, which typically applies to properties declared near the head of the class (by convention) where the same mark is also applied, static members appear throughout the full length of the class body. That is, it should be apparent when jumping anywhere into a static class that its members are static, without having to check the class declaration.
I prefer that the static keyword be required on the members. This supports the common use case of incremental refactoring, where methods are moved from one class to another, or a git merge where one branch adds the class-level static keyword, while another commit adds another method.
> We cannot remove features from a static class that would otherwise be present in a standard class.
I think I get the intent of this statement, but I don't really vibe with the wording. I would say we should limit the scope of a modifier keyword to its essence. And we should not remove features that are already being used in classes that would naturally qualify for the class-level static modifier.

Bilge

2 years ago
On 24/06/2024 09:27, Claude Pache wrote:
> Hi,
Hi Claude! I really appreciate your feedback. Everything you highlighted is an important point that will be included in the RFC!
> A general remark: I appreciate that a static class does not impose arbitrary restrictions (like: implicitly `final`) on the class beyond what is meaningful for staticness. On the other side, I don’t think that we should support markers that are technically possible, but semantically meaningless, like the `readonly` marker on class.
Great point, `readonly` and `static` should be mutually exclusive and generate a compile-time error, since `readonly static` properties are not supported.
> Some more specific remarks: > > * In the intro: “A static class is a class whose members (properties and methods) are all static”. One of the most important point is missing, namely: It is meaningless for a static class to have instances.
I thought that was covered off, but I'll see if I can word it better.
> * In the “Proposal” section, it should be stated explicitly that any attempt to construct an instance, not only with `new`, but also with any of the `ReflectionClass::newInstance*()` methods, or with `unserialize()`, or with whatever other mean, shall fail.
Great point, I'll include that detail.I think we're also missing a similar detail with respect to dynamic properties, which should of course also be forbidden.
> Should a static class be marked `readonly` or `abstract`? I think not, because those have no real semantic meaning for static class; their effects on static members are only consequences of their intended meaning on non-static class: > > * Unless/until the `readonly` marker may be applied to static properties, the only effect of such a keyword, is that it would prevent the creation of static properties. I don’t know if that restriction is useful, but in case it would be used for that purpose, it would be hijacking the `readonly` marker for a something it wasn’t intended for.
Agree, as above, we'll make them mutually exclusive.
> * The main purpose of the `abstract` keyword is to prevent a class to be instantiated, which (in case of static class) is more semantically described by the `static` marker. Beyond that, it just allows to declare a method that, if implemented by a subclass, should have a compatible signature. Most notably, it does not prevent the other static members of the class to be used directly.
I tend to find inheritance is not a very useful concept in static contexts, but others have already pointed out that some have found uses for it. Due to my lack of experience I cannot confidently say that `abstract static` has no value, but you make a compelling argument. Happy to add a similar mutual exclusivity prohibition for this keyword too, unless and until someone protests it with an equally compelling argument to the contrary.
> The RFC says that a static class may extend a class not explicitly marked as static, but with no instance member. This is not sound, because a class with no instance members is not necessarily static. The most obvious example is `stdClass` (which has no member at all, even if their instances may have properties).
Do you mean it is not simply sufficient for a class to be regarded as implicitly static by virtue of the fact that it has no instance members? I'm not sure I agree, but I may be missing something. If we extend `stdClass` then we gain nothing, and our class so extending it can still be safely marked static, can it not? Please elaborate so I might understand better. Kind regards, Bilge

Robert Landers

2 years ago
On Mon, Jun 24, 2024 at 10:22 PM Bilge <bilge@scriptfusion.com> wrote:
> > On 24/06/2024 09:27, Claude Pache wrote: > > Hi, > > Hi Claude! I really appreciate your feedback. Everything you highlighted is an important point that will be included in the RFC! > > A general remark: I appreciate that a static class does not impose arbitrary restrictions (like: implicitly `final`) on the class beyond what is meaningful for staticness. On the other side, I don’t think that we should support markers that are technically possible, but semantically meaningless, like the `readonly` marker on class. > > Great point, `readonly` and `static` should be mutually exclusive and generate a compile-time error, since `readonly static` properties are not supported. > > Some more specific remarks: > > * In the intro: “A static class is a class whose members (properties and methods) are all static”. One of the most important point is missing, namely: It is meaningless for a static class to have instances. > > I thought that was covered off, but I'll see if I can word it better. > > * In the “Proposal” section, it should be stated explicitly that any attempt to construct an instance, not only with `new`, but also with any of the `ReflectionClass::newInstance*()` methods, or with `unserialize()`, or with whatever other mean, shall fail. > > Great point, I'll include that detail. I think we're also missing a similar detail with respect to dynamic properties, which should of course also be forbidden. > > Should a static class be marked `readonly` or `abstract`? I think not, because those have no real semantic meaning for static class; their effects on static members are only consequences of their intended meaning on non-static class: > > * Unless/until the `readonly` marker may be applied to static properties, the only effect of such a keyword, is that it would prevent the creation of static properties. I don’t know if that restriction is useful, but in case it would be used for that purpose, it would be hijacking the `readonly` marker for a something it wasn’t intended for. > > Agree, as above, we'll make them mutually exclusive. > > * The main purpose of the `abstract` keyword is to prevent a class to be instantiated, which (in case of static class) is more semantically described by the `static` marker. Beyond that, it just allows to declare a method that, if implemented by a subclass, should have a compatible signature. Most notably, it does not prevent the other static members of the class to be used directly. > > I tend to find inheritance is not a very useful concept in static contexts, but others have already pointed out that some have found uses for it. Due to my lack of experience I cannot confidently say that `abstract static` has no value, but you make a compelling argument. Happy to add a similar mutual exclusivity prohibition for this keyword too, unless and until someone protests it with an equally compelling argument to the contrary. > > The RFC says that a static class may extend a class not explicitly marked as static, but with no instance member. This is not sound, because a class with no instance members is not necessarily static. The most obvious example is `stdClass` (which has no member at all, even if their instances may have properties). > > Do you mean it is not simply sufficient for a class to be regarded as implicitly static by virtue of the fact that it has no instance members? I'm not sure I agree, but I may be missing something. If we extend `stdClass` then we gain nothing, and our class so extending it can still be safely marked static, can it not? Please elaborate so I might understand better. > > Kind regards, > Bilge
Hey Bilge,
> Do you mean it is not simply sufficient for a class to be regarded as implicitly static by virtue of the fact that it has no instance members? I'm not sure I agree, but I may be missing something. If we extend `stdClass` then we gain nothing, and our class so extending it can still be safely marked static, can it not? Please elaborate so I might understand better.
I think it is sound in theory, but in practice, it will be a foot gun. Imagine if you were to take my HTTP code library and make something static from it. Later, I added an instance member. Now, when you upgrade, everything starts crashing because of one little change unrelated to your code.

Mike Schinkel

2 years ago
> On Jun 24, 2024, at 3:53 AM, Ayesh Karunaratne <ayesh@php.watch> wrote: > - Why is it a class-level flag and not an attribute (similar to the > `#[Override]` attribute in PHP 8.3) ?
From my perspective that would create much confusion among every PHP developer who has not committed to memory when to use `static` as a class keyword and when to use it as an attribute. Given the concept already exists in keywords I would strongly argue that it makes no sense to introduce as an attributes in core PHP. Attributes are great for concepts new to PHP, IMO, but not for existing concepts already part of PHP implemented with keywords.
> On Jun 24, 2024, at 4:27 AM, Claude Pache <claude.pache@gmail.com> wrote: > * The main purpose of the `abstract` keyword is to prevent a class to be instantiated, which (in case of static class) is more semantically described by the `static` marker. Beyond that, it just allows to declare a method that, if implemented by a subclass, should have a compatible signature. Most notably, it does not prevent the other static members of the class to be used directly.
Given a primary purpose for being able to declare a class `static` is to signal intent, disallowing `abstract static` classes seems at odds with that goal. Of course it is currently true that `static` methods of `abstract` classes can be called from outside a class and its class hierarchy, so if we allow declaring `abstract static` classes then it would never in the future be possible to lock down calls of `static` methods to those `abstract` classes. So IMO it would be better to disallow calling `static` methods from outside a declared `abstract static` class and its inheritance hierarchy as part of this RFC. That would be backward compatible since there are currently no classes that are declared in that manner. Doing otherwise would force those who want to declare a class as both `static` and `abstract` to have to make a choice rather than being able to signal their full intent. Which brings us back to the "implied" vs. "explicitly declared" bifurcation I mentioned in a prior email. BTW, I am assuming it is technically possible to disallow calling methods for classes declared both `abstract` and `static` without considerable difficulty in implementation and without creating a significant drain on performance. -Mike P.S. I would argue the same for `readonly static` properties, but as that seems those would require an involved discussion about the exact rules to implement I am demurring on that topic.

Stephen Reay

2 years ago
> On 25 Jun 2024, at 09:22, Mike Schinkel <mike@newclarity.net> wrote: > >> On Jun 24, 2024, at 3:53 AM, Ayesh Karunaratne <ayesh@php.watch <mailto:ayesh@php.watch>> wrote: >> - Why is it a class-level flag and not an attribute (similar to the >> `#[Override]` attribute in PHP 8.3) ? > > From my perspective that would create much confusion among every PHP developer who has not committed to memory when to use `static` as a class keyword and when to use it as an attribute. > > Given the concept already exists in keywords I would strongly argue that it makes no sense to introduce as an attributes in core PHP. > > Attributes are great for concepts new to PHP, IMO, but not for existing concepts already part of PHP implemented with keywords. > >> On Jun 24, 2024, at 4:27 AM, Claude Pache <claude.pache@gmail.com <mailto:claude.pache@gmail.com>> wrote: >> * The main purpose of the `abstract` keyword is to prevent a class to be instantiated, which (in case of static class) is more semantically described by the `static` marker. Beyond that, it just allows to declare a method that, if implemented by a subclass, should have a compatible signature. Most notably, it does not prevent the other static members of the class to be used directly. > > Given a primary purpose for being able to declare a class `static` is to signal intent, disallowing `abstract static` classes seems at odds with that goal. > > Of course it is currently true that `static` methods of `abstract` classes can be called from outside a class and its class hierarchy, so if we allow declaring `abstract static` classes then it would never in the future be possible to lock down calls of `static` methods to those `abstract` classes. > > So IMO it would be better to disallow calling `static` methods from outside a declared `abstract static` class and its inheritance hierarchy as part of this RFC. That would be backward compatible since there are currently no classes that are declared in that manner. Doing otherwise would force those who want to declare a class as both `static` and `abstract` to have to make a choice rather than being able to signal their full intent. Which brings us back to the "implied" vs. "explicitly declared" bifurcation I mentioned in a prior email. > > BTW, I am assuming it is technically possible to disallow calling methods for classes declared both `abstract` and `static` without considerable difficulty in implementation and without creating a significant drain on performance. > > -Mike > > P.S. I would argue the same for `readonly static` properties, but as that seems those would require an involved discussion about the exact rules to implement I am demurring on that topic.
Hi Mike,
> So IMO it would be better to disallow calling `static` methods from outside a declared `abstract static` class
Can you explain what you mean by this, or more specifically the "why". What (implied wrong/bad) scenario are you trying to prevent, by disallowing a static method on an abstract class to be called? As you point out, it's already possible to call (public) static methods on abstract classes, the same as it's possible to call (public) static methods without instantiating a regular class. So why should static abstract classes be different? If the static method shouldn't be called publicly, it shouldn't be public. Are you suggesting that public static methods shouldn't be callable on *any* abstract class? If so, this sounds like a huge BC break, and if not, it sounds like a confusing "if-then-but-why" scenario. I agree that the `static` keyword is a much better fit here, however there is one other aspect here that may come into it (as much as I prefer the keyword approach): the Attribute approach is backwards compatible (with a polyfill) to let code using this feature also run on previous PHP releases. Given that this is mostly intended as a way to signal intent about a pattern that's already in use, this may be significant for library authors. Personally (as a library author) I don't think that ability is worth the weirdness of an attribute vs a keyword, but it may be more important for others who are voting, and I'd rather have the feature with slightly odd syntax rather than not at all. Cheers Stephen

Bilge

2 years ago
Hi guys, On 25/06/2024 04:17, Stephen Reay wrote:
>> >> Given a primary purpose for being able to declare a class >> `static` is to *signal intent*, disallowing `abstract static` >> classes seems at odds with that goal. >>
What is the /intent/ of `abstract static`? How is such intent different from just `static`?
> I agree that the `static` keyword is a much better fit here, however > there is one other aspect here that may come into it (as much as I > prefer the keyword approach): the Attribute approach is backwards > compatible (with a polyfill) to let code using this feature also run > on previous PHP releases. Given that this is mostly intended as a way > to signal intent about a pattern that's already in use, this may be > significant for library authors. > > Personally (as a library author) I don't think that ability is worth > the weirdness of an attribute vs a keyword, but it may be more > important for others who are voting, and I'd rather have the feature > with slightly odd syntax rather than not at all.
When I first saw the proposal to use an attribute instead of the keyword, I thought it absurd, but this idea has now been entertained by three people, and for the first time I have seen a compelling argument in favour of (early adoption). I must admit, I was too quick to judge this one. I had not considered that libraries will still not be able to use the `static` modifier with classes unless and until they drop support for PHP < 8.4, which may take a while! Of course, it is still of real benefit to first-party proprietary projects whom have upgraded. Nevertheless, the allure of early adoption is curious, and made me wonder whether we could do both, just to support early adoption in a backwards-compatible manner. However, this would be unprecedented and most likely not accepted; never before has the same feature been implemented two ways just to appease early adopters. I think the best compromise would be, for anyone so eager, to implement such support in community tools, e.g. PHPStan. That is, it should be perfectly possible to enforce the core semantics of the static class feature with a userland attribute and the necessary logic in static analysis tools, provided the library is well behaved and doesn't do anything too weird with variable variables, references, reflection or unserialize() to deliberately circumvent the restriction. Cheers, Bilge

Stephen Reay

2 years ago
Hi,
> On 25 Jun 2024, at 15:03, Bilge <bilge@scriptfusion.com> wrote: > > Hi guys, > > On 25/06/2024 04:17, Stephen Reay wrote: >>> Given a primary purpose for being able to declare a class `static` is to signal intent, disallowing `abstract static` classes seems at odds with that goal. > What is the intent of `abstract static`? How is such intent different from just `static`?
Sorry I should have been clearer. It isn't that `abstract` represents an intent, it's that adding `static` is mostly about conveying intent - and thus there's no *technical* reason that *any* existing class with only static members can't be marked as a static class, so why introduce an artificial barrier if that class happens to already be abstract? It's already possible to have a class with only static members, that is abstract, and call the public static methods of it. It's also possible to have a class that partially implements an interface that has static methods on it. Unless your RFC is going to propose preventing calling static methods on *all* abstract classes it's a needless complication when there's no technical reason to do so.
>> I agree that the `static` keyword is a much better fit here, however there is one other aspect here that may come into it (as much as I prefer the keyword approach): the Attribute approach is backwards compatible (with a polyfill) to let code using this feature also run on previous PHP releases. Given that this is mostly intended as a way to signal intent about a pattern that's already in use, this may be significant for library authors. >> >> Personally (as a library author) I don't think that ability is worth the weirdness of an attribute vs a keyword, but it may be more important for others who are voting, and I'd rather have the feature with slightly odd syntax rather than not at all. > When I first saw the proposal to use an attribute instead of the keyword, I thought it absurd, but this idea has now been entertained by three people, and for the first time I have seen a compelling argument in favour of (early adoption). I must admit, I was too quick to judge this one. I had not considered that libraries will still not be able to use the `static` modifier with classes unless and until they drop support for PHP < 8.4, which may take a while! Of course, it is still of real benefit to first-party proprietary projects whom have upgraded. Nevertheless, the allure of early adoption is curious, and made me wonder whether we could do both, just to support early adoption in a backwards-compatible manner. However, this would be unprecedented and most likely not accepted; never before has the same feature been implemented two ways just to appease early adopters. I think the best compromise would be, for anyone so eager, to implement such support in community tools, e.g. PHPStan. That is, it should be perfectly possible to enforce the core semantics of the static class feature with a userland attribute and the necessary logic in static analysis tools, provided the library is well behaved and doesn't do anything too weird with variable variables, references, reflection or unserialize() to deliberately circumvent the restriction.
Yes it should be possible to use a user land attribute or phpdoc tag or what have, but that's *already* true. You've essentially just made a case against your own RFC - that it can mostly be done already in userland. The entire appeal of a *builtin* attribute would be that it's something you could apply *now* (as in today, June 2024) and know that it won't *break* anything, while having some {compile,run} time benefits in later versions (e.g. enforcing static-only, and inability to instantiate). Like I said, I much prefer the keyword syntax - but I also recognise that others may have different priorities in terms of supporting older language versions, which is why I think that aspect is worth consideration (perhaps a secondary vote, or an informal vote to gauge consensus?), because too many good RFCs get rejected over small details.
> Cheers, > Bilge >
Cheers Stephen

Tim Düsterhus

2 years ago
Hi On 6/25/24 10:41, Stephen Reay wrote:
> Like I said, I much prefer the keyword syntax - but I also recognise that others may have different priorities in terms of supporting older language versions, which is why I think that aspect is worth consideration (perhaps a secondary vote, or an informal vote to gauge consensus?), because too many good RFCs get rejected over small details.
Syntax is absolutely not a small detail. It's the most user-visible part of a language feature that is not just a new part in the standard library. And that is probably the reason why it's the most hotly debated part of any new language feature that is proposed. As said in my previous email, I strongly oppose inventing new syntax and instead borrow syntax from existing (related) features as much as possible. An example that comes to my mind would be the initial brace-based syntax for the clone-with RFC that has since been replaced by array-style syntax that should be immediately understandable by any PHP programmer. PHP should continue to look and feel like PHP. Best regards Tim Düsterhus

Mike Schinkel

2 years ago
> On Jun 25, 2024, at 4:03 AM, Bilge <bilge@scriptfusion.com> wrote: > Nevertheless, the allure of early adoption is curious, and made me wonder whether we could do both, just to support early adoption in a backwards-compatible manner.
And that is probably the best solution I've heard regarding this quandary. It could potentially apply with other keywords in the future as well. #fwiw -Mike

Mike Schinkel

2 years ago
> On Jun 24, 2024, at 11:17 PM, Stephen Reay <php-lists@koalephant.com> wrote: > > > >> On 25 Jun 2024, at 09:22, Mike Schinkel <mike@newclarity.net> wrote: >> >>> On Jun 24, 2024, at 3:53 AM, Ayesh Karunaratne <ayesh@php.watch <mailto:ayesh@php.watch>> wrote: >>> - Why is it a class-level flag and not an attribute (similar to the >>> `#[Override]` attribute in PHP 8.3) ? >> >> From my perspective that would create much confusion among every PHP developer who has not committed to memory when to use `static` as a class keyword and when to use it as an attribute. >> >> Given the concept already exists in keywords I would strongly argue that it makes no sense to introduce as an attributes in core PHP. >> >> Attributes are great for concepts new to PHP, IMO, but not for existing concepts already part of PHP implemented with keywords. >> >>> On Jun 24, 2024, at 4:27 AM, Claude Pache <claude.pache@gmail.com <mailto:claude.pache@gmail.com>> wrote: >>> * The main purpose of the `abstract` keyword is to prevent a class to be instantiated, which (in case of static class) is more semantically described by the `static` marker. Beyond that, it just allows to declare a method that, if implemented by a subclass, should have a compatible signature. Most notably, it does not prevent the other static members of the class to be used directly. >> >> Given a primary purpose for being able to declare a class `static` is to signal intent, disallowing `abstract static` classes seems at odds with that goal. >> >> Of course it is currently true that `static` methods of `abstract` classes can be called from outside a class and its class hierarchy, so if we allow declaring `abstract static` classes then it would never in the future be possible to lock down calls of `static` methods to those `abstract` classes. >> >> So IMO it would be better to disallow calling `static` methods from outside a declared `abstract static` class and its inheritance hierarchy as part of this RFC. That would be backward compatible since there are currently no classes that are declared in that manner. Doing otherwise would force those who want to declare a class as both `static` and `abstract` to have to make a choice rather than being able to signal their full intent. Which brings us back to the "implied" vs. "explicitly declared" bifurcation I mentioned in a prior email. >> >> BTW, I am assuming it is technically possible to disallow calling methods for classes declared both `abstract` and `static` without considerable difficulty in implementation and without creating a significant drain on performance. >> >> -Mike >> >> P.S. I would argue the same for `readonly static` properties, but as that seems those would require an involved discussion about the exact rules to implement I am demurring on that topic. > > Hi Mike, > > >> So IMO it would be better to disallow calling `static` methods from outside a declared `abstract static` class > > Can you explain what you mean by this, or more specifically the "why". What (implied wrong/bad) scenario are you trying to prevent, by disallowing a static method on an abstract class to be called? > > As you point out, it's already possible to call (public) static methods on abstract classes, the same as it's possible to call (public) static methods without instantiating a regular class. > > So why should static abstract classes be different? If the static method shouldn't be called publicly, it shouldn't be public. Are you suggesting that public static methods shouldn't be callable on *any* abstract class? If so, this sounds like a huge BC break, and if not, it sounds like a confusing "if-then-but-why" scenario.
Thank you for the question. I was not specifically advocating `static` methods to be disallowed to be called on an `abstract` class. I was instead addressing the desire by another list member to have classes declared `static` not also be able to be declared `abstract.` That person made the argument that if 'abstract static` did not actually have any effect then it should not be allowed by the RFC. What affect are we talking about? If it is both `abstract` and `static` then it would seem to me the only effect such a declaration could have would be to disallow the calling of static methods using the named abstract static class, e.g.: abstract static class Base { static public foo() { echo "foo() called\n" } } static class Concrete extends Base {} Concrete::foo(); // works Base::foo(); // fails My only real argument for disallowing static method calls on abstract static classes is that if we didn't do so initially we would never be able to disallow because of BC. In summary, my argument was really only about ensuring we allow "abstract static" declaration so developers can signal intent and also to address the objections of the other list member. But if given the option, I think it would be good to disallow calling static methods on abstract static classes if only to reserve the right to allow in the future vs. closing that door immediately. Still, that latter is not the hill for me to die on.
> I agree that the `static` keyword is a much better fit here, however there is one other aspect here that may come into it (as much as I prefer the keyword approach): the Attribute approach is backwards compatible (with a polyfill) to let code using this feature also run on previous PHP releases. Given that this is mostly intended as a way to signal intent about a pattern that's already in use, this may be significant for library authors.
Well, as previously stated, I think that would be confusing. Though I do see your reasoning for wanting it to be an attribute. However...
> Personally (as a library author) I don't think that ability is worth the weirdness of an attribute vs a keyword, but it may be more important for others who are voting, and I'd rather have the feature with slightly odd syntax rather than not at all.
I too am not convinced that the ongoing confusion would be better than the temporary need to support older versions of PHP with the same source file. Given that a build step could address the syntax there are certainly other new language features that require developers to bifurcate their libraries to support different versions, why make this one unique? That said, it could be a voting option for the RFC? -Mike

Stephen Reay

2 years ago
> On 26 Jun 2024, at 12:51, Mike Schinkel <mike@newclarity.net> wrote: > >> On Jun 24, 2024, at 11:17 PM, Stephen Reay <php-lists@koalephant.com <mailto:php-lists@koalephant.com>> wrote: >> >> >> >>> On 25 Jun 2024, at 09:22, Mike Schinkel <mike@newclarity.net <mailto:mike@newclarity.net>> wrote: >>> >>>> On Jun 24, 2024, at 3:53 AM, Ayesh Karunaratne <ayesh@php.watch <mailto:ayesh@php.watch>> wrote: >>>> - Why is it a class-level flag and not an attribute (similar to the >>>> `#[Override]` attribute in PHP 8.3) ? >>> >>> From my perspective that would create much confusion among every PHP developer who has not committed to memory when to use `static` as a class keyword and when to use it as an attribute. >>> >>> Given the concept already exists in keywords I would strongly argue that it makes no sense to introduce as an attributes in core PHP. >>> >>> Attributes are great for concepts new to PHP, IMO, but not for existing concepts already part of PHP implemented with keywords. >>> >>>> On Jun 24, 2024, at 4:27 AM, Claude Pache <claude.pache@gmail.com <mailto:claude.pache@gmail.com>> wrote: >>>> * The main purpose of the `abstract` keyword is to prevent a class to be instantiated, which (in case of static class) is more semantically described by the `static` marker. Beyond that, it just allows to declare a method that, if implemented by a subclass, should have a compatible signature. Most notably, it does not prevent the other static members of the class to be used directly. >>> >>> Given a primary purpose for being able to declare a class `static` is to signal intent, disallowing `abstract static` classes seems at odds with that goal. >>> >>> Of course it is currently true that `static` methods of `abstract` classes can be called from outside a class and its class hierarchy, so if we allow declaring `abstract static` classes then it would never in the future be possible to lock down calls of `static` methods to those `abstract` classes. >>> >>> So IMO it would be better to disallow calling `static` methods from outside a declared `abstract static` class and its inheritance hierarchy as part of this RFC. That would be backward compatible since there are currently no classes that are declared in that manner. Doing otherwise would force those who want to declare a class as both `static` and `abstract` to have to make a choice rather than being able to signal their full intent. Which brings us back to the "implied" vs. "explicitly declared" bifurcation I mentioned in a prior email. >>> >>> BTW, I am assuming it is technically possible to disallow calling methods for classes declared both `abstract` and `static` without considerable difficulty in implementation and without creating a significant drain on performance. >>> >>> -Mike >>> >>> P.S. I would argue the same for `readonly static` properties, but as that seems those would require an involved discussion about the exact rules to implement I am demurring on that topic. >> >> Hi Mike, >> >> >>> So IMO it would be better to disallow calling `static` methods from outside a declared `abstract static` class >> >> Can you explain what you mean by this, or more specifically the "why". What (implied wrong/bad) scenario are you trying to prevent, by disallowing a static method on an abstract class to be called? >> >> As you point out, it's already possible to call (public) static methods on abstract classes, the same as it's possible to call (public) static methods without instantiating a regular class. >> >> So why should static abstract classes be different? If the static method shouldn't be called publicly, it shouldn't be public. Are you suggesting that public static methods shouldn't be callable on *any* abstract class? If so, this sounds like a huge BC break, and if not, it sounds like a confusing "if-then-but-why" scenario. > > Thank you for the question. > > I was not specifically advocating `static` methods to be disallowed to be called on an `abstract` class. I was instead addressing the desire by another list member to have classes declared `static` not also be able to be declared `abstract.` That person made the argument that if 'abstract static` did not actually have any effect then it should not be allowed by the RFC. >
> What affect are we talking about? If it is both `abstract` and `static` then it would seem to me the only effect such a declaration could have would be to disallow the calling of static methods using the named abstract static class, e.g.: > > abstract static class Base { > static public foo() { > echo "foo() called\n" > } > } > static class Concrete extends Base {} > > Concrete::foo(); // works > Base::foo(); // fails > > My only real argument for disallowing static method calls on abstract static classes is that if we didn't do so initially we would never be able to disallow because of BC. > > In summary, my argument was really only about ensuring we allow "abstract static" declaration so developers can signal intent and also to address the objections of the other list member. But if given the option, I think it would be good to disallow calling static methods on abstract static classes if only to reserve the right to allow in the future vs. closing that door immediately. Still, that latter is not the hill for me to die on. > >> I agree that the `static` keyword is a much better fit here, however there is one other aspect here that may come into it (as much as I prefer the keyword approach): the Attribute approach is backwards compatible (with a polyfill) to let code using this feature also run on previous PHP releases. Given that this is mostly intended as a way to signal intent about a pattern that's already in use, this may be significant for library authors. > > Well, as previously stated, I think that would be confusing. > > Though I do see your reasoning for wanting it to be an attribute. However... > >> Personally (as a library author) I don't think that ability is worth the weirdness of an attribute vs a keyword, but it may be more important for others who are voting, and I'd rather have the feature with slightly odd syntax rather than not at all. > > I too am not convinced that the ongoing confusion would be better than the temporary need to support older versions of PHP with the same source file. > > Given that a build step could address the syntax there are certainly other new language features that require developers to bifurcate their libraries to support different versions, why make this one unique? > > That said, it could be a voting option for the RFC? > > > -Mike
Hi Mike, 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. 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. 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. 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), or if it applies the change everywhere it's going to be a BC break. Re: attribute vs keyword, Like I said - I'm not personally in favour of the attribute approach; I'm simply making the point (playing devils advocate if you will) that by the very nature of how attributes work, they're more friendly to supporting older versions of the language with a single code base. If the general consensus is that it isn't a concern, great. But this wouldn't be the first time we've seen a relatively minor syntax choice derail an otherwise useful RFC. Cheers Stephen

Mike Schinkel

2 years ago
Hi Stephen,
> On Jun 26, 2024, at 4:26 AM, Stephen Reay <php-lists@koalephant.com> wrote: > > This is an example of code that works today (and all the way back to 5.0): https://3v4l.org/4EKo2 <https://3v4l.org/4EKo2> > > The class hierarchy embody the type of classes this RFC is about: only static members, no instantiation. > > 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. > > 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. > > 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), or if it applies the change everywhere it's going to be a BC break.
It seems what you are asking about is downstream from the reason I stated that I made the argument in the first place, which AFAICT you did not acknowledge. That leaves me unsure of your position on a precursor topic to your above stated objections. As stated, my primary (initial) reason for arguing that `abstract static` should disallow calling static methods using the class name of the static class marked abstract — as my earlier example of calling `Base::foo()` illustrated` — was in response to the argument against allowing `abstract` with `static` because — in Claude's words — it would "have no real semantic meaning for static class; their effects on static members are only consequences of their intended meaning on non-static class." I have copied his complete words on that topic below:
> On Jun 24, 2024, at 4:27 AM, Claude Pache <claude.pache@gmail.com> wrote: > > Should a static class be marked `abstract`? I think not, because those have no real semantic meaning for static class; their effects on static members are only consequences of their intended meaning on non-static class: > > * The main purpose of the `abstract` keyword is to prevent a class to be instantiated, which (in case of static class) is more semantically described by the `static` marker. Beyond that, it just allows to declare a method that, if implemented by a subclass, should have a compatible signature. Most notably, it does not prevent the other static members of the class to be used directly.
Thus my argument supporting `abstract static` was addressing his objections by suggesting that we could have an `abstract static class` declaration *disallow* calling any of the `static` methods of the class using the name of the class declared `abstract` given that it would in fact not cause any BC breaks with untouched code. Before you repeat your objections to disallowing, permit me to ask you which of the following upstream arguments you are making? Is your position that we should: 1. Disallow `abstract` on `static` as Claude argued? 2. Allow `abstract static` in spite of Claude's argument against it? Also, if you answer "2. Allow `abstract static`" then please how do you address Claude's objections? Once I am clear of your position on two above I will address your objections stated in your most recent response to me, which I quoted at the beginning of this email. Thank you in advance for the clarity. -Mike

Stephen Reay

2 years ago
> On 27 Jun 2024, at 10:11, Mike Schinkel <mike@newclarity.net> wrote: > > Hi Stephen, > >> On Jun 26, 2024, at 4:26 AM, Stephen Reay <php-lists@koalephant.com <mailto:php-lists@koalephant.com>> wrote: >> >> 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. >> >> 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. >> >> 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. >> >> 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), or if it applies the change everywhere it's going to be a BC break. > > It seems what you are asking about is downstream from the reason I stated that I made the argument in the first place, which AFAICT you did not acknowledge. That leaves me unsure of your position on a precursor topic to your above stated objections. > > As stated, my primary (initial) reason for arguing that `abstract static` should disallow calling static methods using the class name of the static class marked abstract — as my earlier example of calling `Base::foo()` illustrated` — was in response to the argument against allowing `abstract` with `static` because — in Claude's words — it would "have no real semantic meaning for static class; their effects on static members are only consequences of their intended meaning on non-static class." I have copied his complete words on that topic below: > >> On Jun 24, 2024, at 4:27 AM, Claude Pache <claude.pache@gmail.com <mailto:claude.pache@gmail.com>> wrote: >> >> Should a static class be marked `abstract`? I think not, because those have no real semantic meaning for static class; their effects on static members are only consequences of their intended meaning on non-static class: >> >> * The main purpose of the `abstract` keyword is to prevent a class to be instantiated, which (in case of static class) is more semantically described by the `static` marker. Beyond that, it just allows to declare a method that, if implemented by a subclass, should have a compatible signature. Most notably, it does not prevent the other static members of the class to be used directly. > > Thus my argument supporting `abstract static` was addressing his objections by suggesting that we could have an `abstract static class` declaration *disallow* calling any of the `static` methods of the class using the name of the class declared `abstract` given that it would in fact not cause any BC breaks with untouched code. > > Before you repeat your objections to disallowing, permit me to ask you which of the following upstream arguments you are making? Is your position that we should: > > 1. Disallow `abstract` on `static` as Claude argued? > 2. Allow `abstract static` in spite of Claude's argument against it? > > Also, if you answer "2. Allow `abstract static`" then please how do you address Claude's objections? > > Once I am clear of your position on two above I will address your objections stated in your most recent response to me, which I quoted at the beginning of this email. Thank you in advance for the clarity. > > -Mike
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:
> 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.
That to me says this is an intended feature, and not an unintended consequence. Cheers Stephen

Mike Schinkel

2 years ago
> 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 <https://wiki.php.net/rfc/reclassify_e_strict#abstract_static_methods>) because, as Nikita noted at the time:
Thank you for elaborating. We are on the same page here as I too think `abstract static` should be allowed when declaring a class
>> 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. > > > That to me says this is an intended feature, and not an unintended consequence.
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. As an aside, I had never seen interfaces used that way and found it surprising. Obviously I missed when Nikita made that claim. 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. 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. 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?
> On Jun 26, 2024, at 4:26 AM, Stephen Reay <php-lists@koalephant.com <mailto:php-lists@koalephant.com>> wrote:
>
> This is an example of code that works today (and all the way back to 5.0): https://3v4l.org/4EKo2 <https://3v4l.org/4EKo2>The class hierarchy embody the type of classes this RFC is about: only static members, no instantiation.
>
> 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.
> 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`. 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'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` — which I argued against elsewhere in this thread — 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. My only reason for persisting is to ensure that the argument I was making was understood before it was dismissed. -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."

Stephen Reay

2 years ago
> 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 <mailto: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. > > We are on the same page here as I too think `abstract static` should be allowed when declaring a class > >>> 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. >> >> >> That to me says this is an intended feature, and not an unintended consequence. > > 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. > > As an aside, I had never seen interfaces used that way and found it surprising. Obviously I missed when Nikita made that claim. > > 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. > > 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. >
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? >
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 <php-lists@koalephant.com <mailto:php-lists@koalephant.com>> wrote: > >> > >> 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. > >> > >> 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` — which I argued against elsewhere in this thread — 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. >
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. >
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 Stephen

Tim Düsterhus

2 years ago
Hi On 6/25/24 05:17, Stephen Reay wrote:
> and I'd rather have the feature with slightly odd syntax rather than not at all.
Absolutely not. PHP is already odd enough as it is. We should not add additional oddness in the interest of pushing a new screw-shaped feature in with a sledgehammer. Any additional oddity makes it harder for Junior developers and developers coming from a different language to learn PHP and also required the developer to learn the language details by heart instead of being able to rely on their intuition. I'm already leaning towards "Don't want static classes", but if they use an attribute instead of a keyword, then it's going to be a clear "no". See also my email in response to Ayesh. Best regards Tim Düsterhus PS: Can you please cut the quoted parts to a minimum instead of including the original email in full? That makes it much clearer to see what part you are actually replying to and reduces the amount of storage requiring for all the thousands of ML subscribers :-)

Bilge

2 years ago
Hi Tim, On 27/06/2024 19:24, Tim Düsterhus wrote:
> On 6/25/24 05:17, Stephen Reay wrote: >> and I'd rather have the feature with slightly odd syntax rather than >> not at all. > > Absolutely not. PHP is already odd enough as it is. We should not add > additional oddness in the interest of pushing a new screw-shaped > feature in with a sledgehammer. Any additional oddity makes it harder > for Junior developers and developers coming from a different language > to learn PHP and also required the developer to learn the language > details by heart instead of being able to rely on their intuition.
Don't worry, I couldn't bring myself to seriously entertain using an attribute instead of a keyword. I'll update the RFC to include it as a rejected feature.
> I'm already leaning towards "Don't want static classes"
Why? It is important to understand what your misgiving are in case there anything we can do to assuage them. Cheers, Bilge

Tim Düsterhus

2 years ago
Hi On 6/28/24 21:29, Bilge wrote:
>> I'm already leaning towards "Don't want static classes" > > Why? It is important to understand what your misgiving are in case there > anything we can do to assuage them.
I don't see value in being able to explicitly mark a class as `static` for the reasons that have already been brought forward by other participants (such as Larry). But more importantly, the RFC and discussion so far don't really convince me that any of the participants in favor of static classes really know what they want and thought about *all* the implications of that. I strongly favor an opinionated RFC where the RFC author did their research and makes it clear why the proposal is the right choice and backs this up by proper arguments. Of course this doesn't mean that the RFC author should not listen to the list discussion, but the high level details should be clear right from the beginning. As of now the RFC still has some open questions regarding "core functionality" and even intents to leave them as a secondary vote. In other words, too many cooks spoil the broth. Given the upcoming feature freeze, the current list volume and core folks being busy with finishing up and polishing their own RFCs for PHP 8.4, the timing doesn't really help getting the necessary attention towards your RFC either. I might be more receptive towards a quieter period of the year, where I can take the time to really think about the RFC, like I do for other RFCs as well. My earlier responses towards the Lazy Objects RFC might be a good example towards the level of detail I try to give RFCs. Even when I don't personally understand them or don't see myself using them (yet), I want a best-possible result that will not cause sadness 3 years down the road. Best regards Tim Düsterhus

Mike Schinkel

2 years ago
> On Jun 30, 2024, at 11:10 AM, Tim Düsterhus <tim@bastelstu.be> wrote: > I strongly favor an opinionated RFC where the RFC author did their research and makes it clear why the proposal is the right choice and backs this up by proper arguments. Of course this doesn't mean that the RFC author should not listen to the list discussion, but the high level details should be clear right from the beginning. As of now the RFC still has some open questions regarding "core functionality" and even intents to leave them as a secondary vote.
With respect, I completely support your right to choose what you favor. However, I would like to suggest there might be some negative consequences to your approach and request that you reconsider your what you favor. Yes, if an RFC author does their research and makes it clear why the proposal is the right choice and backs this up by proper arguments then it makes for a better RFC which can more easily be reviewed. OTOH, it limits the pool of RFC authors who can successfully achieve that bar to only those who know what research needs to be done and which arguments resonate well resonate with list members. Effectively it limits the successful pool of RFC authors to the list of prior successful RFC authors who — through experience — have developed enough knowledge about what research needs to be done and which arguments resonate well with list members. Yes someone can overcome these experience barriers by submitting numerous RFCs that fail and over years of time eventually gain enough knowledge and experience to succeed, but realistically how many people will go through that gauntlet? The problem with the approach is there over time there becomes is a diminishing number of individuals who can and will submit RFCs, and the language slowly dies: https://thenewstack.io/why-php-usage-has-declined-by-40-in-just-over-2-years/ Currently the culture of this list is people submit an RFC and if they can endure the crucible to come out the other side their RFC may be adopted. But few can endure that crucible. Further, brainstorming on the list — as recent evidence has shown — is effectively impossible. The experienced RFC submitters know to go off list and work with other experienced RFC submitters to prepare an RFC prior to having to endure an onslaught of criticism from this list. The problem for new RFC writers is they don't know who to ask nor have the clout to approach people to get such collaborators. IMO it would be better if the culture here approached would-be RFC writers differently. I instead experienced RFC writers provided a bit of mentoring to new RFC writers, encourage them to cultivate their ideas in a version-controlled repo off this list, but also have the mentor call for list members to collaborate with the would-be RFC writer in a discussion forum on their repo. Once those collaborators feel the RFC is fully-baked then they could present to the list. If that culture existed, your strongly favored well-researched and clearly-argued RFC could be the norm rather than the exception. Food for thought? -Mike P.S. I currently have a repo for an RFC where I want to make a call for collaboration, but I do not know how to call for help on the list without attracting those who would immediately swarm the discussion forum to flood the channel with criticism before the ideas are even fully baked.

Bilge

2 years ago
On 30/06/2024 16:10, Tim Düsterhus wrote:
> I strongly favor an opinionated RFC where the RFC author did their > research and makes it clear why the proposal is the right choice and > backs this up by proper arguments. Of course this doesn't mean that > the RFC author should not listen to the list discussion, but the high > level details should be clear right from the beginning. As of now the > RFC still has some open questions regarding "core functionality" and > even intents to leave them as a secondary vote. > > In other words, too many cooks spoil the broth.
I won't pretend I had the knowledge and experience to tackle this alone. I stood on the shoulders of giants to make this PR possible, and even before PR, I didn't have a clear view when I first casually strolled into internals. Talking to the experts here helped me find the words I was searching for and ensure edge cases were considered. That said, I hope you don't think me soft, this was not a design by committee; the RFC is a product of my own work and the decisions now made are a result of my own convictions, bolstered but not coerced by the input of others. Moreover, now the RFC is finished at version 1.3, I am very proud of what it has become and stand by every implementation decision made pertaining to specific semantics; I am convinced they are correct and would not benefit from any further modification, with the benefit of experience writing the implementation behind me. I hope you, too, are equally convinced if you would now humble me with another read. Kind regards, Bilge

Jordi Boggiano

2 years ago
On 2024-06-24 01:10, Bilge wrote:
> I am pleased to present my first RFC: Static class > <https://wiki.php.net/rfc/static_class>.
Just one point of feedback: for clarity I think it would be good if it mentions that creating dynamic properties i.e. $this->dynamicProp = 'bar' would also throw at runtime (or even compile time if doable?). As for voting intention, it sounds totally reasonable to me, and while I don't see myself using it much I don't see a reason to be against it, so 👍🏻 Best Jordi Boggiano @seldaek - https://seld.be

Bilge

2 years ago
Hi Jordi, On 24/06/2024 13:22, Jordi Boggiano wrote:
> On 2024-06-24 01:10, Bilge wrote: >> I am pleased to present my first RFC: Static class >> <https://wiki.php.net/rfc/static_class>. > Just one point of feedback: for clarity I think it would be good if it > mentions that creating dynamic properties i.e. $this->dynamicProp = > 'bar' would also throw at runtime (or even compile time if doable?).
Absolutely. I'll include a note about this in the RFC.
> As for voting intention, it sounds totally reasonable to me, and while > I don't see myself using it much I don't see a reason to be against > it, so 👍🏻
Thank you! 🙏🏻 Kind regards, Bilge P.S. Thanks for Composer 🙂

Bilge

2 years ago
Hi again Jordi, On 24/06/2024 21:21, Bilge wrote:
> Hi Jordi, > > On 24/06/2024 13:22, Jordi Boggiano wrote: >> On 2024-06-24 01:10, Bilge wrote: >>> I am pleased to present my first RFC: Static class >>> <https://wiki.php.net/rfc/static_class>. >> Just one point of feedback: for clarity I think it would be good if >> it mentions that creating dynamic properties i.e. $this->dynamicProp >> = 'bar' would also throw at runtime (or even compile time if doable?). > Absolutely. I'll include a note about this in the RFC.
On second thoughts, I don't think dynamic properties can apply at all. The notion of `$this` is a reference to an object instance; static classes cannot be instantiated, so the concept of dynamic properties simply cannot exist, and that's really all there is to it 😅 (unless I'm missing something). Cheers, Bilge

Jordi Boggiano

2 years ago
On 2024-06-24 01:10, Bilge wrote:
>>>> I am pleased to present my first RFC: Static class >>>> <https://wiki.php.net/rfc/static_class>. >>> Just one point of feedback: for clarity I think it would be good if >>> it mentions that creating dynamic properties i.e. $this->dynamicProp >>> = 'bar' would also throw at runtime (or even compile time if doable?). >> Absolutely. I'll include a note about this in the RFC. > > On second thoughts, I don't think dynamic properties can apply at all. > The notion of `$this` is a reference to an object instance; static > classes cannot be instantiated, so the concept of dynamic properties > simply cannot exist, and that's really all there is to it 😅 (unless > I'm missing something).
Yeah sorry that is indeed not needed as it already works that way https://3v4l.org/TYIiV It is not compile time though, but that's a separate concern. Jordi Boggiano @seldaek - https://seld.be

Alex Wells

2 years ago
Hey Bilge, I'm not usually a resident of these discussions, but it seems like this RFC is heading into a wrong direction. Let me explain: as it currently stands, static properties and methods live separately from instance properties and methods. That means a couple of limitations, namely: a static class member can never implement an interface's contract, and hence a static class could never be used as an instance; static class members have a separate reflection API; static class members cannot have expression initializers; there's no static constructor; and so on. Adding `static classes` would not solve any of the above issues, and they would still be barely useful. To counter these issues, Kotlin has a concept of `data objects`: < https://kotlinlang.org/docs/object-declarations.html#data-objects>. They look like regular classes, but only one instance of a data object is created - when it's first accessed by other code. The closest alternative in PHP would be an enum with a single case. Unlike static classes, `data objects` don't have any of the problems associated with static members. They can be passed around as an instance of a class. This allows implementing interfaces, and replacing a `data object` with a regular class instance without having to change all member access from static to instance. They would also be able to inherit the syntax and semantics of regular class constructors: ``` object Converter { private array $map = new Map(); public function __construct() { $this->map['miles to km'] = 1.6; } public function convert(string $type, float $value): float { return $value * $this->map[$type]; } } doSomeConversion(Converter::object); // If `Converter` is ever to become a regular class, none of the related code would require changes. function doSomeConversion(Converter $converter) { $result = $someUtils->convert('miles to km', 123); } ``` That would be functionally equivalent to: ``` class Converter { private static self $instance; public static function instance(): self { return self::$instance ??= new self(); } public function convert(float $something): float {} } doSomeConversion(Converter::instance()); ``` Of course, this is already possible, as shown in the second example. But so is this RFC. Implementing data objects would actually bring them on-par with regular classes, and likely cause less drama overall. On Mon, Jun 24, 2024 at 2:14 AM Bilge <bilge@scriptfusion.com> wrote:

Bilge

2 years ago
On 24/06/2024 22:56, Alex Wells wrote:
> Hey Bilge, > > I'm not usually a resident of these discussions, but it seems like > this RFC is heading into a wrong direction. Let me explain: as it > currently stands, static properties and methods live separately from > instance properties and methods. That means a couple of limitations, > namely: a static class member can never implement an interface's > contract, and hence a static class could never be used as an instance; > static class members have a separate reflection API; static class > members cannot have expression initializers; there's no static > constructor; and so on. Adding `static classes` would not solve any of > the above issues, and they would still be barely useful. > > To counter these issues, Kotlin has a concept of `data objects`: > <https://kotlinlang.org/docs/object-declarations.html#data-objects>.
Hi Alex, If you wish to implement data objects, what part of the current proposal precludes you from doing so? Cheers, Bilge

Alex Wells

2 years ago
On Tue, Jun 25, 2024 at 1:56 AM Bilge <bilge@scriptfusion.com> wrote:
> Hi Alex, > > If you wish to implement data objects, what part of the current proposal > precludes you from doing so? > >
None, but they both try to solve the same problem, so it's highly unlikely that data objects would ever be considered if this RFC is accepted. And while static classes fit better with lots of existing PHP code, data classes (or a similar proposal) would bring more benefit to the table in the long run, which is why I was hoping for an alternative solution to be considered and/or discussed. I'm not planning to pursue the data objects proposal further, but it was still worth mentioning so the internals are aware of the alternatives to this RFC.

Alexandru Pătrănescu

2 years ago
On Tue, Jun 25, 2024 at 1:00 AM Alex Wells <autaut03@gmail.com> wrote:
> > a static class member can never implement an interface's contract >
Well, PHP is a weird one here compared to other languages Interfaces can contain contracts for static methods and they are checked just like usual non-static methods. Example: https://3v4l.org/8cuXW With late static binding, you can define static abstract methods and basically do code reuse through inheritance. Example: https://3v4l.org/pid7W I'm not saying I would ever use such a thing, just that they exist and others might find them useful so why build limits around it? A limit is just extra code to maintain and make sure you use it consistently. I believe that a static class should have the principle of no allowed instance and only static methods, without any impact on inheritance: - There can be a static interface and a static abstract class - A static class can extend a non-static class, or implement an interface or abstract class as long as it does not require to implement a non-static method. - A static class can be extended only by another static class - As static interface can be extended only by a static class Basically, when a static class gets into the inheritance chain, all the extending classes will be static and there is a guarantee that there will never be an instance of that class. Making sure a static class does just that, limiting the methods and properties in it to only be static, will keep things simpler going forward when thinking about interactions with other features. Like readonly static class would not work right now, but it will work if readonly will be implemented for static properties, and that should be doable effortless. Alex

Benjamin Eberlei

2 years ago
On Mon, Jun 24, 2024 at 1:15 AM Bilge <bilge@scriptfusion.com> wrote:
> Hi Internals! > > I am pleased to present my first RFC: Static class > <https://wiki.php.net/rfc/static_class>. > > This work is based on the previous discussion thread on this list of the > same name, and hopefully captured all the relevant details, > notwithstanding anything unanticipated that may present itself during > the implementation. Let me know if you feel anything important has been > missed. I am aware it omits to mention specifics about messages so > emitted when runtime or compile-time errors occur, but I anticipate this > can be hashed out in the PR, unless consensus indicates otherwise. > > I am aware this idea is not supported by everyone, but there seemed to > be enough positive voices for it to be worth a shot. I'd like to get a > better idea of where people might stand when it comes down to a vote, > now there is a formal RFC, so we know whether it's worth completing the > implementation, although any sentiments so proffered are of course not a > commitment to vote any particular way and nobody should feel compelled > to speak to that unless comfortable. Looking forward to feedback! >
while i wouldn't ordinarily find a lot of use-cases for myself for this, i think its consistent with final and readonly to allow static on classes as well. The code I believe would be mostly at compile time anyways to do these checks, except for new $class. So in general I would vote yes for this, if all edge cases are clear and considered.

Bilge

2 years ago
On 24/06/2024 00:10, Bilge wrote:
> Hi Internals!
I am excited to announce the final implementation for Static Class is ready for review at <https://github.com/php/php-src/pull/14861> and includes a brief summary of the key semantics of the feature. Furthermore, the 1.3 (presumed) final version of the RFC is now published at <https://wiki.php.net/rfc/static_class>. Naturally, if there are any further weighty comments or criticisms to be levied against either one of these, they may still be subject to changes as appropriate. Tomorrow the minimum period elapses before the vote may be started. Discussions on this topic have been quiet for about a week as well, so if there are no major concerns outstanding, I expect to announce the vote to start very soon. Kind regards, Bilge

Mike Schinkel

2 years ago
> On Jul 7, 2024, at 4:08 PM, Bilge <bilge@scriptfusion.com> wrote: > > On 24/06/2024 00:10, Bilge wrote: >> Hi Internals! > I am excited to announce the final implementation for Static Class is ready for review at <https://github.com/php/php-src/pull/14861> and includes a brief summary of the key semantics of the feature. > > Furthermore, the 1.3 (presumed) final version of the RFC is now published at <https://wiki.php.net/rfc/static_class>.
Congrats on getting it to this point. I support this in all but the RFC's restriction on `abstract`, as I have previously mentioned on the list. -Mike

Bilge

2 years ago
On 08/07/2024 01:16, Mike Schinkel wrote:
> > Congrats on getting it to this point. >
Thanks. It's been an adventure :^)
> I support this in all but the RFC's restriction on `abstract`, as > I have previously mentioned on the list. >
I must have missed a key point here. Let's suppose we're writing a follow-up RFC to permit `static` and `abstract` together. In as few sentences as possible, what is the benefit conferred to the developer? Cheers, Bilge

Stephen Reay

2 years ago
> On 8 Jul 2024, at 14:42, Bilge <bilge@scriptfusion.com> wrote: > > On 08/07/2024 01:16, Mike Schinkel wrote: >> Congrats on getting it to this point. > Thanks. It's been an adventure :^) > >> I support this in all but the RFC's restriction on `abstract`, as I have previously mentioned on the list. > I must have missed a key point here. Let's suppose we're writing a follow-up RFC to permit `static` and `abstract` together. In as few sentences as possible, what is the benefit conferred to the developer? > > Cheers, > Bilge >
Hi, I'm sure Mike has his own answer for this, but I'm going to reiterate mine again, because it apparently wasn't really understood the first several times I said it: The reasons are two fold: (a) so that a static class can declare abstract methods either explicitly or implicitly (by implementing an interface without providing concrete implementations of all methods in the interface) (b) to keep consistency with regular classes. The suggested static keyword for classes doesn't introduce any additional behaviour that isn't already possible, it just prevents some uses without the need for boilerplate - the main purpose is to "signify developer intent". The actual behaviour applied to "static" classes is already possible in PHP by simply omitting any instance members and declaring a private constructor. These classes work fine with the abstract keyword. There are no ambiguities, no gotchas or foot guns, there is nothing confusing. It's a standard part of class hierarchy. So really the question should be: why do you feel the need to *disallow* a combination that has no technical issues, and works already? If you're going to disallow abstract why not disallow any class inheritance at all? Because that's what abstract relates to: class hierarchy. Why remove some support but keep the rest? Cheers Stephen

Alexandru Pătrănescu

2 years ago
On Mon, Jul 8, 2024 at 1:12 PM Stephen Reay <php-lists@koalephant.com> wrote:
> > > So really the question should be: why do you feel the need to *disallow* a > combination that has no technical issues, and works already? If you're > going to disallow abstract why not disallow any class inheritance at all? > Because that's what abstract relates to: class hierarchy. Why remove some > support but keep the rest? > > >
Exactly what I wanted to say for this topic. Have a look at this example that could be very similar in production code in some systems: <?php interface A { public static function run(); public static function stop(); } abstract class B implements A { public static function run() { static::create(); static::start(); } } class C extends B { public static function create() { echo "Create\n"; } public static function start() { echo "Start\n"; } public static function stop() { echo "Stop\n"; } } function execute($class) { if (!is_subclass_of($class, A::class)) { throw new RuntimeException("Class must implement A interface"); } $class::run(); $class::stop(); }; execute(C::class); If we support inheritance for static classes, we should allow static on both interface and abstract classes. Alex

Bilge

2 years ago
On 08/07/2024 12:48, Alexandru Pătrănescu wrote:
> > On Mon, Jul 8, 2024 at 1:12 PM Stephen Reay <php-lists@koalephant.com> > wrote: > > > > So really the question should be: why do you feel the need to > *disallow* a combination that has no technical issues, and works > already? If you're going to disallow abstract why not disallow any > class inheritance at all? Because that's what abstract relates to: > class hierarchy. Why remove some support but keep the rest? >
The reason to remove abstract was because it was thought `static` means the same as `abstract`. That is, neither can be instantiated. However, clearly that is short-sighted as Alex's example proves. That is, we cannot simply replace `abstract` with `static` because that precludes us from having an incomplete definition where some methods are still not yet implemented. As such, I am quite convinced by your arguments, and will make the change to add `abstract` support back in; mainly because removing abstract support defies our fundamental razor (it's something we can already do).
> > If we support inheritance for static classes, we should allow static > on both interface and abstract classes. >
What do you mean by /allow static on [...] interface/? Are you saying you also expect to see `static interface` support? (This is something I am absolutely not considering at this time, without a very good argument in favour of). Cheers, Bilge

Alexandru Pătrănescu

2 years ago
On Mon, Jul 8, 2024 at 3:22 PM Bilge <bilge@scriptfusion.com> wrote:
> On 08/07/2024 12:48, Alexandru Pătrănescu wrote: > > > If we support inheritance for static classes, we should allow static on > both interface and abstract classes. > > What do you mean by *allow static on [...] interface*? Are you saying you > also expect to see `static interface` support? (This is something I am > absolutely not considering at this time, without a very good argument in > favour of). > > > For completeness, if we think that the static keyword on a class-level
entity means that it will allow only static members on that entity, all class-level entities should be able to be marked as "static".: Yes, it might not be really required, but I can see how it would help define a static interface that can only be implemented by a static class. Reading on :
> A static class only permits extending a parent class that is also marked
static. A non-static class cannot extend a static class. The wording is a bit confusing for me. But I understand that once a class is static, all the chain of inheritance should be made of static classes, going up or down (and I would also think we can add interfaces and traits.). If interfaces are not included, do we allow implementing an interface? If we allow implementing an interface, should that interface contain only static members? Same for traits. Alex

Bilge

2 years ago
On 08/07/2024 15:13, Alexandru Pătrănescu wrote:
> But I understand that once a class is static, all the chain of > inheritance should be made of static classes, going up or down (and I > would also think we can add interfaces and traits.).
Correct.
> If interfaces are not included, do we allow implementing an interface?
Yes.
> If we allow implementing an interface, should that interface contain > only static members?
Yes. You can technically implement an interface that has instance methods, but you will either: get a compile-time error that you didn't implement the instance method, or a compile-time error that you didn't mark the (instance) method as static in a static class. So although you won't get an error about implementing the interface itself, you will be forced to not implement an interface containing any instance methods in a static class, by one of those two (somewhat indirect) errors.
> Same for traits.
Yes
> For completeness, if we think that the static keyword on a class-level > entity means that it will allow only static members on that entity, > all class-level entities should be able to be marked as "static".:
I understand that sentiment, however I'm still not going to entertain it right now because this RFC seems to have a tenuous chance of passing at best. I should think we will be lucky to get 50% of the vote, let alone the 66% required to pass, and any further (unnecessary) language changes, such as introducing `static interface` or `static trait`s will probably lower those odds. I'm sorry it's like that, but that's how the landscape looks to me. But not to be too disheartened; both of those things can be trivially added in a follow-up PR if there is a strong desire. That is, accepting this RFC in no way precludes the possibility of going down that road later. Cheers, Bilge

Bilge

2 years ago
On 07/07/2024 21:08, Bilge wrote:
> On 24/06/2024 00:10, Bilge wrote: >> Hi Internals! > I am excited to announce the final implementation for Static Class is > ready for review at <https://github.com/php/php-src/pull/14861> and > includes a brief summary of the key semantics of the feature. > > Furthermore, the 1.3 (presumed) final version of the RFC is now > published at <https://wiki.php.net/rfc/static_class>. > > Naturally, if there are any further weighty comments or criticisms to > be levied against either one of these, they may still be subject to > changes as appropriate.
For those of you (not) following along at home, a quick update: After consulting with my Design Council (not a design by committee BTW), we agreed to release the restriction on `abstract static`. The RFC has been updated to 1.4 and the PR has been updated in kind to allow abstract static classes, since abstract static methods are already present in PHP and to forbid them in the context of static classes would be to forgo our fundamental design razor.
> I expect to announce the vote to start very soon.
In light of this last-minute change, voting will be postponed until at least next week, to give everyone some time to catch up. Kind regards, Bilge

Bilge

2 years ago
On 24/06/2024 00:10, Bilge wrote:
> Hi Internals! > > I am pleased to present my first RFC: Static class > <https://wiki.php.net/rfc/static_class>.
Gentle reminder that voting is scheduled to start Monday, barring any unexpected developments over this weekend. The RFC is unchanged since including support for `abstract static` last week, other than the addition of a /discussion/ section, to capture some of the criticisms levied against the proposal, and /in the wild/ section to explore the prevalence of implicit static classes in open source that might benefit from the proposal. Cheers, Bilge