I have a function that takes 5 parameters, and as the application grew we needed to add few more parameters which ended up in 9 parameters with 4 of them having default values.
I was wondering is it better to pass parameters like this or use an array?
I prefer to have it like this
fun(array( 'par1' => 'x', 'par2' => 'y', ..... ) )
Insted of
func($par1, $par2, $par3, ...);
What do you think?
Highly depends on the use case.
But here are some solutions to this problem.
Fixed Order
If the order is somewhat fixed and you never have a need to change it, then:
<?php function fn($a1, $a2, $a3, $a4, $a5, $a6 = null, $a7 = "foo", $a8 = "bar", array $a9 = array()) {}
Pros
- Self documenting
- Type hinting
- Default values
Cons
Array
If on the other hand the order is somewhat different all the time, use the array thingy.
<?php function fn($a1, $a2, $a3, $a4, $a5, array $optional = array()) {} fn("", "", "", "", "", array("arg9" => false));
Pros
- Easy to use
- No fixed order
Cons
- Not self documenting
- Costly to validate
Parameter Object
A parameter object is of course a valid solution as well, but impractical to handle:
<?php class Args { public $arg5 = "foo"; public $arg6 = "bar"; public $arg7 = null; public $arg8 = array(); public $arg9 = true; } function fn($arg1, $arg2, $arg3, $arg4, $arg5, \Args $optional = null) {} // Now comes the impractical part. $optional = new Args(); $optional->arg9 = false; fn("", "", "", "", "", $optional);
Pros
- Self documenting
- No fixed order
Cons
- Highly impractical
- Costly to validate
Array to Parameter Object
You could mix the two approaches:
<?php class Args { public $arg5 = "foo"; public $arg6 = "bar"; public $arg7 = null; public $arg8 = array(); public $arg9 = true; public __construct($args) { foreach ($args as $property => $value) { $this->"set{$property}"($value); } } public function setArg5($value) { if (is_string($value) === false) { throw new \InvalidArgumentException; } $this->arg5 = $value; } // and so on ... } function fn($arg1, $arg2, $arg3, $arg4, $arg5, array $optional = null) { if (isset($optional)) { $optional = new Args($optional); } // ... } fn("", "", "", "", "", array("arg9" => false));
Pros
- Easy validation
- Separation of concern
- Easy to pass along
- Easy to handle
- Possible to document the API
Cons
- Still not possible to document it as good as the fixed args approach
- Costly to validate
Variadics
There's a new feature in PHP 5.6 that you might find useful, variadics:
<?php function fn($a1, $a2, $a3, $a4, $a5, ...$optional) {}
Pros
- Very fast
- Allows to build special APIs (e.g. database prepared statement binding)
Cons
- Not easily documented
- Costly to validate
Named Parameters
And we might see named parameters in the future.
You're attacking the wrong problem.
Passing 9 parameters to a function is a clear code smell:
Too many parameters: a long list of parameters in a procedure or function makes readability and code quality worse.
With that many parameters, there's also a good chance that you violate:
Long method: a method, function, or procedure that has grown too large.
Creating an arbitrary array
that groups together those parameters won't solve the real issue. It can make your code somewhat more readable and less vulberable to small problems like omitting a parameter ($arg1, $arg2, $arg4
), but not a real solution.
What you should do instead:
Figure out why the function needs that many parameters, then fix that issue.
- If the single function has too many reponsibilities: split it. You can do a single sequential split: One task after the another.
- If multiple parameters can be aggregated to a single entity, create an object for it (eg: x and y coordinates can be merged to a point. a rectangle can be described by two opposite corner points). Try to create something reusable, so it's sole purpose isn't just to be able to pass arguments to this single function.
There are a lot of techniques, reading a good article/book about (oo) code refactoring can give you some hints.