PHP继承的父方法无法访问孩子的私有属性

Posted

技术标签:

【中文标题】PHP继承的父方法无法访问孩子的私有属性【英文标题】:PHP Inherited parent method can't access child's private property 【发布时间】:2011-04-30 15:43:24 【问题描述】:

首先:quite similar problem 已发布并已以某种方式解决,但仍未回答我的具体问题。稍后再详细介绍。

一句话:我有一个基类,它为所有孩子提供一些方法,但不包含任何属性。我的孩子正在继承这些方法,这些方法应该用于访问孩子的属性。 如果孩子的属性是protectedpublic,一切正常,但如果孩子的属性是private,它会失败而没有错误(只是什么都没有发生)。

在代码中:

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?

上面提到的类似问题得到了使用魔术__set() 方法访问私有属性的解决方案,但是我已经在这样做了。如果我在子级中实现__set(),它当然可以工作,但想法是,子级从其父级继承__set(),但显然它无法访问子级的私有方法。

这是故意的吗?我做错了什么吗?还是我的方法只是设计使然?

背景: 我最初的想法是:关于__set() 的整个动态是我不喜欢的。通常,私有属性永远不能从外部访问,因此我在我的最终基类(所有类都继承自该基类)中实现了抛出 __set- 和 __get-methods。

现在我想从 XML 文件动态生成一个实例,因此需要访问属性。我制定了规则,任何 XML 可实例化的类都需要实现神奇的 __set() 方法,因此可以动态创建。我决定让它们继承自一个名为 class Spawnable 的类,它提供了所需的 __set-method,而不是在每个可能产生的类中实现它。

【问题讨论】:

这不应该工作...这个$myChild->protectedProperty = 'hello world'; 应该产生一个致命错误...与privateProperty 相同。 php.net/manual/en/language.oop5.visibility.php 【参考方案1】:

这是privateprotected 之间的区别。不能继承或访问私有方法和属性。您需要将它们更改为受保护。

见manual on visibility

声明为受保护的成员可以是 仅在类本身内访问 并通过继承和父类。 声明为私有的成员只能 由定义的类访问 成员。

【讨论】:

你当然是对的!我以某种方式“想象”, __set() 方法在孩子的范围内,因为它从基类继承了它。这意味着孩子想要通过使用父母的方法来访问它自己的私有属性。但显然在 PHP 中,子进程实际上并不是从父进程“导入”方法,而是调用它们,例如 Parent::Method()。因此它脱离了可见性,并且可以 - 当然 - 只能访问公共和受保护的字段。这是非常不令人满意的,虽然我现在知道了。非常感谢您的帮助! 是的,这里的主要问题是:方法(它本身不是私有的)在子类上调用时具有什么上下文。这就是 get 的棘手之处:class Mother public function aMethod() var_dump(METHOD, class_basename($this)); 类子扩展母亲 (new Child())->aMethod();结果:string(15) "Mother::aMethod" string(5) "Child"【参考方案2】:

我猜你可以使用反射来设计一些东西。例如,在您的 Spawnable 类中:

public function __set($name, $value)

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

虽然不是最漂亮的代码。

【讨论】:

哦,不错?等一下!使用 Refelction 我实际上可以暂时访问私有属性,注入值并使它们再次无法访问?这将解决我的很多问题。 ...当然这不是最好的方法,但通常我还是会保密。我只需要它来实现 .NET 将 XML-Configs 与可以在运行时实例化的对象一起使用的方式。非常好的功能,我想要在我的 php 框架中。感谢您的提示,我会检查反射文档。 是的,各种肮脏的事情都可以通过反射来完成。但请记住,它相当慢。【参考方案3】:

在回顾了我的概念后,我认为采用这种方法是个坏主意。这是 PHP 在属性和字段之间缺乏差异的普遍问题。当然,私有字段应该永远可以从外部访问,而只能是由程序员定义的属性。缺少自动属性(我不是指这些神奇的方法__set()__get())或一些常规的属性访问规则,使得很难猜测程序员在实现 setter 时使用了哪种命名约定用于他班级中的私有字段。

这里更好的概念可能是,依赖于每个可生成类的命名良好的设置器的存在,尽管如果有人贡献了没有实现预期的常规命名设置器的代码,它可能会中断。

不过,非常感谢您的想法和提示!

【讨论】:

属性怎么不是字段?

以上是关于PHP继承的父方法无法访问孩子的私有属性的主要内容,如果未能解决你的问题,请参考以下文章

92.私有属性和私有方法

92.私有属性和私有方法

无法从继承的模板访问 django 表单

无法在角度 6 中访问从父母到孩子的方法

Java中子类能够继承父类的private属性或方法吗

探讨 java 的三大特性之一:继承