4

I'm used to put method as protected in my PHP classes. But playing with private I'm starting to have doubts.

I know that it might be a duplicate but I can't find clarity from the SO-answers I've read. Checked the documentation and SO answers but still my tests are incoherent with what is being said there (see below).

Especially is not often explained the kind of "tangling" among public and private method, when extending the same type class, in the context of PHP.

For example:

<?php

class A
{
    private function ab() { echo 'test'.PHP_EOL; }
    public function test() { $this->ab(); }
    public function accessprivate($obj)  { $obj->ab(); }
}

class B extends A
{
        public function ab() { echo 'overridden-public'.PHP_EOL; }  // expect notice overriding private parent method
}

$a = new A;
$a2 = new A;
$b = new B;

$a->test(); // expect 'test'
$b->test(); // expect access to B::ab() and print 'overridden-public'
$b->ab();   // expect access to B::ab() and print 'overridden-public'
$a2->accessprivate($a); // expect 'test' since is the same class
$b->accessprivate($a); // expect cannotaccess private of A from class B

When running this is the result:

test test overridden-public test test

The main point is that I expected that a private method is inherited but not accessible to child classes; hence:

  • I shouldn't be able to change the visibility of ab() to public
  • with the override of ab() in B I would expect test() to call ab() on $this as B instance, and get printed "overridden-public"
  • accessprivate() from $a2 on $a is fine because they are the same class
  • accessprivate() from $b on $a should NOT be fine because they are different classes (parent & child)

So the questions are:

  • Why am I wrong? What am I misunderstanding?
  • Is this visibility model the same on other languages or PHP is doing it differently? And in that case, are my expectation fitting more the visibility model of some other language?
4
  • You show the expected results in your question but not the actual results. Adding them might be helpful.
    – Dave
    Commented Aug 2, 2019 at 12:57
  • Added. Thank you for the suggestion 👍 Commented Aug 2, 2019 at 13:06
  • Good question. It's indeed quirky. Sidenote: If you create a class C and define public function accessprivate($obj) { $obj->ab(); } on it, you get the expected error. I am assuming that php's context gets still bound to A when you do $obj->ab() yet I have no clue why.
    – k0pernikus
    Commented Aug 2, 2019 at 13:25
  • 1
    Other sidenote: Your public function ab() on B is not overriding anything. It's creating a completely new method on B. (Other languages require the override keyword. Php lacks this so this can be confusing.) Having A::ab() private hence prohibits its parent call in B, so you cannot do parent::ab() within B due to private access.
    – k0pernikus
    Commented Aug 2, 2019 at 13:27

1 Answer 1

4

I shouldn't be able to change the visibility of ab() to public

An overriding implementation must have the same or higher visibility in order to not violate the LSP. The overriding implementation doesn't expose the parent implementation, so no fundamental concern here. If the parent's implementation is non-public, for all intents and purposes it doesn't exist. For all intents and purposes the child is adding a new method to the class, which is fine.

with the override of ab() in B I would expect test() to call ab() on $this as B instance, and get printed "overridden-public"

private methods are "hard bound" to the declaring class. A::test will call a private A::ab preferably. This is specifically so class internals can remain private. Should an extending class implement an identical method unknowingly, there's no surprising behaviour within A.

accessprivate() from $b on $a should NOT be fine because they are different classes (parent & child)

It's still calling A::accessprivate since B doesn't implement any such method, which rather works the same as the test method and the explanation in the previous paragraph.

The purpose of private is largely to guarantee no interference from outside or extending code. If you're marking methods as private, you can be very sure which implementation of code will be called (always the declaring class), regardless of whether methods are overridden in children. If you keep this in mind, the behaviour is pretty expected and self-explanatory. protected methods explicitly allow and expect overriding to take place and behave accordingly.

1
  • Mmmh, still processing it; but it starts to make sense, thank you – Still, it's tricky since this looks like an implicit behaviour, while I would rather expect to get a notice, in case I am doing something that I am not allowed to do (or that will sort no effect, like the non-working overriding of my examples). On this concern, the PHP documentation didn't appear much clear. Is this behaviour explicitly documented/mentioned anywhere on php.net ? Commented Aug 2, 2019 at 15:09

Not the answer you're looking for? Browse other questions tagged or ask your own question.