PHP允许使用命名参数吗?这样在函数调用时可以省略可选参数吗?

39

在 PHP 中,是否可以像 Python 一样,在调用函数/方法时指定命名可选参数,跳过您不想指定的参数?

例如:

function foo($a, $b = '', $c = '') {
    // whatever
}


foo("hello", $c="bar"); // we want $b as the default, but specify $c

2
实际上在你的代码示例中,$c="bar"bar赋值给了调用者作用域中的$c(而不是在被调用的函数foo中),然后将分配的值作为第二个参数传递给foo(),这将作为本地$b变量接收。 - Petruza
1
命名参数仅在PHP 8.0及以上版本中可用。https://dev59.com/DWkv5IYBdhLWcg3wbgbQ#64072408 - Jsowa
21个回答

50

PHP 8.0增加了对命名参数的支持,通过接受一个RFC来实现。

使用参数名称前缀加冒号将命名参数传递给函数。允许使用保留关键字作为参数名称。参数名称必须是标识符,不允许动态指定。

例如,在您的示例中仅传递第三个可选参数:

foo(timeout: 3);
在PHP 8之前,PHP不支持命名参数。实际上,当你调用foo($timeout=3)时,它首先计算$timeout=3,结果为3,并将其作为第一个参数传递给foo()。同时PHP强制参数顺序,因此相似的调用需要是foo("", "", $timeout=3)。你有其他两个选项:
  • 让你的函数把一个数组作为参数,并检查数组的键。我个人认为这样做很丑陋,但它能工作并且可读性强。优点是简单易懂,而且以后很容易添加新的参数。缺点是你的函数不够自说明,而且你不会从IDE(自动完成、快速函数参数查找等)中得到很多帮助。
  • 设置没有参数的函数,并使用func_get_args()获取PHP传入的参数或者在PHP 5.6+中使用...可变长参数特性。根据参数数量,你可以决定如何处理每个参数。很多jQuery函数都采用这种方式。这是一种简单的方法,但对于调用你的函数的人来说可能会很困惑,因为它也不够自说明。而且你的参数仍然没有名称。

从美学角度来看,赞成这种分配技术。只有一个缺点,它看起来很像命名参数(咳咳,这就是重点吧?)。但这可能会给人留下顺序不再重要的印象,而实际上它确实很重要。我想其中一部分并不是那么容易理解的。所以为了纠正你的答案,从技术上讲,你只能这样做:foo( '', '', $timeout = 3 ); - user1115652
ps:还有一些其他小缺点:性能(很少会成为问题)和潜在的覆盖现有变量。总的来说,与 foo('','',3/*timeout*/); 相比的优势是值得商榷的。 - user1115652
你应该为PHP8更新这个。已经支持命名参数了。 - Jacob Thomason
@JacobThomason 已更新,谢谢。PHP8发布后我会再次更新它。 - Matt S
FYI,使用 ... 解包关联数组(即 foo(...["timeout"=>3]))也是一种选择。它可能在8.0之前就已经存在了,但我不确定。不知道是否值得一提。 - Joe Boris

25
不,这是不可能的(在 PHP 8.0 之前):如果你想传递第三个参数,你必须传递第二个参数。而且命名参数也不可能。
例如:
function foo($params) {
    var_dump($params);
}

并以这种方式调用:(键/值数组)

foo([
    'a' => 'hello',
]);

foo([
    'a' => 'hello',
    'c' => 'glop',
]);

foo([
    'a' => 'hello',
    'test' => 'another one',
]);

将会得到以下输出:

array
  'a' => string 'hello' (length=5)

array
  'a' => string 'hello' (length=5)
  'c' => string 'glop' (length=4)

array
  'a' => string 'hello' (length=5)
  'test' => string 'another one' (length=11)

但我并不是很喜欢这个解决方案:

  • 你将失去 phpdoc
  • 你的 IDE 将不再能够提供任何提示... 这很糟糕

因此,我只会在非常特定的情况下使用它——例如对于有很多可选参数的函数...


抱歉有点跑题,你知道有哪些编辑器可以为PHP提供代码提示吗?有没有适用于MacOS的呢?谢谢! - Petruza
代码提示?我会说所有的IDE都有(NetBeans,Eclipse PDT,PHPStorm,...);不知道Mac怎么样,但这些可能在Mac上也能用。 - Pascal MARTIN
@Petruza 如果你还没有找到的话,可以试试适用于MacOS的Coda。它对PHP的提示做得很好。 - EFC
1
PHP 8.0现在支持命名参数。https://dev59.com/N2Ik5IYBdhLWcg3wl_PE#19127007 - Joshua

19

PHP 8于2020年11月26日发布,带来了一个名为命名参数的新功能。

在这个重大版本发布中,"命名参数"(又称为"命名参数")为开发人员提供了一些非常酷的新技术,用于调用原生和自定义函数。

