Endl
Endl

Reputation: 124

public/protected class properties don't override private properties on parent classes?

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

Upvotes: 4

Views: 2704

Answers (1)

William Perron
William Perron

Reputation: 1398

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.

Upvotes: 4

Related Questions