我的PHP函数应该接受参数数组还是应该明确请求参数?

38
在我正在开发的一个 PHP 网络应用程序中,我看到有两种可能的方式定义函数。

方法 1:

function myfunc($arg1, $arg2, $arg3)

方法2:

// where $array_params has the structure array('arg1'=>$val1, 'arg2'=>$val2, 'arg3'=>$val3)
function myfunc($array_params)

什么时候应该使用一种方法而不是另一种?如果系统需求不断变化,并且因此myfunc的参数数量也在变化,那么使用方法1可能需要很多维护。

7个回答

30

如果系统的变化如此频繁以至于使用索引数组是最好的解决方案,那么我会说这只是你最不用担心的事情。:-)

通常情况下,函数/方法不应该使用太多参数(最多为5个加上或减去2个),我建议您坚持使用命名(理想情况下是带有类型提示)的参数。 (只有在存在大量可选数据的情况下,索引参数数组才真正有意义——一个很好的例子是配置信息。)

正如@Pekka所说,传递参数数组还容易被文档化,因此对于其他人/自己在“n”个月内维护也会造成麻烦。

更新一下...

顺便提一下,经常被提到的书籍 代码大全非常详细地研究了这些问题——它是一本我强烈推荐的好书。


9
+1 好观点。虽然使用配置数组并不一定意味着设计不佳:有时,您只需要设置1或2个参数,并且这些参数会发生变化。而且没有人喜欢一个(夸张的)function(null,null,null,null,null,"hello"); - Pekka
1
@Pekka - 完全同意,但从维护(以及记住参数)的角度来看,我总是尝试首先采用命名参数的方式。 - John Parker
3
没错,我也是这样认为。正确的解决方案应该是在编程语言中引入真正(自由排序的)命名参数,但据我所知,PHP团队已经多次拒绝了这个建议。 - Pekka
如果你问我,php.net/...arguments - 这应该是实现他想要的更好的方法。 - XTard
如果我们可以跳过参数,那么function_name(, , , , , , $value);会更简单。 - Lee Blake

17
使用 params 数组(其他语言中称为“命名参数”的替代品)非常好——我喜欢使用它——但它有一个相当大的缺点:这种方式不能使用标准的 phpDoc 注释来记录参数,因此,当您输入函数或方法名称时,您的 IDE 将无法给出提示。请保留 HTML 标记,不做解释。

这将是我最大的担忧,需要哪些参数。我来自VS.NET背景,现在刚开始重新学习php。在VS中,我很少将数组作为参数传递,除非它们是要被分组的,因为如果我没有得到所有的数据,我就无法继续。最糟糕的是当你有长函数或复杂函数时。现在其他程序员必须查看代码以确保一切都被传递了。通过良好的文档,这不应该成为问题,但如果你正在记录,那么这几乎与更新参数相同。 - JohnathanKong

14

我发现在函数中使用一个可选的参数数组非常有用,当我想要覆盖函数中的一组默认值时。当构建具有许多不同配置选项或仅是信息的哑容器时,这可能会很有用。这是我主要从Ruby世界中学到的。

例如,如果我想为网页中的视频配置容器:

function buildVideoPlayer($file, $options = array())
{
  $defaults = array(
    'showAds' => true,
    'allowFullScreen' = true,
    'showPlaybar' = true
  );

 $config = array_merge($defaults, $options);

 if ($config['showAds']) { .. }
}

$this->buildVideoPlayer($url, array('showAds' => false));

请注意,$options的初始值是一个空数组,所以提供它是可选的。

此外,使用这种方法,我们知道$options将始终是一个数组,我们知道这些键有默认值,因此在引用参数时不需要不断地检查is_array()isset()


2
虽然我知道你的代码已经很老了,但是你的代码应该读作 $config = array_merge($options, $defaults);。因为在 array_merge 函数中,第一个数组的元素优先于第二个数组使用,如果存在匹配的键。否则,默认值将始终覆盖选项。 - harryg
3
我不确定最近有没有变化,但是Bryan的回答似乎是正确的。引用文档中的话:如果输入数组具有相同的字符串键,则该键的后一个值将覆盖先前的值。然而,如果数组包含数字键,则后一个值将不会覆盖原始值,而是被追加到后面。 - user2340612

5

使用第一种方法,您强制函数的用户提供所需的所有参数。而使用第二种方法,您不能确定是否已获取了所有需要的参数。我更喜欢第一种方法。


这就是使用命名参数的巨大优势,也是我喜欢它们的原因。 - Pekka

4

如果你传递的参数可以在逻辑上分组,你可以考虑使用参数对象(《重构》Martin Fowler,第295页),这样如果你需要添加更多的参数,你只需要向参数类中添加更多的字段,就不会破坏现有的方法。


3

每种方法都有其优缺点。

  • 如果是一个简单的函数,不太可能改变,并且只有几个参数,则应明确声明它们。

  • 如果函数有大量参数或将来可能经常更改,则应传递带有键的参数数组并使用它。如果您有一个只需要经常传递一些参数的函数,这也会很有帮助。

我选择数组而不是参数的示例是创建表单字段的函数:

  • 类型
  • ID
  • 样式
  • 选项
  • 是否必填

我可能只需要传递其中的一些参数。例如,如果字段类型为文本,则不需要选项。我可能不总是需要类或默认值。这样,可以轻松地传递多种参数组合,而无需具有大量参数的函数签名并始终传递null。此外,当HTML 5在许多年后成为标准时,我可能想添加其他可能的参数,例如打开或关闭自动完成。


0

我知道这是一个旧帖子,但我想分享我的方法。如果变量在类型上逻辑连接,您可以将它们分组到一个类中,并将其对象传递给函数。例如:

      class user{
           public $id;
           public $name;
           public $address;
           ...
      }

并调用:

      $user = new user();
      $user->id = ...
      ...

      callFunctions($user);

如果需要一个新参数,你只需在类中添加它,函数签名不必更改。

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