PHP 是否允许命名参数,以便可以在函数调用中省略可选参数?

发布于 2024-08-03 14:29:59 字数 227 浏览 4 评论 0原文

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

像这样的东西:

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


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

Is it possible in PHP to specify a named optional parameter when calling a function/method, skipping the ones you don't want to specify (like in python)?

Something like:

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


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

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(15

不顾 2024-08-10 14:29:59

PHP 8.0 添加了对命名参数的支持,并接受 RFC

命名参数的传递方式是在值前加上参数名称前缀,后跟冒号。允许使用保留关键字作为参数名称。参数名称必须是标识符,不允许动态指定。

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

foo(timeout: 3);

在 PHP 8 之前,PHP 中不可能使用命名参数。从技术上讲,当您调用 foo($timeout=3) 时,它首先评估 $timeout=3,结果为 3 并将其传递为foo() 的第一个参数。 PHP 强制执行参数顺序,因此类似的调用需要为 foo("", "", $timeout=3)。您还有其他两个选择:

  • 让您的函数采用数组作为参数并检查数组键。我个人觉得这很丑,但它有效并且可读。优点是简单,以后很容易添加新参数。缺点是您的函数的自记录性较差,并且您不会从 IDE 获得太多帮助(自动完成、快速函数参数查找等)。
  • 设置不带参数的函数,并使用 向 PHP 询问参数func_get_args() 或使用 ... PHP 5.6+ 中的可变长度参数功能。根据参数的数量,您可以决定如何处理每个参数。许多 JQuery 函数都会执行类似的操作。这很简单,但对于那些调用函数的人来说可能会感到困惑,因为它也不是自记录的。而且你的论点仍然没有被命名。

PHP 8.0 added support for named arguments with the acceptance of an RFC.

Named arguments are passed by prefixing the value with the parameter name followed by a colon. Using reserved keywords as parameter names is allowed. The parameter name must be an identifier, specifying dynamically is not allowed.

E.g. to pass just the 3rd optional parameter in your example:

foo(timeout: 3);

Prior to PHP 8 named parameters were not possible in PHP. Technically when you call foo($timeout=3) it is evaluating $timeout=3 first, with a result of 3 and passing that as the first parameter to foo(). And PHP enforces parameter order, so the comparable call would need to be foo("", "", $timeout=3). You have two other options:

  • Have your function take an array as parameter and check the array keys. I personally find this ugly but it works and is readable. Upside is simplicity and it's easy to add new parameters later. Downside is your function is less self-documenting and you won't get much help from IDEs (autocomplete, quick function param lookups, etc.).
  • Set up the function with no parameters and ask PHP for the arguments using func_get_args() or use the ... variable length arguments feature in PHP 5.6+. Based on the number of parameters you can then decide how to treat each. A lot of JQuery functions do something like this. This is easy but can be confusing for those calling your functions because it's also not self-documenting. And your arguments are still not named.
萌吟 2024-08-10 14:29:59

不,这是不可能的(在 PHP 8.0 之前): 如果要传递第三个参数,则必须传递第二个参数。并且命名参数也是不可能的。

A "solution" would be to use only one parameter, an array, and always pass it... But don't always define everything in it.

例如:

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 将无法再提供任何提示...这很糟糕

所以我只会在非常特殊的情况下才使用它 - 例如对于具有大量可选参数的函数......

No, it is not possible (before PHP 8.0): if you want to pass the third parameter, you have to pass the second one. And named parameters are not possible either.

A "solution" would be to use only one parameter, an array, and always pass it... But don't always define everything in it.

For instance :

function foo($params) {
    var_dump($params);
}

And calling it this way : (Key / value array)

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

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

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

Will get you this output :

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)

But I don't really like this solution :

  • You will lose the phpdoc
  • Your IDE will not be able to provide any hint anymore... Which is bad

So I'd go with this only in very specific cases -- for functions with lots of optional parameters, for instance...

何以畏孤独 2024-08-10 14:29:59

PHP 8 于 2020 年 11 月 26 日发布,新增了一项名为 命名参数

在此主要版本中,“命名参数”(又名“命名参数”)为开发人员在调用本机和自定义函数时提供了一些非常酷的新技术。

现在可以使用第一个参数调用此问题中的自定义函数(因为它没有默认值),然后仅使用命名参数传递第三个参数,如下所示: (Demo)

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

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

请注意,第二个参数不需要在函数调用中声明,因为它定义了默认值——默认值会在函数体内自动使用。

