PHP manual for anonymous functions (即闭包)指出:
Anonymous functions are currently implemented using the Closure class. This is an implementation detail and should not be relied upon.
(重点是我自己)
是否可以测试一个变量,以便只有当变量是闭包时测试才返回真,不引用闭包类?
换句话说,当 $bar
不是匿名函数时,我如何重写以下内容以引发错误:
function foo(Closure $bar) { $bar(); }
EDIT: Based on the answers received, here is an example test.
Notes:
- It seems there is no way to differentiate between Functors and Closures, and that the test is probably just as 'implementation specific' as using the Closure class.
- The (seemingly obvious)
ReflectionFunction::isClosure()
method seems to be be almost useless: by the time you've done the checks required to make sure that ReflectionFunction can actually be instantiated (can't take a Class except for a Closure), you've eliminated all other options. - In 5.3.0 you ReflectionClass($closure)->hasMethod('__invoke') returned false, so this could be used as a test against Functors, however (I'm told) this has changed since. This highlights the frailty of the solution too.
- Follow up from Gordon - As of PHP 5.4 you can rely on Closure being a Closure: php.net/manual/en/class.closure.php
Code:
/**
* Return true if and only if the passed argument is a Closure.
*/
function testClosure($a) {
// Must be Callback, Labmda, Functor or Closure:
if(!is_callable($a)) return false;
// Elminate Callbacks & Lambdas
if(!is_object($a)) return false;
// Eliminate Functors
//$r = new ReflectionFunction($a); <-- fails if $a is a Functor
//if($r->isClosure()) return true;
return false;
}
测试用例:
//////////// TEST CASE /////////////
class CallBackClass {
function callBackFunc() {
}
}
class Functor {
function __invoke() {
}
}
$functor = new Functor();
$lambda = create_function('', '');
$callback = array('CallBackClass', 'callBackFunc');
$array = array();
$object = new stdClass();
$closure = function() { ; };
echo "Is it a closure? \n";
echo "Closure: " . (testClosure($closure) ? "yes" : "no") . "\n";
echo "Null: " . (testClosure(null) ? "yes" : "no") . "\n";
echo "Array: " . (testClosure($array) ? "yes" : "no") . "\n";
echo "Callback: " . (testClosure($callback) ? "yes" : "no") . "\n";
echo "Labmda: " .(testClosure($lambda) ? "yes" : "no") . "\n";
echo "Invoked Class: " . (testClosure($functor) ? "yes" : "no") . "\n";
echo "StdObj: " . (testClosure($object) ? "yes" : "no") . "\n";
-
最佳答案
你也可以使用
ReflectionFunctionAbstract::isClosure
— 检查是否关闭
例子:
$poorMansLambda = create_function('', 'return TRUE;');
$rf = new ReflectionFunction($poorMansLambda);
var_dump( $rf->isClosure() ); // FALSE
$lambda = function() { return TRUE; };
$rf = new ReflectionFunction($lambda);
var_dump( $rf->isClosure() ); // TRUE
$closure = function() use ($lambda) { return $lambda(); };
$rf = new ReflectionFunction($closure);
var_dump( $rf->isClosure() ); // TRUE
请注意,对于 PHP 5.3 Lambda 和闭包,上面的代码只会返回 TRUE
。如果你只是想知道一个参数是否可以用作回调,is_callable
会表现得更好。
编辑如果您还想包含仿函数,您可以( as of PHP 5.3.3 )
$rf = new ReflectionObject($functorOrClosureOrLambda);
var_dump( $rf->hasMethod('__invoke') ); // TRUE
或
method_exists($functorOrClosureOrLambda, '__invoke');
后者是更快的选择。
一个 Closure
实例基本上只是一个类,它有一个 __invoke
函数,您可以动态地将其提供给方法体。但由于这是对实现细节的测试,我认为它与测试 Closure
类名一样不可靠。
编辑 由于您提到您无法通过反射 API 可靠地进行测试,因为它在将 Functor 传递给 ReflectionFunctionAbstract::isClosure
时引发错误,请尝试以下解决方案满足您的需求:
function isClosure($arg)
{
if(is_callable($arg, FALSE, $name)) {
is_callable(function() {}, TRUE, $implementation);
return ($name === $implementation);
}
}
这将检查传递的参数是否可调用。 $name
参数存储可调用名称。对于闭包,目前是 Closure::__invoke
。由于这对于任何闭包/Lambda 都是相同的,我们可以将传递的参数的名称与任意其他闭包/Lambda 进行比较。如果它们相等,则参数必须是 Closure/Lambda。在运行时确定可调用名称有一个额外的好处,即您不必将关于实现细节的假设硬编码到源代码中。传递仿函数将返回 FALSE
,因为它不会具有相同的可调用名称。由于这不依赖于反射 API,因此它也可能更快一些。
上面的内容可以更优雅地写成
function isClosure($arg) {
$test = function(){};
return $arg instanceof $test;
}
关于php - 在不引用 Closure 内部类的情况下测试 PHP Closure,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/4127959/