Table of Contents

PHP RFC: Trailing Commas In List Syntax

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:

  1. Offer a consistent API and match the existing array functionality.
  2. Make it easy to append new elements to a list in user-land.
  3. Have cleaner diffs (so only one line shows up as being modified when appending elements instead of two).
  4. 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:

  1. Grouped namepaces
  2. Function/method arguments (declarations & calls)
  3. Interface implementations on a class
  4. Trait implementations on a class
  5. Class member lists
  6. 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
) { ... }
  1. Leading commas would break away from PSRs
  2. Parsing leading commas cause cognitive overhead whereas standard practice calls for trailing commas

The arguments for & against are weak

TL;DR:

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 {
}
Allow trailing commas in function/method arguments (declarations and calls)
Real name Yes No
adambaratz (adambaratz)  
ashnazg (ashnazg)  
bishop (bishop)  
bwoebi (bwoebi)  
cmb (cmb)  
danack (danack)  
daverandom (daverandom)  
derick (derick)  
dmitry (dmitry)  
eliw (eliw)  
galvao (galvao)  
jhdxr (jhdxr)  
joey (joey)  
kalle (kalle)  
kelunik (kelunik)  
keyur (keyur)  
kguest (kguest)  
laruence (laruence)  
lcobucci (lcobucci)  
leigh (leigh)  
malukenho (malukenho)  
marcio (marcio)  
mariano (mariano)  
mbeccati (mbeccati)  
mfonda (mfonda)  
mike (mike)  
nikic (nikic)  
ocramius (ocramius)  
pajoye (pajoye)  
pierrick (pierrick)  
pmjones (pmjones)  
pollita (pollita)  
rmf (rmf)  
salathe (salathe)  
sammyk (sammyk)  
sebastian (sebastian)  
stas (stas)  
svpernova09 (svpernova09)  
thekid (thekid)  
trowski (trowski)  
weierophinney (weierophinney)  
yunosh (yunosh)  
zeev (zeev)  
zimt (zimt)  
Final result: 24 20
This poll has been closed.

Grouped namepaces

<?php
use Foo\Bar\{
    Foo,
    Bar,
    Baz,
};
Allow trailing commas in grouped namepaces
Real name Yes No
adambaratz (adambaratz)  
ajf (ajf)  
ashnazg (ashnazg)  
bishop (bishop)  
bwoebi (bwoebi)  
colinodell (colinodell)  
daverandom (daverandom)  
dmitry (dmitry)  
eliw (eliw)  
galvao (galvao)  
joey (joey)  
kalle (kalle)  
kelunik (kelunik)  
keyur (keyur)  
kguest (kguest)  
lcobucci (lcobucci)  
leigh (leigh)  
levim (levim)  
malukenho (malukenho)  
marcio (marcio)  
mariano (mariano)  
mbeccati (mbeccati)  
mike (mike)  
nikic (nikic)  
ocramius (ocramius)  
pajoye (pajoye)  
pierrick (pierrick)  
pollita (pollita)  
rmf (rmf)  
salathe (salathe)  
sammyk (sammyk)  
sebastian (sebastian)  
stas (stas)  
thekid (thekid)  
trowski (trowski)  
weierophinney (weierophinney)  
yunosh (yunosh)  
zeev (zeev)  
zimt (zimt)  
Final result: 30 9
This poll has been closed.

Interface implementations on a class

class Foo implements
    FooInterface,
    BarInterface,
    BazInterface,
{
}
Allow trailing commas in interface implementations on a class
Real name Yes No
adambaratz (adambaratz)  
ajf (ajf)  
ashnazg (ashnazg)  
bishop (bishop)  
bwoebi (bwoebi)  
cmb (cmb)  
daverandom (daverandom)  
eliw (eliw)  
galvao (galvao)  
jhdxr (jhdxr)  
joey (joey)  
kalle (kalle)  
kelunik (kelunik)  
keyur (keyur)  
kguest (kguest)  
lcobucci (lcobucci)  
leigh (leigh)  
levim (levim)  
malukenho (malukenho)  
marcio (marcio)  
mariano (mariano)  
mbeccati (mbeccati)  
mfonda (mfonda)  
mike (mike)  
nikic (nikic)  
ocramius (ocramius)  
pajoye (pajoye)  
pierrick (pierrick)  
pollita (pollita)  
salathe (salathe)  
sammyk (sammyk)  
sebastian (sebastian)  
stas (stas)  
trowski (trowski)  
weierophinney (weierophinney)  
yunosh (yunosh)  
zeev (zeev)  
Final result: 8 29
This poll has been closed.

