在不引用闭包内部类的情况下测试PHP闭包

在不引用闭包内部类的情况下测试PHP闭包,php,closures,Php,Closures,(即关闭)声明: 匿名函数当前使用闭包类实现。这是一个实施细节,不应依赖于 (重点是我自己的) 是否可以测试一个变量,这样只有当变量是闭包时,测试才会返回true,,而不引用闭包类 换句话说,当$bar不是匿名函数时,如何重写以下内容以使其产生错误: function foo(Closure $bar) { $bar(); } 测试用例: //////////// TEST CASE ///////////// class CallBackClass { function ca

(即关闭)声明:

匿名函数当前使用闭包类实现。这是一个实施细节,不应依赖于

(重点是我自己的)

是否可以测试一个变量,这样只有当变量是闭包时,测试才会返回true,,而不引用闭包类

换句话说,当
$bar
不是匿名函数时,如何重写以下内容以使其产生错误:

function foo(Closure $bar) { $bar(); } 测试用例:

//////////// 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";
-和
!is_array
可能会帮助您。请注意,您不能依赖PHP的类型提示/检查,因为您必须检查函数中的变量并抛出一些东西,例如您自己抛出的
InvalidArgumentException

您也可以使用

-检查是否关闭

例如:

$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
请注意,对于PHP5.3 Lambdas和闭包,上面只返回
TRUE
。如果您只想知道某个参数是否可以用作回调,
是可调用的
将执行得更好


编辑如果还想包含函子,可以执行()

后者是更快的选择

闭包
实例基本上只是一个类,它有一个
\u invoke
函数,您可以动态地将该函数提供给方法体。但由于这是对实现细节的测试,我认为这和测试
闭包
类名一样不可靠


编辑既然您提到无法通过反射API进行可靠的测试,因为它在将函子传递给
反射函数stretc::isClosure
时会引发错误,请尝试以下解决方案以满足您的需要:

function isClosure($arg)
{
    if(is_callable($arg, FALSE, $name)) {
        is_callable(function() {}, TRUE, $implementation);
        return ($name === $implementation);
    }
}
这将检查传递的参数是否可调用。
$name
参数存储可调用的名称。对于闭包,当前是
闭包::u invoke
。因为这对于任何闭包/Lambda都是相同的,所以我们可以将传递的参数的名称与任意其他闭包/Lambda进行比较。如果它们相等,则参数必须是闭包/Lambda。在运行时确定可调用名称还有一个额外的好处,即您不必将关于实现细节的假设硬编码到源代码中。传递一个functor将返回
FALSE
,因为它不会有相同的可调用名称。由于这不依赖于反射API,因此速度也可能快一点

上述内容可以更优雅地写成

function isClosure($arg) {
    $test = function(){};
    return $arg instanceof $test;
}

+1尽管需要注意的是,这个
是可调用的
对于任何可调用项都将返回
TRUE
,而不仅仅是闭包。@Gordon:很对,我在你发布此消息时将其作为对原始问题的注释添加了进来。:-)使用PHP5.3之前的穷人匿名函数
create_function
创建的函数怎么样?那么可以是
\uu invoke()
d的对象呢?我特别感兴趣的是,您是否可以测试闭包(“…仅当变量是闭包时返回true…”)。我的问题是,为什么要区分函子(或在该点的任何回调)和闭包?在5.3中,你的观点没有什么不同(因为你不能重新绑定任何东西),那么这又有什么关系呢?如果它通过了
is_callable()
,那么为什么这还不够呢???从PHP5.4开始,您可以依赖闭包作为闭包:@Gordon感谢您的跟进!我会把它加在上面。很好,我喜欢我的一些反映。很好。这种方法能克服他的“实现细节”问题吗?我想我真的是在问isClosure是否检查类的闭包。@webbiedave-这并不重要,因为我假设对闭包的实现更改将在ReflectionFunction@webbiedave这是一个API调用,因此,您不必担心它的实现细节,只要开发人员更新用于闭包/lambda的类,就可以依靠他们来更新反射API。但既然如此,我想说它以某种不同的方式决定了它(
ZEND\u ACC\u CLOSURE
)。@Gordon-差不多,但不完全一样。如何在StdObj、可调用对象和闭包之间进行测试?
method_exists($functorOrClosureOrLambda, '__invoke');
function isClosure($arg)
{
    if(is_callable($arg, FALSE, $name)) {
        is_callable(function() {}, TRUE, $implementation);
        return ($name === $implementation);
    }
}
function isClosure($arg) {
    $test = function(){};
    return $arg instanceof $test;
}