This is the second post in my Missing in PHP7 series. The previous one is about function references.
2020 update: named parameters will be in PHP 8 since the RFC passed.
Readability of code is very important, and this is most certainly not readable:
1 |
getCatPictures( 10, 0, true ); |
You can make some guesses, and in a lot of cases you’ll be passing in named variables.
1 |
getCatPictures( $limit, $offset, !$includeNonApproved ); |
It’s even possible to create named variables where you would otherwise have none.
1 2 3 4 |
$limit = 10; $offset = 0; $approvedPicturesOnly = true; getCatPictures( $limit, $offset, $approvedPicturesOnly ); |
This gains you naming of the arguments, at the cost of boilerplate, and worse, the cost of introducing local state. I’d hate to see such state be introduced even if the function did nothing else, and it only gets worse as the complexity and other state of the function increases.
Another way to improve the situation a little is by making the boolean flag more readable via usage of constants.
1 |
getCatPictures( 10, 0, CatPictureRepo::APPROVED_PICTURES_ONLY ); |
Of course, long argument lists and boolean flags are both things you want to avoid to begin with and are rarely needed when designing your functions well. It’s however not possible to avoid all argument lists. Using the cat pictures example, the limit and offset parameters can not be removed.
1 |
getApprovedCatPictures( 10, 0 ); |
You can create a value object, though this just moves the problem to the constructor of said value object, and unless you create weird function specific value objects, this is only a partial move.
1 |
getApprovedCatPictures( new LimitOffset( 10, 0 ) ); |
An naive solution to this problem is to have a single parameter that is an associative array.
1 2 3 4 |
getApprovedCatPictures( [ 'limit' => 10, 'offset' => 0 ] ); |
The result of this is catastrophe. You are no longer able to see which parameters are required and supported from the function signature, or what their types are. You need to look at the implementation, where you are also forced to do a lot of checks before doing the actual job of the function. So many checks that they probably deserve their own function. Yay, recursion! Furthermore, static code analysis gets thrown out of the window, making it next to impossible for tools to assist with renaming a parameter or finding its usages.
What I’d like to be able to do is naming parameters with support from the language, as you can do in Python.
1 |
getApprovedCatPictures( limit=10, offset=0 ); |
To me this is not a small problem. Functions with more than 3 arguments might be rare in a well designed codebase, though even with fewer arguments readability suffers greatly. And there is an exception to the no more than 3 parameters per function: constructors. Unless you are being rather extreme and following Object Calisthenics, you’ll have plenty of constructors where the lack of named parameters gets extra annoying. This is especially true for value objects, though that is a topic for another post.
Gladly there’s some discussion about this in some RFC: https://wiki.php.net/rfc/named_params
and later on: https://wiki.php.net/rfc/simplified_named_params
Sadly this discussion has been stalled and there seems to have gotten into a dormant state. Hopefully this can be restarted and will make it into PHP 7.3
Simply do not use triadic functions/methods or worse .. if your method needs to deal with more than 2 max 3 parameters it is doing to much anyway …
Hi Ilja. My post actually mentions that, so I recommend you read the entire thing to see why the lack of named parameters is still a problem.
We need it to….When working with Databse, I often use more than 3 paramaters to the function
dataModel(0, 10, ‘foo’, ‘bar’, ‘baz’, ‘biz’);
dataModel($offset, $limit, $search, $filter1, $filter2, $etc)