如何避免 PHP 函数中位置参数的缺点


how to avoid the disadvantages of positional arguments in php's functions?

我有一些php函数,它们有大量的参数和默认值。像这样:

function foo($a=0,$b=6,$c=2,...

想知道我能做些什么来避免立场争论的恐怖。是否可以仅使用参数集的子集和名称调用函数?如:

foo($c=37)

谢谢!

显然,避免位置论证的恐怖的最好方法是避免使用长而风的参数列表的函数。

要么

可以通过单个数组变量传递大多数参数,要么需要重构函数,将它们拆分为较小的函数或将它们组合到一个类

我完全理解这个问题的来源,但我必须说:如果你的函数需要一系列参数,这些参数是可选的,9/10 你的函数有问题。
现实生活中的功能是做某事的能力。在现实世界中,椅子是可以坐的东西。它可以有 N 条腿,但它只做一项工作。随着时间的推移,在我看到的许多代码中,具有太多可选参数的函数执行不同的任务,具体取决于传递给它的参数。因此,该函数具有多个任务。这不是函数的用途!

请重新考虑你的逻辑。但是,如果您的函数执行单个工作,无论指定了哪些参数,@OZ_的答案都是要选择的:类型提示(通过类名或接口)是执行此操作的正确方法。
关联数组也可以工作,为了避免必须编写大量if,例如:

$a = 'default';
if (isset($args['a']))
{
    $a = $args['a'];
}
$b = isset($args['b']) ? $args['b'] : 'default';//or messy ternary-packed code

你可以选择写这样的东西:

function someF(array $args)
{
    $a = $b = $c = $d = null;//all your arguments and default values
    foreach($args as $name => $val)
    {
        $$name = $val;
    }
    //function body
}
someF(range(1,3));//works, but sets ${'0'}, ${'1'}, ...
someF(array('a' => 1));//works fine
someF(array('a' => new stdClass));//is $a going to be the right type?

尽管如此,就像在if/三元方法中一样,您无法预测值的类型,也不知道正在传递哪种类型的数组(assoc/numeric),因此您最终可能会设置许多不需要的变量。另外,你会说这段代码在几个月后易于阅读/调试/维护吗?

另一方面,使用类或接口:

class ArgumentsForFunc
{
    private $a = null;
    private $email = null;
    public function setA($val = null)//defaults to default value...
    {
        $this->a = $val === null ? null : (int) $val;//cast to correct type
        return $this;
    }
    public function getA()
    {
        return $this->a;
    }
    public function setEmail($email = null)
    {
        if (!$email || !filter_var($email, FILTER_VALIDATE_EMAIL))
        {//check data here!
            $email = null;
            //or better yet:
            throw new InvalidArgumentException((string) $email.' is not a vaild email address');
        }
        $this->email = $email;
        return $this;
    }
    public function getEmail($default = null)
    {
        return $this->email ? : $default;
    }
}

这个类的工作很明确:它保存数据,它通过 setter 接收数据。这些资源库方法验证原始数据,并且可以引发异常或强制转换为正确的类型。这意味着您可以像这样使用它:

function betterF(ArgumentsForFunc $args)
{
    $a = $args->getA();
    $email = $args->getEmail('default@email.arg');
    echo 'send ', $a, ' mails to ', $email;
}
$arg = new ArgumentsForFunc;
$arg->setA(2);
betterF($arg);//send 2 mails to default@email.arg

现在您可以放心,只要用户传递 ArgumentsForFunc 的实例,您的函数就会按预期运行。当然,这种方法最初需要您编写更多的代码,但它可以为您节省大量的调试麻烦,并使测试变得容易得多。

PS:在数据模型中抛出异常。如果正在设置的数据无效,请尽快通知用户。当调用 setter 后立即引发异常时,您可以轻松跟踪该错误。如果你不这样做(并且默默地失败),你可以花几个小时试图弄清楚为什么你的getEmail电话会返回null,而不是你认为你传递给其他地方的设置者的电子邮件地址。

还没有。具有多个参数的函数可以替换为具有一个参数的函数,即数据结构(在php中,它是没有方法和公共字段的类)。

function foo(FooSet $fooSet){
}
class FooSet{
 public $a = 0;
 public $b = 6;
 public $c = 2 ;
}

并且不要为此使用数组 - 这是一种称为"面向数组编程"的不良做法。

如果你有这么多参数,为什么不为它使用一个对象和__invoke:

class FuncClass {
    public $a = 4;
    public $b = 4;
    public $c = 4;
    public $d = 4;
    ...
    function __invoke() {
    }
}

用:

$func = new FuncClass();
$func->a = 3;
$func();

你不能在 PHP 中使用命名参数。

将数组用作单个参数,并将实际参数放入数组中。

$arguments["a"]=1;
$arguments["b"]=2;
$arguments["c"]=3;

function args_test($arguments) {
   extract($arguments);
   echo $a.$b.$c;
}

args_test($arguments) 将输出 123。

在函数的开头将允许您将它们用作适当的参数