这个新功能的优点之一是您不需要注意命名参数的顺序——它们的声明顺序是无关紧要的。 foo(c: "bar", a: "hello"); 的工作原理是一样的。具有“跳过”声明和编写声明性参数的能力将提高脚本的可读性。这个新功能的唯一缺点是函数调用会有点臃肿,但我(和许多其他人)认为好处超过了这种“成本”。

下面是一个本机函数省略 limit 参数、不按正常顺序写入参数并声明引用变量的示例。 (演示

echo preg_replace(
         subject: 'Hello 7',
         pattern: '/[a-z ]/',
         count: $counted,
         replacement: ''
     )
     . " & " . $counted;
// output: H7 & 5

有关此新功能的更多信息。您甚至可以使用关联数组将命名参数传递给函数,其中可以使用 spread/splat 运算符来解包数据!

(*注意声明引用变量时的细微差别。)(演示

$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

有关更多信息,这里有一些进一步解释此功能的线索以及一些常见的相关错误:(我与以下站点没有隶属关系)

PHP 8 was released on November 26, 2020 with a new feature called named arguments.

In this major version release, "named parameters" (aka "named arguments") afford developers some really cool new techniques when calling native and custom functions.

The custom function in this question can now be called with the first parameter (because there is no default for it) and then only the third parameter passed by using named parameters like this: (Demo)

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

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

Notice that the second parameter did not need to be declared in the function call because it has a default value defined -- the default value is automatically used within the function body.

Part of the beauty of this new feature is that you don't need to be careful about the order of your named parameters -- the order of their declaration is irrelevant. foo(c: "bar", a: "hello"); works just the same. Having the ability to "skip" declarations and write declarative parameters will improve the readability of your scripts. The only downside of this new feature is that there will be a little bit more bloat in the function calls, but I (and many others) think the benefits outweigh this "cost".

Here is an example of a native function omitting the limit parameter, writing the parameters out of their normal order, and declaring a reference variable. (Demo)

echo preg_replace(
         subject: 'Hello 7',
         pattern: '/[a-z ]/',
         count: $counted,
         replacement: ''
     )
     . " & " . $counted;
// output: H7 & 5

There is more to tell about this new feature. You can even use an associative array to pass the named parameters to the function where the spread/splat operator can be used to unpack the data!

(*notice the slight difference in declaring the reference variable.) (Demo)

$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

The same behavior can be enjoyed via call_user_func_array(). Demo using my first declared user function

call_user_func_array('foo', ['c' => 9, 'a' => 7]);
// output: 7&&9

For more information, here are a few leads that explain further about this feature and some common related errors: (I have no affiliation with the following sites)

清君侧 2024-08-10 14:29:59

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 函数将值复制到变量中,但前提是它们已经被定义。这可以防止导入您不知道的变量。

PHP did not directly support named parameters.

In PHP, you can use a fairly reasonable substitute. PHP’s array handling is particularly good, and you can use an associative array to pass an array of parameters, as follows:

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

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

Note the use of the ?? operator to allow a simple default if the input array element doesn’t exist. Before PHP 7, you would use ?: which is subtly different, but gives the same result in this case.

Not as slick, but named parameters have another important role: they allow you to work with a large number of options without an excessively long list of ordered parameters.

An alternative way of writing the function is to use the extract function:

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

Here the extract function copies the values into the variables, but only if they have already been defined. This prevents importing variables you didn’t know about.

赏烟花じ飞满天 2024-08-10 14:29:59

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

PHP 8 supports named arguments.

What does this mean?

Both native functions and your current code can use named parameters.

How does it work?

Let's say you have a function defined:

function foo($a = 10, $b = 20, $c = 30) {
    return $a * $b - $c;
}

you will now be able to call this function like so:

foo(a: 30, c: 10, b: 5);
foo(5, c: 60, b: 10);
foo(c: 30, b: 20, a: 10);
时光暖心i 2024-08-10 14:29:59

不,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       
}

查看实际效果

No, PHP cannot pass arguments by name.

If you have a function that takes a lot of arguments and all of them have default values you can consider making the function accept an array of arguments instead:

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       
}

See it in action.

明月夜 2024-08-10 14:29:59

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

命名参数从 PHP 8.0 开始实现

批准的 RFC:https://wiki.php.net/rfc/named_pa​​rams

Stitcher 博客:< a href="https://stitcher.io/blog/php-8-named-arguments" rel="nofollow noreferrer">https://stitcher.io/blog/php-8-named-arguments

Named Arguments are not supported in PHP 7.* or lower.

Named Arguments are implemented since PHP 8.0.

Approved RFC: https://wiki.php.net/rfc/named_params

Stitcher blog: https://stitcher.io/blog/php-8-named-arguments

