rfc:foreachlist

This is an old revision of the document!


RFC: foreach_variable supporting T_LIST

Introduction

This feature introduces list() support in foreach constructs:

<?php
$users = array(
    array('Foo', 'Bar'),
    array('Baz', 'Qux');
);
 
// Before
foreach ($users as $user) {
    list($firstName, $lastName) = $user;
    echo "First name: $firstName, last name: $lastName. ";
}
 
// After
foreach ($users as list($firstName, $lastName)) {
    echo "First name: $firstName, last name: $lastName. ";
}

This feature eliminates the use of a redundant variable ($user in the example above), reduces code verbosity in typical cases of iterating structured data, such as SQL result sets, and it doesn't introduce new keywords, but simply reuses a familiar PHP construct in a new context.

It's a commonly requested feature, and there is evidence that people already expect list() should work in this scenario: #10203 allow foreach($array as list($a,$b)

This RFC provides a behavior specification and implementation for this feature.

Proposal

1. ZEND_FE_FETCH

<?php
$rows = array(array(24,2333), array(31,4666));
 
foreach ($rows as list($a, $b)) {
    printf("a=>%s, b=>%s\n", $a, $b);
}
 
/** Output:
a=>24, b=>2333
a=>31, b=>4666
*/

2. ZEND_FE_FETCH_WITH_KEY

<?php
$rows = array(array(24,2333), array(31,4666));
 
foreach ($rows as $k => list($a, $b)) {
    printf("key:%s, a=>%s, b=>%s\n", $k, $a, $b);
}
 
/** Output:
key:0, a=>24, b=>2333
key:1, a=>31, b=>4666
*/

Samples with generated opcodes:

list.php

<?php
foreach (array(array(1, 2), array(3, 4)) as list($a, $b)) {
    echo $a, $b;
}
   2     0  >   INIT_ARRAY                                       ~0      1
         1      ADD_ARRAY_ELEMENT                                ~0      2
         2      INIT_ARRAY                                       ~1      ~0
         3      INIT_ARRAY                                       ~2      3
         4      ADD_ARRAY_ELEMENT                                ~2      4
         5      ADD_ARRAY_ELEMENT                                ~1      ~2
         6    > FE_RESET                                         $3      ~1, ->16
         7  > > FE_FETCH                                         $4      $3, ->16
         8  >   ZEND_OP_DATA                                             
         9      FETCH_DIM_R                                      $5      $4, 1
        10      ASSIGN                                                   !1, $5
        11      FETCH_DIM_R                                      $7      $4, 0
        12      ASSIGN                                                   !0, $7
   3    13      ECHO                                                     !0
        14      ECHO                                                     !1
   4    15    > JMP                                                      ->7
        16  >   SWITCH_FREE                                              $3              1                1

list_with_key.php

<?php
$array = array(
    array(1, 2),
    array(3, 4)
);
 
foreach ($array as $k => list($a, $b)) {
    echo $k, $a, $b;
}
   3     0  >   INIT_ARRAY                                       ~0      1
         1      ADD_ARRAY_ELEMENT                                ~0      2
         2      INIT_ARRAY                                       ~1      ~0
   4     3      INIT_ARRAY                                       ~2      3
         4      ADD_ARRAY_ELEMENT                                ~2      4
   5     5      ADD_ARRAY_ELEMENT                                ~1      ~2
         6      ASSIGN                                                   !0, ~1
   7     7    > FE_RESET                                         $4      !0, ->19
         8  > > FE_FETCH                                         $5      $4, ->19
         9  >   ZEND_OP_DATA                                     ~10     
        10      FETCH_DIM_R                                      $6      $5, 1
        11      ASSIGN                                                   !3, $6
        12      FETCH_DIM_R                                      $8      $5, 0
        13      ASSIGN                                                   !2, $8
        14      ASSIGN                                                   !1, ~10
   8    15      ECHO                                                     !1
        16      ECHO                                                     !2
        17      ECHO                                                     !3
   9    18    > JMP                                                      ->8
        19  >   SWITCH_FREE                                              $4
  10    20    > RETURN                                                   1

Implementation details of the RFC

In order to avoid the reduce/reduce conflict, new bison rules will be added to the existing “foreach_variable”, to avoid this side effect:

<?php
foreach (array(1,3,4) as &$key => $foo) {
 
}

Without the rules:

PHP Parse error:  syntax error, unexpected '&', expecting T_STRING or T_VARIABLE or '$' in /home/huixc/test.php on line 2

With the rules:

PHP Fatal error:  Key element cannot be a reference in /home/huixc/test.php on line 2

Fatal error: Key element cannot be a reference in /home/huixc/test.php on line 2

Possible additional features (vote separately): silent token

It is possible to add support of the silent token in the new context:

foreach (array(array(1,3,4), array(1, 2)) as @list($a, $b, $c)) {
}

In case we want this, an additional patch will be provided.

Opcodes generated:

list_with_silent.php

<?php
foreach (array(array(1, 2), array(3, 4), array(5)) as @list($a, $b)) {
    echo $a, $b; // Without the silent token, there will be a notice since the third nested array has no second item.
}
   2     0  >   INIT_ARRAY                                       ~0      1
         1      ADD_ARRAY_ELEMENT                                ~0      2
         2      INIT_ARRAY                                       ~1      ~0
         3      INIT_ARRAY                                       ~2      3
         4      ADD_ARRAY_ELEMENT                                ~2      4
         5      ADD_ARRAY_ELEMENT                                ~1      ~2
         6      INIT_ARRAY                                       ~3      5
         7      ADD_ARRAY_ELEMENT                                ~1      ~3
         8    > FE_RESET                                         $4      ~1, ->22
         9  > > FE_FETCH                                         $5      $4, ->22
        10  >   ZEND_OP_DATA                                             
        11      BEGIN_SILENCE                                    ~6      
        12      FETCH_DIM_R                                      $9      $5, 1
        13      FETCH_W                      local               $8      'b'
        14      ASSIGN                                                   $8, $9
        15      FETCH_DIM_R                                      $11     $5, 0
        16      FETCH_W                      local               $7      'a'
        17      ASSIGN                                                   $7, $11
        18      END_SILENCE                                              ~6
   3    19      ECHO                                                     !0
        20      ECHO                                                     !1
   4    21    > JMP                                                      ->9
        22  >   SWITCH_FREE                                              $4
   5    23    > RETURN                                                   1

Patches

Tests

Changelog

  • 2011-07-06 Xinchen Hui: Initial RFC creation
  • 2011-07-06 Xinchen Hui: Updated patch
  • 2011-07-06 Xinchen Hui: Added tests phpt
  • 2011-07-07 Xinchen Hui: Added supporting for slience token(@)
  • 2011-07-07 Xinchen Hui: Updated sencode patch
  • 2011-07-08 Xinchen Hui: Added opcodes with silent token
  • 2011-07-24 Xinchen Hui: Updated patches that fixed a bug: znode->EA may be depend on an uninitialized value (thanks to Felipe)
rfc/foreachlist.1342611773.txt.gz · Last modified: 2017/09/22 13:28 (external edit)