代码之家  ›  专栏  ›  技术社区  ›  Hamish

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

  •  8
  • Hamish  · 技术社区  · 15 年前

    PHP manual for anonymous functions (即关闭)声明:

    匿名函数当前使用闭包类实现。这是一个实现细节 .

    是否可以测试一个变量,以便仅当变量是闭包时测试才返回true, 不涉及闭包类 ?

    换句话说,我如何重写下面的内容,以便在 $bar 不是匿名函数:

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

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

    笔记:

    1. 测试可能和使用闭包类一样“特定于实现”。
    2. (似乎显而易见的) ReflectionFunction::isClosure() 方法似乎几乎是无用的:当您完成检查以确保ReflectionFunction实际上可以被实例化时(除了闭包之外不能接受类),您已经消除了所有其他选项。
    3. 后续行动 Gordon -从PHP 5.4开始,您可以依赖闭包作为闭包: 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";
    

    -

    2 回复  |  直到 9 年前
        1
  •  8
  •   Gordon Haim Evgi    15 年前

    你也可以使用

    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
    

    请注意,以上内容只会返回 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 关闭 类名。


    既然你提到了,你不能通过反射API进行可靠的测试,因为它在将函子传递给 反射功能裂缝::裂缝

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

    这将检查传递的参数是否可调用。这个 $name 参数存储可调用的名称。对于闭包,这是当前 Closure::__invoke . 因为这对于任何闭包/Lambda都是一样的,所以我们可以将传递的参数的名称与任意其他闭包/Lambda进行比较。如果它们相等,则参数必须是闭包/Lambda。在运行时确定可调用的名称有一个额外的好处,即您不必将有关实现详细信息的假设硬编码到源代码中。传递函子将返回 FALSE

    上面可以写得更优雅些

    function isClosure($arg) {
        $test = function(){};
        return $arg instanceof $test;
    }
    
        2
  •  1
  •   janmoesen    15 年前

    is_callable !is_array InvalidArgumentException 你自己。