Surprising change in PHP’s protected visibility between 5.1 and 5.2

I thought protected meant that you could only access a method from within yourself or from your parent class. The PHP documentation for visibility sparingly states:

Protected limits access to inherited and parent classes (and to the class that defines the item).

Imagine the following scenario, contrived in code to make a point. You’ve got a parent class, and two children that inherit from it. One child redefines its parent’s protected method, the other child calls the protected method that its sibling redefined. And then we’ve got an unrelated class that also calls the first child’s redefined protected method. What should happen when this runs?

<?php

class TheMother
{
  protected function askForMoney()
  {
    print "May I please have some money";
  }
}

class TheDaughter extends TheMother
{
  protected function askForMoney()
  {
    print "Please, please, pretty please!";
  }
}

class TheSon extends TheMother
{
  public function askSiblingToAskForMoney()
  {
    $sibling = new TheDaughter();
    $sibling->askForMoney();
  }
}

class TheFriend
{
  public function askFriendToAskForMoney()
  {
    $friend = new TheDaughter();
    $friend->askForMoney();
  }
}

print "when sibling asks:\n";
$son = new TheSon();
$son->askSiblingToAskForMoney();

print "\n\n";

print "when unrelated friend asks:\n";
$friend = new TheFriend();
$friend->askFriendToAskForMoney();

print "\n";

?>

Well, TheFriend, calling TheDaughter’s protected method should obviously fail. But what about TheSon calling his sister’s protected method? That should fail too, right?

If you’re running PHP 5.1.x, you’d be right:

when sibling asks:
PHP Fatal error:  Call to protected method TheDaughter::askForMoney() from context 'TheSon' on line 24

But if you’re running PHP 5.2.x, you’d be wrong:

when sibling asks:
Please, please, pretty please!

when unrelated friend asks:

Fatal error: Call to protected method TheDaughter::askForMoney() from context 'TheFriend' on line 33

Apparently that change in PHP is recorded here and here. And I’m not sure it’s right. I wonder how other languages (aka Java) handle protected visibility between sibling classes?

comments: 3 so far...

name
blog (optional)
comment

everyone knows that siblings don’t get along this way in the natural world, who defined php5 objects thus-ly?!

I thought this was odd too. Furthermore, this new lookup rule is not applied consistently throughout all types of method invocation and does not apply to property access. I brought this up on the php internals mailing list a few weeks ago – see this discussion (it got side tracked, but a couple of replies do actually relate to my original question! :) http://turl.ca/phpprot

I think the main argument defending the current behaviour is that it respects the Liskov substitution principle. If class D extends class C, then “D is a C”, so according to Liskov, it should be possible to replace all occurrences of C with D without introducing errors. Here’s an illustration using your examples: http://pastebin.ca/1035041

In any case, the inconsistencies should be resolved, and I will be following that up shortly.

As for Java, it does indeed have a more restrictive view of the meaning of protected. In fact, if C declares a protected instance method f() and D extends C, from D you will only be able to call the inherited protected method f() on instances of D, not on instance of C. This works around the Liskov issue: I were to re-write my illustration above in Java, the equivalent of line 22 would throw a compiler error.

I know it’s late but…

The son can call the protected method because he shares it with the sister, as both of them extend from TheMother.

This is the same as calling private methods from the same classes. If TheSon has a private function Foobar, you can do $son2->foobar() from a $son1->foobarOtherSon($son2);

This is the default behavior in most OOP languages.

monthchunks

license

Justinsomnia is licensed under a Creative Commons Attribution 3.0 License.

Please see my Attribution Policy for more information.