为什么 PHP 闭包要加 static? PHP 的内存管理机制要理解这一点需要先了解 PHP 如何管理内存。与 Java 等依赖垃圾回收器延迟释放内存的语言不同PHP 使用引用计数当然PHP 实际上也有针对循环引用的垃圾回收器但那是另一回事。当变量被赋值时其内容需要存储在内存中当变量不再使用时内存可以被释放。写出如下代码$a Hello; $b $a;PHP 不会为$b创建第二块内存空间而是直接标记它指向与$a相同的内存空间。如果随后给$a赋新值如Hi则会分配新内存空间并让$a指向它而$b继续指向原来的空间。如果将NULL赋给$b那么原来存储Hello的内存空间就不再被任何变量引用可以被释放。PHP 通过维护引用计数来实现这一点当计数归零时空间即被释放。对象的生命周期对于对象当引用计数归零后在释放内存之前如果类定义了__destruct方法会先调用它class Foo { public function __construct() { echo Construct\n; } public function __destruct() { echo Destruct\n; } } new Foo(); echo End\n;输出Construct Destruct End对象未被赋给任何变量它的计数器在构造函数调用后立即归零__destruct随即被调用。如果将对象赋给变量销毁则会延迟$foo new Foo(); echo End\n;输出Construct End Destruct只要$foo指向对象计数器就保持为 1。销毁发生在脚本末尾所有变量被释放之后。要强制提前销毁只需显式释放变量$foo new Foo(); echo Before release\n; $foo null; echo After release\n;输出Construct Before release Destruct After release闭包会让对象保持存活来看Bar类的例子它定义了getCallback()方法返回一个读取$this-id属性的闭包class Bar { public function __construct(private string $id) { echo Construct\n; } public function __destruct() { echo Destruct\n; } public function getCallback(): Closure { return function(): string { return $this-id; }; } } $bar new Bar(foo); $getId $bar-getCallback(); echo Before releasing the object\n; $bar null; echo After releasing the object\n; echo $getId() . \n; echo End\n;输出Construct Before releasing the object After releasing the object foo End Destruct给$bar赋null时对象并未被销毁因为闭包访问了$this-id这构成了对对象的引用。只要闭包存在计数器就不会归零直到脚本结束。如果提前给$getId重新赋值__destruct会更早被调用因为释放变量同时也释放了对$this的引用。即使不使用 $this对象仍会存活如果闭包内部完全不使用$this会怎样class Bar { public function __construct() { echo Construct\n; } public function __destruct() { echo Destruct\n; } public function getCallback(): Closure { return function(): void {}; } } $bar new Bar(); $callback $bar-getCallback(); echo Before releasing the object\n; $bar null; echo After releasing the object\n; $callback null; echo End\n;输出Construct Before releasing the object After releasing the object Destruct End对象仍然保持存活。原因在于即使闭包内部不使用$thisPHP 会自动将$this绑定到在实例方法中创建的任何闭包无论是否使用它、无论闭包是否为空。闭包因此总是携带对对象的引用这一点在阅读代码时是看不见的。当然如果闭包是在静态方法中创建的就不会有$this引用销毁会在变量释放时立即发生class Bar { public function __construct() { echo Construct\n; } public function __destruct() { echo Destruct\n; } public static function getCallback(): Closure { return function(): void {}; } } $bar new Bar(); $closure $bar::getCallback(); echo Before releasing the object\n; $bar null; echo End\n;输出Construct Before releasing the object Destruct End静态闭包static关键字应用于闭包时会显式禁止闭包绑定到$this。PHP 将不再存储任何对对象的引用即使是隐式的。public function getCallback(): Closure { return static function(): void {}; }输出Construct Before releasing the object Destruct End如果需要在闭包内获取属性值可以通过use传递public function getCallback(): Closure { $id $this-id; return static function() use ($id): string { return $id; }; }这次PHP 会在变量释放后立即销毁对象因为闭包不再保留对它的引用。如果在静态闭包内尝试使用$thisPHP 会报错return static function(): string { return $this-id; // Error: Using $this when not in object context };PHP 引擎以此保护你免受意外捕获。短闭包短闭包fn() 提供了更简洁的语法并自动从外层作用域捕获变量无需use。但它在$this方面的行为与普通闭包相同public function getCallback(): Closure { return fn(): string $this-id; }这里$this被隐式捕获与普通闭包一样。对象会一直保持存活直到闭包被销毁。static关键字同样适用于短闭包。外层作用域的变量仍会被自动捕获但$this不再被捕获public function getCallback(): Closure { return static fn(): string $this-id; // Error: Using $this when not in object context }要在不传对象的情况下传递值只需提前提取public function getCallback(): Closure { $id $this-id; return static fn(): string $id; }变量$id按值捕获$this不再参与对象可以在其显式引用消失后立即被释放。PHP 8.6 将带来的变化目前正在投票中的 Closure Optimizations RFC 正是针对这一行为。它引入了自动推断如果闭包不使用$thisPHP 会自动将其视为静态闭包无需开发者显式声明。本文示例中使用use ($id)的闭包或短闭包fn(): string $id在该 RFC 通过后将不再隐式捕获对象。该 RFC 还包含第二项优化不捕获任何变量既无use也无外层作用域变量的静态闭包会被缓存并在多次调用间复用避免每次重新实例化。