2015-01-20 3 views
6

Закрытие, определенное в PHP, также может содержать модификатор static.Определение, является ли замыкание статическим в PHP

$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.

ответ

5

Если связывание работает, то закрытие будет иметь $ это связанное с ним. Итак, просто привяжите его, а затем проверьте значение $ this. Если это нулевое значение, то это статическое закрытие.

function isBindable(\Closure $closure) { 
    return (new ReflectionFunction(@\Closure::bind($closure, new stdClass)))->getClosureThis() != null; 
} 
+0

Этот фрагмент выглядит замечательно знакомым ;-) – Dan

+0

Это единственный способ, который я видел до сих пор. –

+0

Аналогичным образом. Мне не нравится, что он грязный из-за '@', но он работает надежно, и нет никаких угловых случаев, которые я могу найти. – Dan

2

Кажется невозможным сейчас.
Вы можете найти некоторые дебаты здесь: 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; 
    } 
+0

Это не относится к случаю, когда он связывается, но не связан ни с чем, ни с классом области видимости. – bwoebi

Смежные вопросы