在PHP中有没有指定可选参数值的方法?

24

假设我有一个 PHP 函数 foo:

function foo($firstName = 'john', $lastName = 'doe') {
    echo $firstName . " " . $lastName;
}
// foo(); --> john doe
有没有办法指定只有第二个可选参数?
例如:
foo($lastName='smith'); // output: john smith
13个回答

34

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

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

虽然没有隐式支持,但是有比接受的答案建议更好的解决方法。请参见Walf的解决方案。如果需要更多细节,请查看我的解决方案(我发誓我是独立得出的!) - DJDave
Php8 现在支持命名参数。 - Anther

23

一种变化的数组技巧,允许更轻松地设置默认值:

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

2
对于关联数组,$arguments += $defaults; 更加简洁。 - Walf

9
您可以稍微重构一下您的代码:

您可以对您的代码进行简单的重构:

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

6

如果您有多个可选参数,一种解决方案是传递一个单一的参数,它是一个哈希数组:

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

当然,在这个解决方案中,没有对哈希数组的字段是否存在或拼写是否正确进行验证。验证工作全部由您来完成。

3

不行。通常的做法是使用一些启发式算法来确定隐含的参数,例如字符串长度、输入类型等。

一般来说,你应该按照最需要到最不需要的顺序编写函数参数。


只有当参数像jQuery一样明确地被类型定义时,启发式方法才能可靠地工作。其他方法,如字符串长度,则是一个等待发生的意外。 - Walf

3

您想要的方式:不行。

您可以使用一些特殊标记,如NULL来表示值未提供:

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

foo(null, 'smith');

3

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

在 PHP 8 之前:

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

在 PHP 8 之后:

htmlspecialchars($string, double_encode: false);

参考资料


2
当我2.5年前开始使用PHP时,希望这种解决方案已经在SO上了。它在我创建的示例中运行良好,并且我认为它应该是非常可扩展的。相较于以前提出的解决方案,它具有以下优点:
(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'));

结果:

狗站在驴子上。

猫坐在驴子上。

一只狗站在地席上。


那个解决方案在1.5年前就已经在SO上了。 - Walf
已确认 - 我两周前看到了它并给了你一个点赞。我承认在发布我的解决方案之前没有重新阅读所有的“解决方案”。如果可以的话,我会添加一条评论到被接受的答案中,指向你的解决方案。 - DJDave

1

参数需要按位置顺序传递,不能跳过某个参数;您必须提供默认参数值才能跳过它。可以说这违背了您尝试实现的目的。

如果不重新编写函数以不同方式接受参数,则可以通过调用时间来解决此问题:

$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']);

1
这里提到了一些“选项”风格的实现方式。到目前为止,如果您打算将它们用作标准,则没有一个非常可靠。请尝试这种模式:
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),并防止创建任何没有默认值的变量。这是为了防止任何人意外地覆盖函数内的其他参数或其他变量。

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