在函数中使用默认参数

221

我对 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");   

我完全期望像这样的东西能够起作用。但它根本不像我预期的那样工作。似乎无论我做什么,每次调用函数我都必须手动输入默认参数。而且我一定漏掉了某些显而易见的东西。


95
你可能没有错过什么,PHP开发者可能错过了这个问题。 - Matti Virkkunen
3
最近看到的最好的问题之一!你尝试过什么都不传递吗? foo("blah", , "test"); - Madara's Ghost
3
规则是默认参数只能跟在其他参数后面。也就是说,你可以有类似于 foo("blah"),而 x 和 y 是默认值的情况,或者有类似于 foo("Blah","xyz"),其中 y 是默认值。 - Mouse Food
@ThinkingMonkey,当我将null或""设置为第二个默认值时,它会用null替换默认值...至于foo("blah", $x, $y = "test"),我得到了一个未定义的变量$x。如果我做类似foo("blah", $y = "test")的事情,它会用test替换$x的值,而不是$y。 - renosis
3
期望的行为。请参见“默认参数值”部分:https://www.php.net/manual/en/functions.arguments.php -- null 值和空字符串都可以作为函数的实际参数。 - jmdavalos
显示剩余5条评论
16个回答

221
我建议将函数声明更改为以下形式,这样你可以做你想做的事情:
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仍然获取其默认值。

通过此方法,传递null值意味着您希望为一个参数使用默认值,而当您想要覆盖其后面的一个参数的默认值时,则需要显式地传递该参数。

如其他答案所述:

默认参数只能作为函数的最后一个参数使用。 如果您想在函数定义中声明默认值,则无法省略一个参数并覆盖其后的参数。

如果我有一个可以接受不同数量和类型参数的方法,我经常会声明类似于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
@zloctb 这是一个干净易读的空值检查。看起来这是开发者的个人偏好。无论是 $x = isset($x) ? $x :'默认值'; 还是 $x = is_null($x) ?'默认值':$x; 都可以像这个解决方案一样工作。这是关于可读性和维护的个人选择。 - DeveloperWeeks
10
为了方便未来的读者,需要澄清一点。第一种解决方案显然使得无法将实际的 null 值分配给参数,因为每次都会分配默认值。即使您有另一个特殊值表示想要 null 的情况(例如当 $x == "use_null" 时使 $x = null),那么您也无法将这样的特殊值分配为参数的字面值(在本例中,就是字符串 "use_null")。因此,请确保使用唯一且永远不希望使用的“键”来指定默认值(并且您希望 null 成为有效选项)。 - DiegoDD

45

可选参数仅适用于函数调用的末尾。如果不指定$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'));

1
你的条件应该是 isset($args['x']),因为它目前会用默认值替换空字符串、空数组或 false - user142162
@TimCooper 哈哈,我根据你的第一条评论去修改了代码,将其改为“=== null”,然后又回来看到你也修改了评论。 :D - kitti
啊,该死!我一点也不喜欢这个... 哦,好吧,你不能拥有一切。我想我必须更加考虑我的默认参数的顺序了。谢谢! - renosis
1
很遗憾,您不能使用此答案将null赋值。 这是更好的选择:$x = !array_key_exists('x',$args) ? 'default x value' : $args['x']; - Frank Forte

30

实际上是可能的:

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

你是否想这么做是另一回事 :)


更新:

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

  • 它(可以说)很丑陋
  • 它有一个明显的开销。
  • 正如其他答案所证明的,存在替代方案

但实际上,在以下情况下它可能会有用:

  • 你不想/不能更改原始函数。

  • 你可以更改该函数,但是:

    • 使用null(或等效项)不是选项(请参见DiegoDD的评论
    • 你不想使用关联数组或者func_num_args()
    • 你的生命取决于节省几行代码

关于性能,一个非常简单的测试显示,使用反射API获取默认参数使函数调用慢25倍,而仍然需要不到一微秒。你需要自己决定是否能够接受这个。

当然,如果你打算在循环中使用它,应该事先获取默认值。


1
不想做这件事的原因是什么?看起来非常方便。 - Bryan
可爱但不幸的是,它不能在内部函数上工作:Uncaught ReflectionException: Cannot determine default value for internal functions,这有点愚蠢,因为文档中写道:“获取任何用户定义或内部函数或方法的参数的默认值”。 - But those new buttons though..
啊,没错。PHP之美。 - Obed Parlapiano

10
function image(array $img)
{
    $defaults = array(
        'src'    => 'cow.png',
        'alt'    => 'milk factory',
        'height' => 100,
        'width'  => 50
    );

    $img = array_merge($defaults, $img);
    /* ... */
}

2
我有什么遗漏吗?这似乎根本没有回答问题,但它有7个赞。也许加一点解释会有帮助? - Sean the Bean
3
@SeantheBean的意思是:虽然PHP没有命名参数,但可以使用一个关联数组作为其唯一参数。他认为最好有解释说明。 - MestreLion
2
最好使用问题中给出的示例,例如 $defaults = [ 'x' => 'some value', 'y' => 'some other value']; - Frank Forte

8

使用命名参数的 PHP 8

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

用法-1:

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);

关于特性和文档

命名参数允许基于参数名称而不是参数位置传递参数给函数。这使得参数的含义自我说明,使参数的顺序无关,并且允许任意跳过默认值

IDE支持

Netbeans 12.3 完全 支持 PHP 8.0 语法,包括此功能,但不支持命名参数的代码补全。


5

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

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

然后你可以这样调用 foo:
foo("blah", "test");

这将导致:
$blah = "blah";
$y = "test";
$x = "some value";

4
在PHP 8中,我们可以使用命名参数来解决这个问题。因此,我们可以解决原问题的描述者所提出的问题:“如果我想为$x使用默认参数,并为$y设置不同的参数,该怎么办?” 代码示例:
foo(blah: "blah", y: "test");

参考文献:https://wiki.php.net/rfc/named_params(特别是“Skipping defaults”部分)


3

我最近遇到了这个问题,并找到了这个问题和答案。虽然上面的问题可以解决,但是问题在于它们不会向支持它的IDE(如PHPStorm)显示默认值。

enter image description here

如果使用 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在自身上的内部使用将会节省成本! - SubjectDelta
关于IDE - 在PHPStorm中,很容易通过ctrl +单击函数或类名来获取函数定义。只要您坚持将默认值放在第一行或几行,就很容易适应。话虽如此,没有不带缺点的解决方案 - 它们都有自己的缺点。 - ClarkeyBoy

2

直接做这个是不可能的,但稍作代码调整就可以模拟实现。

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

  // code
}

6
可以这样做,但我认为在这种情况下使用null的概念更加清晰,比使用false更合适。 - TRiG
补充TriG的评论,falsenull 更加冒险,因为PHP是一种非严格语言。对于几个可能令程序员感到惊讶的不同输入,!$x将会是真的:0''。而使用 null 可以更严格地测试,可以使用 !isset - ToolmakerSteve

1
您还可以检查参数是否为空字符串,这样您就可以像这样调用:

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还是"",结果都将相同。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接