Language foundation

The foreach syntax structure provides a simple way to iterate over groups of numbers.

Prior to PHP5, foreach could only be used with array PHP5 +, using foreach to iterate over objects

Foreach can only be applied to data and objects, and attempts to apply to variables of other data types or uninitialized variables will raise an error message.

There are two types of syntax:

/* Iterate over the given array_expression data. Each time through the loop, the value of the current cell is assigned to $value and the pointer inside the array moves forward one step (so the next cell will be given in the next loop) */
foreach (array_expression as $value) {
    // statement
}

foreach (array_expression as $value) :
    // statement
endforeach;
Copy the code
/* Same as above, except that the current cell's key name is also assigned to the variable $key */ on each loop
foreach (array_expression as $key => $value) {
    // statement
}

foreach (array_expression as $key => $value) :
    // statement
endforeach;
Copy the code

You can also customize traversal objects!

When foreach starts executing, the pointer inside the array automatically points to the first cell. This means that you do not need to call reset() before the foreach loop because foreach relies on an internal array pointer, changing its value in the loop could cause unexpected behavior

You can easily modify array elements by prefixing $value with &. This method assigns values by reference rather than copying a value.


      

$arr = [1.2.3.4];
foreach($arr as &$value) {
    $value = $value * 2;
}

// $arr is now [2, 4, 6, 8]
unset($value); // Finally cancel the reference

Copy the code

A reference to $value is available only if the array being traversed can be referenced (for example, a variable).

The following code cannot run:

