6
abstract class AbstractController
{
    protected $repository;
}

class GraphController extends AbstractController
{
    private $repository;
}

I get this error:

Fatal error: Access level to GraphController::$repository must be protected or weaker

Why? What's the theory behind this? On some level it feels wrong that I could have a weaker access level to a class property (i.e. public) when I extend a class, because I am in a way exposing a variable that was meant by parent class to be more restricted...

tereško
  • 58,060
  • 25
  • 98
  • 150
Dennis
  • 7,907
  • 11
  • 65
  • 115

2 Answers2

6

It's a rule of the inheritance. You can make the visibility weaker (more visible) of an inherited member, but you can't hide it more. So you can either make it protected, or public. The rationale being you shouldn't be able to hide members from the base class, or make members less visible than the base class author intended. Add to, yes, take away from, no.

Fred Woolard
  • 151
  • 3
  • alright, that makes sense, although I can take virtually any class, extend it, and make that class' variables public, thereby circumventing any encapsulation that class intended to have. Not that I would do it on purpose but can be done nevertheless – Dennis Jul 26 '17 at 15:28
  • You might think you could derive the base class and make the member vars more visible, but it won't work out the way you think, and end up a bit messy. I'll explain in another response. – Fred Woolard Aug 01 '17 at 21:32
5

This is a response to the comment above having to do with making member vars more visible... not necessarily a response to the original question.

Try running this fragment. You'll see there will still be a private var lurking underneath your public var by the same name, and depending on whether you access it through base-class methods or derived-class methods, you'll get different results since they are two distinct instance values.

class baseclass {
    private $hideme;
    public function getit() { return $this->hideme; }
    public function setit($value) { $this->hideme = $value; }
}

class derived extends baseclass {
    public $hideme;
}

function doobee(baseclass $obj) {
    echo $obj->getit() . "\n";
}

$a = new derived();
$a->hideme = "direct assign";
$a->setit("accessor assign");

?><pre><?php
echo '$a->getit(); // ' . $a->getit() . "\n";
echo '$a->hideme; // ' . $a->hideme . "\n";
echo 'doobee($a); // '; doobee($a);

echo 'print_r($a);' . "\n";
print_r($a);
?></pre>

Output:

$a->getit(); // accessor assign
$a->hideme; // direct assign
doobee($a); // accessor assign
print_r($a);
derived Object
(
    [hideme] => direct assign
    [hideme:baseclass:private] => accessor assign
)

It's not just PHP that behaves this way; C# for instance does likewise.

So I guess the take-away from all this is that it is not a good idea to try to change visibility of base class instance/members vars in derived classes.

Buttle Butkus
  • 9,206
  • 13
  • 79
  • 120
Fred Woolard
  • 151
  • 3