【问题标题】:Using Default Arguments in a Function在函数中使用默认参数
【发布时间】:2012-02-28 07:54:29
【问题描述】:

我对 PHP 函数的默认值感到困惑。假设我有这样的功能:

function foo($blah, $x = "some value", $y = "some other value") {
    // code here!
}

如果我想为 $x 使用默认参数并为 $y 设置不同的参数怎么办?

我一直在尝试不同的方法,但我越来越困惑。比如我试过这两个:

foo("blah", null, "test");
foo("blah", "", "test");

但是这两个都不会导致 $x 的正确默认参数。我也试过用变量名来设置它。

foo("blah", $x, $y = "test");   

我完全期望这样的事情能够奏效。但它根本不像我预期的那样工作。似乎无论我做什么,每次调用该函数时,我最终都必须输入默认参数。而且我一定遗漏了一些明显的东西。

【问题讨论】:

  • 你可能没有错过任何东西,PHP 开发人员可能错过了。
  • 这是一个有趣的问题。正如你所说,我实际上从来没有需要这样做,因为当我不确定什么依赖于原始时,我只使用默认参数来扩展其他程序员继承的函数。我很好奇答案。
  • 一段时间以来我见过的最好的问题之一!你试过什么都不通过吗? foo("blah", , "test");?
  • 规则是默认参数只能跟在参数后面。那就是你可以有 foo("blah") 和 x,y 是默认值,或者 foo("Blah","xyz") 和 y 是默认值。
  • 预期行为。请参阅php.net/manual/en/functions.arguments.php 中的“默认参数值”部分——空值和空字符串被接受为函数的实际参数

标签: php arguments default


【解决方案1】:

使用命名参数的 PHP 8

如果我想使用参数 $x 的默认值并为 $y 设置一个值怎么办? foo($blah, $x = "some value", $y = "some other value")

用法一:

foo(blah: "blah", y: "test");

用法2:

// use names to address last arguments
foo("blah", y: "test");

原生函数也会使用这个特性

htmlspecialchars($string, double_encode: false);
// Same as
htmlspecialchars($string, ENT_COMPAT | ENT_HTML401, 'UTF-8', false);

关于功能和Documentation

命名参数允许根据参数名称而不是参数位置将参数传递给函数。这使得参数的含义可以自我记录,使参数的顺序独立,并且允许任意跳过默认值

IDE 支持

Netbeans 12.3完全支持包含此功能的 PHP 8.0 语法,但命名参数的代码完成除外。

