【问题标题】:Any way to specify optional parameter values in PHP?有什么方法可以在 PHP 中指定可选参数值?
【发布时间】:2010-10-15 23:47:43
【问题描述】:

假设我有一个 PHP 函数 foo:

function foo($firstName = 'john', $lastName = 'doe') {
    echo $firstName . " " . $lastName;
}
// foo(); --> john doe

有没有办法只指定第二个可选参数?

例子:

foo($lastName='smith'); // output: john smith

【问题讨论】:

    标签: php function optional-parameters


    【解决方案1】:

    PHP 本身不支持函数的命名参数。但是,有一些方法可以解决这个问题:

    1. 使用数组作为函数的唯一参数。然后您可以从数组中提取值。这允许在数组中使用命名参数。
    2. 如果您希望根据上下文允许可选数量的参数,则可以使用 func_num_args 和 func_get_args 而不是在函数定义中指定有效参数。然后根据参数数量、字符串长度等,您可以确定要做什么。
    3. 将空值传递给您不想指定的任何参数。没有真正绕过它,但它确实有效。
    4. 如果您在对象上下文中工作,那么您可以使用魔术方法 __call() 来处理这些类型的请求,以便您可以根据传递的参数路由到私有方法。

    【讨论】:

    • 虽然没有隐式支持,但有比公认答案建议的更好的解决方法。请参阅下面的 Walf 解决方案。如果您在我的解决方案中需要它,还有更多细节(我发誓我是独立得出的!)
    • Php8 现在支持命名参数。
    【解决方案2】:

    数组技术的一种变体,可以更轻松地设置默认值:

    function foo($arguments) {
      $defaults = array(
        'firstName' => 'john',
        'lastName' => 'doe',
      );
    
      $arguments = array_merge($defaults, $arguments);
    
      echo $arguments['firstName'] . ' ' . $arguments['lastName'];
    }
    

    用法:

    foo(array('lastName' => 'smith')); // output: john smith
    

    【讨论】:

    • 对于关联数组,$arguments += $defaults; 更干净。
    【解决方案3】:

    你可以稍微重构你的代码:

    function foo($firstName = NULL, $lastName = NULL)
    {
        if (is_null($firstName))
        {
            $firstName = 'john';
        }
        if (is_null($lastName ))
        {
            $lastName = 'doe';
        }
    
        echo $firstName . " " . $lastName;
    }
    
    foo(); // john doe
    foo('bill'); // bill doe
    foo(NULL,'smith'); // john smith
    foo('bill','smith'); // bill smith
    

    【讨论】:

      【解决方案4】:

      如果您有多个可选参数,一种解决方案是传递一个散列数组参数:

      function foo(array $params = array()) {
          $firstName = array_key_exists("firstName", $params) ?
            $params["firstName"] : "";
          $lastName = array_key_exists("lastName", $params) ?
            $params["lastName"] : "";
          echo $firstName . " " . $lastName;
      }
      
      foo(['lastName'=>'smith']);
      

      当然,在这个解决方案中,没有验证哈希数组的字段是否存在或拼写正确。一切由您来验证。

      【讨论】:

        【解决方案5】:

        没有。这样做的常用方法是使用一些启发式方法来确定隐含的参数,例如字符串长度、类型等。

        一般来说,您会编写函数以按照最需要到最不需要的顺序获取参数。

        【讨论】:

        • 只有在参数类型明确无误的情况下,启发式算法才能可靠地工作,就像 jQuery 一样。其他方法,如字符串长度,是等待发生的意外。
        【解决方案6】:

        你想要的方式:不。

        你可以使用一些特殊的标记,比如 NULL 来说明没有提供值:

        function foo($firstName, $lastName = 'doe') {
            if (is_null($firstName))
                $firstName = 'john';
            echo $firstName . " " . $lastName;
        }
        
        foo(null, 'smith');
        

        【讨论】:

          【解决方案7】:

          PHP 8 引入了命名参数,因此现在可以指定传递给函数的参数。

          在 PHP 8 之前:

          htmlspecialchars($string, ENT_COMPAT | ENT_HTML401, 'UTF-8', false);
          

          PHP 8 之后:

          htmlspecialchars($string, double_encode: false);
          

          Reference

          【讨论】:

            【解决方案8】:

            我希望这个解决方案在我 2.5 年前开始使用 PHP 时就已经存在了。它在我创建的示例中效果很好,我不明白为什么它不应该是完全可扩展的。与之前的提议相比,它提供了以下好处:

            (i) 函数内对参数的所有访问都是通过命名变量进行的,就好像参数已完全声明一样,而不需要数组访问

            (ii) 调整现有功能非常快捷方便

            (iii) 任何函数都只需要一行额外的代码(除了不可避免地需要定义默认参数,无论如何您都将在函数签名中执行此操作,而是将它们定义在数组中)。额外线路的功劳完全归功于 Bill Karwin。这一行对于每个函数都是相同的。

            方法

            使用强制参数和可选数组定义您的函数

            将可选参数声明为局部变量

            关键:使用您通过数组传递的参数替换任何可选参数的预先声明的默认值。

            extract(array_merge($arrDefaults, array_intersect_key($arrOptionalParams, $arrDefaults)));
            

            调用函数,传递它的强制参数,并且只传递你需要的那些可选参数

            例如,

            function test_params($a, $b, $arrOptionalParams = array()) {
            
            $arrDefaults = array('c' => 'sat',
            
                                 'd' => 'mat');
            
            extract(array_merge($arrDefaults, array_intersect_key($arrOptionalParams, $arrDefaults)));
            
            echo "$a $b $c on the $d";
            
            }
            

            然后这样称呼它

            test_params('The', 'dog', array('c' => 'stood', 'd' => 'donkey'));
            test_params('The', 'cat', array('d' => 'donkey'));
            test_params('A', 'dog', array('c' => 'stood'));
            

            结果:

            狗站在驴子上

            猫坐在驴子上

            一只狗站在垫子上

            【讨论】:

            • 确认 - 我在两周前看到它并给了你一个观点。我承认在发布我的之前我没有重新阅读所有“解决方案”。如果我被允许,我会在接受的答案中添加评论,指向您的解决方案。
            【解决方案9】:

            这里提到了一些“选项”样式的实现。如果您打算将它们用作标准,那么到目前为止,没有一个是非常防弹的。试试这个模式:

            function some_func($required_parameter, &$optional_reference_parameter = null, $options = null) {
                $options_default = array(
                    'foo' => null,
                );
                extract($options ? array_intersect_key($options, $options_default) + $options_default : $options_default);
                unset($options, $options_default);
            
                //do stuff like
                if ($foo !== null) {
                    bar();
                }
            }
            

            这为您提供了函数局部变量(在此示例中为 $foo)并防止创建任何没有默认值的变量。这样就不会有人意外覆盖函数中的其他参数或其他变量。

            【讨论】:

              【解决方案10】:

              参数需要按位置顺序传递,你不能跳过参数本身;您必须提供默认参数值才能跳过它。可以说,这违背了您要达到的目的。

              无需重写您的函数以以不同的方式接受参数,这是一种解决此问题的调用时方法:

              $func = 'foo';
              $args = ['lastName' => 'Smith'];
              
              $ref = new ReflectionFunction($func);
              $ref->invokeArgs(array_map(function (ReflectionParameter $param) use ($args) {
                  if (array_key_exists($param->getName(), $args)) {
                      return $args[$param->getName()];
                  }
                  if ($param->isOptional()) {
                      return $param->getDefaultValue();
                  }
                  throw new InvalidArgumentException("{$param->getName()} is not optional");
              }, $ref->getParameters()));
              

              换句话说,您使用反射来检查函数的参数并按名称将它们映射到可用参数,跳过带有默认值的可选参数。是的,这既丑陋又麻烦。您可以使用此示例创建如下函数:

              call_func_with_args_by_name('foo', ['lastName' => 'Smith']);
              

              【讨论】:

                【解决方案11】:

                如果经常使用,只需定义一个新的专用函数:

                function person($firstName = 'john', $lastName = 'doe') {
                    return $firstName . " " . $lastName;
                }
                
                function usualFirtNamedPerson($lastName = 'doe') {
                    return person('john', $lastName);
                }
                
                print(usualFirtNamedPerson('smith')); --> john smith
                

                请注意,如果您愿意,您还可以在此过程中更改 $lastname 的默认值。

                当一个新函数估计过大时,只需调用带有所有参数的函数即可。如果你想让它更清楚,你可以将你的文字预先存储在 fin 命名变量中或使用 cmets。

                $firstName = 'Zeno';
                $lastName = 'of Elea';
                print(person($firstName, $lastName));
                print(person(/* $firstName = */ 'Bertrand', /* $lastName = */ 'Russel'));
                

                好的,这不像person($lastName='Lennon') 那样简洁优雅,但似乎你不能在PHP 中拥有它。这不是最性感的编码方式,使用超级元编程技巧或其他方式,但您希望在维护过程中遇到什么解决方案?

                【讨论】:

                  【解决方案12】:

                  遗憾的是,您尝试做的事情没有“语法糖”的方式。它们都是不同形式的 WTF。

                  如果您需要一个带有未定义数量的任意参数的函数,

                  function foo () { 
                       $args = func_get_args(); 
                       # $args = arguments in order 
                  }
                  

                  会成功的。尽量避免使用太多,因为对于 PHP 来说这有点神奇。

                  然后,您可以选择应用默认值并根据参数计数做一些奇怪的事情。

                  function foo() { 
                     $args = func_get_args();
                     if (count($args) < 1 ) { 
                         return "John Smith"; 
                     }
                     if (count($args) < 2 ) { 
                         return "John " . $args[0];
                     }
                     return $args[0] . " " . $args[1];
                  }
                  

                  另外,您可以选择模拟 Perl 样式参数,

                  function params_collect($arglist){ 
                      $config = array();
                      for ($i = 0; $i < count($arglist); $i+=2) { 
                          $config[$i] = $config[$i+1];
                      }
                      return $config; 
                  }
                  function param_default($config, $param, $default){ 
                      if (!isset($config[$param])) { 
                          $config[$param] = $default;
                      }
                      return $config;
                  }
                  
                  function foo() { 
                     $args = func_get_args();
                     $config = params_collect($args); 
                     $config = param_default($config, 'firstname', 'John'); 
                     $config = param_default($config, 'lastname', 'Smith'); 
                     return $config['firstname'] . ' ' . $config['lastname'];   
                  }
                  
                  foo('firstname', 'john', 'lastname', 'bob'); 
                  foo('lastname', 'bob', 'firstname', 'bob'); 
                  foo('firstname', 'smith'); 
                  foo('lastname', 'john');
                  

                  当然,这里更容易使用数组参数,但是你可以选择(甚至是不好的方式)做事。

                  值得注意的是,这在 Perl 中更好,因为你可以只做 foo(firstname => 'john');

                  【讨论】:

                    【解决方案13】:

                    没有,但你可以使用数组:

                    function foo ($nameArray) {
                        // Work out which values are missing?
                        echo $nameArray['firstName'] . " " . $nameArray['lastName'];
                    }
                    
                    foo(array('lastName'=>'smith'));
                    

                    【讨论】:

                      猜你喜欢
                      • 2015-10-15
                      • 2020-12-27
                      • 1970-01-01
                      • 1970-01-01
                      • 2011-05-01
                      • 2012-07-27
                      • 2012-06-30
                      • 1970-01-01
                      • 1970-01-01
                      相关资源
                      最近更新 更多