泪意 2024-08-10 14:29:59

不,不是。

唯一可以做到这一点的方法是使用带有命名键的数组以及其他的数组。

No, it isn't.

The only way you can somewhat do that is by using arrays with named keys and what not.

三五鸿雁 2024-08-10 14:29:59

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

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

如果您愿意,这还可以让您在函数调用中进行严格的类型检查:

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

当然,您可能会为此付出额外的冗长代价设置 FooOptions 对象。不幸的是,没有完全免费的旅程。

You can keep the phpdoc and the ability to set defaults by passing an object instead of an array, e.g.

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

That also lets you do strict type checking in your function call, if you want to:

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

Of course, you might pay for that with extra verbosity setting up the FooOptions object. There's no totally-free ride, unfortunately.

彼岸花似海 2024-08-10 14:29:59

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

As of PHP 5.4 you have shorthand array syntax (not nessecary to specify arrays with cumbersome "array" and instead use "[]").

You can mimic named parameters in many ways, one good and simple way might be:

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;
}
暮年慕年 2024-08-10 14:29:59

有些人可能会说,它并不完全漂亮,但它确实有效。

class NamedArguments {

    static function init($args) {
        $assoc = reset($args);
        if (is_array($assoc)) {
            $diff = array_diff(array_keys($assoc), array_keys($args));
            if (empty($diff)) return $assoc;
            trigger_error('Invalid parameters: '.join(',',$diff), E_USER_ERROR);
        }
        return array();
    }

}

class Test {

    public static function foobar($required, $optional1 = '', $optional2 = '') {
        extract(NamedArguments::init(get_defined_vars()));
        printf("required: %s, optional1: %s, optional2: %s\n", $required, $optional1, $optional2);
    }

}

Test::foobar("required", "optional1", "optional2");
Test::foobar(array(
    'required' => 'required', 
    'optional1' => 'optional1', 
    'optional2' => 'optional2'
    ));

It's not exactly pretty, but it does the trick, some might say.

class NamedArguments {

    static function init($args) {
        $assoc = reset($args);
        if (is_array($assoc)) {
            $diff = array_diff(array_keys($assoc), array_keys($args));
            if (empty($diff)) return $assoc;
            trigger_error('Invalid parameters: '.join(',',$diff), E_USER_ERROR);
        }
        return array();
    }

}

class Test {

    public static function foobar($required, $optional1 = '', $optional2 = '') {
        extract(NamedArguments::init(get_defined_vars()));
        printf("required: %s, optional1: %s, optional2: %s\n", $required, $optional1, $optional2);
    }

}

Test::foobar("required", "optional1", "optional2");
Test::foobar(array(
    'required' => 'required', 
    'optional1' => 'optional1', 
    'optional2' => 'optional2'
    ));
秋叶绚丽 2024-08-10 14:29:59

通常你不能,但我认为有很多方法可以将命名参数传递给 PHP 函数。就我个人而言,我使用数组来传递定义,然后只调用我需要传递的内容:

class Test{
    public $a  = false;
    private $b = false;
    public $c  = false;
    public $d  = false;
    public $e  = false;
    public function _factory(){
        $args    = func_get_args();
        $args    = $args[0];
        $this->a = array_key_exists("a",$args) ? $args["a"] : 0;
        $this->b = array_key_exists("b",$args) ? $args["b"] : 0;
        $this->c = array_key_exists("c",$args) ? $args["c"] : 0;
        $this->d = array_key_exists("d",$args) ? $args["d"] : 0;
        $this->e = array_key_exists("e",$args) ? $args["e"] : 0;
    }
    public function show(){
        var_dump($this);
    }
}


$test = new Test();
$args["c"]=999;
$test->_factory($args);
$test->show();

此处的实时示例:

如果我必须传递 10 个参数 其中 3 个是我真正需要的数据,传递到函数中是不明智的,例如

return myfunction(false,false,10,false,false,"date",false,false,false,"desc");

使用我给出的方法,您可以将 10 个参数中的任何一个设置到数组中:

$arr['count']=10;
$arr['type']="date";
$arr['order']="desc";
return myfunction($arr);

我的博客中有一篇文章解释了这个过程的更多细节。

http://www.tbogard .com/2013/03/07/将命名参数传递给php中的函数

Normally you can't but I think there a lot of ways to pass named arguments to a PHP function. Personally I relay on the definition using arrays and just call what I need to pass:

