dongqucheng3851 2012-10-25 14:46
浏览 25
已采纳

如何获取运行时确定的可调参数?

NOTE: By virtue of writing this quesiton, I've already figured out that I was being overly enthousiastic about using a new language feature. The far cleaner solution was using a Strategy Pattern instead... still, I'm curious if there's a proper way to go about this problem.

TL;DR: Can you reflect on a generic Callable in PHP without resorting to manually typechecking all kinds of callable?

In PHP 5.4 we've got a new typehint: callable. This seems like a lot of fun. I thought I'd make use of this through the following:

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

So far my line of thought has been to do a series of type-checks on the callable, and inferring from that which Reflection* class to use:

<?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.
?>

Now, to me, this feels overly complicated. Am I missing some kind of shortcut way to achieving what I'm trying to do here? Any insight would be welcomed :)

  • 写回答

2条回答 默认 最新

  • dotelauv682684 2015-09-03 01:50
    关注

    I created a shorter version that works much like call_user_func() and tested it on all different types in the PHP manual for callbacks/callables. This way you can use it almost anywhere. call_user_func() doesn't take objects only, and it didn't make sense to me that this function should either, since it is only handling callbacks.

    Tests and suggestions on how to use it are included below and I hope this helps:

    function getNrOfParams($callable)
    {
        $CReflection = is_array($callable) ? 
        new ReflectionMethod($callable[0], $callable[1]) : 
        new ReflectionFunction($callable);
        return $CReflection->getNumberOfParameters();
    }
    

    The test and its result in its entirety:

    <?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);
    
    本回答被题主选为最佳回答 , 对您是否有帮助呢?
    评论
查看更多回答(1条)

报告相同问题?

悬赏问题

  • ¥15 HLs设计手写数字识别程序编译通不过
  • ¥15 Stata外部命令安装问题求帮助!
  • ¥15 从键盘随机输入A-H中的一串字符串,用七段数码管方法进行绘制。提交代码及运行截图。
  • ¥15 TYPCE母转母,插入认方向
  • ¥15 如何用python向钉钉机器人发送可以放大的图片?
  • ¥15 matlab(相关搜索:紧聚焦)
  • ¥15 基于51单片机的厨房煤气泄露检测报警系统设计
  • ¥15 Arduino无法同时连接多个hx711模块,如何解决?
  • ¥50 需求一个up主付费课程
  • ¥20 模型在y分布之外的数据上预测能力不好如何解决