在PHP的面向对象继承体系中,当子类重写父类的静态属性或静态方法后,使用self::关键字调用相关成员时,默认会指向定义self所在的类,而不是实际调用的子类,这就导致了继承场景下的静态成员调用不符合预期的问题。后期静态绑定通过static::关键字解决了这个痛点,让静态调用可以动态绑定到实际调用的类。

self::关键字在继承中的问题
我们先看一个使用self::的典型问题场景,父类定义了一个静态属性,子类重写了这个属性,但是通过子类调用时却返回了父类的值:
<?php
class ParentClass {
public static $name = 'parent';
public static function getName() {
return self::$name;
}
}
class ChildClass extends ParentClass {
public static $name = 'child';
}
// 调用子类的getName方法,期望返回child,实际返回parent
echo ChildClass::getName(); // 输出:parent
?>
出现这个问题的原因是self::的作用域是在定义方法的类中绑定的,ParentClass的getName方法里self指向的是ParentClass,所以即使ChildClass继承了这个方法,self::$name依然指向父类的静态属性。
后期静态绑定与static::关键字
后期静态绑定(Late Static Bindings)的核心就是static::关键字,它的作用是在运行时动态绑定到实际调用的类,而不是定义方法的类。我们把上面的例子改成使用static:::
<?php
class ParentClass {
public static $name = 'parent';
public static function getName() {
// 使用static::代替self::,动态绑定到调用的类
return static::$name;
}
}
class ChildClass extends ParentClass {
public static $name = 'child';
}
// 此时调用会返回子类的属性值
echo ChildClass::getName(); // 输出:child
?>
这里的static::会在运行时判断实际调用getName的类是ChildClass,所以static::$name会指向ChildClass的$name属性,完美解决了继承中的静态成员调用问题。
static::的使用场景与注意事项
静态方法的调用
除了静态属性,static::也可以用于调用静态方法,同样会绑定到实际调用的类:
<?php
class ParentClass {
public static function parentMethod() {
echo "parent methodn";
}
public static function callMethod() {
// 调用当前类的test方法
static::test();
}
public static function test() {
static::parentMethod();
}
}
class ChildClass extends ParentClass {
public static function test() {
echo "child methodn";
}
}
// 调用子类的callMethod,会执行子类的test方法
ChildClass::callMethod(); // 输出:child method
?>
非静态上下文中的使用
static::不仅可以在静态方法中使用,也可以在实例方法中调用静态成员,同样遵循后期绑定的规则:
<?php
class ParentClass {
public static $type = 'parent';
public function showType() {
echo static::$type;
}
}
class ChildClass extends ParentClass {
public static $type = 'child';
}
$child = new ChildClass();
$child->showType(); // 输出:child
?>
注意事项
- static::不能用于调用非静态属性,因为非静态属性属于实例,而static::是静态绑定相关的关键字。
- 如果调用的静态成员在当前类和父类中都不存在,会抛出致命错误,和self::的行为一致。
- 在匿名类中也可以使用static::,绑定到匿名类本身。
self::与static::的对比
我们可以通过下面的表格清晰对比两者的差异:
| 对比项 | self:: | static:: |
|---|---|---|
| 绑定时机 | 编译时,绑定到定义方法的类 | 运行时,绑定到实际调用的类 |
| 继承场景表现 | 始终调用父类成员 | 调用实际调用类的成员(如果重写的话) |
| 适用场景 | 明确要调用当前类定义的静态成员时 | 需要在继承中动态调用子类重写的静态成员时 |
实际开发中的应用示例
后期静态绑定在工厂模式等场景中非常实用,比如我们定义一个基础的模型类,子类继承后可以通过父类的工厂方法自动返回子类的实例:
<?php
class Model {
public static function create() {
// 后期静态绑定,返回实际调用的类的实例
return new static();
}
}
class UserModel extends Model {
public function show() {
echo "this is user model";
}
}
class OrderModel extends Model {
public function show() {
echo "this is order model";
}
}
$user = UserModel::create();
$user->show(); // 输出:this is user model
$order = OrderModel::create();
$order->show(); // 输出:this is order model
?>
这个例子中,Model的create方法使用new static(),当UserModel调用create时,会实例化UserModel,OrderModel调用时则实例化OrderModel,不需要在每个子类中重复定义工厂方法,大大减少了重复代码。
PHP后期静态绑定static::关键字继承问题修改时间:2026-07-18 13:15:28