Закрытие, определенное в PHP, также может нести static
модификатор.
$f = function () { };
$g = static function () { };
Статическое закрытие не может быть связано через Closure::bind
или же Closure::bindTo
, и выдаст предупреждение.
$g = Closure::bind(static function () { }, new stdClass());
// Warning: Cannot bind an instance to a static closure in ...
Это также относится и к замыканиям, созданным путем отражения статического метода с 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 ...
Хотя это раздражает, это приемлемо; однако, как можно проверить между статическим и нестатическим замыканием?
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)
Далее проверка 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
Итак, чтобы переформулировать, как определить, является ли замыкание статическим (или, более конкретно, связываемый) или нет?
Это действительно кажется, что мы должны иметь ReflectionFunctionAbstract::isBindable
, или это ReflectionMethod::isStatic
быть перемещен в ReflectionFunctionAbstract
,
Если привязка работает, у Closure будет $ this, привязанный к нему. Так что, просто свяжите это и затем проверьте на $ this. Если он нулевой, то это статическое замыкание.
function isBindable(\Closure $closure) {
return (new ReflectionFunction(@\Closure::bind($closure, new stdClass)))->getClosureThis() != null;
}
Это кажется невозможным сейчас.
Вы можете найти некоторые дебаты здесь: https://bugs.php.net/bug.php?id=64761
Единственный реальный обходной путь, который я использую для себя сейчас, это добавление ->isBindable
свойство вручную.
Вот код, который я нашел здесь https://github.com/atoum/atoum/blob/master/classes/test/adapter/invoker.php
Может быть, даст вам несколько идей
protected static function isBindable(\closure $closure)
{
$isBindable = (version_compare(PHP_VERSION, '5.4.0') >= 0);
if ($isBindable === true)
{
$reflectedClosure = new \reflectionFunction($closure);
$isBindable = ($reflectedClosure->getClosureThis() !== null || $reflectedClosure->getClosureScopeClass() === null);
}
return $isBindable;
}