Named Parameters in PHP

Posted: 2013-09-01
Category: PHP

Update 06/09/2013: I had initially offered to put together an RFC draft updating the original rather dire efforts at documenting PHP's lack of interest for named parameters. A more objective RFC has now been put together by Nikita Popov so I can ditch my notes. Wonderful._

Converting a Python Twitter API package to PHP turned out to be more trouble than I initially expected due to the usage of Python's named parameters. Here is the converted function signature:

public function getFriends(
    $user_id = null, 
    $screen_name = null, 
    $cursor = -1, 
    $skip_status = false, 
    $include_user_entities = false
) {

In Python a call to that method could look like this:

api.get_friends(screen_name="phpdrama", include_user_entities=true)

Because PHP has no ability for users to specify parameters and is instead done entirely based on the definition order, it's going to look like this:

$api->getFriends(null, 'phpdrama', -1, false, true);

Everything is wrong with this.

  1. I as a user should not have to know or care what the default values of arguments im not using are.
  2. It is unobvious when/if default values change.
  3. I don't know what im saying false to without looking back at the declaration.
  4. It looks shitty.

What about arrays?

Whenever I say I'd like to use named parameters, somebody says "why do you need them, just use arrays!".

Well, if its my only option I guess I will, but how does it look:

public function getFriends($args)
{
    $args += [
        'user_id' => null,
        'screen_name' => null,
        'cursor' => -1,
        'skip_status' => false,
        'include_user_entities' => false,
    ];
    extract($args);
    // ....
}

Update 01/09/2013: I had some nasty-ass isset statements in there but a few people pointed out this array situation would be nicer. They're right of course. I need to stop blogging from the bar.

This syntax will make the following syntax available:

$api->getFriends(['screen_name' => 'phpdrama', 'include_user_entities' => true]);

Ok so yes, technically this will work, but I lose all ability to docblock anything, type hinting is a chore and I have to do it in EVERY method. There are a lot of methods, which means litterally hundreds of lines of boilerplate that could be replaced easily with syntax.

What would it look like in PHP-land? Something like one of these I guess:

$api->getFriends(screen_name => 'phpdrama', include_user_entities => true);
$api->getFriends(screen_name='phpdrama', include_user_entities=true);
$api->getFriends(:screen_name => 'phpdrama', :include_user_entities => true);
$api->getFriends(screen_name: 'phpdrama', include_user_entities: true);

Comment on which you prefer and why. Also feel free to suggest other syntax.

Next Step

Named Parameters for PHP is not a new conversation. It's reared its head several times - so much so that the named parameters RFC says:

Since the topic continually gets re-raised, and now PHP has an RFC process, the discussion should be recorded in an RFC (Note this is yet to be done) so the same arguments don't have to be revisited.

The reason I've not been confident about seeing named parameters make it into PHP is due to the conclusion on this RFC:

Discussion

We don't see the real need for named parameters, as they seem to violate PHP's KISS principle. It also makes for messier code.

Conclusions

We do not want to add it.

The attitude that comes across here is extremely negative and mostly sounds like the response of somebody that does not truly understand what named parameters are. The RFC also says that it needs to be updated to include conversations that have been had since the RFC was initially created.

I can read, I can Google and I can write, so I feel like I am the perfect person for the job.

I've emailed the internals list asking for a green light, then I'll rewrite the RFC to be a fair and balanced outline of what named parameters are. If you guys can make syntax suggestions I can propose the most popular ones as options on the RFC.

I definitely don't think named parameters will get into 5.6, but I do think the RFC can be drastically improved to A) give people a better understanding of the pros and cons and B) perhaps change the minds of enough of the core team to eventually make named parameters a possibility.

Comments

Gravatar
Joseph Scott

2013-09-01

Would love, love, love to get named parameters in PHP. Sooner is better than later, but hey, even if we had to wait until a 5.7 it would be great to finally have them.

I've never understood the argument against them. The 'just use an array' is the only viable option at this point, but is inferior in every way possible to named parameters. As you mentioned, spend some time in Python and that becomes clear very quickly.

Gravatar
Nick Ashley

2013-09-01

screenname='phpdrama' - this is the worst of the suggestions IMHO. I think people would often accidentally write `$screenname='phpdrama' which would not produce an error, but would not be the intended result.

screen_name => 'phpdrama' - Looks like array syntax, especially with 5.4, its just missing some square brackets. As users are used to passing in arrays to functions, I see this as a bad thing, and the difference should be more obvious.

:screen_name => 'phpdrama' - I get the nod to PDO named parameters, but it still has the array syntax, and don't think its the best solution

screen_name: 'phpdrama' - This is my favorite option. Adding a dollar sign would throw an error, Its obviously not an array, and it's clean and simple.

Note, that any of these solutions would still be better then than not having named parameters I think.

Gravatar
Paul

2013-09-01

Your examples for replicating this in PHP are over complicated. Although not ideal, you can achieve this with an array in less code:

function ($opts) { $default = array ( 'var1' => true, 'var2' => false, 'var4' => 1 ); extract( array_merge($default, $opts) ); }

Gravatar
Patrick Nelson

2013-09-01

I agree that the standard array syntax would be superior, i.e. func(['param1' => 'val', ... ]). This is because it's compatible with existing conventions, thus making it simpler to use, simpler to learn and still easy to type. That fact that it's a lot to type (with the extra quotes around the named parameter and the yield => sign) is an artifact of php itself and a worthwhile hit, considering the primary complaint with PHP is its internal inconsistencies and naming conventions. At least this would be fairly consistent and east to use and closest in functionality AND portability to current typical JavaScript implementations (passing objects inline via {...}).

Then you could simply set an array of default expected parameters/values at the top of the method/function and then utilize an extract statement which only extracts keys which match a list of expected keys

Gravatar
Tino

2013-09-01

I would always favour "value objects" over passing an array to a method. The method tells the user what it needs and the user knows what needs to be defined.

It cleary depends on the use case since it requires more effort when it comes to implementation but in my experience it helps users a lot in dealing with your code without raising questions such as: soo, what keys should that array hold??

Example: function addPerson(Person $person) {}

$vo = (new Person())->setName('John')->setAge(36);

addPerson($vo);

Gravatar
Aaron

2013-09-01

Consider using Symfony's OptionResolver component if you decide to go the array way. http://symfony.com/doc/current/components/options_resolver.html

Gravatar
Aaron

2013-09-01

Consider using Symfony's OptionResolver component if you decide to go the array way. http://symfony.com/doc/current/components/options_resolver.html

Gravatar
Chris Duell

2013-09-01

This has given me the shits for years, it makes it even more painful to use someone else's code when they've put the parameters in some weird ord where the most commonly changed param comes after ones that a also always left as default. Also makes wading through code more difficult when you see a bunch of falses, nulls etc when calling a method

Gravatar
Markus

2013-09-01

Yes! Let's get them! Something that helps us write so much more readable code should be a no-brainer!

Gravatar
John

2013-09-01

I would love named parameters in PHP. Here's what I would make them look like

Option 1: named_parameters($bar: 10, $foo: 3);

Option 2: named_parameters($bar => 10, $foo => 3);

Gravatar
Jeff Madsen

2013-09-01

Agree with Paul above - you are absolutely right, named params would be wonderful, but the solutions are far less painful than you paint them. array_merge does virtually the same thing, you just need to setup your __construct() with slightly different syntax

Gravatar
Kyle Mechler

2013-09-01

This feels like a matter of convenience, to me. I understand that it does simplify things considerably and is likely a necessary convenience. I would echo the comments about array syntax similarity and dollar sign concern that Nick mentioned above. While reading my mind went to var: 'value', -- though most likely because when I'm passing around things like this, I'm working in js. I guess I've just gotten used to it there. We all have, clearly.

Paul is right about using array_merge() with a defaults array. That's the way I've always done this. I don't think it's clean, though, and it definitely has drawbacks like not allowing for (awesome -- and frankly proper) docblock use, etc.

Charge onward!

Gravatar
Miguel

2013-09-01

Paul: The problem with using extract() as you propose, is that if the caller makes a mistake in the array arguments, it could populate unwanted variables in the function context. I don't like the idea of facilitating that sort of inward leakage. It's a lesser replay of the register_globals situation.

Gravatar
Jonathan Hanson

2013-09-02

Regarding proposed syntax, why bother with a token at all? Why not just:

$api->getFriends(screenname 'phpdrama', includeuser_entities true);

It looks a little bit like typehinting, which I guess is not a good thing. Still, it's another option.

Gravatar
Wim Vds

2013-09-02

I'd got for the second or fourth one, though - as Nick Ashley mentioned before - using parameter='value' could indeed lead to more accidental errors (when people use $parameter='value' by mistake), so the fourth option (using parameter: value) seems like the most reliable solution.

Gravatar
Filip

2013-09-02

Named parameters will lead to more parameters in functions which will lead to more complicated functions which leads to worse maintainability.

Gravatar

2013-09-02

@Filip: Fear leads to anger, anger leads to hate, hate.. to suffering.

Or, people will use them for the exact same things they're been doing with arrays for years, but have all of the above positives I listed instead of having all of the above negatives they have now.

Gravatar
Petah

2013-09-03

If they are introduced the most consistent syntax for them would be $instance->method($param => 1, $foo => 'bar');

I would like to see them added.

Gravatar
Adam Kelso

2013-09-03

Totally agree. The python people have had a severe advantage in this regard for years. I vote for syntax number 4. Mimics JSON syntax, or just anonymous JavaScript objects.

Alternatively, I could see the argument for passing in an anonymous PHP object mimicking syntax like, $api->getFriends(screenname->'phpdrama', includeuser_entities->true); But I find that cluttery. The fourth syntax above is still cleaner.

Gravatar
Fede Isas

2013-09-04

I think I've read somewhere that named parameters was really hard to implement given the current codebase. Does anyone knows if this is (still) true?

Gravatar
Benjamin

2013-09-04

Maybe the syntax could be as simple as specifying the default value with '=>' instead of '='. So you could have and ordered parameters function like getCheese($type = 'cheddar', $age=0) look instead like getCheese($type => 'cheddar', $age => 0).

Gravatar
Samhennessy

2013-09-04

Here are my unstructured thoughts on the matter:

Does it make the code more readable? Yes. Could it encourage people creating functions with lots of arguments? Yes and name parameters will be used for the justification.

Many times I've seen people change the order of parameters when overloading a class function. This leads me to think people will just change the order of parameters because they "like it better that way." I'm not sure if this is solving this problem or making it worse.

I don't really like the passing arrays to functions pattern. I've always felt that it was better the use a parameter object or specialised public functions that proxied for a long parameter list private function.

The name of the parameter is not currently part of the public API. Named parameters changes that. So change the name of the parameter is no longer free. Not sure if that is an issues or not. At this time I don't think it is.

In the past, PHP has always been a "you can only do it one way" language. This is changing. Passing arrays in place of real parameters is a style thing that won't be going away. Named parameters is a big improvement over that pattern. So you can now do it two ways, but maybe that's OK because people really want to do it two ways.

If 0 was "please don't do it", 5 is "I don't care either way" and 10 was "please do it". I would be a 7. Meaning, I think it will create problems but it will solve more than it creates.

PHP's status as an easy to learn language is very important and I don't feel this will negatively affect that.

If you introduce names parameters people will put less thought into the design of their API. Maybe that's OK because it no longer matters as much. I could even see using name parameters as a recommended practice as it would make for less brittle code.

Gravatar
Mike

2013-09-05

+1 for named parameters. I could give a crap about OO, and that's the main stuff that keeps getting attention in PHP.

KISS? Why is there a short array syntax, variadic function syntax RFC, and all this other stuff, when basic things like named parameters (which I think improve readability, type hinting, everything good) ... I am a procedural PHP programmer, that'd be a great change/addition to PHP. Other stuff seems like constantly morphing it to another language, and other ways that in my mind "makes for messier code."

Gravatar
Daz

2013-09-09

Sadly, this is exactly why we have different programming/scripting languages. Each has their own principles and ideals. If everyone agreed on one thing, we'd only have a single programming language, and it would be amazing. Instead we have lots of different languages; each with their own pros and cons.

It's an interesting problem you've been faced with. A few things spring to my mind:

  1. Create a wrapper object to encapsulate your class. You can then use the __call() magic method to set the right arguments in your array before calling the actual method. The downside to doing this, is that code completion won't work on most advanced IDEs that understand scope. The advantage is that it saves you a fair bit of code (array merging, extracting variables isset()s.

  2. Create an argument object to encapsulate your arguments, which will contain all of your default values (for the entire class). When a method is called with an array as a parameter, you instantiate your argument object with the array. It's a little extra overhead, but you can be sure that all of your arguments are usable. The downside is that you are creating tight coupling between your class and the argument object. The advantage is that you'd only need a single line in each method.

  3. Similar to above, just have a single method that will return an object or an array with all of the indexes you require for the entire class, overidden by the values from the array passed in to the method. This is probably the easiest to implement, and only requires a single line of code in each method.

I'm sure you can find several reasons not to go with any the ideas above, but I just thought I'd put them out there anyway.

As for the syntax for named parameters, I think

$name => 'value' would be neat. It's different from $name = 'value', and should only be valid within the braces of a function/method.

```php $name => 'value'; // Should raise a fatal parse error.

mycoolfunction( $filename => '/tmp/somefile/, $recursive => true ); [/code] ```

Similar to an array... only without the array.

Also, note the omitted comma after the last named argumentin the list. It should work exactly the same way as a regular function/method call. Adding a comma generates a parse error.

Gravatar
Jukka

2013-09-20

Hey, with the array_merge and extract there is a certain issue when the $args contains extra parameters. The extra parameters will also be extracted, so the developer needs to be careful.

Consider this example: http://3v4l.org/oHCfq

It is potentially a bad idea to have those unintended array keys extracted.

Gravatar
Silviu

2013-09-24

Wow! Named params! Now that is something that I would love to see in PHP.

If I may vote, the last presented option (screen_name: 'phpdrama' ) seems to be the cleanes and best choice to be.

On long term, the current "interpret by order" parameter mechanism should be kept, or dropped?

Posting comments after three months has been disabled.