Static property asymmetric visibility was left out of the original RFC, because it seemed like it would be hard and of little use. Turns out, Ilija found a way to make it easy. (Ilija is smart.) So here's a small RFC to add aviz to static properties, because we can't think of a reason to NOT do so.
https://wiki.php.net/rfc/static-aviz
--
Larry Garfield
larry@garfieldtech.com
Hi
Am 2024-11-25 17:52, schrieb Larry Garfield:
Static property asymmetric visibility was left out of the original RFC,
because it seemed like it would be hard and of little use. Turns out,
Ilija found a way to make it easy. (Ilija is smart.) So here's a
small RFC to add aviz to static properties, because we can't think of a
reason to NOT do so.
The RFC has the status “Draft” and does not appear to be linked in the
overview at https://wiki.php.net/rfc. Is it intended to be open for
discussion yet?
Regarding the RFC contents: I am not sure the behavior of private(set)
implying final
is obvious for static properties. Consider this example
(https://3v4l.org/bvQjM):
<?php
class Foo {
public static $baz;
}
class Bar extends Foo {
public static $baz;
}
Foo::$baz = '2';
Bar::$baz = '1';
var_dump(Foo::$baz, Bar::$baz);
Non-static properties only have $this->
for property access, but with
static properties you can distinguish between self::
and static::
,
which makes overriding a private(set)
static property meaningful,
because they are actually independent properties.
Best regards
Tim Düsterhus
Hi
Am 2024-11-25 17:52, schrieb Larry Garfield:
Static property asymmetric visibility was left out of the original RFC,
because it seemed like it would be hard and of little use. Turns out,
Ilija found a way to make it easy. (Ilija is smart.) So here's a
small RFC to add aviz to static properties, because we can't think of a
reason to NOT do so.The RFC has the status “Draft” and does not appear to be linked in the
overview at https://wiki.php.net/rfc. Is it intended to be open for
discussion yet?
Paperwork oversight on my part. Fixed now, thanks.
Regarding the RFC contents: I am not sure the behavior of
private(set)
implyingfinal
is obvious for static properties. Consider this example
(https://3v4l.org/bvQjM):<?php class Foo { public static $baz; } class Bar extends Foo { public static $baz; } Foo::$baz = '2'; Bar::$baz = '1'; var_dump(Foo::$baz, Bar::$baz);
Non-static properties only have
$this->
for property access, but with
static properties you can distinguish betweenself::
andstatic::
,
which makes overriding aprivate(set)
static property meaningful,
because they are actually independent properties.Best regards
Tim Düsterhus
Hm, interesting point. I am not sure of the best way to model that.
Thinking aloud, my expectation would be that it behaves similarly to how final static methods would behave. Which appears to be a syntax error: https://3v4l.org/j8mp0#v8.4.1
So, shouldn't properties do the same?
--Larry Garfield
Thinking aloud, my expectation would be that it behaves similarly to how final static methods would behave. Which appears to be a syntax error: https://3v4l.org/j8mp0#v8.4.1
So, shouldn't properties do the same?
Without final you can still override both private static properties and
private static methods: https://3v4l.org/MS73Y#v8.4.1
When explicitly declared final, static properties do result in a syntax
error: https://3v4l.org/fqI8v#v8.4.1
Re-reading the logic in the original aviz RFC makes me think implicit
final here is unnecessary. All static properties are "Shadowed" like
private properties (even when they're public) so there's no conflicting
behavior.
The two behaviors described as conflicting in the aviz RFC are decided
explicitly in the context of static properties, by the caller accessing
it with self::
or static::
. Not by a combination of the visibility
and child classes.
Consider this example:
class A {
private(set) static int $a = 1;
public static function test(int $val) {
static::$a = $val;
}
}
class B extends A {
private(set) static int $a = 2;
}
B::test(3)
Yes this would produce a fatal error, but doing this with just private static
does the same in current PHP: https://3v4l.org/Y6lZ7#v8.4.1
You might want to discuss banning use of static::
on private statics,
but that's a big BC break.
Since static properties do still have to have equal or wider visibility
when extending I'd say using static::$prop
on a property you know is
private is a known risk and remove the implicit final.
- Jonathan
Thinking aloud, my expectation would be that it behaves similarly to how final static methods would behave. Which appears to be a syntax error: https://3v4l.org/j8mp0#v8.4.1
So, shouldn't properties do the same?
Without final you can still override both private static properties and
private static methods: https://3v4l.org/MS73Y#v8.4.1When explicitly declared final, static properties do result in a syntax
error: https://3v4l.org/fqI8v#v8.4.1Re-reading the logic in the original aviz RFC makes me think implicit
final here is unnecessary. All static properties are "Shadowed" like
private properties (even when they're public) so there's no conflicting
behavior.The two behaviors described as conflicting in the aviz RFC are decided
explicitly in the context of static properties, by the caller accessing
it withself::
orstatic::
. Not by a combination of the visibility
and child classes.Consider this example:
class A { private(set) static int $a = 1; public static function test(int $val) { static::$a = $val; } } class B extends A { private(set) static int $a = 2; } B::test(3)
Yes this would produce a fatal error, but doing this with just
private static
does the same in current PHP: https://3v4l.org/Y6lZ7#v8.4.1You might want to discuss banning use of
static::
on private statics,
but that's a big BC break.Since static properties do still have to have equal or wider visibility
when extending I'd say usingstatic::$prop
on a property you know is
private is a known risk and remove the implicit final.
- Jonathan
Hi folks. Finally getting back to this thread.
Ilija and I have discussed it, and it seems to us that the consistency of implicit-final-on-private-set (on both object and static properties) is more valuable than maximal flexibility, especially as we're dealing with a rare use case to begin with.
Specifically, static::$prop
is still expected to behave the same as the parent class's $prop. Eg, its type can't change, its visibility cannot be narrowed, etc. So a private(set) parent $prop would violate that expectation, the same way it would for a dynamic property. Introducing another subtle difference between self:: and static:: doesn't seem like a good thing for DX.
Additionally, if we find in the future that it really would be better to not have the implicit final and allow people to "opt in" via self::vs static::, it's less of a BC break to remove the implicit final in the future than to try and introduce it.
For that reason, we are going to keep static properties consistent with object properties in this regard.
--Larry Garfield
Static property asymmetric visibility was left out of the original RFC,
because it seemed like it would be hard and of little use. Turns out,
Ilija found a way to make it easy. (Ilija is smart.) So here's a
small RFC to add aviz to static properties, because we can't think of a
reason to NOT do so.
Heads up: Absent any other feedback, I'm going to call the vote on this RFC early next week.
--Larry Garfield
Static property asymmetric visibility was left out of the original RFC,
because it seemed like it would be hard and of little use. Turns out,
Ilija found a way to make it easy. (Ilija is smart.) So here's a
small RFC to add aviz to static properties, because we can't think of a
reason to NOT do so.Heads up: Absent any other feedback, I'm going to call the vote on this RFC early next week.
--Larry Garfield
Hi
I'm not sure how I feel about this.
The current implementation actually uses a workaround because otherwise it interferes with cache slot merging.
This creates duplicated code in the VM handlers. Although the complication is small, it still is one. And the complication may be duplicated in the future in the JIT.
Workarounds/complications can be fine if having the feature is justifiable, but you said yourself that it seems of little use and the RFC text says it's just for completeness.
So honestly, I'm conflicted and leaning towards voting "no".
Kind regards
Niels
Hi Niels
I'm not sure how I feel about this.
The current implementation actually uses a workaround because otherwise it interferes with cache slot merging.
This creates duplicated code in the VM handlers. Although the complication is small, it still is one. And the complication may be duplicated in the future in the JIT.
Workarounds/complications can be fine if having the feature is justifiable, but you said yourself that it seems of little use and the RFC text says it's just for completeness.
So honestly, I'm conflicted and leaning towards voting "no".
I wouldn't necessarily call this a workaround, but more of a missed
optimization (one branch for each static property write that is
unlikely to mispredict). If you wish, I can have a look at separating
cache slots. This may lead to a slowdown due to cache priming (as only
R/IS and RW/W/UNSET will be shared). To be honest, I doubt either of
those will lead to a measurable difference in real code. I can confirm
this by running some benchmarks. The benefit of separate cache slots
is that it can be entirely handled in
zend_fetch_static_property_address(), which would reduce VM/ future
JIT changes, although the separation of the cache slots themselves
might be more complex (given how it's currently implemented).
Ilija
I wouldn't necessarily call this a workaround, but more of a missed
optimization (one branch for each static property write that is
unlikely to mispredict). If you wish, I can have a look at separating
cache slots. This may lead to a slowdown due to cache priming (as only
R/IS and RW/W/UNSET will be shared). To be honest, I doubt either of
those will lead to a measurable difference in real code. I can confirm
this by running some benchmarks. The benefit of separate cache slots
is that it can be entirely handled in
zend_fetch_static_property_address(), which would reduce VM/ future
JIT changes, although the separation of the cache slots themselves
might be more complex (given how it's currently implemented).
I think splitting cache slots will indeed be worse than what it is now.
However, that argument in itself just doesn't really convince me honestly.
You are likely right about the branch prediction, but the CPU still has to do some extra work; and it's also just more things to think about as a maintainer.
I'll think about it more, maybe I'll just abstain from voting.
Kind regards
Niels
Le 25 nov. 2024 à 17:52, Larry Garfield larry@garfieldtech.com a écrit :
Static property asymmetric visibility was left out of the original RFC, because it seemed like it would be hard and of little use. Turns out, Ilija found a way to make it easy. (Ilija is smart.) So here's a small RFC to add aviz to static properties, because we can't think of a reason to NOT do so.
Hi,
About use case: Looking at my code, I’ve found places where I store data (e.g. global config values) in public static properties, but those data is supposed to be initialised and/or modified only inside the class.
In those cases, aviz would allow me to document and enforce what is currently a gentleman's agreement.
—Claude