<? Foreach (array(1, 2, 3, 4) as & (array(1, 2, 3, 4) as &$value) {
    echo $value.The '-';
    $value = $value* 2.echo $value, PHP_EOL;
}
Copy the code

Warning: The $value reference to the last element of the array is retained after the foreach loop. It is recommended to use unset() to destroy it.

Note: Foreach does not support the ability to suppress error messages with @

Foreach is simple, but it can have some unexpected behavior, especially when references are involved in your code.

Studies,

Q1: Why is the result of the following code not 2/4/6?


      
$arr = [1.2.3];

foreach ($arr as $k => &$v) {
    $v = $v * 2;
}

foreach ($arr as $k => $v) {
    echo $v, PHP_EOL;
}

/* Output: 2 4 4 */
Copy the code

We can assume that foreach($arr as &$v) implies assigning the current key and value of the array to $k and $v, respectively. The specific expansion is as follows:

<? php foreach ($arr as $k= >$v) {
    $k = currentKey();
    $v= currentVal(); // Continue to run user code}Copy the code

Based on the above theory, let’s re-analyze the first Foreach:

cycle note $arr value
1-1 cycle Due to the$vIs a reference, therefore$v = &$arr[0].$v = $v * 2The equivalent of$arr[0] * 2 [2, 2, 3]
1-2 cycle $v = &$arr[1] [2, 4, 3)
1-3 cycles $v = &$arr[2] [2, 4, 6]
Loop 2-1 Implicit operations$v = $arr[0]Triggered because at this time$vIs still$arr[2]Is equivalent to$arr[2] = $arr[0] [2, 4, 2)
Loop 2-2 $v = $arr[1], i.e.,$arr[2] = $arr[1] [2, 4, 4]
Loop 2-3 $v = $arr[2], i.e.,$arr[2] = $arr[2] [2, 4, 4]

How to solve such problems? There’s a reminder in the PHP manual:

Warning: The $value reference to the last element of the array is retained after the foreach loop. It is recommended to use unset() to destroy it.


      
$arr = [1.2.3];

foreach ($arr as $k => &$v) {
    $v = $v * 2;
}
unset($v);
foreach ($arr as $k => $v) {
    echo $v, PHP_EOL;
}

/* Output: 2 4 6 */
Copy the code

As you can see from this question, references are likely to have side effects. If you don’t want the data content to change due to unintentional changes, it is best to unset these references in a timely manner.

Question 2: Why is the result of the following code not 0=> A 1=> B 2=> C


      
$arr = ['a'.'b'.'c'];

foreach ($arr as $k => $v) {
    echo key($arr), "= >", current($arr), PHP_EOL;
}

foreach ($arr as $k => &$v) {
    echo key($arr), "= >", current($arr), PHP_EOL;
}
/ * # php5.6 1 = 1 = > b > b = 1 = > b > b = = > > c # php7 0 = > a 0 = > a 0 = > a 0 = > a 0 = > a 0 = > a * /
Copy the code

According to the manual, key and current are the keys to get the current element in the data, respectively. Why is key($arr) always 0 and current($arr) always ‘a’?

Opcode is compiled using VLD:

➜  demo /usr/local/ Cellar/PHP / 7.2.7 / bin/PHP - DVLD. Active = 1 Amy polumbo HP Finding entry points Branch analysis from the position: 0 Jump found. (Code = 77) Position 1 = 2, Position 2 = 15 Branch analysis from position: 2 Jump found. (Code = 78) Position 1 = 3, Position 2 = 15 Branch analysis from position: 3 Jump found. (Code = 42) Position 1 = 2 Branch analysis from position: 2 Branch analysis from position: 15 Jump found. (Code = 62) Position 1 = -2 Branch analysis from position: 15 filename: /Users/jianyong/demo/a.phpfunctionname: (null) number of ops: 17 compiled vars: ! 0 =$arr, !1 = $v, !2 = $k
line     #* E I O op fetch ext return operands------------------------------------------------------------------------------------- 2 0 E > ASSIGN ! 0, <array> 4 1 > FE_RESET_R$4! 0, ->15 2 > > FE_FETCH_R ~5$4,! 1, ->15 3 > ASSIGN ! 2, ~5 5 4 INIT_FCALL'key'5 SEND_VAR ! 0 6 DO_ICALL$7
         7        ECHO                                                     $7
         8        ECHO                                                     '%3D%3E'
         9        INIT_FCALL                                               'current'10 SEND_VAR ! 0 11 DO_ICALL$8
        12        ECHO                                                     $8
        13        ECHO                                                     '%0A'
        14      > JMP                                                      ->2
        15    >   FE_FREE                                                  $4
   7    16      > RETURN                                                   1

branch: # 0; line: 2- 4; sop: 0; eop: 1; out1: 2; out2: 15
branch: # 2. line: 4- 4; sop: 2; eop: 2; out1: 3; out2: 15
branch: # 3. line: 4- 5; sop: 3; eop: 14; out1: 2
branch: # 15; line: 5- 7; sop: 15; eop: 16; out1: -2
path #1: 0, 2, 3, 2, 15
path #2: 0, 2, 15,
path #3: 0, 15,
0=>a
0=>a
0=>a
Copy the code

New PHP7 feature foreach

  • [x] foreachLoops no longer work on Pointers inside arrays. Prior to PHP7, Pointers were moved as data was iterated through foreach.

      
$array = [0.1.2];
foreach ($array as &$val) {
    var_dump(current($array));
}
Copy the code
version The results of instructions
PHP5 int(1) int(2) bool(false) The array pointer moves
PHP7 int(0) int(0) int(0) The data pointer no longer moves
  • [x] Changes to the array do not affect the loop as it loops by value.

When a foreach loops by value, the foreach is operating on a copy of the array. So making changes during the loop does not affect the loop result


      
$arr = [0.1.2];
$ref = &$arr;

foreach ($arr as $val) {
    var_dump($val);
    unset($arr[1]);
}
Copy the code
version The results of instructions
PHP5 int(0) int(2) Unset data will be skipped
PHP7 int(0) int(1) int(2) Changes to the array do not affect the loop
  • When [x] loops by reference, changes to the array affect the loop

      
$arr = [0.1.2];
$ref = &$arr;

foreach ($arr as &$val) {
    var_dump($val);
    unset($arr[1]);
}
Copy the code
version The results of
PHP5 int(0) int(2)
PHP7 int(0) int(2)
  • [x] loops for plain(non-traversable)

Looping over a simple object, whether by value or by reference, behaves the same as looping over an array by reference, but the management of position is more precise

  • [x] Traversable Objects behave the same as before

Traversable objects are one that implements Iterator or IteratorAggregate Interface

An object is called an Iterator if it implements Iterator or IteratorAggregate

reference

  • https://wiki.php.net/rfc/php7_foreach
  • 97fe15db4356f8fa1b3b8eb9bb1baa8141376077