33

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

就像是:

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


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

17 回答 17

22

不,这是不可能的:如果你想传递第三个参数,你必须传递第二个。命名参数也不可能。


“解决方案”是只使用一个参数,一个数组,并始终传递它……但不要总是定义其中的所有内容。

例如 :

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

所以我只会在非常特殊的情况下使用这个——例如,对于具有很多可选参数的函数......

于 2009-08-27T18:29:32.603 回答
12

PHP 8 于 2020 年 11 月 26 日发布,具有名为命名参数的新功能。

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

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

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

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

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

这个新特性的部分优点在于您不需要注意命名参数的顺序——它们的声明顺序无关紧要。foo(c:“酒吧”,a:“你好”);工作原理一样。能够“跳过”声明并编写声明性参数将提高脚本的可读性。这个新功能的唯一缺点是函数调用会有点膨胀,但我(和许多其他人)认为好处超过了这个“成本”。

这是一个省略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

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

于 2020-11-25T01:43:50.760 回答
9

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

看到它在行动

于 2012-05-23T10:12:25.530 回答
3

不,不是。

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

于 2009-08-27T18:30:27.017 回答
2

PHP 5.4开始,您拥有速记数组语法(不必使用繁琐的“数组”来指定数组,而是使用“[]”)。

您可以通过多种方式模仿命名参数,一种好的简单方法可能是:

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;
}
于 2015-03-26T15:42:39.573 回答
1

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

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'
    ));
于 2009-08-30T14:49:18.830 回答
1

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

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

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

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

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

于 2011-12-13T17:30:51.670 回答
1

通常你不能,但我认为有很多方法可以将命名参数传递给 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();

现场示例:http: //sandbox.onlinephpfunctions.com/code/d7f27c6e504737482d396cbd6cdf1cc118e8c1ff

如果我必须传递 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/passing-named-arguments-to-a-function-in-php

于 2014-03-26T20:37:17.400 回答
0

对于 PHP,参数的顺序很重要。您不能不恰当地指定特定参数,但是,您可以通过传递 NULL 来跳过参数,只要您不介意函数中的值具有 NULL 值。

foo("hello", NULL, "bar");
于 2009-08-27T18:29:10.920 回答
0

如果你真的很想,试试反射。并跳过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
于 2021-03-11T02:41:20.933 回答
-1

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

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))
于 2010-11-22T06:44:15.417 回答
-1

不,真的。您可以使用一些替代方案。

test(null,null,"hello")

或者传递一个数组:

test(array('c' => "hello"));

那么,函数可以是:

function test($array) { 
    $c = isset($array[c]) ? $array[c] : '';
}

或者在两者之间添加一个函数,但我不建议这样做:

function ctest($c) { test('','',$c); }
于 2012-05-23T10:13:54.787 回答
-1

我不这么认为...如果您需要调用具有 3 个参数的substr函数,并且想要设置$length而不设置 $start,您将被迫这样做。

substr($str,0,10);

覆盖它的一个好方法是始终使用数组作为参数

于 2012-05-23T10:15:20.523 回答
-1

简而言之,有时是的,通过使用反射和类型变量。但是我认为这可能不是你所追求的。

解决您的问题的更好方法可能是传入 3 个参数,因为函数自己处理函数中缺少的参数

<?php  
   function test(array $params)
   {
     //Check for nulls etc etc
     $a = $params['a'];
     $b = $params['b'];
     ...etc etc
   }
于 2012-05-23T10:19:32.717 回答
-1

你不能用python的方式来做。Anway,您可以传递一个关联数组,然后按名称使用数组条目:

function test ($args=array('a'=>'','b'=>'','c'=>''))
{
    // do something
}

test(array('c'=>'Hello'));

这不会减少输入,但至少它更具描述性,在调用中参数的名称可见且可读。

于 2012-05-23T10:19:41.140 回答
-1

这是一个解决方法:

function set_param_defaults($params) {
  foreach($params['default_values'] as $arg_name => $arg_value) {
    if (!isset($params[$arg_name])) {
      $params[$arg_name] = $arg_value;
    }
  }

  return $params;
}

function foo($z, $x = null, $y = null) {
  $default_values = ['x' => 'default value for x', 'y' => 'default value for y'];
  $params = set_param_defaults(get_defined_vars());

  print "$z\n";
  print $params['x'] . "\n";
  print $params['y'] . "\n";
}

foo('set z value', null, 'set y value');
print "\n";
foo('set z value', 'set x value');

或者: 我个人会采用这种方法。

function foo($z, $x_y) {
  $x_y += ['x' => 'default value for x', 'y' => 'default value for y'];

  print "$z\n";
  print $x_y['x'] . "\n";
  print $x_y['y'] . "\n";
}

foo('set z value', ['y' => 'set y value']);
print "\n";
foo('set z value', ['x' => 'set x value']);

两个示例的打印输出。

第一次调用:

  • 设置 z 值
  • x 的默认值
  • 设置 y 值

第二次调用:

  • 设置 z 值
  • 设置 x 值
  • y 的默认值
于 2015-06-15T19:43:57.953 回答
-1

只需使用 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.
于 2016-02-28T05:25:37.140 回答