Hi, similar to the abstract and readonly modifiers to classes (similar
in syntax only), I propose a class level "static" modifier that ensures:
-
The class cannot be instantiated and will throw an exception if
attempted. -
All methods and variables in the class are implicitly static.
-
(optional based on community input) It should have the same effect on
traits and interfaces
For example:
static class X {
const int k = 1;
private string $a
function foo():void{}
private function bar():void{}
}
would be equivalent to
class X {
const int k = 1;
private static string $a
public static function foo():void{}
private static function bar():void{}
}
and so on. I will be implementing this myself it it gets approved.
Cheers,
Lanre
19 nov 2023 22:28:16 Lanre Waju lanre@online-presence.ca:
Hi, similar to the abstract and readonly modifiers to classes (similar in syntax only), I propose a class level "static" modifier that ensures:
The class cannot be instantiated and will throw an exception if attempted.
All methods and variables in the class are implicitly static.
(optional based on community input) It should have the same effect on traits and interfaces
For example:
static class X {
const int k = 1;
private string $a
function foo():void{}
private function bar():void{}
}
would be equivalent to
class X {
const int k = 1;
private static string $a
public static function foo():void{}
private static function bar():void{}
}
and so on. I will be implementing this myself it it gets approved.
Cheers,
Lanre
--
To unsubscribe, visit: https://www.php.net/unsub.php
Hi,
Super on board with this, I felt the need for this feature uncountable times in many years, usually the temporary solution was just to mark the class as final and create a private constructor, but especially the private constructor part makes it a bit ugly, too indirect and not immediately obvious.
The only doubts I have about this proposal are the omission of static keywords for functions and properties.
I understand this is inline with readonly classes, but I don't feel like static is a keyword that should be omittable when declaring static methods and properties.
Regards,
Daniil Gentili.
Hi again,
Super on board with this
Sorry for top posting everyone, my bad, forgot to delete the rest of the message :)
Regards,
Daniil Gentili
Hi, similar to the abstract and readonly modifiers to classes (similar in syntax only), I propose a class level "static" modifier that ensures:
Hi Lanre,
There was a proposal for this several years ago which was declined at the voting stage: https://wiki.php.net/rfc/abstract_final_class
That doesn't mean we can't look again, but any new proposal would need to at least address the reasons the previous one was declined. I believe these are the relevant discussion threads: https://externals.io/message/79211 https://externals.io/message/79338 https://externals.io/message/79601
My memory is that one of the main points against was that a class with only static methods is just a namespace, and you can already put functions directly in a namespace. The only issue being that we don't have good autoloading support for such functions, and that's a whole different problem...
Regards,
--
Rowan Tommins
[IMSoP]
Thank you for bringing those threads to my attention. I hadn't
encountered them before, but it appears to be the typical argument of "I
don't want this, so you shouldn't," along with "why do x when you can
just do y (notice how y is not x)."
-
I will personally implement this feature.
-
This change is non-breaking and backward compatible.
-
It is entirely opt-in. If you hold reservations about using static
classes, you can simply choose not to use them.
The proposal is designed with the intention of improving code clarity,
reinforcing design principles, and establishing a clear and standardized
approach to expressing the intended usage of a class. While I advocate
for member properties and methods to be implicitly static, I am open to
understanding alternative viewpoints on why this might not be the
optimal choice.
Cheers,
Lanre.
Hi, similar to the abstract and readonly modifiers to classes (similar in syntax only), I propose a class level "static" modifier that ensures:
Hi Lanre,
There was a proposal for this several years ago which was declined at the voting stage: https://wiki.php.net/rfc/abstract_final_class
That doesn't mean we can't look again, but any new proposal would need to at least address the reasons the previous one was declined. I believe these are the relevant discussion threads: https://externals.io/message/79211 https://externals.io/message/79338 https://externals.io/message/79601
My memory is that one of the main points against was that a class with only static methods is just a namespace, and you can already put functions directly in a namespace. The only issue being that we don't have good autoloading support for such functions, and that's a whole different problem...
Regards,
- I will personally implement this feature.
That's good to hear, but the initial implementation is not the main cost of a new feature. Once we add something, it's very hard to remove, and every future change has to consider that feature and make sure it doesn't break.
That's why "do we need X when Y does nearly the same thing" is a more valid argument (in general) than you're giving it credit for: if we included every variation of every feature, the language and its implementation would become unmanageably complex, so we have to choose where to draw the line.
It's up to the person proposing a feature to persuade others that it falls the right side of that line - that the benefit of the feature outweighs the cost of having it in the language.
(It's also worth noting that the previous proposal also had an implementation, linked at the bottom of the RFC.)
It's probably not productive to just say "the people who voted last time are wrong", but it was long enough ago that a new RFC on the topic wouldn't break any rules. So, if you want to proceed with this, you can try to come up with a justification that addresses the points raised previously, and follow the process here: https://wiki.php.net/rfc/howto
Regards,
--
Rowan Tommins
[IMSoP]
On Mon, Nov 20, 2023 at 1:43 PM Rowan Tommins rowan.collins@gmail.com
wrote:
On 20 November 2023 08:35:15 GMT, Lanre Waju lanre@online-presence.ca
wrote:
- I will personally implement this feature.
That's good to hear, but the initial implementation is not the main cost
of a new feature. Once we add something, it's very hard to remove, and
every future change has to consider that feature and make sure it doesn't
break.It's probably not productive to just say "the people who voted last time
are wrong", but it was long enough ago that a new RFC on the topic wouldn't
break any rules.
9 years is long enough to conclude that whatever happens, they weren't
really wrong back then. Maybe at that time the best thing for the language
was not to introduce it. The maintenance cost of a feature and the benefit
it brings are also things subject to change in such a long timeline. Maybe
the current technical debt or the current suite of tests make this feature
less of a burden than it used to be or maybe our understanding of the value
of such a feature has changed in the course of 9 years.
--
Marco Deleu
It's probably not productive to just say "the people who voted last time
are wrong", but it was long enough ago that a new RFC on the topic wouldn't
break any rules.9 years is long enough to conclude that whatever happens, they weren't
really wrong back then.
Absolutely; to make sure the point is clear, I meant that just saying "they were wrong", or even "they would be wrong if they said that now" is not a sufficient argument. You either need to explain why the situation has changed, or why the arguments were not sufficiently considered in the first place.
Regards,
--
Rowan Tommins
[IMSoP]
Hi, similar to the abstract and readonly modifiers to classes (similar in syntax only), I propose a class level "static" modifier that ensures:
Hi Lanre,
There was a proposal for this several years ago which was declined at
the voting stage: https://wiki.php.net/rfc/abstract_final_classThat doesn't mean we can't look again, but any new proposal would need
to at least address the reasons the previous one was declined. I
believe these are the relevant discussion threads:
https://externals.io/message/79211 https://externals.io/message/79338
https://externals.io/message/79601My memory is that one of the main points against was that a class with
only static methods is just a namespace, and you can already put
functions directly in a namespace. The only issue being that we don't
have good autoloading support for such functions, and that's a whole
different problem...
I wasn't a voter at the time, but I would absolutely vote against a static class
RFC today, for largely the same reasons.
- It is entirely opt-in. If you hold reservations about using static
classes, you can simply choose not to use them.
This is a spurious and disingenuous argument, even if it gets trotted out frequently on many RFCs. I'm not picking on you in particular here, but it's a bad argument to use, period, 99% of the time, no matter who is saying it.
Rowan already mentioned the maintenance burden that it entails. I'll go a step further. We've already seen that "readonly" properties have their uses, though not everyone uses them. So far so good. But readonly also was implemented with a stealth extra asymmetric visibility implication, which made the design and implementation of real asymmetric visibility harder, and very likely contributed to that RFC not passing due to the perceived extra complexity of working around readonly's shortcomings.
Similarly, if we add a static marker to classes, that means all future improvements to classes need to consider "but what if the class is static?" Eg, there was discussion a while back about data
classes. What happens if you have a static data class
? Is that a syntax error? Does it work? If it works, what does "work" mean? What about sealed classes? Can you have a sealed static class? What would that mean? Those are questions that would need to be answered, because this feature exists.
It impacts all users, too, because almost no one builds a 100% self-written codebase anymore. 99% of projects are 80% someone else's code via Composer packages large and small, which means every language feature they use, you need to be aware of how to use. If some library has a static class as part of its API, I am now forced to work with static classes, whether I like it or not.
Certainly, many features are worth the extra burden to maintain and to train for. We pass new RFCs all the time that we collectively decide are worth it, I've proposed several, etc. But "if you don't like it, just ignore it" has always been a misleading and ignorant claim to make about any proposed feature. "The implications are small so you probably won't need to worry outside of these situations" is a valid argument to make (and to disagree about), but that's a different thing.
The proposal is designed with the intention of improving code clarity,
reinforcing design principles, and establishing a clear and standardized
approach to expressing the intended usage of a class. While I advocate
for member properties and methods to be implicitly static, I am open to
understanding alternative viewpoints on why this might not be the
optimal choice.
Emphasizing:
reinforcing design principles,
Which is exactly the problem. All-static classes are a bad design principle that should not be encouraged. They're a namespace cheat, not a good design. They should be discouraged, not encouraged.
If the methods are pure functions, then they really ought to just be namespaced functions. Those work. They're fine. Autoloading is frankly mostly solved by using the files
block in your composer.json's autoload config, and then you don't need to care. Pure functions are OK. The obsession with avoiding functions in many parts of PHP culture is really not to its credit. (And I admit going through my own phase where I thought that; it was incorrect then, it's incorrect now, and it's been incorrect pretty much since 5.5 gave us an always-on opcache.)
If the methods are non-pure... then it has no business being a static method. It needs to be an object method, otherwise it breaks testing. Yes, there are a lot of older libraries and APIs that use static methods in a stateful way, or make IO calls to a remote service, etc. Those libraries are wrong. Period. Language features that encourage that anti-pattern are anti-features. Those should be instantiated objects which can be injected and mocked and use mocks and be tested easily and so on.
To be clear, static methods have their place. I'm not against static methods. I'm against static methods as a cheap discount-bin namespace, as that shows a lack of understanding of the language and of OOP design. Yet that is precisely what static class
would be.
If we really wanted the language to reinforce good design principles, we would make a class that has only static methods and nothing else a syntax error. :-) I do not proposed we actually do that, but that's what "reinforcing design principles" would mean in practice.
--Larry Garfield
I will have to disagree with everything you said:
I outlined what a static class would be: A class that cannot be
instantiated in which all members are implicitly static. We already have
static members, so please elaborate on the maintenance burden that
adding static to a class as that could help explain better, i mean what
feature doesn't have technical debt associated with it? The new JIT engine?
- It is entirely opt-in. If you hold reservations about using static
classes, you can simply choose not to use them.
This is a spurious and disingenuous argument, even if it gets trotted out frequently on many RFCs. I'm not picking on you in particular here, but it's a bad argument to use, period, 99% of the time, no matter who is saying it.
If you insist, Mr. Larry.
Similarly, if we add a static marker to classes, that means all future improvements to classes need to consider "but what if the class is static?" Eg, there was discussion a while back about
data
classes. What happens if you have astatic data class
? Is that a syntax error? Does it work? If it works, what does "work" mean? What about sealed classes? Can you have a sealed static class? What would that mean? Those are questions that would need to be answered, because this feature exists.
Either i'm in an alternate reality where the data classes and sealed
classes rfcs passed, or I've just been straw-manned. Either way, is it
really that difficult to decide on as a community what a static data
class implies? We have already established that a static class means all
the members are static, but i can try to dumb it down for you if need be.
It impacts all users, too, because almost no one builds a 100% self-written codebase anymore. 99% of projects are 80% someone else's code via Composer packages large and small, which means every language feature they use, you need to be aware of how to use. If some library has a static class as part of its API, I am now forced to work with static classes, whether I like it or not.
If you had told me we were allowed to pull numbers out of our asses, i
would have mentioned that 100% of php users will benefit from this,
Including people like you who don't necessarily see the value in it. As
a user of a class, it shouldn't matter whether it's static or not, the
static members will be the same as using a normal class with static
members, in fact, people already simulate this in userland by making
__construct private and only using static members, does that impede your
ability to use such libs? How is this forcing you to use static classes?
The dev of said library(which no one is forcing you to use) would be the
one who made that call. After which it's your call to decide whether or
not the library suites you. I don't see any forcing here but do
enlighten me.
The proposal is designed with the intention of improving code clarity,
reinforcing design principles, and establishing a clear and standardized
approach to expressing the intended usage of a class. While I advocate
for member properties and methods to be implicitly static, I am open to
understanding alternative viewpoints on why this might not be the
optimal choice.
Emphasizing:reinforcing design principles,
Which is exactly the problem. All-static classes are a bad design principle that should not be encouraged. They're a namespace cheat, not a good design. They should be discouraged, not encouraged.
They could be used as a namespace cheat yes, they could be used wrongly,
but that doesn't mean it doesn't have any valid uses or make it
inherently bad. You remind me of the people that literally never use
inheritance because they read "inheritance over composition" somewhere.
Please elaborate more on why it's a bad design principle.
If the methods are pure functions, then they really ought to just be namespaced functions. Those work. They're fine. Autoloading is frankly mostly solved by using the
files
block in your composer.json's autoload config, and then you don't need to care. Pure functions are OK. The obsession with avoiding functions in many parts of PHP culture is really not to its credit. (And I admit going through my own phase where I thought that; it was incorrect then, it's incorrect now, and it's been incorrect pretty much since 5.5 gave us an always-on opcache.)
If the methods are non-pure... then it has no business being a static method. It needs to be an object method, otherwise it breaks testing. Yes, there are a lot of older libraries and APIs that use static methods in a stateful way, or make IO calls to a remote service, etc. Those libraries are wrong. Period. Language features that encourage that anti-pattern are anti-features. Those should be instantiated objects which can be injected and mocked and use mocks and be tested easily and so on.
Again that is your opinion, yet you speak it like it's fact. I guess all
those libraries are wrong because Mr. Larry thinks so. So before you
write your next library, ask yourself "what would larry do".
If we really wanted the language to reinforce good design principles, we would make a class that has only static methods and nothing else a syntax error. :-) I do not proposed we actually do that, but that's what "reinforcing design principles" would mean in practice.
And i guess you are also the authority in terms of what design
principles should and shouldn't be reinforced, I will also keep that in
mind.
I am still holding out for a productive conversation based on facts,
regardless, you are entitled to your opinions on the matter.
Cheers,
Lanre.
Hi, similar to the abstract and readonly modifiers to classes (similar in syntax only), I propose a class level "static" modifier that ensures:
Hi Lanre,
There was a proposal for this several years ago which was declined at
the voting stage: https://wiki.php.net/rfc/abstract_final_classThat doesn't mean we can't look again, but any new proposal would need
to at least address the reasons the previous one was declined. I
believe these are the relevant discussion threads:
https://externals.io/message/79211 https://externals.io/message/79338
https://externals.io/message/79601My memory is that one of the main points against was that a class with
only static methods is just a namespace, and you can already put
functions directly in a namespace. The only issue being that we don't
have good autoloading support for such functions, and that's a whole
different problem...
I wasn't a voter at the time, but I would absolutely vote against astatic class
RFC today, for largely the same reasons.
- It is entirely opt-in. If you hold reservations about using static
classes, you can simply choose not to use them.
This is a spurious and disingenuous argument, even if it gets trotted out frequently on many RFCs. I'm not picking on you in particular here, but it's a bad argument to use, period, 99% of the time, no matter who is saying it.Rowan already mentioned the maintenance burden that it entails. I'll go a step further. We've already seen that "readonly" properties have their uses, though not everyone uses them. So far so good. But readonly also was implemented with a stealth extra asymmetric visibility implication, which made the design and implementation of real asymmetric visibility harder, and very likely contributed to that RFC not passing due to the perceived extra complexity of working around readonly's shortcomings.
Similarly, if we add a static marker to classes, that means all future improvements to classes need to consider "but what if the class is static?" Eg, there was discussion a while back about
data
classes. What happens if you have astatic data class
? Is that a syntax error? Does it work? If it works, what does "work" mean? What about sealed classes? Can you have a sealed static class? What would that mean? Those are questions that would need to be answered, because this feature exists.It impacts all users, too, because almost no one builds a 100% self-written codebase anymore. 99% of projects are 80% someone else's code via Composer packages large and small, which means every language feature they use, you need to be aware of how to use. If some library has a static class as part of its API, I am now forced to work with static classes, whether I like it or not.
Certainly, many features are worth the extra burden to maintain and to train for. We pass new RFCs all the time that we collectively decide are worth it, I've proposed several, etc. But "if you don't like it, just ignore it" has always been a misleading and ignorant claim to make about any proposed feature. "The implications are small so you probably won't need to worry outside of these situations" is a valid argument to make (and to disagree about), but that's a different thing.
The proposal is designed with the intention of improving code clarity,
reinforcing design principles, and establishing a clear and standardized
approach to expressing the intended usage of a class. While I advocate
for member properties and methods to be implicitly static, I am open to
understanding alternative viewpoints on why this might not be the
optimal choice.
Emphasizing:reinforcing design principles,
Which is exactly the problem. All-static classes are a bad design principle that should not be encouraged. They're a namespace cheat, not a good design. They should be discouraged, not encouraged.If the methods are pure functions, then they really ought to just be namespaced functions. Those work. They're fine. Autoloading is frankly mostly solved by using the
files
block in your composer.json's autoload config, and then you don't need to care. Pure functions are OK. The obsession with avoiding functions in many parts of PHP culture is really not to its credit. (And I admit going through my own phase where I thought that; it was incorrect then, it's incorrect now, and it's been incorrect pretty much since 5.5 gave us an always-on opcache.)If the methods are non-pure... then it has no business being a static method. It needs to be an object method, otherwise it breaks testing. Yes, there are a lot of older libraries and APIs that use static methods in a stateful way, or make IO calls to a remote service, etc. Those libraries are wrong. Period. Language features that encourage that anti-pattern are anti-features. Those should be instantiated objects which can be injected and mocked and use mocks and be tested easily and so on.
To be clear, static methods have their place. I'm not against static methods. I'm against static methods as a cheap discount-bin namespace, as that shows a lack of understanding of the language and of OOP design. Yet that is precisely what
static class
would be.If we really wanted the language to reinforce good design principles, we would make a class that has only static methods and nothing else a syntax error. :-) I do not proposed we actually do that, but that's what "reinforcing design principles" would mean in practice.
--Larry Garfield
I will have to disagree with everything you said:
I outlined what a static class would be: A class that cannot be
instantiated in which all members are implicitly static. We already have
static members, so please elaborate on the maintenance burden that
adding static to a class as that could help explain better, i mean what
feature doesn't have technical debt associated with it? The new JIT engine?
That's part of the point. Literally every feature added to a system (PHP or otherwise) has some maintenance overhead associated with it. It may be small, it may be huge, it may be worth it, it may not be worth it, but it's always non-zero.
As for what a static class is, from your description I am understanding it to mean this:
static class Foo {
public function beep(): string { ... }
public function boop(): string { ... }
}
Is effectively a shorthand for:
class Foo {
public function __construct() { throw new \Exception("You can't do that"); }
public static function beep(): string { ... }
public static function boop(): string { ... }
}
Much the same way a readonly class is. Is that understanding of the intent more or less accurate?
- It is entirely opt-in. If you hold reservations about using static
classes, you can simply choose not to use them.
This is a spurious and disingenuous argument, even if it gets trotted out frequently on many RFCs. I'm not picking on you in particular here, but it's a bad argument to use, period, 99% of the time, no matter who is saying it.
If you insist, Mr. Larry.
Cultural side note: At least in the US, where I am, "Mr. Firstname" is pretty much never used except with some families that teach their children to use that with adults. It's not necessary and kind of weird. "Mr. Garfield" if you want to be formal is fine, or just Larry, or Crell, as that's the handle I use virtually everywhere and there are people who know me as that more than as Larry. (I am not offended, just noting a cultural mismatch point.)
Similarly, if we add a static marker to classes, that means all future improvements to classes need to consider "but what if the class is static?" Eg, there was discussion a while back about
data
classes. What happens if you have astatic data class
? Is that a syntax error? Does it work? If it works, what does "work" mean? What about sealed classes? Can you have a sealed static class? What would that mean? Those are questions that would need to be answered, because this feature exists.
Either i'm in an alternate reality where the data classes and sealed
classes rfcs passed, or I've just been straw-manned. Either way, is it
really that difficult to decide on as a community what a static data
class implies? We have already established that a static class means all
the members are static, but i can try to dumb it down for you if need be.
There is no need to dumb it down, just to read what I actually wrote. Every feature added has implications for future features added. Data and Sealed classes are ideas that have been floated but never to a full RFC, but may end up as an RFC in the future. They were convenient examples of future work that would be impacted by adding a static keyword to classes, as those RFCs would have to determine how to interact with static classes. Maybe the solution would be simple, maybe it wouldn't, I have no idea. It is just an example to show that the cost of adding a feature is never zero, and so the "if you don't like it don't use it, so you're always wrong for voting against an RFC" argument is nonsensical and disingenuous.
It impacts all users, too, because almost no one builds a 100% self-written codebase anymore. 99% of projects are 80% someone else's code via Composer packages large and small, which means every language feature they use, you need to be aware of how to use. If some library has a static class as part of its API, I am now forced to work with static classes, whether I like it or not.
If you had told me we were allowed to pull numbers out of our asses, i
would have mentioned that 100% of php users will benefit from this,
Including people like you who don't necessarily see the value in it. As
a user of a class, it shouldn't matter whether it's static or not, the
static members will be the same as using a normal class with static
members, in fact, people already simulate this in userland by making
__construct private and only using static members, does that impede your
ability to use such libs? How is this forcing you to use static classes?
The dev of said library(which no one is forcing you to use) would be the
one who made that call. After which it's your call to decide whether or
not the library suites you. I don't see any forcing here but do
enlighten me.
Funny you should mention it, in fact. One of the systems I maintain at work currently is using an older version of Stripe's authentication library, which is written as all static calls. That makes them unmockable. That sucks horribly, because that means when our test suite runs we're sending thousands of auth calls to Stripe, because there is no way to mock out that call with a no-op, when we don't actually care what Stripe does in 99% of those tests.
We're currently in the process of upgrading to a newer version of the library that has a proper instantiated service object instead of global static calls, which will allow us to properly mock it, and save Stripe tens of thousands of API hits per day and us probably multiple minutes on every test run. But it's a slow process, and taking a lot of time/money to clean up.
This is why static APIs are bad. That's not just me making stuff up, this is a well-understood principle around anyone who's done any serious work with unit testing. It's easily the #1 argument people use against Laravel, which also does a lot of static APIs and is, guess what, extremely hard to test properly as a result.
A language-level feature that explicitly endorses and encourages those all-static APIs (which is what is being proposed) would actively encourage code like that Stripe library, which is wasting time, money, and network traffic for us literally every single day. We should be discouraging that design, not encouraging it.
The proposal is designed with the intention of improving code clarity,
reinforcing design principles, and establishing a clear and standardized
approach to expressing the intended usage of a class. While I advocate
for member properties and methods to be implicitly static, I am open to
understanding alternative viewpoints on why this might not be the
optimal choice.
Emphasizing:reinforcing design principles,
Which is exactly the problem. All-static classes are a bad design principle that should not be encouraged. They're a namespace cheat, not a good design. They should be discouraged, not encouraged.They could be used as a namespace cheat yes, they could be used wrongly,
but that doesn't mean it doesn't have any valid uses or make it
inherently bad. You remind me of the people that literally never use
inheritance because they read "inheritance over composition" somewhere.
Please elaborate more on why it's a bad design principle.
See above.
(sarcastic sniping removed)
I am still holding out for a productive conversation based on facts,
regardless, you are entitled to your opinions on the matter.
That static methods make unit testing harder and sometimes impossible is a fact. That is not an opinion, it is an objective fact.
Language features that encourage making testing harder are bad features.
That's the long and short of it.
--Larry Garfield
Again, with so many absolutes. Static APIs aren't inherently bad;
rather, bad programmers write poor code. It seems like you're assuming
that PHP developers can't make decisions about what tools to use
independently and need explicit encouragement or endorsement from you.
Just because you are using what I would call "garbage" (my preferred
term for legacy) Stripe API doesn't mean we all do. When did I ever say
the cost to add this would be zero? If everything has a non-zero
development cost, doesn't that make it implied? What exactly is your point?
For example, the get/set hooks you proposed: why go the C# route instead
of using JS-like modifiers, which PHP already supports? What was the
cost analysis on that? Then, you talk about Laravel. How are Facades
anything like what I have suggested exactly? The idea that Laravel is
somehow more difficult to test than any other framework is simply a
myth; they have a robust test suite. I write embedded code for a living;
I have crafted far more critical tests for code being shipped to
millions of devices that may never update, compared to your "legacy"
Stripe code that you still use for some reason. So, you don't have to
explain how mocks and unit tests work, but thanks for your summary.
I made a suggestion for something i thought would be a nice addition to
the language, and instead of questions, I'm met with absolutes on why my
Idea is bad, and some nonsense about how everything has maintenance cost.
I will have to disagree with everything you said:
I outlined what a static class would be: A class that cannot be
instantiated in which all members are implicitly static. We already have
static members, so please elaborate on the maintenance burden that
adding static to a class as that could help explain better, i mean what
feature doesn't have technical debt associated with it? The new JIT engine?
That's part of the point. Literally every feature added to a system (PHP or otherwise) has some maintenance overhead associated with it. It may be small, it may be huge, it may be worth it, it may not be worth it, but it's always non-zero.As for what a static class is, from your description I am understanding it to mean this:
static class Foo {
public function beep(): string { ... }
public function boop(): string { ... }
}Is effectively a shorthand for:
class Foo {
public function __construct() { throw new \Exception("You can't do that"); }public static function beep(): string { ... }
public static function boop(): string { ... }
}Much the same way a readonly class is. Is that understanding of the intent more or less accurate?
- It is entirely opt-in. If you hold reservations about using static
classes, you can simply choose not to use them.
This is a spurious and disingenuous argument, even if it gets trotted out frequently on many RFCs. I'm not picking on you in particular here, but it's a bad argument to use, period, 99% of the time, no matter who is saying it.
If you insist, Mr. Larry.
Cultural side note: At least in the US, where I am, "Mr. Firstname" is pretty much never used except with some families that teach their children to use that with adults. It's not necessary and kind of weird. "Mr. Garfield" if you want to be formal is fine, or just Larry, or Crell, as that's the handle I use virtually everywhere and there are people who know me as that more than as Larry. (I am not offended, just noting a cultural mismatch point.)Similarly, if we add a static marker to classes, that means all future improvements to classes need to consider "but what if the class is static?" Eg, there was discussion a while back about
data
classes. What happens if you have astatic data class
? Is that a syntax error? Does it work? If it works, what does "work" mean? What about sealed classes? Can you have a sealed static class? What would that mean? Those are questions that would need to be answered, because this feature exists.
Either i'm in an alternate reality where the data classes and sealed
classes rfcs passed, or I've just been straw-manned. Either way, is it
really that difficult to decide on as a community what a static data
class implies? We have already established that a static class means all
the members are static, but i can try to dumb it down for you if need be.
There is no need to dumb it down, just to read what I actually wrote. Every feature added has implications for future features added. Data and Sealed classes are ideas that have been floated but never to a full RFC, but may end up as an RFC in the future. They were convenient examples of future work that would be impacted by adding a static keyword to classes, as those RFCs would have to determine how to interact with static classes. Maybe the solution would be simple, maybe it wouldn't, I have no idea. It is just an example to show that the cost of adding a feature is never zero, and so the "if you don't like it don't use it, so you're always wrong for voting against an RFC" argument is nonsensical and disingenuous.It impacts all users, too, because almost no one builds a 100% self-written codebase anymore. 99% of projects are 80% someone else's code via Composer packages large and small, which means every language feature they use, you need to be aware of how to use. If some library has a static class as part of its API, I am now forced to work with static classes, whether I like it or not.
If you had told me we were allowed to pull numbers out of our asses, i
would have mentioned that 100% of php users will benefit from this,
Including people like you who don't necessarily see the value in it. As
a user of a class, it shouldn't matter whether it's static or not, the
static members will be the same as using a normal class with static
members, in fact, people already simulate this in userland by making
__construct private and only using static members, does that impede your
ability to use such libs? How is this forcing you to use static classes?
The dev of said library(which no one is forcing you to use) would be the
one who made that call. After which it's your call to decide whether or
not the library suites you. I don't see any forcing here but do
enlighten me.
Funny you should mention it, in fact. One of the systems I maintain at work currently is using an older version of Stripe's authentication library, which is written as all static calls. That makes them unmockable. That sucks horribly, because that means when our test suite runs we're sending thousands of auth calls to Stripe, because there is no way to mock out that call with a no-op, when we don't actually care what Stripe does in 99% of those tests.We're currently in the process of upgrading to a newer version of the library that has a proper instantiated service object instead of global static calls, which will allow us to properly mock it, and save Stripe tens of thousands of API hits per day and us probably multiple minutes on every test run. But it's a slow process, and taking a lot of time/money to clean up.
This is why static APIs are bad. That's not just me making stuff up, this is a well-understood principle around anyone who's done any serious work with unit testing. It's easily the #1 argument people use against Laravel, which also does a lot of static APIs and is, guess what, extremely hard to test properly as a result.
A language-level feature that explicitly endorses and encourages those all-static APIs (which is what is being proposed) would actively encourage code like that Stripe library, which is wasting time, money, and network traffic for us literally every single day. We should be discouraging that design, not encouraging it.
The proposal is designed with the intention of improving code clarity,
reinforcing design principles, and establishing a clear and standardized
approach to expressing the intended usage of a class. While I advocate
for member properties and methods to be implicitly static, I am open to
understanding alternative viewpoints on why this might not be the
optimal choice.
Emphasizing:reinforcing design principles,
Which is exactly the problem. All-static classes are a bad design principle that should not be encouraged. They're a namespace cheat, not a good design. They should be discouraged, not encouraged.
They could be used as a namespace cheat yes, they could be used wrongly,
but that doesn't mean it doesn't have any valid uses or make it
inherently bad. You remind me of the people that literally never use
inheritance because they read "inheritance over composition" somewhere.
Please elaborate more on why it's a bad design principle.
See above.(sarcastic sniping removed)
I am still holding out for a productive conversation based on facts,
regardless, you are entitled to your opinions on the matter.
That static methods make unit testing harder and sometimes impossible is a fact. That is not an opinion, it is an objective fact.Language features that encourage making testing harder are bad features.
That's the long and short of it.
--Larry Garfield
Am 20.11.2023 um 22:00 schrieb Lanre Waju lanre@online-presence.ca:
I will have to disagree with everything you said:
Side-note: This is IMHO not a good intro into a discussion ;-)
I outlined what a static class would be: A class that cannot be instantiated in which all members are implicitly static. We already have static members, so please elaborate on the maintenance burden that adding static to a class as that could help explain better, i mean what feature doesn't have technical debt associated with it? The new JIT engine?
That is his point: The benefit has to outweigh the debt and for something like a class modifier this can lead to a matrix of possible combinations, complication things down the line.
- It is entirely opt-in. If you hold reservations about using static
classes, you can simply choose not to use them.
This is a spurious and disingenuous argument, even if it gets trotted out frequently on many RFCs. I'm not picking on you in particular here, but it's a bad argument to use, period, 99% of the time, no matter who is saying it.
If you insist, Mr. Larry.
I’m with Larry there: Being able to just ignore new features is sometimes helpful (i.e. avoids the discussion about BC) but not a strong argument on why something should be added. This lists very much also cares about the implementation and maintenance costs as well as the simplicity/consistency of PHP. Being opt-in just does not carry much weight in that context.
Similarly, if we add a static marker to classes, that means all future improvements to classes need to consider "but what if the class is static?" Eg, there was discussion a while back about
data
classes. What happens if you have astatic data class
? Is that a syntax error? Does it work? If it works, what does "work" mean? What about sealed classes? Can you have a sealed static class? What would that mean? Those are questions that would need to be answered, because this feature exists.
Either i'm in an alternate reality where the data classes and sealed classes rfcs passed, or I've just been straw-manned. Either way, is it really that difficult to decide on as a community what a static data class implies? We have already established that a static class means all the members are static, but i can try to dumb it down for you if need be.
Being a long time member of this list (even though with only very occasional contributions to the source code) I have to say: There is a wide range of experience in both language design and implementation (for both PHP and in general) represented here, some members coming from an almost exclusively language-user point of view. And that is a great thing!
But I’d recommend to not underestimate the knowledge contributed by people who have been involved for some time and possibly even learned from previous mistakes :-)
Having said all that I just wanted to add that I would most probably vote against static classes even though we use class-as-a-namespace here sometimes but still don’t feel the need for it to being a language feature for the reasons mentioned by Rowan and Larry.
Regards,
- Chris
Hi Lanre,
I like your idea and I was even thinking about proposing the same addition.
The thing that is missing in the description is how the static class should
handle magic methods. Should they be disabled for them?
One plus of static classes which exist in other languages are that we
wouldn't need the hack with private constructor to build Singleton. But...
that's not the recommended pattern to use as explained by Larry. So I am
not really sure whether it makes sense to add complexity to the PHP source
code for such small benefit. Or maybe you see other benefits?
I must also admit like others that your further arguments as below, are not
good as someone after your implementation will need to maintain this code.
- I will personally implement this feature.
- This change is non-breaking and backward compatible.
- It is entirely opt-in. If you hold reservations about using static
classes, you can simply choose not to use them.
Kind regards,
Jorg
A class with a private constructor can still be instantiated. A class that
cannot be instantiated is pretty much useless. A static class would be just
a collection of functions. And I know that many developers write such
classes but it doesn't mean the language should encourage it. After all, if
someone wants to create such a "collection of functions" they can already
do that and they don't need a static class.
By definition a class is a template for creation of objects. Everything in
PHP OOP is designed with this in mind. Adding static classes would be a
severe complication in the language syntax that goes against the OOP
principles. That's like saying that some people use a saw to hammer in
nails and we should designs saws that make it easier.
There are so many unanswered questions in this proposals without even
considering things that might be implemented in the future. Such an
addition would be a major change that could have unforseen results.
And by the way, you are not going to win anyone's approval by being rude
and condescending.
Wow.
This thread has gone from zero-to-toxic in no time flat, and with some participants being borderline condescending. It is almost as if some folk are still in kindergarten.
Maybe try a different approach, from both sides of this debate?
For the advocates, how about explaining the use-cases for static classes, why you want or need to use them instead of the proposed alternatives, and especially pointing out any ways in which the proposed alternatives do not address the same issues and/or provide equivalent functionality as static classes would?
And for the status-quoians, while I know you feel namespaces address the reasons you believe people want static classes, you feel it is bad practice to use static classes for those reasons, and you lament that programmers still use static classes instead of namespaces, maybe consider exploring why developers still use static classes instead of namespaces? And please, consider if there is a better way to answer that question rather than falling back on the canard that those programmers either don't know any better, or are actively choosing to cause harm to all the PHP community and all its collective code across the land.
Maybe there are very good reasons developers actively prefer to use static classes instead of namespaces? And if you actively try to discover those reasons, maybe you can consider discussing how to address them rather than just dismissing the desire to use them as developers choosing to use "anti-patterns?"
Are there missing features the language offers users of static classes that it does not offer namespaces? Are static classes more ergonomic to use than namespaces? Or are there other reasons to use static classes over namespaces? (Note for this email I am not taking a position pro or con, I just posing the question.)
If collectively you discover there are gaps between namespaces and static classes, maybe the solution is to fill those gaps instead? Maybe that means improving namespaces, maybe that means adding static classes, or maybe it means some unmentioned 3rd option? Either way such an approach is likely going to be more productive than a quasi-religious/political rally with extremists on both sides protesting the other.
That is, unless those of you participating really prefer to have divisive debates that result in no positive outcomes, in which case, knock yourselves out. 🤷♂️
-Mike
Wow.
Hi,
(This is not a direct reply to Mike, his just seems to be less shouty than other recent emails)
I'm not particularly bothered by the results of this specific RFC either way - a similar enough result is already possible by grouping static methods in an abstract class, but I'm disappointed to see yet again that there's this implied notion that working with PHP in 2023 means "well surely you must be using composer", which leads to "but composer..." somehow being an accepted argument when it comes to missing/incomplete builtin functionality.
Despite the way some treat it, this isn't the composer internals list, nor is composer a required or even bundled part of PHP.
Does that mean you shouldn't use composer? No, if it works for your project, that's great.
Does that mean gaps in the php language/standard library should be automatically ignored/glossed over because composer may provide a work-around? Also, no.
PHP has for a very long time stood out amongst other web-focused languages for having a very rich standard library out of the box ('batteries included', I think the cool kids would say now).
Let's please not take PHP in the direction of "modern JavaScript" where language deficiencies mean that an entire alphabet of constantly changing third-party tooling is required to do anything but the simplest tasks.
Cheers
Stephen
Wow.
Hi,
(This is not a direct reply to Mike, his just seems to be less shouty than other recent emails)
I'm not particularly bothered by the results of this specific RFC either way - a similar enough result is already possible by grouping static methods in an abstract class, but I'm disappointed to see yet again that there's this implied notion that working with PHP in 2023 means "well surely you must be using composer", which leads to "but composer..." somehow being an accepted argument when it comes to missing/incomplete builtin functionality.
Despite the way some treat it, this isn't the composer internals list, nor is composer a required or even bundled part of PHP.
Does that mean you shouldn't use composer? No, if it works for your project, that's great.
Does that mean gaps in the php language/standard library should be automatically ignored/glossed over because composer may provide a work-around? Also, no.
PHP has for a very long time stood out amongst other web-focused languages for having a very rich standard library out of the box ('batteries included', I think the cool kids would say now).
Let's please not take PHP in the direction of "modern JavaScript" where language deficiencies mean that an entire alphabet of constantly changing third-party tooling is required to do anything but the simplest tasks.
Cheers
Stephen
--
To unsubscribe, visit: https://www.php.net/unsub.php
Hello,
I'd venture to say that static classes have some unique benefits over
plain functions. One, you can encapsulate state using private static
variables. That simply isn't possible with regular functions (unless
you use static variables in the functions themselves, which isn't
quite the same thing). Two, you still have inheritance that you can
use with static functions. You can't really have inheritance with
regular functions unless you are strictly passing around closures.
Third, static functions can also use traits (in theory) to allow for
code reuse. Fouthly, not everyone is writing s Symfony/Laravel
application that needs highly testable code and might just be throwing
a prototype together -- static classes could hold static configuration
for the app.
In something like C#, static classes are used as a way to extend parts
of the language.
One thing for Lanre: please do not remove the requirement to
explicitly mark a method as static with this. As someone who has read
very long readonly classes, when you make a change, you usually don't
find out it is readonly until you make that change. This is because
the "readonly" is at, and only at, the very top of the class. Now, if
you need to make the class non-readonly, but want to keep all the old
variables readonly, you have to make dozens of changes. Please, for
the life of sane refactoring, don't make "static class" a shortcut for
not being explicit, just make it so that things MUST be declared
static. Long classes are (unfortunately) a thing, and it makes
everything more obvious as to what is going on. Further, removing
static from the class, the class should still function, IMHO.
Robert Landers
Software Engineer
Utrecht NL
I'd venture to say that static classes have some unique benefits over
plain functions. One, you can encapsulate state using private static
variables. That simply isn't possible with regular functions (unless
you use static variables in the functions themselves, which isn't
quite the same thing).
It's close enough to the same thing that it has the same problems.
As far as I am aware, a static variable in a function is stored in global memory as a singleton. It's logically the same as a global variable, but with access restricted to one function.
A static property of a class is also stored in global memory. It's logically the same as a global variable, but with a funny name that has colons in it. It may be access restricted depending on the code.
(Engine experts, if I am subtly wrong there please do correct me.)
So static variables and static properties are both simply "globals with funny extras." And global-maintained state is well known and understood as a bad idea that hinders testing, refactoring, tractability, maintenance, and everything else developers care about.
Using global state, with or without funny syntax around it, is the anti-pattern we should not be encouraging.
Generally speaking, even my prototypes eschew globals and statics in 95% of cases.
--Larry Garfield
Seriously? Are you really this stupid or just feigning ignorance? How is
this a global:
class X {
private static $count = 0;
}
I am no longer pushing this RFC because idiots like you are in charge of
the votes but seriously your argument is dumb as fuck. If you aren't
sure then why say anything before looking more into it? A class level
static is not equivalent to a global in any sense and if I have to
explain the differences to you, you are not in a position to be speaking
this confidently.
This isn't me taking anything personally, i have just come to the
realization that this community is not for me, so you guys can have fun
jerking each other off, and I will keep my ideas to myself.
Cheers,
Lanre
I'd venture to say that static classes have some unique benefits over
plain functions. One, you can encapsulate state using private static
variables. That simply isn't possible with regular functions (unless
you use static variables in the functions themselves, which isn't
quite the same thing).
It's close enough to the same thing that it has the same problems.As far as I am aware, a static variable in a function is stored in global memory as a singleton. It's logically the same as a global variable, but with access restricted to one function.
A static property of a class is also stored in global memory. It's logically the same as a global variable, but with a funny name that has colons in it. It may be access restricted depending on the code.
(Engine experts, if I am subtly wrong there please do correct me.)
So static variables and static properties are both simply "globals with funny extras." And global-maintained state is well known and understood as a bad idea that hinders testing, refactoring, tractability, maintenance, and everything else developers care about.
Using global state, with or without funny syntax around it, is the anti-pattern we should not be encouraging.
Generally speaking, even my prototypes eschew globals and statics in 95% of cases.
--Larry Garfield
I'm disappointed to see yet again that there's this implied notion that
working with PHP in 2023 means "well surely you must be using composer",
which leads to "but composer..." somehow being an accepted argument when
it comes to missing/incomplete builtin functionality.
While I appreciate your point in the general case, in this particular
thread, the mentions of Composer are really just examples, or can be
reworded that way:
Functions lack autoloading, but in practice this isn't a big problem
because you can just require_once a file defining them, and as long as
OpCache is running there's very little performance penalty. If you're using
a package manager or module loading system to integrate multiple
autoloaders, it's generally easy to add one or more required files as part
of the package / module config - for example Composer has a "files" array
in each package's "autoload" config.
So the actual assumption is "surely you must be using OpCache", which
unlike Composer is bundled with PHP.
Regards,
Rowan Tommins
[IMSoP]
I'm disappointed to see yet again that there's this implied notion that
working with PHP in 2023 means "well surely you must be using composer",
which leads to "but composer..." somehow being an accepted argument when
it comes to missing/incomplete builtin functionality.While I appreciate your point in the general case, in this particular
thread, the mentions of Composer are really just examples, or can be
reworded that way:Functions lack autoloading, but in practice this isn't a big problem
because you can just require_once a file defining them, and as long as
OpCache is running there's very little performance penalty. If you're using
a package manager or module loading system to integrate multiple
autoloaders, it's generally easy to add one or more required files as part
of the package / module config - for example Composer has a "files" array
in each package's "autoload" config.So the actual assumption is "surely you must be using OpCache", which
unlike Composer is bundled with PHP.Regards,
Rowan Tommins
[IMSoP]
(Resending from list-address)
Hi Rowan,
Respectively, I disagree that it's "not a big problem" if your goal is to encourage people to use regular functions over classes with static methods.
"No autoloading" is clearly a well known barrier to the use of userland functions, and "rewriting 'use composer' to 'use require_once'" isn't a comparable workaround, as evidenced by:
- https://wiki.php.net/rfc/core-autoloading (from April 2023) paragraph 2 says:
The need for such a feature seems very clear as users will create “helper” classes with static methods to take advantage of autoloading via the class autoloading mechanism.
An email earlier in this thread makes the same point (shiny gold sticker if you remember who wrote this):
The only issue being that we don't have good autoloading support for such functions, and that's a whole different problem...
you can just require_once a file defining them,
To paraphrase a certain secret agent: Do you want people writing faux-namespace classes? Because this is how you get people writing faux-namespace classes.
So the actual assumption is
No, the assumption was very clearly written:
Autoloading is frankly mostly solved by using the
files
block in your composer.json's autoload config, and then you don't need to care.
Saying "a package loader" instead of "composer" isn't any better really. Autoloading of classes in PHP doesn't require a "package loader" to work. PHP ships with a built in class autoloader function, and pretending that using 'require_once' everywhere a function is used, is just as easy for the developer seem disingenuous to be honest.
Cheers
Stephen
I wouldn't support this, personally. I'm not a voter but I mean just in
terms of gauging community feel about the proposal, without unnecessarily
repeating what's already been said I just want to register that I'm in
agreement with Larry and Kamil's comments.
What static classes would achieve goes against the design principles of
what a class is and what it's for. Others have noted we already have an
adequate namespacing system and via Composer which is a de facto standard
in almost all PHP projects today, a means to autoload namespaced functions.
But more than that, I think for me - look, I get a lot of language features
which are sincerely useful can be argued to be just convenient syntax sugar
for things you could technically already do another way. But when I
consider features like readonly properties, constructor promotion and
readonly classes, I think the functional change goes beyond just syntax
sugar. You're getting an enormous saving versus say, trying to make a class
with all properties readonly in PHP 7.4. It's not just a little bit of
boilerplate trimmed down, it's you now have to write one line of code
instead of fifty.
So even if I supported the idea of static classes in principle, I'd still
be looking at it and thinking really the only convenience, the only syntax
sugar here is that you now have to type the word static N-1 fewer times
where N is the number of properties and functions in your class. This is
not a huge benefit or saving to me. While I wouldn't encourage anyone to
design their code this way, for the same reasons Larry has articulated, if
you want to design your code this way you already can at basically the same
cost of your time and effort and same cost to the PHP engine.
I think there'd need to be some persuasively more substantial gain for
either the developer or the execution of a script for this proposal to be
worth anything.
Thanks.
-David