浅谈PHP中的static关键字

static 的几种使用情况:

1. 静态变量

静态变量仅在局部函数域中存在,但当程序执行离开此作用域时,其值并不丢失。看看下面的例子:

1
2
3
4
5
6
7
8
9
<?php
function testStatic() {
static $val = 1;
echo $val;
$val++;
}
testStatic(); //output 1
testStatic(); //output 2
testStatic(); //output 3

如果不声明static,则每次$val的值都会初始化为1。这是由于static关键字会在请求初始化时存储与静态存储区,并存在于整个请求生命周期内,而普通变量则存在于栈区,函数执行结束时这些存储单元自动被释放。

2. 静态属性

1
2
3
4
5
6
class A {
public static $age;
public function roster () {
//...
}
}

3. 静态方法

1
2
3
4
5
6
class A {
public static $age;
public static function roster () {
//...
}
}

4. 后期静态绑定

首先,后期静态绑定这个词怎么理解,官方手册对这个解释的很清楚,这里引用一下:『“后期绑定”的意思是说,static:: 不再被解析为定义当前方法所在的类,而是在实际运行时计算的。也可以称之为“静态绑定”,因为它可以用于(但不限于)静态方法的调用』。也就是说,static 的值只有在调用时才能确定下来,而 self 则是在定义时就确定下来的。

接下来,让我们用几个例子来具体说明一下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<?php
class A {
public static function who() {
echo __CLASS__;
}
public static function test() {
static::who(); // 后期静态绑定从这里开始
}
}

class B extends A {
public static function who() {
echo __CLASS__;
}
}

B::test();

这段代码的最终输出是什么?A还是B

让我们来运行一下,发现结果是B。这是为什么呢?

因为当我们调用B中的静态方法test()时,B 中不存在,于是向上查找(调用查找顺序依次为 当前类 ==> trait ==> 父类),当在父类A中找到时,执行static::who()。(这里的static与我们常用的self关键字不同,如果是self,则只会向上查找,而static则会向下查找,具体调用方法也是在运行时确定的)因此接下来调用的是B中的 who(),而非A中的who(),最终也就自然输出为B

需要注意的是,后期静态绑定的解析会一直到取得一个完全解析了的静态调用为止(即最下层可执行的方法)。另一方面,如果静态调用使用 parent:: 或者 self:: 将转发调用信息。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
<?php
class A {
public static function foo() {
static::who();
}

public static function who() {
echo __CLASS__."\n";
}
}

class B extends A {
public static function test() {
A::foo();
parent::foo();
self::foo();
}

public static function who() {
echo __CLASS__."\n";
}
}
class C extends B {
public static function who() {
echo __CLASS__."\n";
}
}

C::test();// A C C

What & When

现在,让我们来看一下什么是static,以及什么时候该使用static

什么是static?

如果我们在代码中声明了static关键字,代码在初始化阶段会与常量、全局变量一同存储在一段连续空间内(静态存储区),这会存在于整个请求周期,除非我们手动销毁。

由于这些值是在初始化时确定的,相比于在运行时确定的值,就是“静态”(static)的。

什么时候用?

由于是一段连续的空间,相比于运行时的碎片化产生的内容读取效率更高,所以,我们应将一些常用基础工具类设为静态,既避免了实例化时的资源消耗,有提升了书写体验。但要注意不能滥用static,曾经的一家知名公司内部项目model层全部是static方法,你能想象到这维护起来有多么让人崩溃。


参考资料

  1. 使用静态变量 - PHP手册
  2. 后期静态绑定 - PHP手册
因为热爱,所以执着。