如何以任何顺序使用 PHP 函数或方法参数?

Posted

技术标签:

【中文标题】如何以任何顺序使用 PHP 函数或方法参数?【英文标题】:How to use PHP function or method arguments in any order? 【发布时间】:2012-10-10 12:09:31 【问题描述】:

假设我用这样的方法定义类:

class Test 
    public function doStuff($a, $b, $c) 
    // -- Do stuff --
    

是否可以使用此方法,但参数顺序不同,如下所示:

$test = new Test();
$test->doStuff($b, $c, $a);

它们的名称相同,只是顺序不同。

我看到 Symfony2 可以通过它的调度程序来做到这一点,你可以按照你想要的任何顺序使用参数。 链接:Symfony2 controller can do it

问题是,如何做到这一点? Symfony2 如何调用适当的动作控制器,然后它可以按您喜欢的任何顺序接受参数?

编辑: 我不能使用数组,而且我知道 php 不使用命名参数。但不知何故 Symfony2 设法做到了。

【问题讨论】:

@kalpaitch - 好吧,symfony2 做到了,所以有办法。 看看这里的建议:***.com/questions/4697705/php-function-overloading 是的,他们可能会检查每个参数的对象类型。这仅在您的方法签名具有所有不同数据类型的参数时才有效。例如。如果您打算以任何顺序传递三个字符串,那么您就不走运了。 @otporan 编辑:意思不是没有帮助,感谢其他 cmets 的澄清 Passing named parameters to a php function through call_user_func_array 的可能副本 【参考方案1】:

我认为你误解了 Symfony 的意思。您不能以任何顺序将参数传递给控制器​​操作,它必须按特定顺序。然而,他们正在做的事情是动态的,就是弄清楚你的路由参数在函数定义中的顺序。

例如我们定义一个路由:

pattern:      /hello/first_name/last_name
defaults:      _controller: AcmeHelloBundle:Hello:index, color: green 

在路由中,参数命名为first_namelast_namecolor

他们的意思是,你在动作中使用什么顺序并不重要。

以下每一项都是等价的:

public function indexAction($first_name, $last_name, $color) ...
public function indexAction($color, $last_name, $first_name) ...
public function indexAction($last_name, $color, $first_name) ...

由于您的参数与路由中的参数命名相同,Symfony 会根据您的定义确定参数的正确顺序是什么

如果您要手动调用以下操作:

public function indexAction($first_name, $last_name, $color)

那么参数仍然必须$first_name, $last_name, $color 的形式传入,而不是以任何其他顺序传入。使用不同的顺序只会将错误的值与参数相关联。 Symfony 只是不关心您定义函数的顺序,因为它决定了顺序,因为您的路由参数必须与您的方法参数命名相同。

希望这能消除混乱。

【讨论】:

那么他们如何动态地计算出参数在函数定义中出现的顺序呢?我认为这不能以编程方式完成,除非文件以文本形式打开并进行解析。 @Mahn 他们的关键是函数参数的命名必须与路由参数完全相同。然后,为了获取类、方法及其参数的信息,他们可能会在类方法上使用ReflectionClass 和ReflectionMethod 来获取参数的顺序。 嗯,对了,反射类也可以做到。 @Mahn 看看这个问题:***.com/questions/12976855/… 这回答了如何以任意顺序使用参数实例化类和调用方法:)【参考方案2】:

Sable Foste 的想法启发了我。

您可以使用另一个参数指定顺序,然后使用variable variables:

function test($order, $_a, $_b, $_c)
  $order = explode(';', $order);
  $$order[0] = $_a;
  $$order[1] = $_b;
  $$order[2] = $_c;

  echo "a = $a; b = $b; c = $c<br>";



test('a;b;c', 'a', 'b', 'c');
test('b;a;c', 'b', 'a', 'c');

但是说真的,为什么不能使用数组呢?这是最好的方法。


更新:这是我写的。我一定是真的很无聊。

现在我觉得很脏。

