在 PHP 中模拟命名函数参数,是好主意还是坏主意?

发布于 2024-07-16 05:14:44 字数 462 浏览 13 评论 0原文

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

编辑:

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

Named function parameters can be emulated in PHP if I write functions like this

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
}

Apart from losing intellisense in IDEs what are the other possible downsides of this approach?

Edit:

Security: Shouldn't security be a non-issue in this case, as the extracted variables are limited to function scope?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

寂寞清仓 2024-07-23 05:14:44

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

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

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

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

I would suggest using the associative array to pass named parameters, but keep them in the array without extracting them.

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

There's a couple of reasons for this. Looking at that function, you can quite clearly see that I'm referring to one of the arguments passed into the function. If you extract them, and don't notice the extract() you (or the next guy) will be there scratching your head wondering where this "$name" variable came from. Even if you do know you're extracting the arguments to local variables, it's still a guessing game to a certain degree.

Secondly, it ensures that other code doesn't overwrite the args. You may have written your function expecting only to have arguments named $foo and $bar, so in your other code, you define $baz = 8;, for example. Later on, you might want to expand your function to take a new parameter called "baz" but forget to change your other variables, so no matter what gets passed in the arguments, $baz will always be set to 8.

There are some benefits to using the array too (these apply equally to the methods of extracting or leaving in the array): you can set up a variable at the top of each function called $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"

You could even remove all items from $args which don't have a corresponding $default value. This way you know exactly which variables you have.

南风起 2024-07-23 05:14:44

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

/**
 * 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 通过仅提取已经作为变量存在的数组键来使其安全。

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

Here's another way you could do this.

/**
 * 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);
    //...
}

With this set up, you get default params, you don't lose intellisense in IDEs and EXTR_IF_EXISTS makes it secure by just extracting array keys that are already existing as variables.

(By the way, creating default values from the example you provided is not good, because if an array of param is provided without a 'name' index, your default value is lost.)

最初的梦 2024-07-23 05:14:44

根据我的经验,只有当两件事之一为真时,这种方法才真正有益。

  1. 无论出于什么情有可原的原因,你的论点签名很大。 我有点把 6 作为最大值 - 没有任何具体原因,尽管看起来是正确的 - 但我坦率地承认这个数字是任意的。
  2. 您的所有或许多参数都是可选的,有时您只需要为第五个或类似的东西设置一个值。 编写 someFunc( null, null, null, null, 1 ); 很烦人。

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

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

In my experience, this approach is really only beneficial if one of two things is true

  1. For whatever extenuating reasons, your argument signature is big. I kinda go by 6 as a maximum - not for any specific reason though just seems right - but I freely admit that this number is arbitrary.
  2. All or many of your arguments are optional, and sometimes you only need to set a value for the 5th one or some such thing. It's annoying to write someFunc( null, null, null, null, 1 );

If either of these is true for you, faking named params with an associative array might be the right implementation. Aside from knowing when to avoid extract (or avoiding it altogether) I can't immediately think of other downsides.

That being said, oftentimes both of these problems can be solved through refactoring as well.

依 靠 2024-07-23 05:14:44

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

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 语法之类的语法。

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

In my experience, the downside of this method is more code to write.
consider something like this:

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

To simulate this behavior when passing everything in an array you'll need to write more code, and the "function signature" will be less "clear and obvious".

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

I'm wondering if it would be a good idea for PHP to address that in a future release, maybe have something like Pascal or VB syntax available for function arguments.

Anyway, I only pass parameters in a single array when really needed - like functions that have a parameter set that is likely to change a lot during development. These functions usually also have numerous parameters.

沫离伤花 2024-07-23 05:14:44

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

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

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

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() 函数(我认为我自己从未使用过它)。

Others have alreay answered your other points, I would just like to comment on security aspect.

Security: Shouldn't security be a non-issue in this case, as the
extracted variables are limited to function scope?

Yes and no. The code you have written could (depends on whether you always initialize your variables after this call) overwrite your vars. Example:

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 ])
}

What makes this code "kind-of-secure" is that YOU are the one who is calling it - so user can't supply arbitrary parameters (unless you redirect POST vars there, of course ;).

As a rule of thumb you should avoid such magic. The line with extract() could have unintended side effects, so you should not use it. Actually, I can't think of a legitimate use of extract() function in any application (I don't think I have ever used it myself).

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文