PHP 里的 self 和 static,到底差在哪?

季宁
季宁
发布时间:2026-08-25 | 162人浏览过
原创

前言

在 PHP 面向对象代码里,`self` 和 `static` 经常一起出现,尤其在继承、工厂方法、单例和 Active Record 这类写法中最容易让人踩坑。它们表面上都能写在类方法里,但真正决定结果的不是“写法像不像”,而是绑定发生在编译期还是运行期;顺着这个线索看,哪些场景该用 `self`、哪些场景必须交给 `static`,就会清楚很多。

在 PHP 面向对象代码里,self 和 static 经常一起出现,尤其在继承、工厂方法、单例和 Active Record 这类写法中最容易让人踩坑。它们表面上都能写在类方法里,但真正决定结果的不是“写法像不像”,而是绑定发生在编译期还是运行期;顺着这个线索看,哪些场景该用 self、哪些场景必须交给 static,就会清楚很多。

为什么同样的调用方式,返回的类却不一样?

先看一个最典型的例子:

class A {
    public static function getSelf() {
        return new self();
    }

    public static function getStatic() {
        return new static();
    }
}

class B extends A {}

echo get_class(B::getSelf());   // 输出:A
echo get_class(B::getStatic()); // 输出:B

这段代码最容易让人误判的地方在于:两个方法都是通过 B 调用,为什么一个拿到 A,另一个却拿到 B?

关键不在调用方式,而在关键字绑定到谁。self 会直接指向“定义当前方法的类”,也就是这里的 A;static 则会保留“实际是谁发起了这次调用”,因此 B::getStatic() 最终返回的是 B 的实例。

如果只记一句话,可以这样理解:self 看定义位置,static 看调用者。

本质区别:早期绑定与后期静态绑定

self:在定义时就确定目标

self 采用的是早期绑定,可以把它理解为编译阶段就已经把目标类写死了。无论这个方法后来被父类自己调用,还是被子类继承后调用,self 都不会改指向。

用纸面卡片对比 PHP 中 self 与 static 的绑定方向与返回结果
self 与 static 的绑定方向用两组调用路径说明 self 固定指向定义类、static 跟随实际调用类。

因此,只要方法定义在父类中,self 指向的就始终是父类本身。这种行为的好处是稳定、直接、可预测,特别适合那些“不希望子类改变返回类型或访问目标”的逻辑。

static:在运行时根据调用者决定

static 使用的是后期静态绑定,也就是常说的 Late Static Binding。这是 PHP 5.3 引入的重要特性,它允许父类中的静态上下文在运行时感知“当前究竟是哪个类在调用”。

用编辑便签展示 PHP 开发中该选 self 还是 static 的常见场景
开发里怎么做选型把固定行为和可扩展行为拆开看,选型会比单记概念更直接。

也正因为如此,static 更适合写在希望被继承、被扩展的基类方法中。父类只提供通用逻辑,真正返回哪个类的实例、读取哪个类的静态属性,则交给实际调用方决定。

从设计角度看,self 更偏向固定行为,static 更偏向多态扩展。

不只静态方法:实例方法和静态调用里都能看出差异

实例方法中,规则并不会改变

很多人第一次接触这两个关键字时,会以为它们只在静态方法里才有意义。实际上,放到实例方法中,绑定规则仍然完全一致:

class ParentClass {
    public function createWithSelf() {
        return new self();
    }

    public function createWithStatic() {
        return new static();
    }
}

class ChildClass extends ParentClass {}

$child = new ChildClass();
echo get_class($child->createWithSelf());   // 输出:ParentClass
echo get_class($child->createWithStatic()); // 输出:ChildClass

这里虽然是通过 $child 这个子类对象去调用实例方法,但 createWithSelf() 仍然会返回 ParentClass,因为它只认方法定义处;而 createWithStatic() 会根据实际调用类返回 ChildClass。

这说明问题的核心从来不是“静态方法还是实例方法”,而是绑定策略本身。

访问静态属性和静态方法时,区别同样明显

当你不是用它们来 new 对象,而是访问静态属性时,这种差异也会直接体现出来:

class Base {
    public static $value = 'Base';

    public static function showSelf() {
        echo self::$value;
    }

    public static function showStatic() {
        echo static::$value;
    }
}

