PHP闭包:为什么在绑定到静态类时匿名函数声明中需要“static”?

42

在 php 文档的 Closure::bind 示例中,匿名函数声明中包含了 static。为什么?如果去掉它,我找不到区别。

class A {
    private static $sfoo = 1;
}
$cl1 = static function() { // notice the "static"
    return self::$sfoo;
};
$bcl1 = Closure::bind($cl1, null, 'A');
echo $bcl1(); // output: 1

without:

没有:
class A {
    private static $sfoo = 1;
}
$cl1 = function() {
    return self::$sfoo;
};
$bcl1 = Closure::bind($cl1, null, 'A');
echo $bcl1(); // output: 1
4个回答

55

我不太同意。虽然通常情况下这并不重要,但有时它非常重要。

保留对$this引用的闭包可能会阻止该对象被垃圾收集,进而可能严重影响性能。以下是一个真实案例,它能够产生巨大的影响:

class LargeObject {
    protected $array;

    public function __construct() {
        $this->array = array_fill(0, 2000, 17);
    }

    public function getItemProcessor(): Closure {
        // Try with and without 'static' here
        return static function () {
            // do some processing unrelated to $this
        };
    }
}

$start = microtime(true);

$processors = [];
for ($i = 0; $i < 2000; $i++) {
    $lo = new LargeObject();
    $processors[] = $lo->getItemProcessor();
}

$memory = memory_get_usage() >> 20;
$time = (microtime(true) - $start) * 1000;
printf("This took %dms and %dMB of memory\n", $time, $memory);

这是使用正常闭包的输出结果:

This took 55ms and 134MB of memory

这是一个带有静态闭包的输出:

This took 22ms and 1MB of memory

我在Debian Buster上使用PHP 7.3.19进行测试,结果可能因人而异。显然,这是一个特别构造的示例,以展示差异。但在实际应用程序中也可能发生类似的情况。我开始使用Slevomat的SlevomatCodingStandard.Functions.StaticClosure嗅探器来提醒我始终使用静态闭包。


4
使用静态变量:此操作花费107毫秒和7MB内存/不使用静态变量:此操作花费545毫秒和1338MB内存。在PHP 7.4.16 / Linux Mint (Ubuntu Beaver)上。 - Karel Wintersky
5
请尝试在不同版本的PHP环境中运行此代码:https://3v4l.org/QnDuA,它在PHP 8.1中仍然有效。 - n0099

41

找到了区别:你不能将静态闭包绑定到对象上,只能改变对象范围。

class foo { }

$cl = static function() { };

Closure::bind($cl, new foo); // PHP Warning:  Cannot bind an instance to a static closure
Closure::bind($cl, null, 'foo') // you can change the closure scope

1
不错的发现;我已经注意到$this是未绑定的,但它不能被绑定的事实很有趣。 - Dan Lugg

21

静态闭包(Static Closures)和其他静态方法一样,无法访问$this

像任何其他方法一样,一个不访问$this的非静态闭包通常可以在静态上下文中工作。


3
你能提供一份静态闭包性能提升的来源吗? - Danon
我也想看到性能提升的源代码,以及在代码库中它可能有多大的可衡量性。对于我们大多数人来说,需要解析的额外字符可能无法抵消任何性能增益。 - William Patton
我之前的性能论点只是听说而已,但我后来进行了基准测试,发现影响可以忽略不计。 - Hendrik Rombach

8

正如您所注意到的那样,这并不重要。

这就像在类方法上使用static关键字。如果您在方法中不引用$this,那么您不一定需要它(尽管这会违反严格标准)。

我想PHP可以通过将null作为bind()的第二个参数来确定您指的是Closure以静态方式访问A


error_reporting 被设置为 E_ALL,但我没有收到任何 E_STRICT 的通知。 - learning php
仍然没有错误。我尝试了 error_reporting(-1)error_reporting(E_ALL | E_STRICT)。我正在使用 PHP 5.5.5。 - learning php

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接