PHP RFC: Trailing Commas In List Syntax
- Version: 0.1
- Date: 2015-11-03 (discussion); 2017-01-27 (voting)
- Author: Sammy Kaye Powers, me@sammyk.me
- Status: Implemented in PHP 7.2 (Grouped namepaces only)
Introduction
This RFC proposes allowing trailing commas for all list syntax.
Per the feedback on the internals list, this RFC broadens the scope of the original RFC to allow trailing commas in function arguments to all list syntax.
Arrays in PHP have long since supported trailing commas.
Trailing array commas
$foo = [ 'foo', 'bar', ];
This makes for clean diffs and easy appending of new values in user-land.
Unfortunately, the other lists do not share the same luxury.
Raises a parse error
<?php use Foo\Bar\{ Foo, Bar, Baz, };
Proposal
This RFC proposes allowing trailing commas in all list syntax in order to:
- Offer a consistent API and match the existing array functionality.
- Make it easy to append new elements to a list in user-land.
- Have cleaner diffs (so only one line shows up as being modified when appending elements instead of two).
- It makes code reviews a tiny bit faster and for really large codebases, that time adds up. (Sara Golemon explained why HHVM added the trailing comma syntax.)
The following lists would allow trailing commas:
- Grouped namepaces
- Function/method arguments (declarations & calls)
- Interface implementations on a class
- Trait implementations on a class
- Class member lists
- Inheriting variables from the parent scope in anonymous functions
Marcio Almada posted a gist with examples of trailing commas for the various lists (shown below):
Trailing commas for all lists
<?php // Grouped namepaces use Foo\Bar\{ Foo, Bar, Baz, }; // Arrays (already possible) $array = [1, 2, 3,]; // Function/method arguments (call) fooCall($arg1, $arg2, $arg3,); class Foo implements // Interface implementations on a class FooInterface, BarInterface, BazInterface, { // Trait implementations on a class use FooTrait, BarTrait, BazTrait, ; // Class member lists const A = 1010, B = 1021, C = 1032, D = 1043, ; protected $a = 'foo', $b = 'bar', $c = 'baz', ; private $blah, ; // Function/method arguments (declaration) function something(FooBarBazInterface $in, FooBarBazInterface $out,) : bool { } } // Inheriting variables from the parent scope in anonymous functions $foo = function ($bar) use ( $a, $b, $c, ) { // . . . };
Discussion Points
There are a number of questions that have already been discussed on the internals list.
How will it work with variadics?
PHP allows for multiple splats in one call so trailing commas would work the same way.
foo( ...$args, ...$moreArgs, ...$evenMoreArgs, );
Why allocate resources to make this happen
The actual implementation for adding tailing commas to function arguments/calls is two lines. Implementing the functionality to all lists would not require many more changes to the php-src codebase.
You could just use leading commas
function foo( $bar ,$baz ,$boo ) { ... }
- Leading commas would break away from PSRs
- Parsing leading commas cause cognitive overhead whereas standard practice calls for trailing commas
The arguments for & against are weak
TL;DR:
- For it: It's handy. (And the reasons detailed above).
- Against: It's ugly. There are only minor gains.
Backward Incompatible Changes
This change would have no breaking changes.
Proposed PHP Version
PHP 7.2
Proposed Voting Choices
Each trailing comma list syntax has its own vote and requires a 2/3 majority to pass.
Function/method arguments (declarations & calls)
// Function/method arguments (call) fooCall( $arg1, $arg2, $arg3, ); // Function/method arguments (declaration) function something( FooBarBazInterface $in, FooBarBazInterface $out, ) : bool { }
Grouped namepaces
<?php use Foo\Bar\{ Foo, Bar, Baz, };
Interface implementations on a class
class Foo implements FooInterface, BarInterface, BazInterface, { }
Trait implementations on a class
class Foo { use FooTrait, BarTrait, BazTrait, ; }
Class member lists
class Foo { const A = 1010, B = 1021, C = 1032, D = 1043, ; protected $a = 'foo', $b = 'bar', $c = 'baz', ; private $blah, ; }
Inheriting variables from the parent scope in anonymous functions
$foo = function ($bar) use ( $a, $b, $c, ) { // . . . };