class Derived extends Base {
    public static $value = 'Derived';
}

Derived::showSelf();   // 输出:Base
Derived::showStatic(); // 输出:Derived

当子类覆盖了父类的静态属性后,self::$value 依然读取的是父类里的值;static::$value 则会落到子类当前版本上。

这也是很多框架基类喜欢用 static 的原因之一:同一段父类逻辑,可以自然适配不同子类的静态配置。

实际开发怎么选:哪些场景该用 self,哪些该用 static?

需要固定行为时,用 self

如果你的目标是让行为保持稳定,不因为继承而变化,那么 self 往往更合适。一个常见例子就是单例模式:

class Database {
    private static $instance;

    private function __construct() {}

    public static function getInstance() {
        if (self::$instance === null) {
            self::$instance = new self();
        }
        return self::$instance;
    }
}

这里使用 self 的意图很明确:getInstance() 要管理的是这个类自己对应的实例,而不是把实例化结果交给可能存在的子类。也就是说,设计者想要的是“确定性”,不是“可扩展性”。

凡是类似这种场景,例如内部工具类、明确不打算给子类改写结果的工厂入口、需要严格锁定返回类型的公共方法,都更适合选择 self。

需要继承扩展时,用 static

如果父类方法本来就是为子类复用而写,希望调用结果能够跟着子类变化,那么就应该优先考虑 static。例如工厂模式和 Active Record 风格的模型基类:

abstract class Model {
    public static function find($id) {
        $instance = new static();
        // 从数据库加载数据
        return $instance;
    }
}

class User extends Model {}

$user = User::find(1); // 返回User实例,而不是Model实例

这里的核心诉求不是“在父类里创建父类对象”,而是“让父类提供通用查询流程,返回结果由具体模型类决定”。如果把 new static() 改成 new self(),这个基类就会失去面向子类扩展的意义。

所以,当你在写一个可复用基类、抽象模型、通用工厂,或者任何依赖多态的结构时,static 往往才是正确选择。

版本要求、性能差异与常见误区

后期静态绑定依赖 PHP 5.3 及以上

static 这种后期静态绑定能力并不是所有老版本 PHP 都支持。它要求环境至少是 PHP 5.3。如果项目仍运行在 PHP 5.2 或更早版本,这套行为就不能成立。

放到现在看,大多数项目早已越过这个门槛,但如果你在维护老系统,还是要先确认运行环境。

性能不是选型重点

从原理上说,self 因为在编译时就确定目标,开销会比运行时判断调用者的 static 稍低一些。但在真实业务代码里,这个差距通常小到不足以成为设计依据。

相比那点理论性能收益,更重要的是语义是否准确:你到底是想锁定当前类,还是想把行为开放给子类。这里选错,后续维护成本往往远比性能损失大得多。

一个容易忽略的限制

还有一个常见错误是,把 static 用在类属性默认值中:

class Foo {
    public $name = static::class; // 错误!
}

这种写法是不成立的。这里应该改用 self:

class Foo {
    public $name = self::class; // 正确
}

如果你遇到类似场景,可以先判断这段值是否需要依赖运行时调用者。类属性默认值显然不属于这种情况,因此应当使用确定性更强的 self。

一份够用的判断规则

把前面的例子压缩成一句实用判断标准,可以得到下面这组规则:

特性 self static
绑定方式 早期绑定(编译时) 后期静态绑定(运行时)
指向目标 定义该方法的类 实际调用的类
多态支持 不支持 支持
适用场景 单例模式、需要稳定行为时 工厂模式、需要多态性时

实际写代码时,可以直接按下面的思路判断:

  • 希望行为固定、结果稳定,就选 self
  • 希望父类逻辑能对不同子类自动生效,就选 static
  • 设计可扩展基类时,优先检查是否应该使用 static
  • 实现单例或明确不打算开放多态时,self 更稳妥

self 和 static 的区别,归根到底就是“是否把最终决定权交给调用者”。把这一点想明白,继承体系里的很多返回值问题、静态属性访问问题,基本都能更快定位。

相关标签:

本站声明

本文内容仅供参考,不代表本站观点。转载或引用请注明出处。如发现内容问题,请通过联系我们页面反馈。

Python 相关产品和应用

全部下载

专题列表