class FuncCaller

    var $func;
    var $order_wanted;
    var $num_args_wanted;

    function FuncCaller( $func, $order )
        $this->func = $func;
        if( is_set($order) )
            // First version: receives string declaring parameters
            // We flip the order_wanted array so it maps name => index
            $this->order_wanted = array_flip( explode(';', $order) );  
            $this->num_args_wanted = count($this->order_wanted);
         else 
            // Second version: we can do better, using reflection
            $func_reflection = new ReflectionFunction($this->func);
            $params = $func_reflection->getParameters();

            $this->num_args_wanted = func_reflection->getNumberOfParameters();
            $this->order_wanted = [];

            foreach( $params as $idx => $param_reflection )
                $this->order_wanted[ $param_reflection->getName() ] = $idx;
            
        
    


    function call()
        if( func_num_args() <= 1 )
            // Call without arguments
            return $this->func();
        
        else if( func_num_args() == $this->num_args_wanted )
            // order argument not present. Assume order is same as func signature
            $args = func_get_args();
            return call_user_func_array( $this->func, $args );
        
        else 
            // @TODO: verify correct arguments were given
            $args_given = func_get_args();
            $order_given = explode( ';', array_shift($args_given) );
            $order_given = array_flip( $order_given );  // Map name to index
            $args_for_call = array();
            foreach( $this->order_wanted as $param_name => $idx )
                $idx_given = $order_given[$param_name];
                $val = $args_given[ $idx_given ];
                $args_for_call[$idx] = $val;
            
            return call_user_func_array( $this->func, $args_for_call );
        
    

    // This should allow calling the FuncCaller object as a function,        
    // but it wasn't working for me for some reason
    function __invoke()
        $args = func_get_args();
        return call_user_func( $this->call, $args );
    



// Let's create a function for testing:
function test( $first, $second, $third )
    $first  = var_export($first , TRUE);
    $second = var_export($second, TRUE);
    $third  = var_export($third , TRUE);
    echo "Called test( $first, $second, $third );<br>";


// Now we test the first version: order of arguments is specified
$caller = new FuncCaller( test, '1st;2nd;3rd' );
$caller->call(1, 2, 3);
$caller->call('3rd;1st;2nd', 'c', 'a', 'b');
$caller->call('2nd;3rd;1st', 'Two', 'Three', 'One');
$caller->call('3rd;2nd;1st', 'Go!', 'Get Set...', 'Get Ready...');


echo "<br>";

// Now we test the second version: order of arguments is acquired by reflection
// Note we you won't be able to rename arguments this way, as we did above
$reflection_caller = new FuncCaller( test ); 
$reflection_caller->call(1, 2, 3);
$reflection_caller->call('third;first;second', 'c', 'a', 'b');
$reflection_caller->call('second;third;first', 'Two', 'Three', 'One');
$reflection_caller->call('third;second;first', 'Go!', 'Get Set...', 'Get Ready...');

【讨论】:

【参考方案3】:

不,但是您可以使用不同顺序的重载方法。或者你可以尝试通过反射或智能猜测参数来做到这一点。我怀疑你能否想出一个适用于所有功能的优雅解决方案。

【讨论】:

【参考方案4】:

我感觉他们使用反射来检查控制器动作函数的声明;然后他们以正确的顺序使用参数进行函数调用。

看看http://www.php.net/manual/en/class.reflectionparameter.php。它有一个 getName 和 getPosition。

【讨论】:

【参考方案5】:

为什么不添加第四个变量$d,它定义了函数的传入顺序。

class Test 
    public function doStuff($d, $a, $b, $c) 
      $v=explode(',', $d);
      foreach($v as $key => $value)
        switch ($value)
           case 'a':
             $aa = $v[a];
             break;
           case 'b':
             $bb = $v[b];
             break;
           case 'a':
             $cc = $v[c];
             break;
          
         echo "a is $aa, b is $bb, c is $cc"; //output  
      

// -- Do stuff --


$d= "b,c,a"; // the order of the variables
$test = new Test();
$test->doStuff($d, $b, $c, $a);

【讨论】:

@Zecc,是的,抱歉,我无法测试代码,只是一个草图。不过,我喜欢你在上面所做的。 谢谢。你启发了我。而且我有一些空闲时间来全面调试它:)

以上是关于如何以任何顺序使用 PHP 函数或方法参数?的主要内容,如果未能解决你的问题,请参考以下文章

在java语言里如何在子类中调用父类的有参构造函数

深入类的方法

php源代码被加密了,请问如何解密?

Java类中各个成员初始化顺序

Python函数中的参数

序列化,反序列化与空参构造函数