Traits and redeclaring static properties

php.internals

Ilija Tovilo

3 years ago
Hi everyone A few days ago we received a bug report about an inconsistency with traits and static properties. https://github.com/php/php-src/issues/10935 https://3v4l.org/Dll1m ``` class A { static $test; } class B extends A { static $test; } A::$test = 'A'; B::$test = 'B'; var_dump(A::$test, B::$test); // string(1) "A" // string(1) "B" ``` https://3v4l.org/SClLn ``` trait Foo { static $test; } class A { use Foo; } class B extends A { use Foo; } A::$test = 'A'; B::$test = 'B'; var_dump(A::$test, B::$test); // string(1) "B" // string(1) "B" ``` To summarize, a trait does *not* add a property to a class that has already inherited the same property from its parent. This makes sense for non-static properties, as redeclaring the property essentially has no effect. For static properties, however, there's a semantic difference in that redeclaring a static property creates a new slot in the current class that is separate from the parent's static property slot. This was never explicitly specified by the traits RFC. However, this sentence hints at the fact that the two examples above should behave equivalently. https://wiki.php.net/rfc/horizontalreuse
> It is almost like a language supported and failsafe copy'n'paste mechanism to build classes.
I created a PR to align the behavior of the two examples. It targets the master branch to avoid any potential backwards incompatibility in a patch. https://github.com/php/php-src/pull/10937 Please let me know if there are any concerns about this change. Ilija

Larry Garfield

3 years ago
On Fri, Mar 31, 2023, at 1:07 PM, Ilija Tovilo wrote:
> Hi everyone > > A few days ago we received a bug report about an inconsistency with > traits and static properties. > > https://github.com/php/php-src/issues/10935 > > https://3v4l.org/Dll1m > ``` > class A { > static $test; > } > class B extends A { > static $test; > } > A::$test = 'A'; > B::$test = 'B'; > var_dump(A::$test, B::$test); > // string(1) "A" > // string(1) "B" > ``` > > https://3v4l.org/SClLn > ``` > trait Foo { > static $test; > } > class A { > use Foo; > } > class B extends A { > use Foo; > } > A::$test = 'A'; > B::$test = 'B'; > var_dump(A::$test, B::$test); > // string(1) "B" > // string(1) "B" > ``` > > To summarize, a trait does *not* add a property to a class that has > already inherited the same property from its parent. This makes sense > for non-static properties, as redeclaring the property essentially has > no effect. For static properties, however, there's a semantic > difference in that redeclaring a static property creates a new slot in > the current class that is separate from the parent's static property > slot. > > This was never explicitly specified by the traits RFC. However, this > sentence hints at the fact that the two examples above should behave > equivalently. > > https://wiki.php.net/rfc/horizontalreuse >> It is almost like a language supported and failsafe copy'n'paste mechanism to build classes. > > I created a PR to align the behavior of the two examples. It targets > the master branch to avoid any potential backwards incompatibility in > a patch. > https://github.com/php/php-src/pull/10937 > > Please let me know if there are any concerns about this change. > > Ilija
This seems reasonable to me. "Compile time copy and paste" is the mental model we've been training people to have for a decade, so ensuring it actually works that way in corner cases seems like the right move. I don't think I've ever seen code that would have a behavior change as a result. --Larry Garfield