测试一个PHP Closure而不引用Closure内部类 [英] Testing for a PHP Closure without referring to the Closure internal class

查看:130
本文介绍了测试一个PHP Closure而不引用Closure内部类的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

匿名功能的PHP手册(即Closures)说明:

The PHP manual for anonymous functions (ie, Closures) states that:


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

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

Is it possible to test a variable, such that the test returns true only if the variable is a Closure, without referring to the Closure class?

换句话说,如何重写以下内容,如果 $ bar 是一个匿名函数:

In other words, how can I rewrite the following such that it will raise an error when $bar is anything but an anonymous function:

function foo(Closure $bar) {
    $bar();
}

编辑:根据收到的答案,这里是一个示例测试。

Based on the answers received, here is an example test.

注意:


  1. 似乎没有办法区分Functor和Closures,和
    的测试可能只是作为实现特定使用Closure类。

  2. (似乎很明显) ReflectionFunction :: isClosure()方法似乎几乎没有用:

  3. 在5.3.0版本中,ReflectionClass可以实例化(除了Closure之外不能使用Class) ($ closure) - > hasMethod('__ invoke')返回false,所以这可以用来测试Functors,但是(我告诉)这已经改变了。

  4. 后续追踪 Gordon - As的PHP 5.4,你可以依赖Closure是一个闭包: php.net/manual/en/class.closure.php

  1. 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.
  2. 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.
  3. 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.
  4. Follow up from Gordon - As of PHP 5.4 you can rely on Closure being a Closure: php.net/manual/en/class.closure.php

代码:

/**
 * 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 - 检查是否关闭

ReflectionFunctionAbstract::isClosure — Checks if closure

示例:

$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

请注意,上述操作只会返回 TRUE PHP 5.3 Lambdas和Closures。如果你只是想知道一个参数是否可以用作回调, is_callable 将表现更好。

Note that the above will only return TRUE for PHP 5.3 Lambdas and Closures. If you just want to know whether an argument can be used as a callback, is_callable will perform better.

EDIT 如果您也想要包含Functors,您可以 PHP 5.3.3

EDIT If you want to include Functors as well, you can do (as of PHP 5.3.3)

$rf = new ReflectionObject($functorOrClosureOrLambda);
var_dump( $rf->hasMethod('__invoke') ); // TRUE

method_exists($functorOrClosureOrLambda, '__invoke');

后者是更快的替代品。

A Closure 实例基本上只是一个具有 __ invoke 函数的类, 。但由于这是测试一个实现细节,我想说,它是不可靠的测试 Closure 类名。

A Closure instance is basically just a class that has an __invoke function which you fed the method body on the fly. But since this is testing for an implementation detail, I'd say it is as unreliable as testing for the Closure Class Name.

EDIT 由于提及您无法通过Reflection API可靠地进行测试,因为在将Functor传递给 ReflectionFunctionAbstract :: isClosure ,请尝试如果以下解决方案适合您的需要:

EDIT Since you mention you cannot reliably test via the Reflection API due to it raising an error when passing a Functor to ReflectionFunctionAbstract::isClosure, try if the following solution suits your needs:

function isClosure($arg)
{
    if(is_callable($arg, FALSE, $name)) {
        is_callable(function() {}, TRUE, $implementation);
        return ($name === $implementation);
    }
}

这将检查传递的参数是否可调用。 $ name 参数存储可调用名称。对于闭包,这是目前 Closure :: __ invoke 。因为对于任何Closures / Lambda,这将是相同的,我们可以比较传递的参数的名称和任意其他Closure / Lambda。如果它们相等,则参数必须是Closure / Lambda。在运行时确定可调用名称有额外的好处,你不必将关于实现细节的假设硬编码到你的源代码中。传递函数将返回 FALSE ,因为它不具有相同的可调用名称。因为这不依赖于Reflection API,它也可能有点快。

This will check if the passed argument is callable. The $name argument stores the callable name. For closures, this is currently Closure::__invoke. Since this will be the same for any Closures/Lambdas, we can compare the name of the passed argument against an arbitrary other Closure/Lambda. If they are equal, the argument must be a Closure/Lambda. Determining the callable name at runtime has the added benefit that you dont have to hardcode assumptions about the implementation details into your sourcecode. Passing a functor will return FALSE, because it wont have the same callable name. Since this does not rely on the Reflection API, it is also likely a bit faster.

上面可以更优雅地写为

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

这篇关于测试一个PHP Closure而不引用Closure内部类的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