# RFC: Allow specifying keys in list()

- Status: Implemented (PHP 7.1)
- Authors: Andrea Faulds, ajf@ajf.me
- Date: 2016-01-17
- Version: 1.1.1
- Original: https://wiki.php.net/rfc/list_keys
- Page: https://externals.io/rfc/list_keys
- More for agents: search with https://externals.io/search.md?q={query} · RFCs at https://externals.io/rfc.md · JSON API at https://externals.io/api · documentation at https://externals.io/llms.txt

## Votes

### Accept the Allow specifying keys in list() RFC for PHP 7.1, and merge the patch into master? (closed)

Yes: 23 · No: 11

Voters: ajf (Yes), bishop (Yes), bwoebi (Yes), danack (Yes), davey (Yes), derick (No), dm (Yes), dmitry (No), fmargaine (Yes), francois (Yes), googleguy (Yes), gooh (No), guilhermeblanco (No), jhdxr (No), kguest (Yes), krakjoe (Yes), laruence (No), leigh (Yes), levim (No), marcio (Yes), mariano (Yes), mbeccati (Yes), mike (No), ocramius (Yes), pajoye (Yes), salathe (No), ssb (No), stas (Yes), svpernova09 (Yes), thijs (Yes), trowski (Yes), weierophinney (Yes), yohgaki (Yes), zimt (No)

## Discussions on the mailing list

