2

While going through the PHP code of one of my projects, I noticed something that - I thought - shouldn't work at all. And yet it doesn't result in any errors or notices in either PHP 5.2.17 or 5.6.14.

class Base {

    private $privateVariable;

    public function __construct() {
    }
}

class Derived extends Base {

  public function __construct() {

    parent::__construct();

    $this->privateVariable = 'Setting a private variable in the base class.';
  }
}

$derived = new Derived();

How can this be? Why is PHP permitting Derived to assign to a private variable defined in Base?

Well, it turns out, it's not: the assignment to $this->privateVariable in Derived is creating a brand new member variable on Derived called privateVariable - which is completely independent from Base's privateVariable.

That wasn't my intention. Rather, it was a bug introduced when I lowered Base->privateVariable's visibility, and didn't catch that derived classes were attempting to reference it.

Why isn't this an error? Does the PHP spec explicitly allow this?

Is there an error_reporting value that will catch this?

Or is there some other automated way I can catch this?

WalterGR
  • 311
  • 3
  • 13
  • 1
    You could trap for it at run-time with a magic setter that used reflection to test for the existence of a variable of the same name in any parent class; but there's no linter that I'm aware of that would detect it.... and (while it could be considered bad practice) it isn't an error – Mark Baker Oct 23 '15 at 22:55
  • That's... unfortunate. I should call out that I'm aware that some PHP editors/IDEs will flag this as an issue, but that's not something that can be executed - say - as a pre-commit hook. – WalterGR Oct 23 '15 at 23:21
  • To answer the updated "question" in your title... it's not an error to assign a variable that is declared private in the base class because the derived class has no knowledge of the variable. – Jon Oct 23 '15 at 23:29
  • So what's the question? How to raise an exception when you are trying to work with a "private" property which is not explicitly declared or? – Axalix Oct 24 '15 at 00:01
  • Btw: http://stackoverflow.com/questions/9136464/how-to-avoid-dynamic-properties-in-php-raise-an-error-when-setting-an-undeclare – Axalix Oct 24 '15 at 00:02
  • @Axalix The questions are at the bottom of the description. And that link to another StackOverflow question has an answer that describes the same solution that is already in an answer below. – WalterGR Oct 24 '15 at 00:52

1 Answers1

3

That isn't an error in PHP. It's called property overloading and is perfectly valid.

If you want to prevent this you can use the __set() magic method to catch when you assign a value to an undeclared property and throw an error when you do so:

public function __set($name, $value) {
    throw new \Exception("'$name' does not exist and cannot be assigned the value '$value'");
}
John Conde
  • 217,595
  • 99
  • 455
  • 496
  • I just tested this. Insanely, `__set` function aren't inherited by derived classes. So that means that the above declaration has to appear in *every* class. Is there a... less invasive... solution? Perhaps a separate linter tool that will catch this? – WalterGR Oct 23 '15 at 23:09
  • 1
    The closest you can get is [traits](http://php.net/manual/en/language.oop5.traits.php) which is like a PHP include for classes. – John Conde Oct 24 '15 at 04:40