在这个问题中的自定义函数现在可以通过使用命名参数来调用第一个参数(因为它没有默认值),然后只传递第三个参数,就像这样:(演示

function foo($a, $b = '', $c = '') {
    echo $a . '&' . $b . '&' . $c;
}

foo("hello", c: "bar"); 
// output: hello&&bar

注意,函数调用中的第二个参数不需要在函数调用中声明,因为它有一个默认值定义 - 默认值会自动在函数体内使用。
这个新特性的一部分美妙之处在于,您不需要担心命名参数的顺序 - 它们的声明顺序是无关紧要的。foo(c: "bar", a: "hello"); 也能正常工作。具备“跳过”声明和编写声明性参数的能力将提高脚本的可读性。这个新特性唯一的缺点是函数调用中会有一些冗余,但我(以及许多其他人)认为其好处超过了这个“代价”。
以下是一个省略了limit参数、按照非正常顺序编写参数并声明引用变量的本地函数示例(Demo)。
echo preg_replace(
         subject: 'Hello 7',
         pattern: '/[a-z ]/',
         count: $counted,
         replacement: ''
     )
     . " & " . $counted;
// output: H7 & 5

关于这个新功能还有更多要说的。你甚至可以使用关联数组将命名参数传递给函数,其中可以使用展开运算符来解包数据!
(*注意在声明引用变量时的细微差别。)(演示
$params = [
    'subject' => 'Hello 7',  // normally third parameter
    'pattern' => '/[a-z ]/', // normally first parameter
    // 'limit'               // normally fourth parameter, omitted for this demonstration; the default -1 will be used
    'count' => &$counted,    // normally fifth parameter
    //         ^-- don't forget to make it modifiable!
    'replacement' => '',     // normally second parameter
];
echo preg_replace(...$params) . " & " . $counted;
// same output as the previous snippet

通过call_user_func_array()函数,可以享受相同的行为。使用我第一个声明的用户函数的演示
call_user_func_array('foo', ['c' => 9, 'a' => 7]);
// output: 7&&9

点击这里获取更多信息,以下是一些进一步解释这个功能以及一些常见相关错误的参考资料:(我与下列网站无任何关联)

9

PHP并不直接支持命名参数。

在PHP中,你可以使用一个相当合理的替代方法。PHP的数组处理非常好,你可以使用关联数组来传递参数数组,如下所示:

function foo($parms=[]) {
    $username = $parms['username'] ?? '…';
    $password = $parms['password'] ?? '…';
    $timeout  = $parms['timeout'] ?? '…';
}

foo(['timeout'=>10]);

注意使用`??`运算符,以便在输入数组元素不存在时提供一个简单的默认值。在PHP 7之前,您将使用`?:`,这在细微之处有所不同,但在这种情况下得到相同的结果。
命名参数虽然不够简洁,但具有另一个重要作用:它们允许您在没有过长的有序参数列表的情况下处理大量选项。
编写函数的另一种替代方式是使用`extract`函数:
function foo($parms=[]) {
    $username = '…';
    $password = '…';
    $timeout = '…';
    extract($parms,EXTR_IF_EXISTS);
}

这里的extract函数将值复制到变量中,但只有在它们已经被定义的情况下才会执行。这样可以防止导入你不知道的变量。

8
PHP 8 支持命名参数。
这是什么意思?
无论是原生函数还是您当前的代码都可以使用命名参数。
它是如何工作的?
假设您定义了一个函数:
function foo($a = 10, $b = 20, $c = 30) {
    return $a * $b - $c;
}

现在你可以这样调用这个函数了:
foo(a: 30, c: 10, b: 5);
foo(5, c: 60, b: 10);
foo(c: 30, b: 20, a: 10);

没用,谢谢。// 公共函数getPaginatedRecords($queryObject = '', $currentPage = 1, $limit = 10){}. // $this->getPaginatedRecords(queryObject: $user_data, currentPage: 1, limit: 10); - Kamlesh

7

不可以,PHP不能通过名称传递参数。

如果你有一个需要很多参数并且它们都有默认值的函数,你可以考虑让该函数接受一个参数数组:

function test (array $args) {
    $defaults = array('a' => '', 'b' => '', 'c' => '');
    $args = array_merge($defaults, array_intersect_key($args, $defaults));

    list($a, $b, $c) = array_values($args);
    // an alternative to list(): extract($args);

    // you can now use $a, $b, $c       
}

在此处查看实际操作.


6

在 PHP 7.* 或更低版本中不支持命名参数。

但是它将在 PHP 8 中实现 https://wiki.php.net/rfc/named_params

RFC 已经获得批准。 命名参数将在 PHP 8 中被实现


3
不,不是这样的。 唯一的方法是使用具有命名键等的数组。

1
CakePHP -至少-广泛使用这种模式。 - Adriano Varoli Piazza

2

您可以通过传递一个对象而不是数组来保留phpdoc和设置默认值的能力,例如:

class FooOptions {
  $opt1 = 'x';
  $opt2 = 'y';
  /* etc */
};

如果您想要,在函数调用中也可以进行严格的类型检查:

function foo (FooOptions $opts) {
  ...
}

当然,你可能需要付出额外的冗长来设置FooOptions对象。不幸的是,没有完全免费的午餐。

1
自PHP 5.4起,您可以使用简写数组语法(无需使用笨重的“array”指定数组,而是使用“[]”)。
您可以以许多方式模拟命名参数,其中一种好的简单方法可能是:
bar('one', ['a1' => 'two', 'bar' => 'three', 'foo' => 'four']);
// output: twothreefour

function bar ($a1, $kwargs = ['bar' => null, 'foo' => null]) {
    extract($kwargs);
    echo $a1;
    echo $bar;
    echo $foo;
}

3
但是如果我调用bar('one', ['x' => 'blah']);$bar不是未定义吗?即使在这种情况下,extract()似乎是危险和不必要的... - binki
此答案在函数调用时省略了数组元素时会生成警告,并且不能按预期工作。https://3v4l.org/ZnnCM extract() 函数无法填充所需的变量到作用域中。https://3v4l.org/UdkAq - mickmackusa

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