引言:一个改变一切的小字母
\n\n想象一下:您正在编写一个很棒的PHP框架,或者只是为自己项目写一个库。一切运行完美,但一个月后,您发现子类的行为与预期不符。您创建了一个对象,但它——啪!——返回了父类的实例。熟悉吗?欢迎加入那些遇到new self vs. new static问题的俱乐部。
\n\n这个问题在Stack Overflow上获得了数千次浏览,这并非没有原因。PHP中self和static之间的区别不仅仅是语法糖,而是一个基础机制,它既可以拯救您的项目,也可以将其变成维护的噩梦。在2025年,当PHP继续演进时,理解后期静态绑定(Late Static Binding)已成为任何有自尊心的开发者的必备技能。
在本文中,我们不仅仅是复述文档。我们将分析真实案例,展示反模式,并给出清晰的最佳实践,您可以立即应用。开始吧!
\n\n主体部分:self vs. static——上下文的较量
\n\n1. self——编译器的保守派
\n\nPHP中的关键字self始终引用它被写入的类。把它想象成一个永远不会改变的老朋友。当您在父类中编写new self()时,这段代码将始终创建该父类的对象——即使它被子类调用。
<?php\nclass Animal {\n public static function create() {\n return new self(); // 始终是 Animal\n }\n}\n\nclass Dog extends Animal {\n // 继承 create()\n}\n\n$dog = Dog::create();\necho get_class($dog); // 输出:Animal\n?>\n\n看到了吗?我们调用了Dog类的方法,却得到了Animal。这对新手来说常常是个意外。问题出在哪里?在于self是在编译阶段解析的。PHP“封存”了对声明该方法的类的引用,任何继承的魔法都无法改变这一点。
2. static——后期绑定的革命者
\n\n而static则完全相反。它在方法调用时工作,并确定是哪个类调用了它。这就是后期静态绑定(Late Static Binding),自PHP 5.3.0引入。
<?php\nclass Animal {\n public static function create() {\n return new static(); // 创建调用该方法的类的实例\n }\n}\n\nclass Dog extends Animal {\n // 继承 create()\n}\n\n$dog = Dog::create();\necho get_class($dog); // 输出:Dog\n?>\n\n这是一个转折点!现在Dog::create()返回一只狗,而不是一个抽象的动物。static查看是谁调用了该方法,并创建该类的对象。这在构建类层次结构时提供了难以置信的灵活性。
3. 何时使用self,何时使用static?
\n\n黄金法则:当您需要硬绑定到当前类时使用self,当您希望子类能够重写行为时使用static。
以下是一些具体场景:
\n\n场景A:工厂方法(使用static)
\n经典示例——工厂方法模式。您希望每个子类都能创建自身的实例。
\n\n<?php\nabstract class Vehicle {\n abstract public function getType(): string;\n\n public static function create(): static {\n return new static();\n }\n}\n\nclass Car extends Vehicle {\n public function getType(): string {\n return '汽车';\n }\n}\n\nclass Bike extends Vehicle {\n public function getType(