在PHP中模拟命名函数参数,有什么好主意?

时间:2009-03-25 05:52:10

标签: php function named-parameters

如果我编写像这样的函数

,可以在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中失去intellisense之外,这种方法的其他可能缺点是什么?

编辑:

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

5 个答案:

答案 0 :(得分:35)

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

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

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

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

使用数组也有一些好处(这些同样适用于在数组中提取或保留的方法):您可以在每个函数顶部设置一个名为$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值的所有项目。通过这种方式,您可以确切地知道您拥有哪些变量。

答案 1 :(得分: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只需提取已存在为变量的数组键即可使其安全。

(顺便说一句,从你提供的例子中创建默认值并不好,因为如果提供了一个没有'name'索引的param数组,那么你的默认值就会丢失。)

答案 2 :(得分:6)

根据我的经验,如果两件事之一是真的,这种方法真的是有益的

  1. 无论出于什么情有可原,你的论点签名都很大。我有点过去6 - 尽管看起来似乎没有任何具体原因 - 但我承认这个数字是任意的。
  2. 您的所有或许多参数都是可选的,有时您只需要为第5个或某些此类事物设置值。写someFunc( null, null, null, null, 1 );
  3. 很烦人

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

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

答案 3 :(得分: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语法。

无论如何,我只在真正需要时才在单个数组中传递参数 - 就像具有参数集的函数一样,这些参数集在开发期间可能会发生很大变化。这些功能通常也有很多参数。

答案 4 :(得分:1)

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

  

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

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

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变量;)。

根据经验,你应该避免这种魔法。 extract()行可能会产生意想不到的副作用,因此您不应该使用它。实际上,我无法想到在任何应用程序中合法使用extract()函数(我不认为我自己曾经使用过它)。

相关问题