PHP函数中的可选参数不考虑顺序

6
在PHP中有没有一种方式可以使用一个有可选参数的函数,在声明中不必传递已经声明值的可选参数,只需传递参数列表中进一步不同的值。假设我有一个有4个参数的函数,其中2个是强制性的,2个是可选的。我不想使用可选参数的空值。在使用中,有些情况下我想使用该函数,第3个参数的值与默认值相同,但第4个参数的值不同。我正在寻找一种不那么冗长的解决方案,允许我只传递与默认值不同的参数,而不考虑函数声明中的顺序。
  createUrl($host, $path, $protocol='http', $port = 80) {
    //doSomething
    return $protocol.'://'.$host.':'.$port.'/'.$path;
  }

我发现自己经常重复声明变量,以便使用函数,例如为了使用$port,我在函数范围之外重新声明具有默认值的$protocol。

$protocol = "http";
$port = 8080;

有没有办法在不传递$protocol的情况下传递第二个可选参数($port),并且它会“自动”填充$protocol的默认值,即

 getHttpUrl($server, $path, $port);

在某些语言中,例如Dart语言,可以通过命名可选参数来实现此功能。请参阅以下链接中的用法示例:SO。那么在PHP中是否有类似的解决方案呢?

1
我也对这个问题很好奇,但在设计函数/方法时,参数排序的重要性就体现出来了。 - kchason
1
我感觉这不是你真正的问题,而是你将其作为一个附加示例来使用,只是作为一个例子。有方法可以解决你在这里展示给我们的问题,但可能无法将该解决方案应用于你真正的问题。你能再澄清一下吗? - Severin
1
另一个可能性是使用可变长度参数列表 - Jeff
1
@SeverinDK将编辑和澄清 - Blacksmith
1
是的。作为一个编程人员,你可以使用两种方法来区分传递的参数:顺序或类型。或者你可以像@ChrisForrence在他的回答中描述的那样使用一种构造方式。所以对于你的问题,答案是:不行。但是你可以在回答中找到解决方法。 - Jeff
显示剩余5条评论
8个回答

4

3

目前PHP不允许以我们想要的顺序调用函数参数,也许将来会实现。但是,您可以通过将关联数组作为唯一参数,并在函数中定义默认参数轻松实现目的。对于调用,您需要传递一个仅包含感兴趣的值的数组。这个数组将与默认数组合并。您甚至可以实现必需的参数,并以任何顺序调用它们。

    function mysweetcode($argument){
    $required=['first'];//specify required parameters here
    $default=['first'=>0,'second'=>1,'third'=>2];//define all parameters with their default values here
    $missing=[];
    if(!is_array($argument)) return false;
    $argument=array_intersect_key($argument,$default);
    foreach($required as $k=>$v){//check for missing required parameters
        if(!isset($argument[$v]))
            $missing[]=$v;
    }
    if(!empty($missing)){// if required are missing trigger or throw error according to the PHP version 
        $cm=count($missing);
        if (version_compare(PHP_VERSION, '7.0.0') < 0) {
            trigger_error(call_user_func_array('sprintf',
            array_merge(array('Required '.(($cm>1)?'parameters:':'parameter:').
            str_repeat('%s,',$cm).(($cm>1)?' are':' is').' missing'),$missing)),
            E_USER_ERROR);
        }else{
            throw new Error(call_user_func_array('sprintf',array_merge(
            array('Required '.(($cm>1)?'parameters:':'parameter:').
            str_repeat('%s',$cm).(($cm>1)?' are':' is').' missing'),$missing)));
        }
    }
    $default=array_merge($default,$argument);//assign given values to parameters
    extract($default);/*extract the parameters to allow further checking    
    and other operations in the function or method*/
    unset($required,$missing,$argument,$default,$k,$v);//gain some space 

    //then you can use $first,$second,$third in your code

    return $first+$second+$third;

}  

var_dump(mysweetcode(['first'=>9,'third'=>8]));//the output is 18

var_dump(mysweetcode(['third'=>8]));//this throws Error on PHP7 and trigger fatal error on PHP5 

您可以在此处查看实时工作代码

这正是我所期望的方向,尽管现在我需要更多地使用关联数组进行输入。 - Blacksmith
嗨@AlbertM,答案已更新...不是为了再次标记为解决方案,而是为了给您未来的实现提供更多想法...祝您有美好的一天。 - Elementary

2

好的,这应该可以工作:

