20

如果我编写这样的函数,可以在 PHP 中模拟命名函数参数

function pythonic(array $kwargs)
{
    extract($kwargs);
    // .. rest of the function body
}

// if params are optional or default values are required
function pythonic(array $kwargs = array('name'=>'Jon skeet'))
{
    extract($kwargs);
    // .. rest of the function body
}

除了在 IDE 中失去智能感知之外,这种方法还有哪些其他可能的缺点?

编辑:

安全性:在这种情况下,安全性不应该不是问题吗,因为提取的变量仅限于函数范围?

4

5 回答 5

35

我建议使用关联数组来传递命名参数,但将它们保留在数组中而不提取它们。

function myFunc(array $args) {
    echo "Hi, " . $args['name'];
    // etc
}

这有几个原因。查看该函数,您可以非常清楚地看到我指的是传递给函数的参数之一。如果您提取它们,并且没有注意到extract()您(或下一个人)会在那里挠头想知道这个“ $name”变量来自哪里。即使您确实知道您正在提取局部变量的参数,它在一定程度上仍然是一个猜谜游戏。

其次,它确保其他代码不会覆盖 args。您可能已经编写了函数,只希望有名为$fooand的参数$bar,因此在您的其他代码中,您定义$baz = 8;了例如。稍后,您可能希望扩展您的函数以获取一个名为“baz”的新参数,但忘记更改您的其他变量,因此无论在参数中传递什么,$baz都将始终设置为 8。

使用数组也有一些好处(这些同样适用于提取或留在数组中的方法):您可以在每个名为的函数的顶部设置一个变量$defaults

function myFunc (array $args) {
    $default = array(
        "name" => "John Doe",
        "age" => "30"
    );
    // overwrite all the defaults with the arguments
    $args = array_merge($defaults, $args);
    // you *could* extract($args) here if you want

    echo "Name: " . $args['name'] . ", Age: " . $args['age'];
}

myFunc(array("age" => 25)); // "Name: John Doe, Age: 25"

您甚至可以删除所有$args没有相应$default值的项目。这样您就可以确切地知道您拥有哪些变量。

于 2009-03-25T07:21:12.230 回答
8

这是您可以执行此操作的另一种方法。

/**
 * Constructor.
 * 
 * @named string 'algorithm'
 * @named string 'mode'
 * @named string 'key'
 */
public function __construct(array $parameter = array())
{
    $algorithm = 'tripledes';
    $mode = 'ecb';
    $key = null;
    extract($parameter, EXTR_IF_EXISTS);
    //...
}

通过此设置,您将获得默认参数,您不会在 IDE 中丢失智能感知,并且 EXTR_IF_EXISTS 通过提取已经作为变量存在的数组键使其安全。

(顺便说一句,从您提供的示例中创建默认值并不好,因为如果提供的参数数组没有“名称”索引,您的默认值将丢失。)

于 2009-03-25T11:29:51.853 回答
6

以我的经验,这种方法只有在两件事之一为真时才真正有益

  1. 无论出于何种情有可原的原因,您的论点签名都很大。我最多会增加 6 个 - 不是出于任何特定原因,虽然看起来是正确的 - 但我承认这个数字是任意的。
  2. 您的所有或许多参数都是可选的,有时您只需要为第 5 个或类似的东西设置一个值。写的很烦someFunc( null, null, null, null, 1 );

如果其中任何一个对您来说都是正确的,那么使用关联数组伪造命名参数可能是正确的实现。除了知道何时避免提取(或完全避免提取)之外,我无法立即想到其他缺点。

话虽如此,通常这两个问题也可以通过重构来解决。

于 2009-03-25T06:02:17.803 回答
2

以我的经验,这种方法的缺点是要编写更多的代码。考虑这样的事情:

function someFunc($requiredArg, $arg1 = "default11", $arg2 = "default2") {

要在传递数组中的所有内容时模拟这种行为,您需要编写更多代码,并且“函数签名”将不那么“清晰和明显”。

function someFunc($requiredArg, $optionalArgs) {
    // see other answers for good ways to simulate "named parameters" here

我想知道 PHP 在未来的版本中解决这个问题是否是一个好主意,也许有类似 Pascal 或 VB 语法的东西可用于函数参数。

无论如何,我只在真正需要时才在单个数组中传递参数——比如具有在开发过程中可能会发生很大变化的参数集的函数。这些函数通常也有许多参数。

于 2011-11-04T16:47:05.207 回答
1

其他人已经回答了你的其他观点,我只想对安全方面发表评论。

安全性:在这种情况下,安全性不应该不是问题吗,因为提取的变量仅限于函数范围?

是和不是。您编写的代码可能(取决于您是否总是在此调用后初始化变量)覆盖您的 vars。例子:

function pythonic(array $kwargs = array('name'=>'Jon skeet'))
{
    $is_admin = check_if_is_admin();  // initialize some variable...

    extract($kwargs);

    // Q: what is the value of $is_admin now? 
    // A: Depends on how this function was called... 
    // hint: pythonic([ 'is_admin' => true ])
}

使这段代码“有点安全”的原因是你是调用它的人 - 所以用户不能提供任意参数(当然,除非你在那里重定向 POST vars ;)。

根据经验,你应该避免这种魔法。与extract()可能有意想不到的副作用,所以你不应该使用它。实际上,我想不出在任何应用程序中对 extract() 函数的合法使用(我认为我自己从未使用过它)。

于 2014-10-06T07:47:59.337 回答