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 关于大棚监测的pcb板设计
  • ¥20 sim800c模块 at指令及平台
  • ¥15 stm32开发clion时遇到的编译问题
  • ¥15 lna设计 源简并电感型共源放大器
  • ¥15 如何用Labview在myRIO上做LCD显示?(语言-开发语言)
  • ¥15 Vue3地图和异步函数使用
  • ¥15 C++ yoloV5改写遇到的问题
  • ¥20 win11修改中文用户名路径
  • ¥15 win2012磁盘空间不足,c盘正常,d盘无法写入
  • ¥15 用土力学知识进行土坡稳定性分析与挡土墙设计