PHP继承的父方法无法访问孩子的私有属性
Posted
技术标签:
【中文标题】PHP继承的父方法无法访问孩子的私有属性【英文标题】:PHP Inherited parent method can't access child's private property 【发布时间】:2011-04-30 15:43:24 【问题描述】:首先:quite similar problem 已发布并已以某种方式解决,但仍未回答我的具体问题。稍后再详细介绍。
一句话:我有一个基类,它为所有孩子提供一些方法,但不包含任何属性。我的孩子正在继承这些方法,这些方法应该用于访问孩子的属性。
如果孩子的属性是protected
或public
,一切正常,但如果孩子的属性是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】:
这是private
和protected
之间的区别。不能继承或访问私有方法和属性。您需要将它们更改为受保护。
见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继承的父方法无法访问孩子的私有属性的主要内容,如果未能解决你的问题,请参考以下文章