38

In a PHP web application I'm working on, I see functions defined in two possible ways.

Approach 1:

function myfunc($arg1, $arg2, $arg3)

Approach 2:

// where $array_params has the structure array('arg1'=>$val1, 'arg2'=>$val2, 'arg3'=>$val3)
function myfunc($array_params)

When should I use one approach over another? It seems that if system requirements keep changing, and therefore the number of arguments for myfunc keep changing, approach 1 may require a lot of maintenance.

John Parker
  • 54,048
  • 11
  • 129
  • 129
John
  • 32,403
  • 80
  • 251
  • 422

7 Answers7

30

If the system is changing so often that using an indexed array is the best solution, I'd say this is the least of your worries. :-)

In general functions/methods shouldn't take too many arguments (5 plus or minus 2 being the maximum) and I'd say that you should stick to using named (and ideally type hinted) arguments. (An indexed array of arguments only really makes sense if there's a large quantity of optional data - a good example being configuration information.)

As @Pekka says, passing an array of arguments is also liable to be a pain to document and therefore for other people/yourself in 'n' months to maintain.

Update-ette...

Incidentally, the oft mentioned book Code Complete examines such issues in quite a bit of detail - it's a great tome which I'd highly recommend.

Julix
  • 598
  • 1
  • 9
  • 20
John Parker
  • 54,048
  • 11
  • 129
  • 129
  • 9
    +1 Good points. Although using configuration arrays is not necessarily a sign of poor design: Sometimes, you will simply need only 1 or 2 arguments set, and which ones those are, can change. And nobody likes a (exaggerated) `function(null,null,null,null,null,"hello");` – Pekka Jan 21 '10 at 21:15
  • 1
    @Pekka - Totally agree, but from a maintenance (and indeed remembering the args) perspective I always try and go down the named args route first. – John Parker Jan 21 '10 at 21:18
  • 3
    Yup, same here. The proper solution would be introducing real (freely ordered) named arguments in the programming language, but the PHP team has rejected that repeatedly as far as I know. – Pekka Jan 21 '10 at 21:23
  • If you ask me - [php.net/...arguments](http://php.net/manual/en/functions.arguments.php#functions.variable-arg-list.new) - this should be a better way to achieve what he wants. – XTard Nov 26 '18 at 18:26
  • Would be simpler if we could simply skip arguments `function_name(, , , , , , $value);` – Lee Blake Apr 04 '19 at 16:35
17

Using a params array (a surrogate for what is called "named arguments" in other languages") is great - I like to use it myself - but it has a pretty big downside: Arguments are not documentable using standard phpDoc notation that way, and consequently, your IDE won't be able to give you hints when you type in the name of a function or method.

Pekka
  • 442,112
  • 142
  • 972
  • 1,088
  • This would be my greatest worry, what arguements are required. I come from a VS.NET Background and am just starting to get back into php. In VS, I rarely pass arrays as parameters, unless they are meant to be grouped, because If I don't get all the data, I can't continue. The worst is when you have long functions or complicated functions. Now other programmers have to look through the code to ensure that everything is passed. With good documentation, this shouldn't be an issue, but if you are documenting, it's almost the same as updating parameters. – JohnathanKong Jan 23 '10 at 01:00
14

I find using an optional array of arguments to be useful when I want to override a set of defaults in the function. It might be useful when constructing an object that has a lot of different configuration options or is just a dumb container for information. This is something I picked up mostly from the Ruby world.

An example might be if I want to configure a container for a video in my web page:

function buildVideoPlayer($file, $options = array())
{
  $defaults = array(
    'showAds' => true,
    'allowFullScreen' = true,
    'showPlaybar' = true
  );

 $config = array_merge($defaults, $options);

 if ($config['showAds']) { .. }
}

$this->buildVideoPlayer($url, array('showAds' => false));

Notice that the initial value of $options is an empty array, so providing it at all is optional.

Also, with this method we know that $options will always be an array, and we know those keys have defaults so we don't constantly need to check is_array() or isset() when referencing the argument.

Bryan M.
  • 17,142
  • 8
  • 46
  • 60
  • 2
    Old answer I know but your code should read `$config = array_merge($options, $defaults);` as the elements from the first array in the `array_merge` function are used over the second if there are matching keys. Otherwise the defaults will always override the options – harryg May 16 '13 at 09:45
  • 3
    I don't know if it changed recently, but it seems like Bryan's answer is correct. Quoting from documentation: `If the input arrays have the same string keys, then the later value for that key will overwrite the previous one. If, however, the arrays contain numeric keys, the later value will not overwrite the original value, but will be appended. ` – user2340612 Jun 30 '16 at 14:02
5

with the first approach you are forcing the users of your function to provide all the parameters needed. the second way you cannot be sure that you got all you need. I would prefer the first approach.

4

If the parameters you're passing in can be grouped together logically you could think about using a parameter object (Refactoring, Martin Fowler, p295), that way if you need to add more parameters you can just add more fields to your parameter class and it won't break existing methods.

3

There are pros and cons to each way.

  • If it's a simple function that is unlikely to change, and only has a few arguments, then I would state them explicitly.

  • If the function has a large number of arguments, or is likely to change a lot in the future, then I would pass an array of arguments with keys, and use that. This also becomes helpful if you have function where you only need to pass some of the arguments often.

An example of when I would choose an array over arguments, for an example, would be if I had a function that created a form field. possible arguments I may want:

  • Type
  • Value
  • class
  • ID
  • style
  • options
  • is_required

and I may only need to pass a few of these. for example, if a field is type = text, I don't need options. I may not always need a class or a default value. This way It is easier to pass in several combinations of arguments, without having a function signature with a ton arguments and passing null all the time. Also, when HTML 5 becomes standard many many years from now, I may want to add additional possible arguments, such as turning auto-complete on or off.

GSto
  • 41,512
  • 37
  • 133
  • 184
0

I know this is an old post, but I want to share my approach. If the variables are logically connected in a type, you can group them in a class, and pass an object of them to the function. for example:

      class user{
           public $id;
           public $name;
           public $address;
           ...
      }

and call:

      $user = new user();
      $user->id = ...
      ...

      callFunctions($user);

If a new parameter is needed you can just add it in the class and the the function signature doesn't need to change.

Aris
  • 4,643
  • 1
  • 41
  • 38