3

Declare a private property on a Parent class, and then redeclare that property as public or protected on a Child class.

When you create an instance of the Child class, and invoke a method that is inherited from the Parent class. The property on the Parent class is used, and not the property on the Child class.

This is not the case if the initial declaration of the property on the Parent class is public or protected.

Could someone explain to me why this is?

<?php

class P {
    private $foo = 'bar';

    public function e()
    {
        echo get_class($this);
        echo "\n";
        echo $this->foo;
        echo "\n";
    }
}

class C extends P
{
    protected $foo = 'baz';
}

$parent = new P();
$child = new C();

$parent->e();
$child->e();

Output:
P
bar
C
bar

Expected Output:
P
bar
C
baz
Endl
  • 114
  • 7
  • 6
    private is private, if you want to allow a change then use `protected` in the parent class. RTM: http://php.net/manual/en/language.oop5.visibility.php or use a setter. – Lawrence Cherone Jan 18 '18 at 21:04
  • 6
    This is the expected behavior. `private properties` are visible only within the scope of the class it's defined in. You can think of it like it doesn't exist in any child classes and as such having something with the same name will not override it, instead it just creates a new property in the child's scope that just happens to have the same name. – ArtisticPhoenix Jan 18 '18 at 21:05
  • Very good explanation right here. https://stackoverflow.com/questions/4361553/what-is-the-difference-between-public-private-and-protected – Raphael Cunha Jan 18 '18 at 21:16
  • 2
    I like this question, I don't see why it is being downvoted: it is concise, clearly states the area OP is having trouble with, doesn't seem to be a duplicate (at least at first glance) and includes only the necessary code to reproduce the problem. – William Perron Jan 18 '18 at 21:16

1 Answers1

3

Private properties are, as the name implies, private. This means they only be accessed from within the class. As others have mentioned in the comments, this question offers a very good in depth explanation of variable scope in PHP, so by all means go check it out, it's an interesting read.

In your specific however, you seem to be concerned about renaming/overloading variables in a child class. More specifically, we're looking at what happens when a parent and child class happen to both have a property of the same name, and the parent class has a function which return that property.

class A {
    private $foo = 'bar';

    public function getFoo() {
        return $this->foo;
    }
}

class B extends A {
    protected $foo = 'something something';
}

$b = new B;

echo $b->getFoo();

In the example above, the output would be bar, and that's actually the expected behavior. Since the function getFoo is not overwritten in the child class, PHP executes the function in the parent class. When it gets there, $this refers to A and therefore prints the value of $foo as it is defined in A. It's interesting to note that in this specific example, the scope of B::foo does not matter. In fact, it could omitted entirely and the code would still run perfectly fine.

We can experiment with different function scopes and see what happens:

class A {
    private $foo = 'bar';

    public function getFoo() {
        return $this->foo;
    }

    private function getCapitalizedFoo() {
        return strtoupper($this->foo);
    }

    protected function getDoubleFoo() {
        return $this->foo . $this->foo;
    }
}

class B extends A {
    protected $foo = 'something something';

    public function getParentDoubleFoo() {
        return parent::getDoubleFoo();
    }
}

$b = new B;

echo $b->getFoo().PHP_EOL;      // Output: bar
echo $b->getParentDoubleFoo();  // Output: barbar
echo $b->getDoubleFoo();        // Output: Uncaught Error: Call to protected method ...
echo $b->getCapitalizedFoo();   // Output: Uncaught Error: Call to private method ...

Now, the question remains: How can you access private members from a parent class?

If you have read on the subject, you probably know that, unless completely necessary, your object's properties should be private so as to encapsulate its logic and data as much as possible. Knowing that, the best to allow a child class to access and modify its parent's class properties is by creating accessors and mutators for them.

class A {
    private $foo = 'foo';
    private $bar = 'bar';

    public function setFoo($foo) {
        $this->foo = $foo;
    }

    public function getFoo() {
        return $this->foo;
    }

    protected function setBar($bar) {
        $this->bar = $bar;
    }

    protected function getBar() {
        return $this->bar;
    }
}

class B extends A {
    public function __construct() {
        parent::setFoo('more foo');
        parent::setBar('more bar');
    }

    public function getCapitalizedFoo() {
        return strtoupper(parent::getFoo());
    }

    public function getCapitalizedBar() {
        return strtoupper(parent::getBar());
    }
}

$b = new B;

echo $b->getCapitalizedFoo();    // Output: MORE FOO
echo strtoupper($b->getFoo());   // Output: MORE FOO
echo $b->getCapitalizedBar();    // Output: MORE BAR
echo strtoupper($b->getBar());   // Output: Uncaught Error: Call to protected method ...

By creating public functions to access and modify the parent class' properties you allow the child class to overwrite them and perform logic of its own without having to duplicate the variable. However this also means that any instance of B can potentially alter the values defined in A. In order to mitigate this, you can create protected function that will allow B to internally access and consume the parent class attributes, but the code that consumes B will not be able to do the same.

William Perron
  • 1,288
  • 13
  • 18
  • Good answer, except for *”`$this` refers to `A`“*. That’s not correct. – deceze Oct 03 '21 at 07:57
  • @deceze I just re-tested with PHP8 just to make sure that was still correct and it does indeed work in the way I describe in the original answer. – William Perron Oct 07 '21 at 15:04