- [\[RFC\] Allow specifying keys in list()](https://externals.io/message/90678.md) — started on 2016-01-17, 15 messages
- [\[RFC\] Allow specifying keys in list()](https://externals.io/message/90699.md) — started on 2016-01-18, 4 messages
- [\[RFC\]\[VOTE\] Allow specifying keys in list()](https://externals.io/message/91084.md) — started on 2016-02-05, 19 messages

---

## Introduction

Unpacking array elements into variables is a common operation in certain types of PHP code. For example, it is common to use an array as an “argument bag” when initialising an object:

```php
class ElePHPant
{
    private $name, $colour, $age, $cuteness;
 
    public function __construct(array $attributes) {
        $this->name = $attributes["name"];
        $this->colour = $attributes["colour"];
        $this->age = $attributes["age"];
        $this->cuteness = $attributes["cuteness"];
    }
 
    // ...
}
```

However, doing so line-by-line, as in the above example, is somewhat cumbersome, because the name of the array must be repeated each line.

Apparently ideal for this use-case, PHP has a language construct for unpacking multiple array elements into variables, namely `list()`, which is used like so:

```php
list($first, $second, $third) = $someArray;
```

And is equivalent to:

```php
$first = $someArray[0];
$second = $someArray[1];
$third = $someArray[2];
```

Unfortunately for use cases like our first example however, `list()` has no support for unpacking array elements with specified keys, instead it only works for numbered keys starting from zero.

This RFC proposes to introduce a way to specify keys in the `list()` construct, such that our first example could be rewritten like so, behaving identically:

```php
class ElePHPant
{
    private $name, $colour, $age, $cuteness;
 
    public function __construct(array $attributes) {
        list(
            "name" => $this->name,
            "colour" => $this->colour,
            "age" => $this->age,
            "cuteness" => $this->cuteness
        ) = $attributes;
    }
 
    // ...
}
```

## Proposal

Instead of having implied numbered keys from zero, `list()` can also now contain explicit keys, instead.

As well as being strings, these keys can also be numbers:

```php
$powersOfTwo = [1 => 2, 2 => 4, 3 => 8];
list(1 => $oneBit, 2 => $twoBit, 3 => $threeBit) = $powersOfTwo;
```

Keys can not only be literals like `2` or `"foo"`, but also any expression , including constants or variables (this was not the case in an earlier revision of the RFC, see *Resolved issues*).

```php
list(
    CURLOPT_GET => $isGet,
    CURLOPT_POST => $isPost,
    CURLOPT_URL => $url
) = $curlOptions;
```

```php
list($foo => $bar) = $bar;
```

`list()` elements with and without keys cannot be mixed, unlike in the `array()` syntax:

```php
// Parse error: syntax error, ...
list($unkeyed, "key" => $keyed) = $array;
```

Implementing this would be more complicated, and it also is not very useful behaviour given arrays typically have either explicit (usually string) keys or implicit sequential numbered keys, not both.

Empty elements are not allowed where keys are specified, because there is no need for them:

```php
// Parse error: syntax error, ...
list(,,,, "key" => $keyed) = $array;
```

However, a trailing comma is permitted in `list()` with keys, like in `array()`:

```php
list(
    CURLOPT_GET => $isGet,
    CURLOPT_POST => $isPost,
    CURLOPT_URL => $url,
) = $curlOptions;
```

Nesting of `list()` is still possible for this syntax, including nesting a `list()` with keys inside a `list()` without keys, and vice-versa:

```php
$points = [
    ["x" => 1, "y" => 2],
    ["x" => 2, "y" => 1]
];
 
list(list("x" => $x1, "y" => $y1), list("x" => $x2, "y" => $y2)) = $points;
 
$points = [
    "first" => [1, 2],
    "second" => [2, 1]
];
 
list("first" => list($x1, $y1), "second" => list($x2, $y2)) = $points;
```

Explicit keys in `list()` also work in `foreach` statements, as does every other feature of `list()`:

```php
$points = [
    ["x" => 1, "y" => 2],
    ["x" => 2, "y" => 1]
];
 
foreach ($points as list("x" => $x, "y" => $y)) {
    echo "Point at ($x, $y)", PHP_EOL;
}
```

As with regular `list()`, objects implementing [ArrayAccess](https://php.net/ArrayAccess) are supported.

Handling of implicit conversions of keys, and of accessing undefined keys, follows the same rules as for regular array indexing.

### Further Examples

The use of explicit integer keys can be clearer than regular un-keyed `list()` in the same situations. Compare these two code snippets performing routing:

```php
$result = $dispatcher->dispatch($httpMethod, $uri);
switch ($result[0]) {
    case \FastRoute\Dispatcher::FOUND:
        list(, $handler, $parts) = $result;
 
        // ...
}
```

```php
$result = $dispatcher->dispatch($httpMethod, $uri);
switch ($result[0]) {
    case \FastRoute\Dispatcher::FOUND:
        list(1 => $handler, 2 => $parts) = $result;
 
        // ...
}
```

The comma in the version using implicit keys could be missed when reading the code, and here we are mixing two different kinds of indexing: explicit (`$result[0]`) and positional (`list(, $handler, $parts)`. In the version using explicit keys, it is harder to miss that the second and third element of the array are being used, and all three keys are expressed in the same, explicit fashion.

## Backward Incompatible Changes

None. The existing `list()` continues to work.

## Proposed PHP Version(s)

This is proposed for the next minor or major PHP version, currently PHP 7.1.

## RFC Impact

### To SAPIs

No specific impact I am aware of.

### To Existing Extensions

PHP extensions generally do not directly interact with the language syntax, so impact here is unlikely.

However, the addition of this syntax means that the content of the `ZEND_AST_LIST` abstract syntax tree node will now be different for arrays with keys, so this will affect extensions that deal with the AST.

### To Opcache

**I have not yet verified the RFC's compatibility with opcache.** The `ZEND_FETCH_LIST` opcode can now take a string OP2, and OP2 can now be TMPVAR as well as CONST (so that constants resolved at runtime work), which might create problems if Zend Optimizer handles this opcode.

### New Constants

No new constants are introduced.

### php.ini Defaults

No impact on php.ini.

## Resolved Issues

### Should arbitrary expressions be allowed as keys?

Given this syntax, it was thought there might be potential confusion as to what `$foo` does:

```php
list($foo => $bar) = $array;
```

The problem was that there were two different behaviours that might be expected here:

1. `$foo` is set to the key of the first element in `$array`, and `$bar` is set to its key `$array` (i.e. `$bar = reset($array); $foo = key($array);`)
2. `$bar` is set to the value of the element in `$array` with the key `$foo` (i.e. `$bar = $array[$foo];`)

So, this was initially not allowed in order to prevent misunderstanding. However, after discussions and further thought, it seemed as though misinterpreting it as doing the first thing (taking the key of the first element) was unlikely, and so we do not need to restrict arbitrary expressions. It also is more useful to accept them, since this means things like object keys (e.g. with SplObjectStorage) can work.

## Open Issues

None.

## Unaffected PHP Functionality

This does not impact the `array()` syntax or the behaviour of array indexing.

## Future Scope

*The following is merely **potential future scope** and **is not part of the proposal proper, nor being voted on in this RFC**.*

It would be useful to support `list()` in function parameter lists, so that an “argument bag” parameter could be immediately destructured:

```php
class ElePHPant
{
    private $name, $colour, $age, $cuteness;
 
    public function __construct(list("name" => $name, "colour" => $colour, "age" => $age, "cuteness" => $cuteness)) {
        $this->name = $name;
        $this->colour = $colour;
        $this->age = $age;
        $this->cuteness = $cuteness;
    }
 
    // ...
}
 
$myElePHPant = new ElePHPant(["name" => "Andrea", "colour" => "fuchsia", "age" => 19, "cuteness" => rand()]);
```

This would be more practical to implement than the [Named Parameters](https://externals.io/rfc/named_params) RFC, and could be applied to existing functions which already use the “argument bag” pattern. Destructuring syntax in parameter declarations is a feature in some other programming languages, for example [in ECMAScript 6](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Destructuring_assignment#Pulling_fields_from_objects_passed_as_function_parameter). Destructuring of arrays is also a subset of what is possible with pattern matching, which is a feature of many functional programming languages.

If we implemented this, we might want to extend the syntax to support type declarations:

```php
class ElePHPant
{
    private $name, $colour, $age, $cuteness;
 
    public function __construct(list("name" => string $name, "colour" => \Colour $colour, "age" => int $age, "cuteness" => float $cuteness)) {
        $this->name = $name;
        $this->colour = $colour;
        $this->age = $age;
        $this->cuteness = $cuteness;
    }
 
    // ...
}
```

It was the future possibility of using `list()` in function parameter lists that motivated the creation of this RFC. It is not part of this RFC, however, but would be proposed in a subsequent RFC if this one passes.

It has been suggested that at some point `list()` may be given an alternate syntax of `[]`, just as happened for `array()`. This would mean that there would be symmetry between the syntax for creating an array (`[1, 2, 3]`) and destructuring it (`[$a, $b, $c]`), as there is in some other programming languages. This proposal to allow specifying keys would not present any issues for this alternate syntax that I am aware of.

## Vote

The vote is a simple Yes/No vote on whether to accept the RFC and merge the patch into PHP master. As this adds a feature to the language, this RFC requires a 2/3 majority to succeed.

Voting started on 2016-02-05 and ended on 2016-02-14.

**Vote: Accept the Allow specifying keys in list() RFC for PHP 7.1, and merge the patch into master?** — Yes: 23 · No: 11 (closed)

## Patches and Tests

php-src has a complete pull request including tests: <https://github.com/php/php-src/pull/1730>

There is a language specification pull request, also with tests: <https://github.com/php/php-langspec/pull/152>

## Implementation

Merged into php-src for PHP 7.1: <https://github.com/php/php-src/commit/37c8bb58686b2d86f145ebe4fe39854f5951dcd7>

Merged into php-langspec for PHP 7.1: <https://github.com/php/php-langspec/commit/0b1a497a6c847f6566f32057f55259f68bb9ce38>

After the project is implemented, this section should contain

1. a link to the PHP manual entry for the feature

## References

- [Named Parameters RFC](https://externals.io/rfc/named_params)

## Rejected Features

None as yet.

## Changelog

- v1.1.1 (2016-02-12) - Added further example
- v1.1 - Resolved issue of whether to permit arbitrary expressions in keys (yes)
- v1.0 - First announced version