class Test{
    public $a  = false;
    private $b = false;
    public $c  = false;
    public $d  = false;
    public $e  = false;
    public function _factory(){
        $args    = func_get_args();
        $args    = $args[0];
        $this->a = array_key_exists("a",$args) ? $args["a"] : 0;
        $this->b = array_key_exists("b",$args) ? $args["b"] : 0;
        $this->c = array_key_exists("c",$args) ? $args["c"] : 0;
        $this->d = array_key_exists("d",$args) ? $args["d"] : 0;
        $this->e = array_key_exists("e",$args) ? $args["e"] : 0;
    }
    public function show(){
        var_dump($this);
    }
}


$test = new Test();
$args["c"]=999;
$test->_factory($args);
$test->show();

live example here:
http://sandbox.onlinephpfunctions.com/code/d7f27c6e504737482d396cbd6cdf1cc118e8c1ff

If I have to pass 10 arguments, and 3 of them are the data I really need, is NOT EVEN SMART to pass into the function something like

return myfunction(false,false,10,false,false,"date",false,false,false,"desc");

With the approach I'm giving, you can setup any of the 10 arguments into an array:

$arr['count']=10;
$arr['type']="date";
$arr['order']="desc";
return myfunction($arr);

I have a post in my blog explaining this process in more details.

http://www.tbogard.com/2013/03/07/passing-named-arguments-to-a-function-in-php

阳光下的泡沫是彩色的 2024-08-10 14:29:59

这是我一直在使用的。函数定义采用一个可选数组参数,该参数指定可选命名参数:

function func($arg, $options = Array()) {
  $defaults = Array('foo' => 1.0,
                    'bar' => FALSE);
  $options = array_merge($default, $options);

  // Normal function body here.  Use $options['foo'] and
  // $options['bar'] to fetch named parameter values.
  ...
}

您通常可以在没有任何命名参数的情况下进行调用:

func("xyzzy")

要指定可选命名参数,请将其传递到可选数组中:

func("xyzzy", Array('foo' => 5.7))

Here's what I've been using. A function definition takes one optional array argument which specifies the optional named arguments:

function func($arg, $options = Array()) {
  $defaults = Array('foo' => 1.0,
                    'bar' => FALSE);
  $options = array_merge($default, $options);

  // Normal function body here.  Use $options['foo'] and
  // $options['bar'] to fetch named parameter values.
  ...
}

You can normally call without any named arguments:

func("xyzzy")

To specify an optional named argument, pass it in the optional array:

func("xyzzy", Array('foo' => 5.7))
等往事风中吹 2024-08-10 14:29:59

只需使用 Drupal 使用的关联数组模式即可。对于可选的默认参数,只需接受一个 $options 参数,它是一个关联数组。然后使用数组 + 运算符设置数组中缺少的键。

function foo ($a_required_parameter, $options = array()) {
    $options += array(
        'b' => '',
        'c' => '',
    );
    // whatever
}

foo('a', array('c' => 'c’s value')); // No need to pass b when specifying c.

Just use the associative array pattern Drupal uses. For optional defaulted arguments, just accept an $options argument which is an associative array. Then use the array + operator to set any missing keys in the array.

function foo ($a_required_parameter, $options = array()) {
    $options += array(
        'b' => '',
        'c' => '',
    );
    // whatever
}

foo('a', array('c' => 'c’s value')); // No need to pass b when specifying c.
小姐丶请自重 2024-08-10 14:29:59

如果你真的想要的话,可以尝试一下反射。
并用 null 跳过。

function getDefaultValueByNull($fn, $inputs) {
    $ref = new ReflectionFunction($fn);
    
    $args = array_map(function($p) {
        return [
            $p->getName(),
            $p->isDefaultValueAvailable() ? $p->getDefaultValue() : NULL,
        ];
    }, $ref->getParameters());

    foreach($inputs as $i=>$val) { if ($val!==NULL) $args[$i][1] = $val; }
    
    return array_column($args, 1, 0);
}

function sum($a=9, $b) {
    extract(getDefaultValueByNull(__FUNCTION__, func_get_args()));
    return $a+$b;
}
echo sum(NULL, 1); // 10

If you really really want, try the reflection.
And skip with null.

function getDefaultValueByNull($fn, $inputs) {
    $ref = new ReflectionFunction($fn);
    
    $args = array_map(function($p) {
        return [
            $p->getName(),
            $p->isDefaultValueAvailable() ? $p->getDefaultValue() : NULL,
        ];
    }, $ref->getParameters());

    foreach($inputs as $i=>$val) { if ($val!==NULL) $args[$i][1] = $val; }
    
    return array_column($args, 1, 0);
}

function sum($a=9, $b) {
    extract(getDefaultValueByNull(__FUNCTION__, func_get_args()));
    return $a+$b;
}
echo sum(NULL, 1); // 10
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文