Introdução: Por que foreach não é apenas "para cada"
Se você programa em PHP há pelo menos uma semana, já usou foreach centenas de vezes. Este é, provavelmente, o loop favorito de todo desenvolvedor: curto, elegante, sem contadores desnecessários. Mas você já parou para pensar no que realmente acontece nos bastidores quando escreve foreach ($array as $value)?
A pergunta "How does PHP 'foreach' actually work?" tem milhares de visualizações no Stack Overflow. E não é por acaso. O comportamento do foreach esconde algumas surpresas, especialmente quando se trata de referências, loops aninhados e arrays não convencionais.
Neste tutorial, não vamos apenas revisar a sintaxe. Vamos olhar dentro do motor do PHP, entender o ponteiro interno de array, ver como os iteradores funcionam e aprender a evitar armadilhas. Vamos nessa!
1. Anatomia do foreach: o que a documentação diz
Vamos começar pelo básico. O foreach é usado para percorrer arrays e objetos. A sintaxe é simples:
<?php$fruits = ['apple', 'banana', 'cherry'];
foreach ($fruits as $fruit) { echo $fruit . "";}// Saída: apple banana cherry?>Mas também existe uma segunda forma — com chave:
<?php$prices = ['apple' => 100, 'banana' => 50];
foreach ($prices as $name => $price) { echo "$name custa $price reais.";}?>Parece tudo óbvio. Mas vamos mais fundo. O PHP processa o foreach de forma diferente do for ou while. Internamente, ele usa o ponteiro interno de array (internal array pointer) e um mecanismo especial de iteração.
2. Ponteiro interno de array: o agente secreto
Cada array no PHP tem um "cursor" invisível — o ponteiro interno. Ele aponta para o elemento atual. As funções current(), next(), prev(), end() e reset() controlam exatamente isso.
Quando você executa foreach, o PHP faz o seguinte:
- Redefine o ponteiro interno para o primeiro elemento (como
reset()). - A cada iteração, pega o elemento para o qual o ponteiro aponta.
- Avança o ponteiro para o próximo elemento (como
next()). - Repete até chegar ao final.
Vamos ver isso em ação:
<?php$array = [10, 20, 30];
// Simulação manual do foreachreset($array); // Ponteiro no primeiro elementowhile ($value = current($array)) { echo $value . " "; next($array); // Avança o ponteiro}// Saída: 10 20 30?>Mas há um detalhe: se o array estiver vazio ou contiver false, esse truque quebra. O foreach funciona corretamente sempre, porque usa um mecanismo interno mais confiável — os iteradores.
3. Iteradores e foreach: abordagem moderna
A partir do PHP 5, o foreach pode funcionar não apenas com arrays, mas também com qualquer objeto que implemente a interface Traversable (ou sua herdeira Iterator).
Quando você passa um objeto para o foreach, o PHP chama os métodos do iterador:
rewind()— redefine para o iníciovalid()— verifica se existe um elementocurrent()— obtém o valorkey()— obtém a chavenext()— avança para o próximo
Exemplo de iterador personalizado:
<?phpclass NumbersIterator implements Iterator { private $position = 0; private $numbers = [1, 2, 3, 4, 5];
public function rewind() { $this->position = 0; }
public function current() { return $this->numbers[$this->position]; }
public function key() { return $this->position;