在PHP中确定闭包是否是静态的

Dan*_*ugg 10 php closures static-methods scope

PHP中定义的闭包也可以携带static修饰符.

$f = function () { };

$g = static function () { };
Run Code Online (Sandbox Code Playgroud)

静态闭包不能通过Closure::bind或绑定Closure::bindTo,并会发出警告.

$g = Closure::bind(static function () { }, new stdClass());

// Warning: Cannot bind an instance to a static closure in ...
Run Code Online (Sandbox Code Playgroud)

这也是通过反映静态方法创建的闭包的情况ReflectionMethod::getClosure.

class MyClass
{
    public static function myStaticMethod() { }
}

// reflect MyClass::myStaticMethod,  create an unbound closure, and try to bind it
$f = (new ReflectionMethod(MyClass::class, 'myStaticMethod'))
    ->getClosure()
    ->bindTo(new stdClass());

// Warning: Cannot bind an instance to a static closure in ...
Run Code Online (Sandbox Code Playgroud)

虽然烦人,但这是可以接受的; 但是,如何在静态和非静态闭包之间进行测试?

ReflectionMethod::isStatic看起来它可能会起作用,但明智的不是Closure::__invoke实例级,而不是静态的.

$f = static function () { };

// reflect Closure::__invoke because I think I'm tricky
$r = new ReflectionMethod($f, '__invoke');

// and it's not static anyway
var_dump($r->isStatic()); // bool(false)
Run Code Online (Sandbox Code Playgroud)

此外,检查ReflectionMethod::getClosureThis通常可以工作,因为静态方法必须是未绑定的,但是不包括在实例方法之外定义的闭包,或者未绑定的实例方法的角落情况.

class MyClass
{
    public function myInstanceMethod() { }
}

$o = new MyClass();

// reflect MyClass::myInstanceMethod, create a bound closure, and then unbind it
$f = (new ReflectionMethod($o, 'myInstanceMethod'))
    ->getClosure($o)
    ->bindTo(null);

// then reflect the closure
$r = new ReflectionFunction($f);

// and see it's bound to nothing, as would be the case of a static closure
var_dump($r->getClosureThis()); // NULL
Run Code Online (Sandbox Code Playgroud)

那么,重申一下,如何确定闭包是否是静态的(或更具体地说,是可绑定的)?

看起来好像我们应该有一个ReflectionFunctionAbstract::isBindable,或者ReflectionMethod::isStatic被提升到ReflectionFunctionAbstract.

bwo*_*ebi 6

如果绑定有效,则Closure将绑定$ this.所以,只需绑定它然后检查$ this.如果它为null,那么它就是静态闭包.

function isBindable(\Closure $closure) {
    return (new ReflectionFunction(@\Closure::bind($closure, new stdClass)))->getClosureThis() != null; 
}
Run Code Online (Sandbox Code Playgroud)