【问题标题】:Emulating named function parameters in PHP, good or bad idea?在 PHP 中模拟命名函数参数,好主意还是坏主意?
【发布时间】:2010-10-15 09:18:05
【问题描述】:

如果我编写这样的函数,可以在 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 中失去智能感知之外,这种方法还有哪些其他可能的缺点?

编辑:

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

【问题讨论】:

    标签: php function named-parameters


    【解决方案1】:

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

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

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

    其次,它确保其他代码不会覆盖 args。您可能编写了函数,只希望有名为 $foo$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 值的项目。这样你就可以准确地知道你有哪些变量。

    【讨论】:

    • 处理它的好方法。我很遗憾在写我的之前没有阅读您的答案。
    • 非常有用,谢谢。可以将其与强制参数结合起来,例如function myFunc($foo,$bar,$args=array())。我发现默认情况下将$args 设置为空的array() 很有用。这样,可以调用myFunc(a, b)myFunc(a, b, array("name"=>"Mary")) 等。几乎和Python 一样好:-)
    【解决方案2】:

    这是您可以做到这一点的另一种方法。

    /**
     * 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 只需提取已作为变量存在的数组键即可确保安全。

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

    【讨论】:

    • 这是否适用于非类方法的函数? (默认值:发布问题后我也得到了)
    • 当然,这只是我的一类sn-p。它适用于任何事情。
    • 请您解释一下如何使智能感知与@named 标签一起工作?我正在使用 PHPStorm,它具有出色的智能感知,但我无法让它识别 @named 标记。我不认为这是一个“真正的”PHP 注释标签,是吗?不在PHPDoc list of tags
    • 说真的,你用什么 IDE 来让智能感知理解这个?
    【解决方案3】:

    根据我的经验,这种方法只有在以下两种情况之一为真时才真正有用

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

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

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

    【讨论】:

      【解决方案4】:

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

      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 语法可用于函数参数。

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

      【讨论】:

        【解决方案5】:

        其他人已经回答了你的其他问题,我只想评论一下安全方面。

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

        是的,不是的。您编写的代码可能(取决于您是否总是在此调用后初始化变量)覆盖您的 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 变量;)。

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

        【讨论】:

          猜你喜欢
          • 2010-11-23
          • 1970-01-01
          • 1970-01-01
          • 2014-02-27
          • 2011-10-26
          • 1970-01-01
          • 2011-02-20
          • 2011-10-21
          • 2010-11-27
          相关资源
          最近更新 更多