【问题标题】:Check if callable can receive class as parameter in php检查callable是否可以接收类作为php中的参数
【发布时间】:2018-09-06 09:16:24
【问题描述】:

我有一个可调用的$f,我想知道它是否可以接收某个类Foo 的实例作为输入。

目前我正在做类似的事情

try {
    $f($foo);
} catch (\TypeError $e) {
    throw new \InvalidArgumentException('The provided function can not evaluate inputs of this type');
}

有没有办法在不实际调用可调用对象的情况下进行检查?也许用反射或其他一些黑魔法?

【问题讨论】:

    标签: php types callable


    【解决方案1】:

    如果您希望能够反映任何类型的可调用对象,则需要将逻辑封装在一个小函数中。根据您是否拥有数组、函数名或匿名函数,您需要创建ReflectionFunctionReflectionMethod。幸运的是,它们都扩展了ReflectionFunctionAbstract,因此我们可以对返回值进行类型提示。

    function reflectCallable($arg): ReflectionFunctionAbstract {
        if (is_array($arg)) {
            $ref = new ReflectionMethod(...$arg);
        } elseif (is_callable($arg)) {
            $ref = new ReflectionFunction($arg);
        }
    
        return $ref;
    }
    

    这将为您的可调用值返回适当的对象,然后您可以使用它来获取参数并采取相应措施:

    function definedFunc(Foo $foo) {}
    $callable = function(Foo $foo) {};
    class Bar { public function baz(Foo $foo) {} }
    
    foreach (['definedFunc', $callable, ['Bar', 'baz']] as $callable) {
        $reflected = reflectCallable($callable);
    
        if ((string) $reflected->getParameters()[0]->getType() === 'Foo') {
            echo 'Callable takes Foo', PHP_EOL;
        }
    }
    

    https://3v4l.org/c5vmM

    请注意,这不会进行任何错误处理 - 如果可调用对象不带任何参数或第一个参数没有类型,您可能会收到警告/通知。它还需要 PHP 7+,但希望这不是问题。

    它目前不支持实现__invoke 的对象或定义为"Foo::bar" 的静态调用,但如果需要,添加它们不会太难。 我刚刚找到了一些东西在 Twig 的源代码中非常相似,它做得更彻底:https://github.com/twigphp/Twig/blob/v2.8.0/src/Node/Expression/CallExpression.php#L280

    【讨论】:

    • 感谢您的回答!现在我在徘徊哪种方法更快,捕捉异常或使用反射......
    【解决方案2】:

    你可以用ReflectionParameter::getType:

    $f = function(Foo $foo) {};
    
    $reflectionFunc = new ReflectionFunction($f);
    $reflectionParams = $reflectionFunc->getParameters();
    $reflectionType1 = $reflectionParams[0]->getType();
    
    echo $reflectionType1;
    

    输出:

    【讨论】:

    • 我可以为每个可调用对象执行此操作吗?我想这仅适用于功能
    • ReflectionFunction 适用于函数(包括匿名函数)。如果它是类的一部分,则可以使用 ReflectionMethod - 它以对象/类名称作为第一个参数,将方法名称作为第二个参数。
    • @marcosh 是的,这是可能的,我已经更新以更好地匹配问题
    • 可调用对象也可以是具有类名和方法名的数组。我不认为你可以通过反射轻松处理这个问题
    • @marcosh 您可以解压缩数组并将其传递给 ReflectionMethod - 3v4l.org/ogFKT
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2012-11-30
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-06-20
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多