11

我喜欢使用 key=>value 对(数组)作为参数而不是单个参数来创建我的 PHP 函数。

例如,我更喜欢:

function useless_func($params) {
    if (!isset($params['text'])) { $params['text'] = "default text"; }     
    if (!isset($params['text2'])) { $params['text2'] = "default text2"; }   
    if (!isset($params['text3'])) { $params['text3'] = "default text3"; }   
    echo $params['text'].$params['text2'].$params['text3'];
    return;
}

我不喜欢:

function useless_func($text = "default text", $text2 = "default text2", $text3 = "default text3") {
        echo $text.$text2.$text3;
    return;
}

我第一次看到在 Wordpress 代码库中广泛使用这种方法。

我更喜欢数组的原因:

  • 可以按任何顺序提供函数参数
  • 更容易阅读代码/更多的自我记录(在我看来)
  • 不太容易出错,因为在调用函数时我必须调查正确的数组键

我正在与一位同事讨论这个问题,他说它没有用,只会导致额外的代码,而且设置默认值要困难得多。基本上,他在所有三点上都完全不同意我的观点。

我正在寻找可能能够提供见解的专家的一些一般性建议和指导:有什么更好或更合适的方法来做到这一点?

4

9 回答 9

16

不要那样做!

在大多数情况下,将所有内容传递给数组是一个坏主意。

  • 它可以防止人们在不知道它需要操作什么的情况下使用您的功能。
  • 当您可能应该创建一个具有更精确参数需求和更窄目标的函数时,它可以让您创建需要大量参数的函数

这似乎与在函数中注入它需要的东西相反。

可以按任何顺序提供函数参数

我没有这种偏好。我不明白这种需求。

更容易阅读代码/更多的自我记录(在我看来)

大多数 IDE 会为您提供函数所需的不同参数。如果有人看到这样的函数声明foo(Someclass $class, array $params, $id),就很清楚该函数需要什么。我不同意单个参数参数更易于阅读或自我记录。

不太容易出错,因为在调用函数时我必须调查正确的数组键

允许人们在不知道默认值的情况下传入数组并不接近“不易出错”。强制人们在使用你的函数之前阅读它是一种永远不会被使用的可靠方法。声明它需要三个参数及其默认值不太容易出错,因为调用您的函数的人会知道参数将被默认为哪些值,并相信它会呈现他们期望的结果。


如果您要解决的问题是参数数量过多,那么正确的决定是将函数重构为更小的函数,而不是将函数依赖项隐藏在数组后面。

于 2015-08-06T18:49:09.370 回答
14

嗯,有点用处。但是对于某些始终传递的参数,最好使用经典传递,例如function some($a1, $a2). 我在我的代码中这样做:

function getSome(SomeClass $object, array $options = array())
{
    // $object is required to be an instance of SomeClass, and there's no need to get element by key, then check if it's an object and it's an instance of SomeClass

    // Set defaults for all passed options
    $options = array_merge(array(
        'property1' => 'default1',
        'property2' => 'default2',
        ... => ...
    ), $options); 
}

所以,正如你所看到的,我也喜欢这种代码风格,但对于核心参数,我更喜欢经典风格,因为如果我使用你的代码风格,PHP 可以控制更多我应该做的事情。

于 2012-04-17T05:40:33.787 回答
10

我假设您在问编写所有函数是否是一件好事,以便它们只接受一个参数,并且该参数是一个数组?

如果你是唯一一个会处理你的代码的人,那么你可以做你喜欢做的事。但是,通过将所有参数值传递给数组,其他任何人都将不得不更加努力地理解该函数的作用以及他们为什么/如何使用它,特别是如果他们使用的是具有函数名称自动完成功能的 IDE等等。他们不会无缘无故地将其称为“功能签名”。

我建议为您不知道会有多少的项目(例如一系列数据项)或相关选项/设置组(这可能是 Wordpress 中发生的事情)保留数组参数你提到的例子?)。

如果您确实继续对数组参数采用一揽子方法,那么您至少应该意识到它对可读性的影响并采取一些措施来解决这个问题。

于 2012-04-17T06:04:19.890 回答
5

你的同事是对的。不仅相同功能的代码更多,而且更难阅读并且可能降低了性能(因为您需要调用isset每个参数并且需要访问数组来设置值)。

于 2012-04-17T05:59:27.537 回答
5

这与Cargo Cult 编程接壤。你说这更具可读性和自我记录。我想问怎么做?要知道如何使用您的函数/方法,我必须阅读代码本身。我无法从签名本身知道如何使用它。如果您使用任何支持方法签名提示的半体面的 IDE 或编辑器,这将是一个真正的 PITA。另外,您将无法使用 PHP 的类型提示语法。

