self指向定义类,static指向调用类;继承中用self访问静态属性或方法会忽略子类重写,导致逻辑错误,必须用static实现后期静态绑定。

self 和 static 在静态方法中调用自身类时行为不同
关键区别在于: self 指向定义该方法的类,而 static 指向实际调用该方法的类(即“后期静态绑定”)。这在继承场景下立刻显现出差异。
常见错误现象:子类重写父类静态属性后,用 self 访问仍拿到父类的值,导致逻辑错乱;比如父类定义 public static $type = 'base',子类覆盖为 'child',但父类中用 self::$type 永远返回 'base'。
- 使用场景:需要严格限定为当前定义类时用
self(如工具类内部固定逻辑);需支持子类定制时必须用static -
self是编译期绑定,解析发生在代码写死时;static是运行期绑定,由实际调用栈决定 - 性能影响几乎可忽略,但语义错误比性能问题更致命
静态属性访问必须匹配绑定方式
如果静态方法中要读写本类声明的静态属性,且该属性可能被子类重定义,就绝不能用 self —— 否则子类调用时会操作父类的副本。
示例:
立即学习“PHP免费学习笔记(深入)”;
class A {
public static $name = 'A';
public static function getName() {
return self::$name; // ❌ 总是 'A'
// return static::$name; // ✅ 子类调用时返回子类的值
}
}
class B extends A {
public static $name = 'B';
}
echo B::getName(); // self 版输出 'A',static 版输出 'B'
- 构造函数、静态初始化块(PHP 8.1+
static属性初始化)中也适用相同规则 - 若属性是
private,子类无法覆盖,则self和static效果一致(但此时用self更清晰) - PHP 5.3+ 支持
static,旧项目升级时需重点扫描self的静态属性访问点
静态方法内调用同名方法时 self/static 决定目标类
当父类静态方法中调用一个同名方法(比如 init()),用 self::init() 会固定调父类的 init,而 static::init() 会调用子类重写的版本 —— 即使子类没重写,也会按继承链向上找最近实现。
- 典型误用:父类
create()中写return new self();,子类调用时却实例化了父类 - 正确做法:写
return new static();,才能实现“返回调用者实际类”的工厂模式 - 注意:
new static()要求类可实例化,且构造函数兼容;若子类构造参数不同,仍会报错
trait 中的静态绑定要特别小心
trait 本身不是类,但其中的 self 和 static 行为依然生效。问题在于:trait 被多个类 use 时,self 指向 trait 所在作用域(即定义处),而 static 指向最终 use 它的那个类。
这意味着:trait 中若含 self::$config,所有 use 它的类共享同一份静态属性;若用 static::$config,每个类各自维护一份。
- 想让每个类独立配置?必须用
static,并在各子类中声明对应静态属性 - 想全局统一配置?可用
self,但要注意命名空间冲突风险(不同 trait 可能撞名) - PHP 7.4+ 支持 trait 中定义
static属性,但初始化时机与类中不同,建议避免在 trait 中直接初始化复杂静态值
self 当成“当前类”的直觉理解,而没意识到它锁死在定义位置。一旦涉及继承或复用,这个假设就会崩。











