PHP Inherited parent method can't access child

2019-02-06 18:09发布

问题:

First of all: A quite similar problem has been posted and somehow solved already, but is still not answering my specific problem. More about this later.

In words: I have a base class which provides some methods to all childs, but doesn't contain any property. My child is inheriting these methods, which should be used to access the child's properties. If the child's property is protected or public, all works fine, but if the child's property is private, it fails without error (just nothing happens).

In code:

class MyBaseClass {
    public function __set($name, $value) {
        if(!property_exists($this, $name))
            throw new Exception("Property '$name' does not exist!");
        $this->$name = $value;
    }
}

class ChildClass extends MyBaseClass {
    public $publicProperty;
    protected $protectedProperty;
    private $privateProperty;
}

$myChild = new ChildClass();
$myChild->publicProperty = 'hello world';    //works of course!
$myChild->protectedProperty = 'hello world'; //works as expected
$myChild->privateProperty = 'hello world';   //doesn't work?

The above mentioned similar problem got the solution to use the magic __set() method to access the private properties, but this I am already doing. If I implement __set() within the child, it works of course, but the idea is, that the child inherits the __set() from it's parent, but obviously it can't access the child's private method.

Is that on purpose? Am I doinf something wrong? or is my approach just crap by design?

Background: My original idea was: The whole dynamic thing about __set() is something I don't like. Usually a private property should never be accessible from outside, so I implemented throwing __set- and __get-methods in my ultimate base class (from which all classes inherit).

Now I want to dynamicially spawn an instance from an XML file and therefore need access to properties. I made the rule, that any XML-instantiatable class needs to implement the magic __set() method and so can be created dynamicially. Instead of implementing it in every Class that might be spawned some day, I decided to make them inherit from a class called like class Spawnable { } which provides the needed __set-method.

回答1:

This is the difference between private and protected. Private methods and properties cannot be inherited or reached. You will need to change them to protected instead.

See the manual on visibility

Members declared protected can be accessed only within the class itself and by inherited and parent classes. Members declared as private may only be accessed by the class that defines the member.



回答2:

I guess you could fashion something using Reflection. For example, in your Spawnable class:

public function __set($name, $value)
{
    $reflector = new ReflectionClass(get_class($this));
    $prop = $reflector->getProperty($name);
    $prop->setAccessible(true);
    $prop->setValue($this, $value);
}

Not the prettiest of code, though.



回答3:

After reviewing my concept, I think it's a bad idea, to go with that approach. This is a general issue with PHP's lack of differing between properties and fields. Of course private fields should never be accessible from outside, but only properties, which are defined by the programmer. The absence of auto-properties (and I don't mean these magical methods __set() and __get()) or some conventional rules for property access, makes it difficult to guess, which naming convention has been used by the programmer when implementing setters for private fields in his class.

The better concept here might be, to rely on the existence of well-named setters for each spawnable class, although it might break, if someone contributed code, which is not implementing the expected conventional named setters.

However, many thanks for your thoughts and hints!