function myFunc($arg1, $arg2, $arg3=null, $arg4= null){
  if ( is_null( $arg3 ) && is_null( $arg4 ) {
    $arg3 = 3;
    $arg4 = 4;
  } else if ( is_null( $arg4 ) ) {
    $arg4 = $arg3;
    $arg3 = 3;
  }
    echo $arg1 + $arg2 + $arg3 + $arg4;
}

然而,我建议您重新考虑整个问题,因为这不是一个很好的想法。


1
你可以重构代码,使用参数对象;这样,你可以在该对象中包含默认参数,并以任何顺序设置它们(代价是代码更冗长)。以下是使用你上面的代码作为示例:
<?php

class AdditionParameters
{
    private $arg1 = 0;
    private $arg2 = 0;
    private $arg3 = 3;
    private $arg4 = 4;

    public function getArg1() { return $this->arg1; }
    public function getArg2() { return $this->arg2; }
    public function getArg3() { return $this->arg3; }
    public function getArg4() { return $this->arg4; }

    public function setArg1($value) { $this->arg1 = $value; return $this; }
    public function setArg2($value) { $this->arg2 = $value; return $this; }
    public function setArg3($value) { $this->arg3 = $value; return $this; }
    public function setArg4($value) { $this->arg4 = $value; return $this; }
}

从那里开始,您可以简单地调用该函数并传入这个新对象。

function myFunc(AdditionParameters $request) {
    return $request->getArg1()
        + $request->getArg2()
        + $request->getArg3()
        + $request->getArg4();
}

echo myFunc((new AdditionParameters)->setArg1(1)->setArg2(2)->setArg4(6));
// or echo myFunc((new AdditionParameters)->setArg1(1)->setArg4(6)->setArg2(2));

否则,PHP不允许您拥有命名的可选参数。(例如:myFunc(1, 2, DEFAULT, 4);

0

在PHP中没有像Python那样的方法。

你必须使用一些技巧来实现,但并不总是有效。 例如:

// creates instances of a class with $properties.
// if $times is bigger than 1 an array of instances will be returned instead.(this is just an example function)
function getInstance($class, $properties = [], $times = 1){
    //my code
}

$user = getInstance("User", ["name" => "John"]); // get one instance
$users = getInstance("User", ["name" => "John"],2); // get two instances.

如果您想在不传递$parameters参数的情况下使用该函数,就像这样:

$users = getInstance("User",2);

您可以将该函数更改为:

// creates instances of a class with $properties.
// if times is bigger than 1 an array of instances will be returned instead.
function getInstance($class, $properties = [], $times = 1){
    if(is_numberic($properties)){
        $times = $properties;
        $properties = [];
    }
    //my code
}

当然,这种策略只有在您的参数具有不同类型时才有效。

附注:这种方法在Laravel框架中被广泛使用。我从那里得到了灵感。


谢谢。您的解决方案的弱点是类型检查。 - Blacksmith
不幸的是,是的。 - Ionel Lupu

0

这是从一个答案修改而来的,它允许使用关联数组为可选参数添加任何顺序的参数。

 function createUrl($host, $path, $argument = []){
   $optionalArgs = [
    'protocol'=>'http',
    'port'=>80];
  if( !is_array ($argument) ) return false;
  $argument = array_intersect_key($argument,$optionalArgs);
  $optionalArgs = array_merge($optionalArgs,$argument);
  extract($optionalArgs);
  return $protocol.'://'.$host.':'.$port.'/'.$path;
 } 



  //No arguments with function call
  echo createUrl ("www.example.com",'no-arguments'); 
  // returns http://www.example.com:80/no-arguments

  $argList=['port'=>9000];
  //using port argument only
  echo createUrl ("www.example.com",'one-args', $argList); 
  //returns http://www.example.com:9000/one-args

  //Use of both parameters as arguments. Order does not matter
  $argList2 = ['port'=>8080,'protocol'=>'ftp'];
  echo createUrl ("www.example.com",'two-args-no-order', $argList2); 
  //returns ftp://www.example.com:8080/two-args-no-order

0

你在问题中已经有了答案,你可以像这样声明你的函数:

function myFunc($arg1, $arg2, $arg3 = null, $arg4 = null){
    //here you check if the $arg3 and $arg4 are null
}

然后你可以使用以下方式调用你的函数

myFunc($arg1, $arg2);

2
但是他/她想调用 myFunc($arg1, $arg2, NOTHING, $arg4); - Jeff
谢谢。我不想要空值。正在寻找一种方法,可以避免传递已经具有默认值的参数。 - Blacksmith

0
截至版本8.0,PHP现在具备命名参数。如果在调用函数时命名参数,您可以以任何顺序传递它们,并且可以跳过早期的默认值而无需明确传递值。
例如:
function createUrl($host, $path, $protocol = 'http', $port = 80)
{
    return "$protocol://$host:$port/$path";
}

createUrl(host: 'example.com', path: 'foo/bar', port: 8080);

// returns: "http://example.com:8080/foo/bar"

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