Trait implementations on a class

class Foo
{
    use
        FooTrait,
        BarTrait,
        BazTrait,
    ;
}
Allow trailing commas in trait implementations on a class
Real name Yes No
adambaratz (adambaratz)  
ajf (ajf)  
ashnazg (ashnazg)  
bishop (bishop)  
bwoebi (bwoebi)  
cmb (cmb)  
daverandom (daverandom)  
eliw (eliw)  
galvao (galvao)  
jhdxr (jhdxr)  
joey (joey)  
kalle (kalle)  
kelunik (kelunik)  
keyur (keyur)  
kguest (kguest)  
lcobucci (lcobucci)  
leigh (leigh)  
levim (levim)  
malukenho (malukenho)  
marcio (marcio)  
mariano (mariano)  
mbeccati (mbeccati)  
mike (mike)  
nikic (nikic)  
ocramius (ocramius)  
pajoye (pajoye)  
pierrick (pierrick)  
pollita (pollita)  
salathe (salathe)  
sammyk (sammyk)  
sebastian (sebastian)  
stas (stas)  
trowski (trowski)  
weierophinney (weierophinney)  
yunosh (yunosh)  
zeev (zeev)  
Final result: 7 29
This poll has been closed.

Class member lists

class Foo
{
    const
        A = 1010,
        B = 1021,
        C = 1032,
        D = 1043,
    ;
    protected
        $a = 'foo',
        $b = 'bar',
        $c = 'baz',
    ;
    private
        $blah,
    ;
}
Allow trailing commas in class member lists
Real name Yes No
adambaratz (adambaratz)  
ajf (ajf)  
ashnazg (ashnazg)  
bishop (bishop)  
bwoebi (bwoebi)  
cmb (cmb)  
daverandom (daverandom)  
eliw (eliw)  
galvao (galvao)  
jhdxr (jhdxr)  
joey (joey)  
kalle (kalle)  
kelunik (kelunik)  
keyur (keyur)  
kguest (kguest)  
lcobucci (lcobucci)  
leigh (leigh)  
levim (levim)  
malukenho (malukenho)  
marcio (marcio)  
mariano (mariano)  
mbeccati (mbeccati)  
mike (mike)  
nikic (nikic)  
ocramius (ocramius)  
pajoye (pajoye)  
pierrick (pierrick)  
pollita (pollita)  
salathe (salathe)  
sammyk (sammyk)  
sebastian (sebastian)  
stas (stas)  
trowski (trowski)  
weierophinney (weierophinney)  
yunosh (yunosh)  
zeev (zeev)  
Final result: 7 29
This poll has been closed.

Inheriting variables from the parent scope in anonymous functions

$foo = function ($bar) use (
    $a,
    $b,
    $c,
) {
	// . . . 
};
Allow trailing commas in use lists for anonymous functions
Real name Yes No
adambaratz (adambaratz)  
ashnazg (ashnazg)  
bishop (bishop)  
bwoebi (bwoebi)  
cmb (cmb)  
daverandom (daverandom)  
derick (derick)  
dmitry (dmitry)  
eliw (eliw)  
galvao (galvao)  
jhdxr (jhdxr)  
joey (joey)  
kalle (kalle)  
kelunik (kelunik)  
keyur (keyur)  
kguest (kguest)  
lcobucci (lcobucci)  
leigh (leigh)  
levim (levim)  
malukenho (malukenho)  
marcio (marcio)  
mariano (mariano)  
mbeccati (mbeccati)  
mike (mike)  
nikic (nikic)  
ocramius (ocramius)  
pajoye (pajoye)  
pierrick (pierrick)  
pmjones (pmjones)  
pollita (pollita)  
salathe (salathe)  
sammyk (sammyk)  
sebastian (sebastian)  
stas (stas)  
trowski (trowski)  
weierophinney (weierophinney)  
yunosh (yunosh)  
zeev (zeev)  
Final result: 16 22
This poll has been closed.

Patches and Tests

The original patch by Sara Golemon.