本文目录导读:

在 PHP 项目中,静态变量(static 关键字)的生命周期取决于它被定义在函数/方法内部还是类属性中,两者行为不同。
函数/方法内部的静态变量
生命周期:从该函数第一次被调用开始,到整个 PHP 脚本执行结束(请求结束)为止。
- 关键行为:它在函数执行完毕后不会销毁,会保留上一次的值,直到下一次调用该函数。
- 作用域:仅在定义它的函数内部可见。
- 类比:可以看作一个“只能被该函数访问的全局变量”。
示例代码:
<?php
function counter() {
static $count = 0; // 第一次调用时初始化,之后不再执行
$count++;
echo $count . "\n";
}
counter(); // 输出: 1
counter(); // 输出: 2
counter(); // 输出: 3
// 当整个PHP文件执行完毕,$count 消失
?>
常见场景: 缓存函数内部耗时的计算结果(如正则解析、配置读取),避免重复计算。
类中的静态属性
生命周期:从类第一次被加载(或第一次被访问)开始,到整个 PHP 脚本执行结束(请求结束)为止。
- 关键行为:它属于类本身,而不是某个对象实例,所有该类的实例(对象)共享这一个变量。
- 作用域:在类定义范围内通过
self::或static::访问,在类外部通过ClassName::$property访问。 - 类比:相当于一个“属于类级别的全局变量”。
示例代码:
<?php
class MyClass {
public static $count = 0; // 类属性,全局唯一
public function increment() {
self::$count++;
}
}
$obj1 = new MyClass();
$obj1->increment();
echo MyClass::$count; // 输出: 1
$obj2 = new MyClass();
$obj2->increment();
echo MyClass::$count; // 输出: 2 (所有实例共享)
// 当PHP脚本执行完毕,MyClass::$count 消失
?>
常见场景: 数据库连接的单例模式、全局配置项、计数器、共享资源池。
| 特性 | 函数/方法内部 static 变量 |
类静态属性 static |
|---|---|---|
| 生命周期 | 单次请求(脚本执行期间) | 单次请求(脚本执行期间) |
| 属主 | 函数/方法本身 | 类本身 |
| 初始化时机 | 函数第一次被调用时 | 类第一次被加载时 |
| 作用域 | 函数内部 | 整个脚本(通过类访问) |
| 跨请求持久化? | 不会 (与PHP的请求-响应模型一致) | 不会 (与PHP的请求-响应模型一致) |
非常重要:PHP 的“无状态”特性
PHP 是短生命周期、无状态的语言,每一个 HTTP 请求都会创建一个全新的 PHP 进程/环境。
- 静态变量无法跨请求保持数据。
- 请求 A 中静态变量的值为
100,请求 B 中的同名静态变量会重新初始化为0,并独立递增。 - 如果需要跨请求(跨页面刷新) 保存数据,必须使用文件、数据库、Memcached/Redis、Session/Cookie 等外部存储。
总结一句话
在 PHP 项目中,静态变量的生命周期仅限于当前 HTTP 请求(脚本执行周期)。 请求结束,一切归零,它不是 Java 或 C# 中静态变量那种“应用级别”的持久化。
附加:现代 PHP 项目中的注意事项
在现代 PHP 框架(如 Laravel、Symfony)或常驻内存的 Swoole 环境下,情况会有所不同:
- 传统 PHP-FPM 模式:上面所述完全正确,静态变量每次请求重置。
- 常驻内存模式 (Swoole, ReactPHP, Workerman):
- 生命周期会变长:静态变量会一直保留在内存中,跨越多个请求!
- 巨大风险:这很容易导致内存泄漏、数据污染(非预期的全局状态)和竞态条件。
- 代码检查:在这类环境下开发,必须十分小心静态变量的使用,避免意外地缓存请求数据,这是从传统 PHP 转向常驻内存环境最常见的坑之一。
如果你是在传统 PHP-FPM 下开发,放心使用静态变量作为请求内的缓存(如避免重复加载配置),如果你在 Swoole 下开发,建议谨慎使用或完全避免使用静态变量来存储请求级别的数据。