Re: Re: Re: Ambiguity in the oop5 visibility docs
| From: | Nikita Popov | Date: | Thu, 28 Mar 2019 09:32:32 +0000 |
| Subject: | Re: Re: Re: Ambiguity in the oop5 visibility docs | ||
| References: | 1 2 3 | Groups: | php.doc |
| Request: | Send a blank email to phpdoc+get-969387196@lists.php.net to get a copy of this message | ||
On Tue, May 29, 2018 at 1:41 PM Rowan Collins <rowan.collins@gmail.com>
wrote:
> On 27 May 2018 at 19:02, Andrew Gromov <andrewgrom@rambler.ru> wrote:
>
>> class A {
>> public function foo(){
>> echo $this->b; // No property $b in parent class
>> }
>> }
>>
>> class B extends A {
>> protected $b = 'foo';
>> }
>>
>> $a = new B();
>> $a->foo(); // prints 'foo'
>>
>> In classic OOP, parent class can't access unknown to him member of child
>> class.
>>
>
>
> In PHP, classes don't really "know" anything about their structure -
> everything is looked up dynamically at run-time. At run-time, there is a
> property called "b" on the instance, and it is visible in the current
> scope, so the access is allowed.
>
> You mention "classic OOP", but I'm not aware of any formal definition we
> could reference here, only precedent from other languages. Most languages
> don't need to worry about this case, because the reference to $this->b
> would fail to compile unless b was declared; but PHP can't raise that
> error, because accessing a completely undeclared property is allowed.
>
>
>
>
>> And phrases like
>>
>> > can be accessed within the class itself and parent classes.
>>
>> explode the brain! How it can be - reverse inheritance, from child to parent!?
>>
>>
> I discussed a common use case for this in my first response on this
> thread:
> https://marc.info/?l=phpdoc&m=152727049709620&w=2
>
> There is no *inheritance* from child to parent, just access from code
> written in a parent class to polymorphic behaviour in the child class.
>
Want to bump this discussion again, because I still think that the current
behavior is incorrect. I think there are two different cases that got
conflated here:
// This should work.
class A {
protected function foo() {}
}
class B extends A {
protected function test() { $this->foo(); }
}
class C extends B {
protected function foo() {}
}
(new C)->test();
This code should of course work. It should work because we have an A::foo()
method (the "prototype"), which happens to be overridden by C::foo(), but
that's not actually relevant. The inheritance check here is whether B has
access to the prototype A::foo(), which is has. If you consider that
inheritance is checked against the prototype, then there is actual "reverse
visibility" going on here.
The other case, and the case that I believe should not work, is the
following:
// This shouldn't work, but does.
class A {
protected function test() { $this->foo(); }
}
class B extends A {
protected function foo() {}
}
(new B)->test();
Here B::foo() does not have a prototype higher up in the hierarchy. We're
calling B::foo() from A, which should by any reasonable interpretation not
be possible. To allow this call, it should be necessary to also add a
protected function foo() to class A, possibly as an abstract method:
// This should work.
abstract class A {
protected abstract function foo();
protected function test() { $this->foo(); }
}
class B extends A {
protected function foo() {}
}
(new B)->test();
Here once again B::foo() overrides the prototype A::foo(), and A::foo() is
of course visible from A.
I believe that the current behavior is a leftover from before the
introduction of the prototype mechanism in PHP 5.2. Previously just
checking visibility in both directions was a poor mans approximation of the
correct behavior. However, it both disallowed some legal cases (involving
non-linear hierarchies) and allowed illegal cases (accessing protect method
from a child).
Since PHP 5.2 all the legal cases should be covered by the prototype
mechanism and the bidirectional visibility check is just a legacy leftover.
We should consider removing it for PHP 8.
Nikita