文字

foreach

(PHP 4, PHP 5)

foreach 语法结构提供了遍历数组的简单方式。foreach 仅能够应用于数组和对象,如果尝试应用于其他数据类型的变量,或者未初始化的变量将发出错误信息。有两种语法:

foreach (array_expression as $value)
    statement
foreach (array_expression as $key => $value)
    statement

第一种格式遍历给定的 array_expression 数组。每次循环中,当前单元的值被赋给 $value 并且数组内部的指针向前移一步(因此下一次循环中将会得到下一个单元)。

第二种格式做同样的事,只除了当前单元的键名也会在每次循环中被赋给变量 $key

还能够自定义遍历对象。

Note:

foreach 开始执行时,数组内部的指针会自动指向第一个单元。这意味着不需要在 foreach 循环之前调用 reset()

由于 foreach 依赖内部数组指针,在循环中修改其值将可能导致意外的行为。

可以很容易地通过在 $value 之前加上 & 来修改数组的元素。此方法将以引用赋值而不是拷贝一个值。

<?php
$arr 
= array( 1 2 3 4 );
foreach (
$arr  as & $value ) {
    
$value  $value  2 ;
}
// $arr is now array(2, 4, 6, 8)
unset( $value );  // 最后取消掉引用
?>

$value 的引用仅在被遍历的数组可以被引用时才可用(例如是个变量)。以下代码则无法运行:

<?php
foreach (array( 1 2 3 4 ) as & $value ) {
    
$value  $value  2 ;
}

?>
Warning

数组最后一个元素的 $value 引用在 foreach 循环之后仍会保留。建议使用 unset() 来将其销毁。

Note:

foreach 不支持用“@”来抑制错误信息的能力。

用户可能注意到了以下的代码功能完全相同:

<?php
$arr 
= array( "one" "two" "three" );
reset ( $arr );
while (list(, 
$value ) =  each ( $arr )) {
    echo 
"Value:  $value <br>\n" ;
}

foreach (
$arr  as  $value ) {
    echo 
"Value:  $value <br />\n" ;
}
?>

以下代码功能也完全相同:

<?php
$arr 
= array( "one" "two" "three" );
reset ( $arr );
while (list(
$key $value ) =  each ( $arr )) {
    echo 
"Key:  $key ; Value:  $value <br />\n" ;
}

foreach (
$arr  as  $key  =>  $value ) {
    echo 
"Key:  $key ; Value:  $value <br />\n" ;
}
?>

示范用法的更多例子:

<?php


$a  = array( 1 2 3 17 );

foreach (
$a  as  $v ) {
   echo 
"Current value of \$a:  $v .\n" ;
}



$a  = array( 1 2 3 17 );

$i  0

foreach ( $a  as  $v ) {
    echo 
"\$a[ $i ] =>  $v .\n" ;
    
$i ++;
}



$a  = array(
    
"one"  =>  1 ,
    
"two"  =>  2 ,
    
"three"  =>  3 ,
    
"seventeen"  =>  17
);

foreach (
$a  as  $k  =>  $v ) {
    echo 
"\$a[ $k ] =>  $v .\n" ;
}


$a  = array();
$a [ 0 ][ 0 ] =  "a" ;
$a [ 0 ][ 1 ] =  "b" ;
$a [ 1 ][ 0 ] =  "y" ;
$a [ 1 ][ 1 ] =  "z" ;

foreach (
$a  as  $v1 ) {
    foreach (
$v1  as  $v2 ) {
        echo 
" $v2 \n" ;
    }
}



foreach (array( 1 2 3 4 5 ) as  $v ) {
    echo 
" $v \n" ;
}
?>

用 list() 给嵌套的数组解包

(PHP 5 >= 5.5.0)

PHP 5.5 增添了遍历一个数组的数组的功能并且把嵌套的数组解包到循环变量中,只需将 list() 作为值提供。

例如:

<?php
$array 
= [
    [
1 2 ],
    [
3 4 ],
];

foreach (
$array  as list( $a $b )) {
    
// $a contains the first element of the nested array,
    // and $b contains the second element.
    
echo  "A:  $a ; B:  $b \n" ;
}
?>

以上例程会输出:

A: 1; B: 2
A: 3; B: 4

list() 中的单元可以少于嵌套数组的,此时多出来的数组单元将被忽略:

<?php
$array 
= [
    [
1 2 ],
    [
3 4 ],
];

