PHP延迟静态绑定中static::与$this调用私有方法的疑问
测试代码
class ParentClass { private function first(): void { p(__METHOD__); } public function test(): void { static::first(); $this->first(); $B = new B(); $B->first(); } } class B extends ParentClass { public function first(): void { p(__METHOD__); } } $B = new B(); $B->test();
执行输出
B::first ParentClass::first ParentClass::first
疑问
为何当$this指向B类实例,且B类的first()方法为public访问修饰符时,调用的却是ParentClass中的私有first()方法?而移除ParentClass中first()的private修饰符允许继承后,就会调用B类的first()方法?
这看起来是否不符合逻辑?原本预期至少直接调用时会执行B::first(),难道是因为B类的first()并未重写ParentClass的first()方法,而是仅同名,所以在ParentClass作用域中无法被识别?即只有重写而非仅同名的方法,才能在父类作用域中被调用?
解答
核心原因是PHP的私有方法无法被子类重写,子类中同名的first()只是一个全新的独立方法,和父类的私有first()没有任何继承关联。
关于
static::first():static是后期静态绑定,会指向当前运行时的实际类(也就是B类),所以调用的是B类定义的first(),这就是第一个输出B::first的原因。关于
$this->first():
虽然$this指向的是B类实例,但这段代码是在ParentClass的test()方法作用域内执行的。对于私有方法,PHP采用编译时绑定——在解析ParentClass代码阶段,就已经确定$this->first()调用的是ParentClass自身的私有first(),完全不会去子类中查找同名方法。因为私有方法不允许被继承,子类的同名方法对父类来说是“不可见”的。关于
$B->first():
同样是在ParentClass的作用域内调用,由于父类的first()是私有,PHP会直接判定你要调用的是父类自己的私有方法,不会触发子类同名方法的执行。只有当父类方法为public/protected时,才会触发运行时多态,调用子类重写后的方法。
当你将ParentClass的first()改为public/protected后,子类的first()才是真正的重写,此时无论是static::还是$this->调用,都会遵循多态规则执行子类的方法。
内容的提问来源于stack exchange,提问作者Max Denusenko