【讨论】:

    【解决方案2】:

    另一种写法:

    function sum($args){
        $a = $args['a'] ?? 1;
        $b = $args['b'] ?? 1;
        return $a + $b;
    }
    
    echo sum(['a' => 2, 'b' => 3]); // 5 
    echo sum(['a' => 2]); // 3 (2+1)
    echo sum(['b' => 3]); // 4 (1+3)
    echo sum([]); // 2 (1+1)
    

    【讨论】:

      【解决方案3】:

      PHP 8 的做法:

      function foo($blah, ?$x, ?$y) {
          $x = $x ?? "some value";
          $y = $y ?? "some other value";
      }
      

      【讨论】:

      • “现代”?这个答案不会很好。请指定与哪些 PHP 版本相关。
      【解决方案4】:

      在 PHP 8 中,我们可以使用命名参数来解决这个问题。

      所以我们可以解决这个问题的原始发布者描述的问题:

      如果我想为 $x 使用默认参数并为 $y 设置不同的参数怎么办?

      与:

      foo(blah: "blah", y: "test");
      

      参考:https://wiki.php.net/rfc/named_params(特别是“跳过默认设置”部分)

      【讨论】:

        【解决方案5】:

        我建议按如下方式更改函数声明,以便您可以随心所欲:

        function foo($blah, $x = null, $y = null) {
            if (null === $x) {
                $x = "some value";
            }
        
            if (null === $y) {
                $y = "some other value";
            }
        
            code here!
        
        }
        

        这样,您可以像foo('blah', null, 'non-default y value'); 一样进行调用并让它按您的意愿工作,其中第二个参数$x 仍然获得其默认值。

        使用此方法,传递空值意味着当您想要覆盖其后参数的默认值时,您需要一个参数的默认值。

        如其他答案所述,

        默认参数仅用作函数的最后一个参数。 如果要在函数定义中声明默认值, 没有办法省略一个参数并覆盖它后面的一个。

        如果我有一个方法可以接受不同数量的参数和不同类型的参数,我通常会声明类似于 Ryan P 给出的答案的函数。

        这是另一个例子(这不能回答你的问题,但希望能提供信息:

        public function __construct($params = null)
        {
            if ($params instanceof SOMETHING) {
                // single parameter, of object type SOMETHING
            } elseif (is_string($params)) {
                // single argument given as string
            } elseif (is_array($params)) {
                // params could be an array of properties like array('x' => 'x1', 'y' => 'y1')
            } elseif (func_num_args() == 3) {
                $args = func_get_args();
        
                // 3 parameters passed
            } elseif (func_num_args() == 5) {
                $args = func_get_args();
                // 5 parameters passed
            } else {
                throw new \InvalidArgumentException("Could not figure out parameters!");
            }
        }
        

        【讨论】:

        • 为什么不 $x = isset($x) ? $x :'默认值'; ?
        • @zloctb 这是一个干净且可读的空检查。看起来这是开发人员的偏好。要么 $x = isset($x) 吗? $x :'默认值';或 $x = is_null($x) ?'默认值':$x;将与此解决方案一样有效。这是关于可读性和维护性的偏好选择。
        • 只是为了向未来的读者澄清。第一个解决方案显然无法为参数分配真正的null 值,因为每次它都会分配默认值。即使您在实际需要 null 时有另一个特殊值(例如,当 $x == "use_null" make $x = null 时),那么您将无法分配这样的特殊值作为参数(在本例中为“use_null”字符串)。因此,当您想使用默认值(并且您希望 null 成为有效选项)时,请确保使用唯一的、永远不需要的“键”
        【解决方案6】:

        我的 2 美分与 空合并运算符 ??(自 PHP 7 起)

        function foo($blah, $x = null, $y = null) {
            $varX = $x ?? 'Default value X';
            $varY = $y ?? 'Default value Y';
            // ...
        }
        

        您可以在我的repl.it 上查看更多示例

        【讨论】:

          【解决方案7】:

          将数组而不是单个参数传递给函数并使用空合并运算符 (PHP 7+)。

          下面,我传递了一个包含 2 个项目的数组。在函数内部,我正在检查 item1 的值是否已设置,如果未分配默认保管库。

          $args = ['item2' => 'item2',
                  'item3' => 'value3'];
          
              function function_name ($args) {
                  isset($args['item1']) ? $args['item1'] : 'default value';
              }
          

          【讨论】:

          • 您可以在 PHP 7+ 中使用$args['item1'] = $args['item1']??'default value'; 。如果 $args['item1'] 为 null 则 default value 字符串将分配给 $args['item1']
          【解决方案8】:
          <?php
          function info($name="George",$age=18) {
          echo "$name is $age years old.<br>";
          }
          info();     // prints default values(number of values = 2)
          info("Nick");   // changes first default argument from George to Nick
          info("Mark",17);    // changes both default arguments' values
          
          ?>
          

          【讨论】:

            【解决方案9】:

            我最近遇到了这个问题,发现了这个问题和答案。虽然上述问题有效,但问题是它们没有向支持它的 IDE(如 PHPStorm)显示默认值。

            如果您使用null,如果您将其留空,您将不知道该值是什么。

            我更喜欢的解决方案是将默认值也放在函数定义中:

            protected function baseItemQuery(BoolQuery $boolQuery, $limit=1000, $sort = [], $offset = 0, $remove_dead=true)
            {
                if ($limit===null) $limit =1000;
                if ($sort===null) $sort = [];
                if ($offset===null) $offset = 0;
                ...
            

            唯一的区别是我需要确保它们是相同的 - 但我认为这是为额外的清晰度付出的小代价。

            【讨论】:

            • ReflectionFunction 的内部使用会节省成本!
            • 关于 IDE - 很容易获得函数定义 - ctrl + 单击 PHPStorm 中的函数或类名。只要您坚持一些将默认值作为第一行的常用方法,就很容易适应。话虽如此,没有没有缺点的解决方案 - 它们都有自己的缺点。
            【解决方案10】:

            其实是可以的:

            foo( 'blah', (new ReflectionFunction('foo'))->getParameters()[1]->getDefaultValue(), 'test');
            

            您是否愿意这样做是另一回事:)


            更新:

            避免使用此解决方案的原因是:

            • 它(可以说)很丑
            • 它有明显的开销。
            • 作为其他答案的证明,还有其他选择

            但它实际上在以下情况下很有用:

            • 你不想/不能改变原来的功能。

            • 你可以改变函数但是:

              • 使用null(或等效)不是一种选择(请参阅 DiegoDD 的comment
              • 您不想使用联想或func_num_args()
              • 你的生命取决于拯救几个 LOC

            关于性能,very simple test 表明使用反射 API 获取默认参数会使函数调用慢 25 倍,而它仍然需要 不到 1 微秒时间>。你应该知道你是否可以忍受。

            当然,如果你要循环使用,你应该事先获取默认值。

            【讨论】:

            • 不想这样做的原因是什么?看起来很方便。
            • 可爱但不幸的是不适用于内部函数:Uncaught ReflectionException: Cannot determine default value for internal functions 这有点愚蠢,因为文档读取:“获取任何用户定义或内部参数的默认值函数或方法"
            【解决方案11】:
            function image(array $img)
            {
                $defaults = array(
                    'src'    => 'cow.png',
                    'alt'    => 'milk factory',
                    'height' => 100,
                    'width'  => 50
                );
            
                $img = array_merge($defaults, $img);
                /* ... */
            }
            

            【讨论】:

            • 我错过了什么吗?这似乎根本没有回答这个问题,但它有 7 个赞成票。也许添加一些解释会有所帮助?
            • @SeantheBean: 是的,解释会更好,但他的观点是:由于 PHP 没有命名参数,所以使用关联数组作为其单个参数。
            • 使用问题中给出的示例更有帮助,例如$defaults = [ 'x' =&gt; 'some value', 'y' =&gt; 'some other value'];
            【解决方案12】:

            你还可以检查你是否有一个空字符串作为参数,这样你就可以这样调用:

            foo('blah', "", 'non-default y value', null);

            函数下方:

            function foo($blah, $x = null, $y = null, $z = null) {
                if (null === $x || "" === $x) {
                    $x = "some value";
                }
            
                if (null === $y || "" === $y) {
                    $y = "some other value";
                }
            
                if (null === $z || "" === $z) {
                    $z = "some other value";
                }
            
                code here!
            
            }
            

            不管你填null还是"",你都会得到同样的结果。

            【讨论】:

              【解决方案13】:

              可选参数仅在函数调用结束时起作用。如果不指定 $x,就无法在函数中指定 $y 的值。某些语言通过命名参数(例如 VB/C#)支持这一点,但 PHP 不支持。

              如果您对参数使用关联数组而不是参数,则可以模拟这一点——即

              function foo(array $args = array()) {
                  $x = !isset($args['x']) ? 'default x value' : $args['x'];
                  $y = !isset($args['y']) ? 'default y value' : $args['y'];
              
                  ...
              }
              

              然后像这样调用函数:

              foo(array('y' => 'my value'));
              

              【讨论】:

              • 您的条件确实应该是isset($args['x']),因为它当前会将空字符串、空数组或false 替换为默认值。
              • @TimCooper 大声笑我根据您的第一条评论进行更改,它应该是“=== null”,然后将我的答案更改为使用 isset,然后回来看到您更改了您的评论也。 :D
              • 啊,废话!我根本不喜欢那样...哦,好吧,你不能拥有一切。我想我将不得不多考虑一下我的默认参数的顺序。谢谢!
              • 不幸的是,您将无法使用此答案分配 null。这是一个更好的选择:$x = !array_key_exists('x',$args) ? 'default x value' : $args['x'];
              【解决方案14】:

              这是一种情况,当对象更好时 - 因为您可以将对象设置为保存 x 和 y ,设置默认值等。

              使用数组的方法接近于创建对象(实际上,对象是一堆参数和函数,它们将作用于对象,而带数组的函数将作用于一些 ov 参数)

              当然,您总是可以做一些技巧来将 null 或类似的东西设置为默认值

              【讨论】:

                【解决方案15】:

                您不能直接执行此操作,但稍微修改一下代码就可以进行模拟。

                function foo($blah, $x = false, $y = false) {
                  if (!$x) $x = "some value";
                  if (!$y) $y = "some other value";
                
                  // code
                }
                

                【讨论】:

                • 这可行,但我发现在这种情况下使用null 在概念上比false 更简洁。
                • 添加到 TriG 的评论中,false 是比null 更冒险的选择,因为 php 是一种非严格语言。 !$x 将适用于几个不同的输入,这可能会让程序员感到惊讶:0''。而对于null,您可以使用!isset 作为更严格的测试。
                【解决方案16】:

                我知道的唯一方法是省略参数。省略参数的唯一方法是重新排列参数列表,以便您要省略的参数位于您 HAVE 要设置的参数之后。例如:

                function foo($blah, $y = "some other value", $x = "some value")
                

                然后你可以像这样调用 foo:

                foo("blah", "test");
                

                这将导致:

                $blah = "blah";
                $y = "test";
                $x = "some value";
                

                【讨论】:

                  猜你喜欢
                  • 1970-01-01
                  • 2012-11-20
                  • 1970-01-01
                  • 2020-08-28
                  • 1970-01-01
                  • 2011-11-18
                  • 2013-04-26
                  • 1970-01-01
                  相关资源
                  最近更新 更多