目前使用Closure类实现匿名函数.这是一个实现细节,不应该依赖.
(重点是我自己的)
是否可以测试一个变量,这样只有当变量是一个Closure 而且没有引用Closure类时,测试才返回true ?
换句话说,我怎样才能重写以下内容,除非$bar是匿名函数,否则会引发错误:
function foo(Closure $bar) {
$bar();
}
编辑:根据收到的答案,这是一个示例测试.
笔记:
ReflectionFunction::isClosure()方法似乎几乎没用:当你完成所需的检查以确保ReflectionFunction可以实际实例化(除了Closure之外不能使用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;
}
Run Code Online (Sandbox Code Playgroud)
测试用例:
//////////// 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";
Run Code Online (Sandbox Code Playgroud)
-
你也可以使用
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
Run Code Online (Sandbox Code Playgroud)
请注意,以上内容仅返回TRUEPHP 5.3 Lambdas和Closures.如果您只想知道参数是否可以用作回调,那么性能is_callable会更好.
编辑如果你想包括Functors,你可以做(从PHP 5.3.3开始)
$rf = new ReflectionObject($functorOrClosureOrLambda);
var_dump( $rf->hasMethod('__invoke') ); // TRUE
Run Code Online (Sandbox Code Playgroud)
要么
method_exists($functorOrClosureOrLambda, '__invoke');
Run Code Online (Sandbox Code Playgroud)
后者是更快的选择.
一个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);
}
}
Run Code Online (Sandbox Code Playgroud)
这将检查传递的参数是否可调用.该$name参数存储调用的名称.对于封闭,这是目前的Closure::__invoke.由于这对于任何Closures/Lambdas都是相同的,我们可以将传递的参数的名称与任意其他Closure/Lambda进行比较.如果它们相等,则参数必须是Closure/Lambda.在运行时确定可调用名称具有额外的好处,您无需将有关实现细节的假设硬编码到源代码中.传递一个仿函数将返回FALSE,因为它没有相同的可调用名称.由于这不依赖于Reflection API,因此它也可能更快一些.
以上可以写得更优雅
function isClosure($arg) {
$test = function(){};
return $arg instanceof $test;
}
Run Code Online (Sandbox Code Playgroud)