如果您发现您正在编写大量参数,尤其是可选参数,则表明您的设计可能有问题。考虑一下你可能会怎么做。如果某些或所有参数是相关的,那么它们可能属于它们自己的类。

于 2012-04-17T06:34:41.590 回答
4

usingarray_merge()可以,但是+也可以使用操作符;它以另一种方式工作,它只添加尚未给出的默认值。

function useless_func(array $params = array())
{
    $params += array(
        'text' => 'default text',
        'text2' => 'default text2',
        'text3' => 'default text3',
    );
}

另请参阅:函数将数组传递给定义的键

使用数组作为函数参数时你不会得到的一些东西是:

  1. 类型检查(仅适用于对象和数组,但它可能很有用并且在某些情况下是预期的)。
  2. smart(er) 文本编辑器具有代码洞察功能,可以显示函数理解的参数;尽管您可以在函数 docblock 中添加可能的键,但使用数组会消除该功能。
  3. 由于#2,它实际上变得更容易出错,因为您可能会错误输入数组键。
于 2012-09-28T06:40:29.847 回答
1

你的同事疯了。将数组作为函数参数传入是完全可以接受的。它在许多开源应用程序中很普遍,包括 Symfony 和 Doctrine。我一直遵循 2 参数规则,如果一个函数需要两个以上的参数,或者您认为它将来会使用两个以上的参数,请使用数组。IMO 这允许最大的灵活性并减少如果参数传递不正确可能出现的任何调用代码缺陷。

当然,从数组中推断值需要更多的工作,并且您确实必须考虑所需的元素,但它确实使添加功能更加容易,并且比每次需要时向函数传递 13 个参数要好得多被称为。

这是一段代码,显示必需参数与可选参数,只是为了给您一个想法:

// Class will tokenize a string based on params
public static function tokenize(array $params)
{
    // Validate required elements
    if (!array_key_exists('value', $params)) {
        throw new Exception(sprintf('Invalid $value: %s', serialize($params)));
    }        

    // Localize optional elements
    $value            = $params['value'];
    $separator        = (array_key_exists('separator', $params)) ? $params['separator'] : '-';
    $urlEncode        = (array_key_exists('urlEncode', $params)) ? $params['urlEncode'] : false;
    $allowedChars     = (array_key_exists('allowedChars', $params)) ? $params['allowedChars'] : array();
    $charsToRemove    = (array_key_exists('charsToRemove', $params)) ? $params['charsToRemove'] : array();

....
于 2012-04-17T05:46:29.847 回答
1

我在很多场合都使用数组来替换一长串参数,并且效果很好。我同意这篇文章中提到的关于代码编辑器无法为参数提供提示的人的观点。问题是,如果我有 10 个参数,而前 9 个是空白/null,那么在调用该函数时它就会变得笨拙。

我也有兴趣了解如何重新设计需要大量参数的函数。例如,当我们有一个基于设置的某些参数构建 SQL 语句的函数时:

function ($a1, $a2, ... $a10){

        if($a1 == "Y"){$clause_1 = " something = ".$a1." AND ";}
        ...
        if($a10 == "Y"){$clause_10 = " something_else = ".$a10." AND ";}

        $sql = "
        SELECT * FROM some_table 
        WHERE
        ".$clause_1." 
        ....
        ".$clause_10." 
        some_column = 'N'
        ";

        return $sql;
    }

我希望看到 PHP 添加一个本机帮助函数,该函数可以在被调用的函数中使用,该函数将通过进行必要的类型检查来帮助传递参数数组。PHP 通过创建 func_get_args() 函数在一定程度上认识到了这一点,该函数允许以任何顺序传递参数。但是这只会传递值的副本,所以如果你想将对象传递给函数,这将是一个问题。如果存在这样的函数,那么代码编辑器将能够选择它并提供有关可能参数的详细信息。

于 2014-02-20T14:51:47.790 回答
0

@Mike,您还可以将您的 $params 参数“提取()”到局部变量中,如下所示:

// Class will tokenize a string based on params
public static function tokenize(array $params)
{
    extract($params);
    // Validate required elements
    if (!isset($value)) {
        throw new Exception(sprintf('Invalid $value: %s', serialize($params)));
    }

    // Localize optional elements
    $value         = isset($value) ? $value : '';
    $separator     = isset($separator) ? $separator] : '-';
    $urlEncode     = isset($urlEncode) ? $urlEncode : false;
    $allowedChars  = isset($allowedChars) ? $allowedChars : array();
    $charsToRemove = isset($charsToRemove) ? $charsToRemove : array();

……

相同的实现,但更短。

于 2013-07-23T10:07:49.147 回答