How many parameters is too many?

Now, that is a classic question, that is often a minefield for anyone writing an increasing long list of argument in a method, or simply trying to set up auditing tools.

Obviously, the answer is not immediate. Parameters may be needed, but on the other hands, currying functions allows to reduce the amount of parameter to one for every function. In between, probably exists a reasonable level that is a golden rule, and also very elusive. So, we decided to check the current practice in PHP code.

PHP native functions

Pay honor to whom honor is due: let’s start with PHP itself. According to the manual, PHP functions may accept up to 12 arguments. This is the case for two functions : imapepstext and snmp3_set.

imagepstext() was removed in PHP 7.0. It drew a text in PostScript font over an image, and needed coordinates, font, background, angles and other details.

snmp3_set set a value for a SNMP object, which sounds like a lot more work than simply $snmp3Object->value = $value. It actually needs login/pass, timeout and protocol. Some may be skipped.

We’ll skip the functions like array_merge fscanf and array_uintersect_uassoc, that accept an arbitrary number of argument. Although, it would be interesting to check of those are actually used with long list of arguments, or simply called with an array, thanks to call_user_func_array.

After 12 parameters, there are functions with 11, then 10, including with pictures. Those are here to stay.

Number of parameters by function

To check the practice of parameter counts, we collected 1900 OSS projects, and counted the number of parameters for each method, function and closure that we could analyze with Exakat. Variadic were counted as one, and func_get_args was ignored. We only took into account arguments that were explicitly defined.

The X axis show the number of parameter in a method, and the Y axis has the % of projects that were found, with at least one method with the X number of arguments.

The graph has many interesting features.

Methods take at least one parameter

As expected, the graph actually drops from a maximum value of 100% to a absurdly long queue. It is interesting to see that most methods take at least one parameter. About 10% of PHP projects have strictly no methods or function that take no parameters.

In a way, a method without parameter has less use than a method with parameters. Though, a few situations come to mind: + Accessors + Federating a batch of methodcalls for re-usage + Orders given to an object to change itself + Methods that read information, like random values or environment variables + Singletons or methods that provide global access to some context

The reasonable amount of parameters is 5

5 parameters in a method is found in almost 2 code base out of 3 (61%). Reaching 6 parameters is below average, so the common sense that set the bar around 3 or 4, and “for sure, nothing beyond 6”, can be read on the actual coding.

Methods with 10 arguments or more appear in less that 20% of projects. That’s still quite a lot.

A long queue up to 57

Some classes may collect a huge amount of arguments. What are they?

Database generated classes

First, there is a category of methods that are database access. They may be created automatically, like this one:

`


function create($user_name, $email, $user_pw, $realname, $register_purpose, $status, $shell, $unix_status, $unix_uid, $unix_box, $ldap_id, $add_date, $confirm_hash, $mail_siteupdates, $mail_va, $sticky_login, $authorized_keys, $email_new, $timezone, $theme, $language_id, $expiry_date, $last_pwd_update) {

`

Note the total absence of typehint, and the consistence of the naming scheme: this was generated from the database model.

Dependency injection

Other long list of arguments are needed to handle dependency injection. This is the case for constructor, and they are easy to spot: they make systematic usage of Typehint. Those are usually handcrafted.

 public function __construct(
 IdentifiableObjectRepositoryInterface $repository,
 JobRegistry $jobRegistry,
 NormalizerInterface $jobInstanceNormalizer,
 ObjectUpdaterInterface $updater,
 SaverInterface $saver,
 RemoverInterface $remover,
 ValidatorInterface $validator,
 JobParametersValidator $jobParameterValidator,
 JobParametersFactory $jobParamsFactory,
 JobLauncherInterface $jobLauncher,
 TokenStorageInterface $tokenStorage,
 RouterInterface $router,
 FormProviderInterface $formProvider,
 ObjectFilterInterface $objectFilter,
 NormalizerInterface $constraintViolationNormalizer,
 JobInstanceFactory $jobInstanceFactory,
 EventDispatcherInterface $eventDispatcher,
 CollectionFilterInterface $inputFilter,
 string $uploadTmpDir
 ) {

There is a small peak : methods with 26 arguments

We checked, and that is not related to methods using every letter of the alphabet as arguments. They do have longer name.

Conclusion

  • Anything up to 5 arguments is OK, and it is probably a good baseline.
  • Starting with 6 arguments, you should see if this needs some refactoring. Sometimes, the best solution is to replace the method with a class. All parameters may be set independently, with methods, and the result is processed once all parameters are collected.
  • Beyond 12 arguments, the code is automatically generated, and is not meant to be handled manually. Otherwise, refactor.