Hi Internals,
I've recently realized that class constant values cannot always be trusted
when late static binding is involved (e.g. static::FOO or $this::FOO),
since they can be freely overridden in child classes. That's why the engine
knows neither the type and the value before run-time.
Doctrine coding standards has a specific rule which disallows referencing a
possibly overridden class constant (
https://github.com/doctrine/coding-standard/blob/8d75920bb04a9be4c73c2a775ee5766262cf2a0d/lib/Doctrine/ruleset.xml#L123
),
but I'm wondering if it would be better to simply make it possible to allow
declaring class constants final (thus disallowing overriding) instead?
In my opinion, final constants would come with two advantages:
- make class constant declarations paranoidly safe
- allow the engine to optimize class constants references in LSB context
Although I admit that the win is not extra huge, I do think that it would
still be useful to have this feature, especially considering that the
implementation is very straightforward (
https://github.com/php/php-src/pull/6878). So that's why I'm proposing this
as an RFC.
Recently, a similar attempt to make class constant declarations safer was
the Typed Constants RFC (https://externals.io/message/110755#110755), but
unfortunately it has halted since then. I think final and typed constants
would be a nice little addition, and would complement each other well.
Regards,
Máté
Hi Internals,
I've recently realized that class constant values cannot always be trusted
when late static binding is involved (e.g. static::FOO or $this::FOO),
since they can be freely overridden in child classes. That's why the engine
knows neither the type and the value before run-time.Doctrine coding standards has a specific rule which disallows referencing a
possibly overridden class constant (https://github.com/doctrine/coding-standard/blob/8d75920bb04a9be4c73c2a775ee5766262cf2a0d/lib/Doctrine/ruleset.xml#L123
),
but I'm wondering if it would be better to simply make it possible to allow
declaring class constants final (thus disallowing overriding) instead?In my opinion, final constants would come with two advantages:
- make class constant declarations paranoidly safe
- allow the engine to optimize class constants references in LSB context
Although I admit that the win is not extra huge, I do think that it would
still be useful to have this feature, especially considering that the
implementation is very straightforward (
https://github.com/php/php-src/pull/6878). So that's why I'm proposing
this
as an RFC.Recently, a similar attempt to make class constant declarations safer was
the Typed Constants RFC (https://externals.io/message/110755#110755), but
unfortunately it has halted since then. I think final and typed constants
would be a nice little addition, and would complement each other well.Regards,
Máté
I think if we add final class constants, we should also clean up the
current semantics for class constant overriding at the same time. The
problem is that interface constants currently cannot be overridden directly
(https://3v4l.org/jsDRO), but they can be overridden indirectly (
https://3v4l.org/IkvJe). If we have final class constants, then we should
say that class constants are always overridable by default (regardless of
whether they are declared in an interface or class), as we now have an
explicit way to opt into forbidding the override.
Regards,
Nikita
Hi Mate,
Can I ask why you use late static binding while you don't want your constant value to be changed/overridden by child classes? IMHO, self::FOO
is all you need.
Regards,
CHU Zhaowei
-----Original Message-----
From: Máté Kocsis kocsismate90@gmail.com
Sent: Sunday, April 18, 2021 10:20 PM
To: PHP Internals List internals@lists.php.net
Subject: [PHP-DEV] [RFC] [Draft] Final constants
Hi Internals,
I've recently realized that class constant values cannot always be trusted when late static binding is involved (e.g. static::FOO or $this::FOO), since they can be freely overridden in child classes. That's why the engine knows neither the type and the value before run-time.
Doctrine coding standards has a specific rule which disallows referencing a possibly overridden class constant (
https://github.com/doctrine/coding-standard/blob/8d75920bb04a9be4c73c2a775ee5766262cf2a0d/lib/Doctrine/ruleset.xml#L123
),
but I'm wondering if it would be better to simply make it possible to allow declaring class constants final (thus disallowing overriding) instead?
In my opinion, final constants would come with two advantages:
- make class constant declarations paranoidly safe
- allow the engine to optimize class constants references in LSB context
Although I admit that the win is not extra huge, I do think that it would still be useful to have this feature, especially considering that the implementation is very straightforward ( https://github.com/php/php-src/pull/6878). So that's why I'm proposing this as an RFC.
Recently, a similar attempt to make class constant declarations safer was the Typed Constants RFC (https://externals.io/message/110755#110755), but unfortunately it has halted since then. I think final and typed constants would be a nice little addition, and would complement each other well.
Regards,
Máté
Can I ask why you use late static binding while you don't want your
constant value to be changed/overridden by child classes? IMHO,self::FOO
is all you need.
Personally, I don't use late static binding with class constants since the
coding standard rule I previously referenced forbids this, but I don't
think a coding style rule should be our only possibility to disallow
constant overriding.
After learning about the strange behavior Nikita pointed out, final
constants would be even more useful than what I initially thought.
Can I ask why you use late static binding while you don't want your constant value to be changed/overridden by child classes? IMHO, self::FOO
is all you need.
Personally, I don't use late static binding with class constants since the coding standard rule I previously referenced forbids this, but I don't think a coding style rule should be our only possibility to disallow constant overriding.
After learning about the strange behavior Nikita pointed out, final constants would be even more useful than what I initially thought.
I agree we should fix the weird behavior but that’s another topic. My point is actually what’s your scenario for a final constant? If you want to make sure the value you access are not changed by child class, self::
can serve the purpose already. If you want to make sure
child classes can access the original value, parent::
or ParentClassName::
can do the job as well.
Regards,
CHU Zhaowei
My point is actually what’s your scenario for a final constant? If you
want to make sure the value you access are not changed by child class,
self::
can serve the purpose already. If you want to make sure
Yes, my intention is to actually make class constant overriding impossible
when it is desired. You can't really achieve this with only using self::,
since you can only control your own code. Yes, I know that child classes
could still declare a new class constant to overcome this limitation, but
at least there will be no doubt what they should expect if they try to
override the constant value. Otherwise, they have to check whether the
parent class uses self:: or static:: calls.
I agree we should fix the weird behavior but that’s another topic.
No, I believe it isn't. Currently, only interface constants are not
(directly) overridable. If we simply made all class/interface constans
overridable, then one could not retain the original behavior in case of
interfaces. While the solution offered by my proposal wouldn't be
compatible with older PHP versions, it's still better to offer the
possibility than not doing anything.
Regards:
Máté
Am 21.04.2021 um 14:25 schrieb Máté Kocsis kocsismate90@gmail.com:
My point is actually what’s your scenario for a final constant? If you
want to make sure the value you access are not changed by child class,
self::
can serve the purpose already. If you want to make sureYes, my intention is to actually make class constant overriding impossible
when it is desired. You can't really achieve this with only using self::,
since you can only control your own code. Yes, I know that child classes
could still declare a new class constant to overcome this limitation, but
at least there will be no doubt what they should expect if they try to
override the constant value. Otherwise, they have to check whether the
parent class uses self:: or static:: calls.
I never really understood the desire to restrict how people can use your code.
If there is no good reason to override the value of a class constant people won't do it.
If there might be a good reason (even one you as the original designer didn't predict) then why not leave that door open.
While I understand the theoretical benefit of being able to specify this behavior I do think it is almost always counter productive and not a pattern I would encourage. Especially not in a dynamic language like PHP.
- Chris
I never really understood the desire to restrict how people can use
your code.
If there is no good reason to override the value of a class constant people won't do it.
If there might be a good reason (even one you as the original designer didn't predict) then why not leave that door open.While I understand the theoretical benefit of being able to specify this behavior I do think it is almost always counter productive and not a pattern I would encourage. Especially not in a dynamic language like PHP.
Such restrictions also convey information and intent. One could argue
defining a specific return type to a method is restricting its use, but
it also clarifies how something should work and avoids unintended
changes. final constants might be more niche, but it gives you a choice
on how you want a class constant to behave if child classes are
necessary and possibly done by someone else.
Having "final" for constants seems a good addition to me, especially if
the inconsistent behavior with interface constants can be solved at the
same time.
Am 21.04.2021 um 17:08 schrieb Andreas Leathley a.leathley@gmx.net:
I never really understood the desire to restrict how people can use
your code.
If there is no good reason to override the value of a class constant people won't do it.
If there might be a good reason (even one you as the original designer didn't predict) then why not leave that door open.While I understand the theoretical benefit of being able to specify this behavior I do think it is almost always counter productive and not a pattern I would encourage. Especially not in a dynamic language like PHP.
Such restrictions also convey information and intent. One could argue
defining a specific return type to a method is restricting its use, but
it also clarifies how something should work and avoids unintended
changes.
To me, the term 'constant' is good enough for that...
final constants might be more niche, but it gives you a choice
on how you want a class constant to behave if child classes are
necessary and possibly done by someone else.
... but if child classes are necessary then how can you say that the constant's value should never be different for children?
Why not leave that to the user of your class? I've seen my share of damage done by assumptions about how code will be used later.
Having "final" for constants seems a good addition to me, especially if
the inconsistent behavior with interface constants can be solved at the
same time.
Fixing this inconsistency should be discussed separately IMHO. Both because it is a side-effect and should not be used as a reason to add 'final' as well as not fixing the problem for code not using 'final' with constants.
Side-note: Back in my Java days people argued with performance to add 'final' but that only lead to it being sprinkled all over the place making code less flexible than it needed to be without real performance benefit. That's what I mean by promoting harmful patterns.
- Chris