foreach (
$array  as list( $a )) {
    
// Note that there is no $b here.
    
echo  " $a \n" ;
}
?>

以上例程会输出:

1
3

如果 list() 中列出的单元多于嵌套数组则会发出一条消息级别的错误信息:

<?php
$array 
= [
    [
1 2 ],
    [
3 4 ],
];

foreach (
$array  as list( $a $b $c )) {
    echo 
"A:  $a ; B:  $b ; C:  $c \n" ;
}
?>

以上例程会输出:

Notice: Undefined offset: 2 in example.php on line 7
A: 1; B: 2; C: Notice: Undefined offset: 2 in example.php on line 7
A: 3; B: 4; C: 

用户评论:

[#1] pnc at balintx dot me [2015-09-21 10:14:42]

Just a simple strange behavior I have ran into:

If you accidentally put a semicolon after the foreach statement, you get no errors, but the loop will only run on the last element of the array:
<?php
$array 
= array(1,2,3);
foreach (
$array as $key);
{
    echo 
$key;
}
// output: 3
?>


Correctly:
<?php
$array 
= array(1,2,3);
foreach (
$array as $key)
{
    echo 
$key;
}
// output: 123
?>


It took me a while to find that semicolon.

[#2] 84td84 at gmail dot com [2015-03-16 03:49:44]

@tedivm at tedivm dot com

With foreach you can specify the & for $value. In this case you modify the original array. Otherwise you operate on a "copy" (not on the original array).
Consider the following example, that is identical with yours, except i use the & for $value:

$arr = array(1,2,3,4,5,6,7,8,9);
foreach($arr as $key=>&$value)
{
    unset($arr[$key + 1]);
    echo "\n\n--------\nKey: $key\nValue: $value\n";
    var_dump($arr);
    echo   "--------";
}
echo "\n\nARR at the end:\n";
var_dump($arr);

In foreach it prints the following values:
1, 3, 5, 7, 9

And you can see, that in the loop the corresponding elements are removed from arr.
So at the end you'll have an array, that contains only 1, 3, 5, 7, 9.

[#3] Anonymous [2015-01-08 12:38:00]

modifying array while foreach'ing it(yeah, such slime code;-)
if elements were added on last iteration or into array with 1 element, then added elements wont be iterated as foreach checks for pointer before iteration cycle
so it just quit and added elements wont be treated

[#4] Anteaus [2015-01-02 09:35:51]

"In order to be able to directly modify array elements within the loop precede $value with &. In that case the value will be assigned by reference. "

PLEASE CLARIFY! -Does this still hold true in php5.4+, where explicit passing by reference is seemingly forbidden- or is this now illegal syntax?

[#5] Fred [2014-12-16 20:53:32]

If you want to use the list for multidimension arrays, you can nest several lists:

<?php
$array 
= [
    [
12, array(34)],
    [
34, array(56)],
];

foreach (
$array as list($a$b, list($c$d))) {
    echo 
"A: $a; B: $b; C: $c; D: $d;<br>";
};
?>


Will output:
A: 1; B: 2; C: 3; D: 4;
A: 3; B: 4; C: 5; D: 6;

And:

<?php
$array 
= [
    [
12, array(3, array(45))],
    [
34, array(5, array(67))],
];

foreach (
$array as list($a$b, list($c, list($d$e)))) {
    echo 
"A: $a; B: $b; C: $c; D: $d; E: $e;<br>";
};
Will output:
A1B2C3D4E5;
A3B4C5D6E7;
?>

[#6] admin at gadelkareem dot com [2014-09-17 23:23:08]

Not sure if this is considered a bug
<?php
    $array 
= ['numbers' => [123]];
    foreach (
$array as &$numbers) {
        
$numbers = [456];
    }
    
var_dump($array);
    
modify_array($array);
    
var_dump($array);
    
    function 
modify_array($array_arg)
    {
        
$array_arg['numbers'] = [123];
    }

Prints

    
array(1) {
      [
"numbers"]=>
      &array(
3) {
        [
0]=>
        
int(4)
        [
1]=>
        
int(5)
        [
2]=>
        
int(6)
      }
    }
    array(
1) {
      [
"numbers"]=>
      &array(
3) {
        [
0]=>
        
int(1)
        [
1]=>
        
int(2)
        [
2]=>
        
int(3)
      }
    }
?>

Notice that $array was not passed by reference to modify_array() however the nested array was modified. It sounds logical but not sure if this is documented!

[#7] liam666 at donnelly-house dot net [2014-06-12 13:12:16]

This is a decent, simple, and easy way to reference other values of an associative array when using foreach. (effective "next", "prev", etc.)
The only care that needs to be taken is if the array is HUGE in size, so you don't run into memory use problems. (and potential speed issues)

This example uses the 'primary' array, $aPublishSeq, which is ksort-ed to put the array in order according to the associative keys. The array is then copied using a foreach loop to make a duplicate array where the key and value order correspond to the first array, but the keys are sequential numeric starting at zero.

ksort ($aPublishSeq, SORT_STRING);     // put them all in the right order keeping array keys
foreach ($aPublishSeq as $aValue)
   $aPublishIdx[] = $aValue;          // duplicate array using corresponding sequential numeric keys

Now, in the usage foreach loop, an index variable is used to keep in sync with the associative array.

$PubIdx = -1;     // start at -1 to start at 0 below

foreach ($aPublishSeq as $sKey => $sValue) {

      ++$PubIdx;     // index into $aPublishIdx array of corresponding element in $aPublishSeq array (for "next" element check, etc.)

   echo $aPublishIdx[$PubIdx  - 1]     // previous array value
   echo $aPublishIdx[$PubIdx]          // current array value
   echo $aPublishIdx[$PubIdx  + 1]     // next array value

....

It's simple, but it works, and without much muss or fuss.

[#8] nehuen [2014-04-20 03:27:48]

foreach by reference internally deleted and created a new reference in each iteration, so it is not possible to directly use this value as a variable parameter values??, look at the following example where the problem is observed and a possible solution:

<?php
 class test
 {
private $a = false;
private $r = null;
public function show(&$v)
{
if(!$this->a)
{
$this->a = true;
$this->r = &$v;
}
var_dump($this->r);
}
public function reset()
{
$this->a = false;
}
 }
 
 $t = new test();
 
 $a = array(array(1,2),array(3,4),array(5,6));
 foreach($a as &$p)
$t->show($p);

 
  
  $t->reset();
  foreach($a as $p)
  {
$b = &$p;
$t->show($b);
  }
  
 

[#9] Alastair Hole [2014-04-02 09:52:47]

What happened to this note:
"Unless the array is referenced, foreach operates on a copy of the specified array and not the array itself. foreach has some side effects on the array pointer. Don't rely on the array pointer during or after the foreach without resetting it."

Is this no longer the case?
It seems only to remain in the Serbian documentation: http://php.net/manual/sr/control-structures.foreach.php

[#10] sainthyoga2003 at gmail dot com [2014-03-29 23:14:33]

Note that prior to PHP 5.5 you will get a T_LIST parse error for:
<?php
foreach($list as list($a$b)) {...
?>

[#11] Delian Krustev [2013-07-02 13:43:44]

I want to add some inline comments to dtowell's piece of code about the iteration by reference:

<?php

$a 
= array('abe','ben','cam');

foreach (
$a as $k=>&$n)
    
$n strtoupper($n);

# At the end of this cycle the variable $n refers to the same memory as $a[2]
# So when the second "foreach" assigns a value to $n :

foreach ($a as $k=>$n// notice NO reference here!
    
echo "$n\n";

# it is also modifying $a[2] .
# So on the three repetitions of the second "foreach" the array will look like:
# 1. ('abe','ben','abe') - assigned the value of the first element to the last element
# 2. ('abe','ben','ben') - assigned the value of the second element to the last element
# 3. ('abe','ben','ben') - assigned the value of the third element to itself

print_r($a);
?>

[#12] Ashus [2013-04-02 13:25:09]

If you wondered how to create a list of all possible combinations of variable amount of arrays (multiple foreach), you might use this:

<?php

$a
[0] = array('a1','a2');
$a[1] = array('b1','b2','b3');
$a[2] = array('c1','c2');

function 
getAllCombinations($a,$i,$s)
    {
    foreach (
$a[$i] as $v)
        {
        if (!isset(
$a[$i+1]))
            {
            echo 
$s.$v."\n";
            } else {
            
getAllCombinations($a,$i+1,$s.$v);
            }
        }
    return 
$s;
    }

echo 
getAllCombinations($a,0,'');

?>


the result:

a1b1c1
a1b1c2
a1b2c1
a1b2c2
a1b3c1
a1b3c2
a2b1c1
a2b1c2
a2b2c1
a2b2c2
a2b3c1
a2b3c2

[#13] php at darkain dot com [2013-03-16 21:56:33]

"Reference of a $value and the last array element remain even after the foreach loop. It is recommended to destroy it by unset()."

I cannot stress this point of the documentation enough! Here is a simple example of exactly why this must be done:

<?php
$arr1 
= array("a" => 1"b" => 2"c" => 3);
$arr2 = array("x" => 4"y" => 5"z" => 6);

foreach (
$arr1 as $key => &$val) {}
foreach (
$arr2 as $key => $val) {}

var_dump($arr1);
var_dump($arr2);
?>


The output is:
array(3) { ["a"]=> int(1) ["b"]=> int(2) ["c"]=> &int(6) }
array(3) { ["x"]=> int(4) ["y"]=> int(5) ["z"]=> int(6) }

Notice how the last index in $arr1 is now the value from the last index in $arr2!

[#14] dtowell [2012-08-11 00:44:58]

References created by foreach hang around past their best-used-by date. For example, the following:

<?php
$a 
= array('abe','ben','cam');
foreach (
$a as $k=>&$n)
    
$n strtoupper($n);
foreach (
$a as $k=>$n// notice NO reference here!
    
echo "$n\n";
print_r($a);
?>


will result in:

ABE
BEN
BEN
Array
(
    [0] => ABE
    [1] => BEN
    [2] => BEN
)

[#15] Voitcus at wp dot pl [2012-02-15 20:55:25]

You can even iterate through "dynamic" arrays that do not physically exist, but are objects that implement Iterator interface. They don't need to be stored in memory when foreach starts.

Consider the array that contains some values (I called it $allValues in the example below) and we want to have only some of them (eg. the ones that are dividable by 2). I create an object that would serve as dynamic array, that means it would "dynamically update" its values together with $allValues. The main advantage is that I store only one array, and it's the only array I serialize.

An object of MyIter class will not contain any values itself:
<?php
class MyIter implements Iterator // you can implement ArrayAccess and Countable interfaces too, this will make class MyIter behave like a "real" array
  
private $position 0// an internal position of the current element
  // please note that $position has nothing common with $allValues!

  
private function getTable(){ // prepare a temporary "static" table of all objects in the class
    
global $allValues;
    
$result=array(); // temporary variable
    
foreach($allValues as $obj){
      if(
$obj == 0// check if the value is even
        
$result[]=$obj// if yes, I want it
      
}
    return 
$result;
  }    

  
// the all below declared methods are public and belong to the Iterator interface
  
function rewind() { // a method to start iterating
    
$this->position 0// just move to the beginning
  
}

  function 
current() { // retrieves the current element
    
$table=$this->getTable(); // let us prepare a table
    
return $table[$this->position]; // and return the current element
  
}

  function 
key() { // retrieves the current element's key
    
return $this->position// this is used by foreach(... as $key=>$value), not important here
  
}

  function 
next() { // move to next element
    
++$this->position;
  }

  function 
valid() { // check if the current element is valid (ie. if it exists)
    
return array_key_exists($this->position$this->getTable());
  }
// end of class

// now prepare the array of 12 elements
$allValues=array(0,1,2,3,4,5,6,7,8,9,10,11);

//we would like to have a dynamic array of all even values
$iterator=new MyIter();

foreach(
$iterator as $value){
  echo 
$value."<br />";
}
?>

This will result in:
0
2
4
6
8
10

(You may also like to see what var_dump($iterator) produces).

Another great advantage is that you can modify the main table "on-the-fly" and it has its impact. Let us modify the last foreach loop:
<?php
// ...all above shall stay as it was
foreach($iterator as $value){
  echo 
$value."<br />";
  if(
$value==6){
    
$allValues=array(2,3);
    echo 
"I modified source array!<br />";
  }
}
?>

This produces now:
0
2
4
6
I modified source array!

However, if you feel it is rather a catastrophic disadvantage (maybe for example, it shows the values 0, 4, and 6 which were removed when we reached 6), and wish to have a "static" array that will iterate even in modified objects, just call getTable() in rewind() method and save it in temporary (private perhaps) field. In my example getTable() is called every iteration, and it calls another foreach through $allValues, which together might be time-consuming. Consider what you need.

[#16] nobody at nobody dot com [2011-12-22 01:11:51]

<?php
$d3 
= array('a'=>array('b'=>'c'));
foreach(
$d3['a'] as &$v4){}
foreach(
$d3 as $v4){}
var_dump($d3);
?>

will get something look like this:
array(1) {
  ["a"]=>
  array(1) {
    ["b"]=>
    &array(1) {
      ["b"]=>
      *RECURSION*
    }
  }
}
then you try to walk some data with this array.
the script run out of memory and connect reset by peer

the document says:
Warning
Reference of a $value and the last array element remain even after the foreach loop. It is recommended to destroy it by unset().

so what I learn is that NEVER ignore """Warning""" in document....

[#17] billardmchl at aol dot com [2011-05-20 01:20:17]

This function find well the words, add well adds a () around short words, but the 
array at the end of th function is the same as at the beginning.

<?php
function isole_mots($chaine)
{
    
$chaine "le petit chat est fou";
    
$mots preg_split('/[!,-.;?:()[ ]/'$chaine, -1PREG_SPLIT_NO_EMPTY);
    foreach (
$mots as $mot)
    {
        if (
strlen($mot) <= 3)
            
$mot "(".$mot.")";
    print 
" inside foreach $mot <br>";
    }
print 
"after foreach array mots";    
    
print_r($mots);
    die();
    return 
$mots;
}
?>


inside foreach (le)
inside foreach petit
inside foreach chat
inside foreach (est)
inside foreach (fou)
after foreach array motsArray ( [0] => le [1] => petit [2] => chat [3] => est [4] => fou )

[#18] Oleg englishman at bigmir dot net [2010-09-14 07:12:08]

For those who'd like to traverse an array including just added elements (within this very foreach), here's a workaround:

<?php
$values 
= array(=> 'a'=> 'b'=> 'c');
while (list(
$key$value) = each($values)) {
    echo 
"$key => $value \r\n";
    if (
$key == 3) {
        
$values[4] = 'd'
    }
    if (
$key == 4) {
        
$values[5] = 'e';
    }
}
?>


the code above will output:

1 => a
2 => b
3 => c
4 => d
5 => e

[#19] tedivm at tedivm dot com [2009-01-29 14:44:47]

foreach and the while/list/each methods are not completely identical, and there are occasions where one way is beneficial over the other.

<?php
$arr 
= array(1,2,3,4,5,6,7,8,9);

foreach(
$arr as $key=>$value)
{
    unset(
$arr[$key 1]);
    echo 
$value PHP_EOL;
}
?>

Output:
1 2 3 4 5 6 7 8 9 

<?php
$arr 
= array(1,2,3,4,5,6,7,8,9);

while (list(
$key$value) = each($arr))
{
    unset(
$arr[$key 1]);
    echo 
$value PHP_EOL;
}
?>

Output:
1 3 5 7 9


[EDIT BY danbrown AT php DOT net: Contains a typofix by (scissor AT phplabs DOT pl) on 30-JAN-2009.]

[#20] adam dot sindelar at gmail dot com [2008-04-14 06:45:46]

You can also use the alternative syntax for the foreach cycle:

<?php
foreach($array as $element):
  
#do something
endforeach;
?>


Just thought it worth mentioning.

[#21] simplex [2006-12-19 12:12:18]

"As of PHP 5, you can easily modify array's elements by preceding $value with &. This will assign reference instead of copying the value."

There are cases where array_walk or array_map are inadequate (conditional required) or you're just too lazy to write a function and pass values to it for use with array_map...

My solution to foreach for php 4 and 5 to modify values of an array directly:

<?php

$testarr 
= array("a" => 1"b" => 2"c" => 3"d" => 4);

$testarr_keys array_keys($testarr);
$testarr_values array_values($testarr);

for (
$i 0$i <= count($testarr) - 1$i++) {
    
$testarr[$testarr_keys[$i]] = $testarr_values[$i] * 2;
}

print_r($testarr);
?>

[#22] php at electricsurfer dot com [2003-04-22 13:33:59]

[Ed Note:  You can also use array_keys() so that you don't have to have the $value_copy variable --alindeman at php.net]

I use the following to modify the original values of the array:

<?php
foreach ($array as $key=>$value_copy)
{
     
$value =& $array[$key];
     
// ...
     
$value 'New Value';
}
?>

上一篇: 下一篇: