Emulating named function parameters in PHP, good or bad idea?

社会主义新天地 提交于 2019-12-17 23:27:34

问题


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?


回答1:


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.




回答2:


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.)




回答3:


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.




回答4:


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.




回答5:


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).



来源:https://stackoverflow.com/questions/680368/emulating-named-function-parameters-in-php-good-or-bad-idea

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!