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.]
foreach
(PHP 4, PHP 5)
El constructor foreach proporciona un modo sencillo de iterar sobre arrays. foreach funciona sólo sobre arrays y objetos, y emitirá un error al intentar usarlo con una variable de un tipo diferente de datos o una variable no inicializada. Existen dos sintaxis:
foreach (expresión_array as $valor)
sentencias
foreach (expresión_array as $clave => $valor)
sentencias
La primera forma recorre el array dado por expresión_array. En cada iteración, el valor del elemento actual se asigna a $valor y el puntero interno del array avanza una posición (así en la próxima iteración se estará observando el siguiente elemento).
La segunda forma además asigna la clave del elemento actual a la variable $clave en cada iteración.
También es posible personalizar la iteración de objetos.
Nota:
Cuando foreach inicia su ejecución, el puntero interno del array se pone automáticamente en el primer elemento del array. Esto significa que no es necesario llamar la función reset() antes de un bucle foreach.
Ya que foreach depende el puntero de array interno, cambiar éste dentro del bucle puede conducir a un comportamiento inesperado.
Para poder modificar directamente los elementos del array dentro de bucle, se ha de anteponer & a $valor. En este caso el valor será asignado por referencia.
<?php
$array = array(1, 2, 3, 4);
foreach ($array as &$valor) {
$valor = $valor * 2;
}
// $array ahora es array(2, 4, 6, 8)
unset($valor); // rompe la referencia con el último elemento
?>
Referenciar $valor sólo es posible si el array iterado puede ser referenciado (es decir, si es una variable). El siguiente código no funcionará:
<?php
foreach (array(1, 2, 3, 4) as &$valor) {
$valor = $valor * 2;
}
?>
La referencia del $valor y el último elemento del array permanecen aún después del bucle foreach. Se recomienda destruirlos con unset().
Nota:
foreach no soporta la capacidad de suprimir mensajes de error usando '@'.
Se puede haber notado que las siguientes construcciones son funcionalmente idénticas:
<?php
$array = array("uno", "dos", "tres");
reset($array);
while (list(, $valor) = each($array)) {
echo "Valor: $valor<br />\n";
}
foreach ($array as $valor) {
echo "Valor: $valor<br />\n";
}
?>
Las siguientes construcciones también son funcionalmente idénticas:
<?php
$array = array("uno", "dos", "tres");
reset($array);
while (list($clave, $valor) = each($array)) {
echo "Clave: $clave; Valor: $valor<br />\n";
}
foreach ($array as $clave => $valor) {
echo "Clave: $clave; Valor: $valor<br />\n";
}
?>
Algunos ejemplos más para demostrar su uso:
<?php
/* Ejemplo 1 de foreach: sólo el valor */
$a = array(1, 2, 3, 17);
foreach ($a as $v) {
echo "Valor actual de \$a: $v.\n";
}
/* Ejemplo 2 de foreach: valor (con su notación de acceso manual impreso con fines ilustrativos) */
$a = array(1, 2, 3, 17);
$i = 0; /* sólo para efectos ilustrativos */
foreach ($a as $v) {
echo "\$a[$i] => $v.\n";
$i++;
}
/* Ejemplo 3 de foreach: clave y valor */
$a = array(
"uno" => 1,
"dos" => 2,
"tres" => 3,
"diecisiete" => 17
);
foreach ($a as $k => $v) {
echo "\$a[$k] => $v.\n";
}
/* Ejemplo 4 de foreach: arrays multidimensionales */
$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";
}
}
/* Ejemplo 5 de foreach: arrays dinámicos */
foreach (array(1, 2, 3, 4, 5) as $v) {
echo "$v\n";
}
?>
Utilizando arrays anidados con list()
(PHP 5 >= 5.5.0)
PHP 5.5 añade la posibilidad de recorrer un array de arrays y utilizar el array interior en las variables del bucle proporcionando list() como el valor.
Por ejemplo:
<?php
$array = [
[1, 2],
[3, 4],
];
foreach ($array as list($a, $b)) {
// $a contiene el primer elemento del array interior,
// y $b contiene el segundo elemento.
echo "A: $a; B: $b\n";
}
?>
El resultado del ejemplo sería:
A: 1; B: 2 A: 3; B: 4
Puedes proporcionar menos elementos a list() de los que hay en el array interior, en cuyo caso los elementos sobrantes del array serán descartados:
<?php
$array = [
[1, 2],
[3, 4],
];
foreach ($array as list($a)) {
// Observa que no hay $b en este caso.
echo "$a\n";
}
?>
El resultado del ejemplo sería:
1 3
Se generará un notice si no hay suficientes elementos en el array para completar el list():
<?php
$array = [
[1, 2],
[3, 4],
];
foreach ($array as list($a, $b, $c)) {
echo "A: $a; B: $b; C: $c\n";
}
?>
El resultado del ejemplo sería:
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:
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
)
You can also use the alternative syntax for the foreach cycle:
<?php
foreach($array as $element):
#do something
endforeach;
?>
Just thought it worth mentioning.
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, -1, PREG_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 )
<?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....
For those who'd like to traverse an array including just added elements (within this very foreach), here's a workaround:
<?php
$values = array(1 => 'a', 2 => 'b', 3 => '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
"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!
[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';
}
?>
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
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 % 2 == 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.
Alright, I had a little error. I had one foreach() declaration, and then another foreach() declaration.
They went:
<?php
//$connections is an array of Socket resources
foreach ($connections as $key => &$value) {
//the code here is impertinent
}
//$users is an associative array
foreach ($users as $key => &$value) {
//the code here is impertinent
}
?>
Alright, now, what error was produced as a result of this?
This one:
"Warning: Cannot use scalar value as array in filename.php on line 69."
I then realized something; the reason for this came from the fact that I used $key, and $value for both of them in the exact same way.
As a response to this, I've developed two ways to fix this:
<?php
//add this to the end of every foreach() you use
unset($key,$value)
?>
OR
Simply use different variables for each one.
"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);
?>
