在每次调用静态方法期间,PHP运行时都知道两条信息:
- calling context-通常是指called方法时引用的类;这就是
static::
所指的类
- 方法的实际definition所在的类;这就是
self::
所指的类,也是魔力常数__CLASS__
将解析到的类
让我们先看一个更简单的例子:
class A {
public static function test() {
echo 'Defined in ', self::class, '; called in ', static::class, "\n";
}
}
class B extends A {
}
呼叫A::test();
将输出Defined in A; called in A
-self
,而static
指的是同一类.
调用B::test();
将输出Defined in A; called in B
-尽管在类B
中没有定义名为test()
的方法,但PHP仍然知道您在引用B
时调用了它.
当您多次使用self::
时,就会出现"转发"--PHP会跟踪original调用上下文:
class A {
public static function test() {
echo __FUNCTION__, ' defined in ', self::class, '; called in ', static::class, "\n";
}
public static function forward_test() {
echo __FUNCTION__, ' defined in ', self::class, '; called in ', static::class, "\n";
self::test();
}
}
class B extends A {
public static function test() {
echo 'this method is not called from forward_test()';
}
}
B::forward_test();
输出:
forward_test defined in A; called in B
test defined in A; called in B
这里发生的事情是这样的:
- PHP将调用上下文设置为类
B
,并查找方法forward_test()
forward_test
中的definition在类A
中,并输出我们的第一行调试
- Now
forward_test
calls self::test()
, and two things happen
test
中的definition在class A中查找,因为这是forward_test
中的definition
- but将calling context保留为
B
类,因为self
调用"转发"该信息
- 因此调用了
A::test()
,但调用上下文为B
,导致我们的第二行调试
在内部,您可以想象编译器将每个方法调用替换为需要目标类名、方法名和调用上下文的call_method
函数.
对于self::test()
,它可以立即用当前类A
替换self
,并输出类似以下内容:
call_method(targetClass: 'A', methodName: 'test', callingContext: $currentCallingContext)
只有当它运行时,$currentCallingContext
才被定义和转发.
对A::test()
的显式调用显式定义了目标类and和调用上下文:
call_method(targetClass: 'A', methodName: 'test', callingContext: 'A')
相反,对static::test()
的"后期静态绑定"调用基于调用上下文定义目标类:
call_method(targetClass: $currentCallingContext, methodName: 'test', callingContext: $currentCallingContext)
同样的情况也发生在问题中示例中的parent
个调用中:
- 我们呼叫
C::test()
- 定义在
B
中,但调用上下文是C
- 对
parent::foo()
的呼叫解析到A
中的definition,但calling context被转接,C
也是如此
- 因此,我们运行
A::foo()
,调用上下文为C
- 然后我们调用
static::who()
,它查看calling context以决定运行哪个方法("后期静态绑定"),因此它运行C::who()
- 我们现在处于类
C
中定义的方法中,魔术常量__CLASS__
是字符串'C'
下面是一个扩展示例,其中显示了更多信息和更多变体:
class A {
public static function foo() {
echo __FUNCTION__, ' defined in ', self::class, '; called in ', static::class, "\n";
static::who();
}
public static function who() {
echo __FUNCTION__, ' defined in ', self::class, '; called in ', static::class, "\n";
echo __CLASS__."\n";
}
}
class B extends A {
public static function foo() {
echo __FUNCTION__, ' defined in ', self::class, '; called in ', static::class, "\n";
static::who();
}
public static function test() {
echo __FUNCTION__, ' defined in ', self::class, '; called in ', static::class, "\n";
echo "A::foo():\n";
A::foo();
echo "B::foo():\n";
B::foo();
echo "C::foo():\n";
C::foo();
echo "parent::foo():\n";
parent::foo();
echo "self::foo():\n";
self::foo();
}
public static function who() {
echo __FUNCTION__, ' defined in ', self::class, '; called in ', static::class, "\n";
echo __CLASS__."\n";
}
}
class C extends B {
public static function who() {
echo __FUNCTION__, ' defined in ', self::class, '; called in ', static::class, "\n";
echo __CLASS__."\n";
}
}
C::test();
输出:
test defined in B; called in C
A::foo():
foo defined in A; called in A
who defined in A; called in A
A
B::foo():
foo defined in B; called in B
who defined in B; called in B
B
C::foo():
foo defined in B; called in C
who defined in C; called in C
C
parent::foo():
foo defined in A; called in C
who defined in C; called in C
C
self::foo():
foo defined in B; called in C
who defined in C; called in C
C