On Sat, Aug 2, 2025, at 22:18, Valentin Udaltsov wrote:
> On Sat, Aug 2, 2025, at 22:17, Rob Landers <rob@bottled.codes> wrote:
>> __
>> On Sat, Aug 2, 2025, at 19:04, Alexandru Pătrănescu wrote:
>>>
>>>
>>> On Sat, Aug 2, 2025, 17:10 Rob Landers <rob@bottled.codes> wrote:
>>>> __
>>>>
>>>>
>>>> On Sat, Aug 2, 2025, at 16:04, Alexandru Pătrănescu wrote:
>>>>>
>>>>>
>>>>> On Sat, Aug 2, 2025 at 12:10 PM Rob Landers <rob@bottled.codes> wrote:
>>>>>> __
>>>>>> On Tue, Jul 29, 2025, at 20:11, Jonathan Vollebregt wrote:
>>>>>>> I came across this edge case today:
>>>>>>>
>>>>>>> https://3v4l.org/R3Q8D
>>>>>>>
>>>>>>> Both psalm and phpstan think this code is A-OK (Once you add the
>>>>>>> requisite type hints) but it causes fatal errors way back to PHP 5.0.0
>>>>>>>
>>>>>>> ...<snip>...
>>>>>>
>>>>>> It's not an edge case, in C2, you redefine a protected variable with the same name and shadowed the original $v. That $v is different than C's $v. It's easiest to see this with static access: https://3v4l.org/0SRWb#v8.4.10
>>>>>>
>>>>>> However, I don't know of any way to unshadow a property from $this to access the ancestor's value (other than using private access), but it exists and takes up memory; just accessing it is the hard part.
>>>>>>
>>>>>> — Rob
>>>>>
>>>>>
>>>>> Hi Rob,
>>>>>
>>>>> I'm pretty sure that there is no shadowing happening in the example.
>>>>> When the child instance is created, there is just one slot for the property, as the child one replaces the parent one.
>>>>> So basically the child property overrides the parent property rather than shadowing it.
>>>>>
>>>>> True shadowing (two slots) only occurs when the parent property is declared private.
>>>>>
>>>>> It's just that when redefining, it stores the declaring class, and so there is this sibling class access issue.
>>>>>
>>>>> I'm wondering now if the access shouldn't be relaxed, in case we have the parent class that initially defined the property.
>>>>>
>>>>> Of course, we should focus on non-static properties, as static ones are different things, and there is some shadowing there.
>>>>>
>>>>> --
>>>>> Alex
>>>>
>>>> Hi Alex,
>>>>
>>>> I’m not sure what you mean? https://3v4l.org/WKILh#v8.4.10
>>>>
>>>> There is clearly shadowing going on.
>>>>
>>>>
>>>
>>> Hi Rob,
>>>
>>> As I said, let's leave aside the static case, as the question from Jonathan was not about that.
>>>
>>> Given the class P that defines a protected property with value 1,
>>> and a class C that extends P and re-defines the protected property with the value 2,
>>> please show me an example where you could get from an instance of class C the value 1 of the parent class property that you think it's shadowed.
>>> Bonus point, if you manage that, you could also set it to something else, and so have a hidden storage for any object of class C that is not really visible normally.
>>>
>>> As far as I know, there is no way to achieve that, and the reason is because at runtime the objects have a single slot for the protected property; the child class property overrides the parent class property when redeclared, and does not shadow it.
>>> But please prove me wrong.
>>>
>>>
>>> Thanks,
>>> Alex
>>>
>>
>> I mentioned in my first reply, there is no way to get an instance-level property unshadowed. It is there though (according to inheritance.c, if I’m reading it right, it is still accessible, just not from user-land).
>>
>> In any case, there are lots of interesting footguns with properties and inheritance: Problem with abstract nested object · Issue #47 · Crell/Serde <https://github.com/Crell/Serde/issues/47#issuecomment-1890966829>.
>>
>>> Could it be considered a bug that my first example produces a fatal
>>> error instead of trying to access the shadowed parent property that it
>>> has access to and is typed to use?
>>
>> Other languages (such as C#, Java, etc: https://www.programiz.com/online-compiler/0ud6UO24mHOTU) don’t allow you to access protected properties/methods on sibling classes. This is because "protected" is usually used in the context of inheritance; access is usually restricted to "myself" or "children" and a sibling is neither of those. If there is a bug, the bug is that you can access a sibling’s protected properties, at all.
>>
>> — Rob
>
> > If there is a bug, the bug is that you can access a sibling’s protected properties, at all.
>
> In 2006 the absence of this feature was fixed as a bug and meged in PHP 5.2: https://bugs.php.net/bug.php?id=37632
> In 2020 Nikita Popov agreed that this is expected: https://x.com/nikita_ppv/status/1261633126687805440
>
> So one way is to explicitly mention this feature in the Visibility docs <https://www.php.net/manual/en/language.oop5.visibility.php> and fix the redeclaration issue to make things consistent.
>
> The other way is to deprecate sibling relations.
>
> --
> Valentin
I don’t think the redeclaration is a bug though, as is mentioned in the linked bug report about properties: https://bugs.php.net/bug.php?id=37212, it is pretty clear to me that people expect a redeclaration would be a fatal error, but not accessing a property declared in a shared parent scope (emphasis mine):
> The property *is not being redeclared in C*, though. *It is still a property of A, structure-wise.* A method declared and called in the same way as the property does not cause any error.
This has been the case for years, so I don’t think it is a bug. I was only saying that if there is a bug, the bug would be that you can access another class’s protected properties that aren’t a parent or sibling. I didn’t really go into why, but IMHO, it breaks LSP, since it allows sibling classes to depend on each other’s internals, breaking substitutability (particularly in regards to hooks).
— Rob