Php 如何获取一个运行时确定的可调用函数的参数数?

Php 如何获取一个运行时确定的可调用函数的参数数?,php,reflection,callback,Php,Reflection,Callback,注意:通过写这个问题,我已经发现我对使用一个新的语言特性过于热情了。更清洁的解决方案是使用策略模式来代替。。。尽管如此,我还是很好奇是否有一个合适的方法来解决这个问题 TL;DR:你能在PHP中思考一个通用的可调用函数而不用手动检查所有类型的可调用函数吗 在PHP5.4中,我们得到了一个新的类型提示:callable。这看起来很有趣。我想我应该通过以下方式利用这一点: <?php public function setCredentialTreatment(callable $cr

注意:通过写这个问题,我已经发现我对使用一个新的语言特性过于热情了。更清洁的解决方案是使用策略模式来代替。。。尽管如此,我还是很好奇是否有一个合适的方法来解决这个问题

TL;DR:你能在PHP中思考一个通用的可调用函数而不用手动检查所有类型的可调用函数吗

在PHP5.4中,我们得到了一个新的类型提示:callable。这看起来很有趣。我想我应该通过以下方式利用这一点:

<?php
    public function setCredentialTreatment(callable $credentialTreatment) {
       // Verify $credentialTreatment can be used (ie: accepts 2 params)
       ... magic here ...
    }
?>

到目前为止,我的思路是对可调用对象进行一系列类型检查,并从中推断反射*类要使用的类型:

<?php
if(is_array($callable)) {
    $reflector = new ReflectionMethod($callable[0], $callable[1]);
} elseif(is_string($callable)) {
    $reflector = new ReflectionFunction($callable);
} elseif(is_a($callable, 'Closure') || is_callable($callable, '__invoke')) {
    $objReflector = new ReflectionObject($callable);
    $reflector    = $objReflector->getMethod('__invoke');
}

// Array of ReflectionParameters. Yay!
$parameters = $reflector->getParameters();
// Inspect parameters. Throw invalidArgumentException if not valid.
?>


现在,对我来说,这感觉太复杂了。我是否错过了某种捷径来实现我在这里的目标?任何洞察都是受欢迎的:)

您可以传递带有参数的数组,并且可以计算数组值以了解传递给回调函数的参数。

我创建了一个与call_user_func()类似的较短版本,并在PHP手册中针对回调/可调用函数的所有不同类型上对其进行了测试。这样你几乎可以在任何地方使用它。call_user_func()不仅接受对象,而且这个函数也不应该接受对象,因为它只处理回调

关于如何使用它的测试和建议包括在下面,我希望这有助于:

function getNrOfParams($callable)
{
    $CReflection = is_array($callable) ? 
    new ReflectionMethod($callable[0], $callable[1]) : 
    new ReflectionFunction($callable);
    return $CReflection->getNumberOfParameters();
}
整个试验及其结果:

<?php   
class Smart
{
    public function __invoke($name)
    {

    }

    public function my_callable($one, $two, $three)
    {

    }

    public static function myCallableMethod($one, $two) 
    {

    }

    public static function who()
    {
            echo "smart the parent class";
    }
}

class Smarter extends Smart
{
    public static function who()
    {
        echo "smarter";
    }
}

function my_ca($one)
{

}

function getNrOfParams($callable)
{
    $CReflection = is_array($callable) ? new ReflectionMethod($callable[0], $callable[1]) : new ReflectionFunction($callable);
    return $CReflection->getNumberOfParameters();
}
// Test 1 Callable Function
echo "Test 1 - Callable function:" . getNrOfParams('my_ca');

// Test 2 Static method
echo " Test 2 - Static class method:" . getNrOfParams(array('Smart', 'myCallableMethod'));

// Test 3 Object method
$smart = new Smart();
echo " Test 3 - Object method:" . getNrOfParams(array($smart, 'my_callable'));

// Test 4 Static method call (As of PHP 5.2.3)
//echo " Test 4 - Static class method call:" . getNrOfParams('Smart::myCallableMethod');
// Calling a static method this way does not work in ReflectionFunction.
// However, Test 2 provides a solution.

// Test 5 Relative static method (As of PHP 5.3.0)
//echo " Test 5 - Relative static class method:" . getNrOfParams(array('Smarter', 'parent::who'));
// Calling a relative static method doesn't work either. ReflectionMethod lacks support.
// All other tests work.

// Tesy 6 __invoke
echo " Test 6 - __invoke:" . getNrOfParams(array($smart, '__invoke'));

// Test 7 Closure
$closure = function($one, $two, $three)
{
    // Magic
};
echo " Test 7 - Closure:" . getNrOfParams($closure);

TL;博士,我不这么认为。如果需要通用解决方案,则需要检查所有可调用类型

以下函数可用于获取PHP中任何通用可调用的
ReflectionFunctionBStract
实例:

function reflectionOf(callable $callable): ReflectionFunctionAbstract
{
    if ($callable instanceof Closure) {
        return new ReflectionFunction($callable);
    }
    if (is_string($callable)) {
        $pcs = explode('::', $callable);
        return count($pcs) > 1 ? new ReflectionMethod($pcs[0], $pcs[1]) : new ReflectionFunction($callable);
    }
    if (!is_array($callable)) {
        $callable = [$callable, '__invoke'];
    }
    return new ReflectionMethod($callable[0], $callable[1]);
}
然后可以得到如下参数数量:

reflectionOf($func)->getNumberOfParameters();
希望这对某人有所帮助。
这个答案对当事人来说可能太晚了,但其他任何解决方案都不能完全覆盖通用可调用项。

我遇到了类似的问题,并最终创建了一个帮助函数来获取任何可调用项的反射对象。此函数的代码与您的代码基本相同。我不知道您是否继续使用此函数,但您是否考虑创建自己的CallableReflection,以帮助更轻松地打包差异?最后,我没有进一步探讨这一途径,但是创建一个可重用的CallableReflection看起来确实是朝着正确方向迈出的一步。不过,在某种程度上,我希望PHP的反射模块会附带类似的东西。解决方案中缺少的一件事是带有
\u invoke
方法的对象。@KevinBond感谢您发现了这一点!令人惊叹的!接受这个答案,因为它涵盖了我想要的一切。。。干得好:)派对可能晚了,但我同意这似乎是我们迄今为止看到的最全面的报道。公认的答案——第一个答案很好!:)谢谢@kander:-)