Menu

PHP static:静态方法、静态属性与 self 和 static 的区别

static 方法或属性属于类,而不属于某个对象:用 ClassName::method() 调用,用 ClassName::$count 读取,不需要 new。学习 self:: 和 static:: 的区别(后期静态绑定)、new static、函数内的静态变量,以及什么时候不该用 static。

本页包含可运行的编辑器。编辑、运行,立即查看输出。

static 方法或属性属于类本身,而不属于任何对象。用 ClassName::method() 调用静态方法,用 ClassName::$property 读取静态属性,不需要先创建对象。在类的内部,self:: 指向同样的成员。

整段代码里没有一处 new Temperature()。静态成员适合不需要对象状态的辅助方法(转换、工厂、校验),也适合属于整个类的数据,比如计数器。

静态属性由所有对象共享

普通属性每个对象各有一个值。静态属性整个类只有一个值,所以每个对象看到的都是同一个。下面每新建一个 User,就让共享计数器加一,并拿到下一个 ID:

注意 self::$nextId 里的 $:静态属性在 :: 后面保留 $,而常量(self::MAX)没有。静态属性只在一次 PHP 请求内有效。在常见的网站环境(PHP-FPM 或 Apache)中,每次加载页面它都会重新开始,所以不能用它在不同访问者之间保存数据。

静态方法没有 $this

静态方法在没有对象的情况下运行,所以里面不存在 $this。静态方法可以用 self:: 调用其他静态方法,但不能读取普通属性。以静态方式调用普通方法同样会失败:

反过来没问题:普通方法可以调用静态方法,就像 withSeparator() 调用 self::make() 那样。

self 和 static:后期静态绑定

self:: 指向代码所在的类。static:: 指向运行时方法被调用的那个类。只有在继承中才能看出两者的区别,而对工厂方法来说这一点最重要:

Post::createWithSelf() 返回的是 Model,这几乎从来不是调用者想要的。static 解析为 Post,因为调用时写的就是这个类,PHP 把这叫作后期静态绑定。在打算被继承的方法里用 static:: 和 new static(),真正指这个类本身时才用 self::。常量也是同样的规则:static::TABLE 会取到子类自己的 TABLE,而 self::TABLE 始终读取代码所在类里的那个。

继承来的静态属性是共享的

子类会共享父类的静态属性,除非它自己声明一个。这是 static 最典型的意外:两个子类往同一个数字上累加。

Apples 没有自己的 $count,所以 Apples::$count 就是 Counter::$count,两者都打印 2。Pears 重新声明了这个属性,自己单独计数。在 trait 里声明的静态属性表现不同:每个使用该 trait 的类都会得到一份独立的副本。

函数内的静态变量

在函数体里,static 还有第二种含义:静态变量在多次调用之间保留它的值。初始值只在函数第一次运行时赋予:

第二次 slowSquare(12) 跳过了计算。和静态属性一样,这些值在每次请求结束时重置。变量作用域的整体介绍见变量页面。

什么时候不该用 static

静态方法很方便,所以也很容易被滥用。一个所有方法都在读写静态属性的类,其实就是挂着类名的全局状态:每个调用者都共享它,测试拿不到一份干净的副本,你也无法换成另一种实现。

static 的合适用法:没有状态的纯辅助方法(Slug::make())、命名构造函数(Color::fromHex())和整个类共用的常量。如果一个方法需要的数据在两次使用之间可能不同,那它需要的是一个对象。

常见问题

PHP 中怎么调用静态方法?

用类名加 :::MathHelper::square(4)。在同一个类内部,用 self::square(4) 或 static::square(4)。不需要对象,而且静态方法里没有 $this。

PHP 中 self 和 static 有什么区别?

self:: 始终指代码所在的那个类。static:: 指方法实际被调用时所用的类,可能是一个子类,这叫作后期静态绑定。所以在父类的 create() 方法里,new self() 永远创建父类,而 new static() 创建调用它的那个子类。

PHP 中的静态属性是什么?

用 public static int $count = 0; 声明的属性属于类,所以这个类的所有对象(以及它的子类,除非子类重新声明)共享同一个值。用 ClassName::$count 或 self::$count 读写它,注意那个 $。

PHP 中可以在对象上调用静态方法吗?

可以。$obj->square(4) 和 $obj::square(4) 都会调用该对象所属类的静态方法,但方法里依然没有 $this。反过来才会失败:用 ClassName::method() 调用非静态方法,从 PHP 8 开始会抛出 Error。

静态方法里能用 $this 吗?

不能。静态方法在没有对象的情况下运行,所以那里没有定义 $this,使用它会抛出 Error("Using $this when not in object context")。要访问其他静态成员,用 self:: 或 static::。

Coddy 编程语言插图

和 Coddy 一起学编程

开始