PHP manual for anonymous functions(即闭包)声明:
目前使用Closure类实现匿名函数。这是一个实施细节,不应该依赖。
(强调是我自己的)
是否可以测试一个变量,这样只有当变量是一个Closure时,测试才返回true,没有引用Closure类?
换句话说,我怎样才能重写以下内容,以便在$bar
不是匿名函数时会引发错误:
function foo(Closure $bar) { $bar(); }
编辑:根据收到的答案,这是一个示例测试。
注意:
ReflectionFunction::isClosure()
方法似乎几乎没用:当你完成所需的检查以确保ReflectionFunction可以实际实例化时(除了a之外不能使用Class)关闭),你已经取消了所有其他选择。代码:
/**
* 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";
-
答案 0 :(得分:8)
您也可以使用
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 Lambdas和Closures的TRUE
。如果您只是想知道参数是否可以用作回调,is_callable
将会表现得更好。
编辑如果您想要包含Functors,您可以(as of PHP 5.3.3)
$rf = new ReflectionObject($functorOrClosureOrLambda);
var_dump( $rf->hasMethod('__invoke') ); // TRUE
或
method_exists($functorOrClosureOrLambda, '__invoke');
后者是更快的选择。
Closure
实例基本上只是一个具有__invoke
函数的类,您可以动态地为方法体提供信息。但由于这是测试实现细节,我认为它与测试Closure
类名一样不可靠。
编辑由于您提到您无法通过Reflection API进行可靠测试,因为在将Functor传递给ReflectionFunctionAbstract::isClosure
时会引发错误,请尝试以下解决方案是否符合您的需求:
function isClosure($arg)
{
if(is_callable($arg, FALSE, $name)) {
is_callable(function() {}, TRUE, $implementation);
return ($name === $implementation);
}
}
这将检查传递的参数是否可调用。 $name
参数存储可调用名称。对于闭包,目前为Closure::__invoke
。由于这对于任何Closures / Lambdas都是相同的,我们可以将传递的参数的名称与任意其他Closure / Lambda进行比较。如果它们相等,则参数必须是Closure / Lambda。在运行时确定可调用名称具有额外的好处,您无需将有关实现细节的假设硬编码到源代码中。传递一个仿函数将返回FALSE
,因为它没有相同的可调用名称。由于这不依赖于Reflection API,因此它可能会更快一些。
以上可以更优雅地写成
function isClosure($arg) {
$test = function(){};
return $arg instanceof $test;
}
答案 1 :(得分:1)
is_callable
和!is_array
可能对您有所帮助。请注意,您不能依赖于PHP的类型提示/检查这种方式,因为您必须检查函数内部的变量并抛出一些内容,例如:一个InvalidArgumentException
你自己。