Timeline Taxi Out now: my sci-fi novel Timeline Taxi is published!

Why we need named arguments in PHP

There's a new RFC in town for PHP 8, and its name is the named arguments RFC.

If you're eligible to vote, or know someone who can: I want to ask you to take five minutes to read this, and to be clear up front: I want you to vote yes.

Here's why from the point of view of a userland developer, both for client projects and open source.

# As an OSS maintainer

The main argument against named arguments — the PHP 8 puns continue — is that they would make maintaining open source software a pain: changing the name of an argument would become a breaking change.

Here's what that means. Imagine an open source package which has this function as part of its public API:

public function toMediaCollection(string $collection, string $disk = null);

Named parameters would allow to call this function like so:

$pendingMedia
    ->toMediaCollection(collection: 'downloads', disk: 's3');

If, for some reason, the open source maintainer would want to change the name of the $collection or $disk variables, they would have to tag a major release, because named arguments would make that a breaking change.

Now, let me tell you something from my point of view as an open source maintainer: this rarely happens.

As a matter of fact, I can only think of a handful occurrences. And the only reason we decided to do renames on those occurrences, was because we were already working on a new major version and we figured we might as well improve the naming a little bit while we were at it.

Noticed a tpyo? You can submit a PR to fix it. If you want to stay up to date about what's happening on this blog, you can subscribe to my mailing list: send an email to brendt@stitcher.io, and I'll add you to the list.

I'm not the only one with that opinion by the way, Nicolas Grekas is amongst the people voting yes, and he knows a thing or two about OSS development. Oh and here's Mohamed Said, one of the core maintainers of Laravel:

I've been working at Laravel for 4 years now and I rarely find us wanting to change argument names in a refactor. We either add or remove arguments, but I'd say we never had to change names.

Actually that's an interesting thought: argument lists already are prone to breaking backwards compatibility: changing the order of arguments already is a breaking change! And we're dealing with that just fine now, aren't we?

Now even if you, as an open source maintainer, don't want to take the responsibility of making sure argument names don't change between major releases, here's what you do: tell your users you won't actively support named arguments, and using them is at their own risk. Just put this in the README:

**Heads up**: this package doesn't actively support named arguments. 
This means that argument names might change in minor and patch releases. 
You can use them, but at your own risk.

Don't deny all PHP developers this flexibility, because you're afraid of a slight chance it might break something somewhere in the far far future. Don't be afraid.

# As a programmer doing client work

Another argument is that this RFC would encourage bad API design. It would encourage people to write large method definitions, which in turn often indicates a code smell.

I as well can come up with lots of examples that aren't a good fit for named parameters. But that doesn't mean there are no use cases for them at all! Have you heard of data transfer objects or value objects before? If you're following this blog, chances are you have.

I'm not going to copy my writing on them in this post, but I can summarise the main thought behind them: treat data as a first class citizen of your application, model them with objects. For example: an address has a street, number, postal code, city, country, sometimes even more than that. That data should be represented by a strongly typed object in PHP, and not passed between contexts as an array full of random stuff, its constructor would look like this:

class Address
{
    public function __construct(
        string $street,
        string $number,
        string $postal,
        string $city,
        string $country,
    ) { /* … */ }
}

DTOs and VOs are valid cases where these kinds of large constructors are allowed, it's no code smell at all. I had a quick look at an old project of ours, at the time of tracking its stats, it already had 63 DTO classes, and the project was far from finished at that point!

Large constructors happen, and named parameters would not only add more clarity, but also offer the flexibility of changing the parameter order after the fact, without having to worry about fixing the order at all.

Take our Address object, for example. Let's say we need to support number suffixes. We can add that argument without having to worry about the order that other places called it:

class Address
{
    public function __construct(
        string $street,
        string $number,
        string $numberSuffix,
        string $postal,
        string $city,
        string $country,
    ) { /* … */ }
}

Sure the calling site still need to add it, but at least you don't have to worry about micro managing the parameter order anymore.

But what if users decide to use ordered arguments instead? You'd need some way to ensure named arguments are used in these cases. The answer is surprisingly dull: establish conventions with your team, and optionally enforce them with tools like phpcs.

Yes, ideally, we'd want the language to prevent us from any possible misstep; but that simply isn't a realistic expectation. To me, that still isn't an argument for voting against this RFC. I've been working with teams of developers for years now, and project conventions need to be established anyway. They work just fine.

# Dealing with PHP's own legacy

Pop quiz! How to set a cookie without a value, which expires two hours from now?

Did you look up the docs or consult your IDE?

That's fine, it's a confusing function after all. Named arguments can offer a little more clarity though. Compare the two following notations:

setcookie(
    'test', 
    '', 
    time() + 60 * 60 * 2
);

Or:

setcookie(
    name: 'test',
    expires: time() + 60 * 60 * 2,
);

I know which I would pick, everyone benefits from named arguments in this case. And since chances are slim that PHP's internal functions will change anytime soon, it's another very good reason to add them.

# Looking at other languages

Lastly, let's face the simple facts: several other languages — many also focused on web development — already support named arguments. Some deal with them in slightly different ways, but the base concept is known to many other programmers, and it's a good thing.

Here are a few examples:

So let's not spread fear about harder-to-maintain code, or open source software that will become a nightmare to maintain. Named arguments are a known feature in the larger software community, and have proven their worth. No need for hypothetical problems, we will manage.


By the way the vote will pass at this point! You can read about named arguments in depth on this blog!