Por que a pergunta "primeira e última iteração foreach" explode no Stack Overflow?
Se você já escreveu em PHP e tentou adicionar uma classe CSS ao primeiro elemento de uma lista ou inserir um separador após cada elemento, exceto o último, então você já enfrentou esse problema. No Stack Overflow, esta pergunta acumulou mais de 150.000 visualizações e centenas de comentários. Por quê? Porque o foreach é um loop poderoso, mas "cego": ele não sabe em qual iteração está.
Neste artigo, vamos analisar 4 abordagens funcionais em PHP 8.x, desde a ingênua até a profissional. Você aprenderá como evitar "gambiarras" e escrever código que não seja vergonhoso de mostrar em uma revisão de código.
O problema: por que o foreach não sabe seu próprio número?
Ao contrário do for, onde você tem um contador $i, o foreach opera com o ponteiro interno do array. Ele é ideal para iterações simples, mas quando é necessário executar uma ação "apenas para o primeiro" ou "apenas para o último" elemento, começam as complicações.
Exemplo de uma tarefa típica:
// Exibir uma lista de produtos separados por vírgula, mas sem vírgula após o último$items = ['Leite', 'Pão', 'Ovos', 'Queijo'];
foreach ($items as $item) { echo $item . ', '; // Opa, a última vírgula é desnecessária!}Saída: Leite, Pão, Ovos, Queijo, — isso não é bonito. Vamos corrigir.
Método 1: Contador + count() — o clássico do gênero
A maneira mais óbvia é criar uma variável contadora e compará-la com o número total de elementos.
$items = ['Leite', 'Pão', 'Ovos', 'Queijo'];$total = count($items);$i = 1;
foreach ($items as $item) { if ($i === 1) { echo "<strong>Primeiro: $item</strong><br>"; } elseif ($i === $total) { echo "<em>Último: $item</em><br>"; } else { echo "$item<br>"; } $i++;}Vantagens:
- Simplicidade e legibilidade.
- Funciona com qualquer array, incluindo associativos.
Desvantagens:
- É preciso lembrar de usar
count()e inicializar o contador. - Se o array estiver vazio,
$total = 0, e a primeira iteração nunca ocorrerá — é necessário verificar. - Não é adequado para objetos
Iterator(por exemplo, geradores).
Método 2: Flags e reset() / end() — elegância sem contador
Você pode não contar as iterações, mas simplesmente armazenar o primeiro e o último elemento do array antes de iniciar o loop.
$items = ['Leite', 'Pão', 'Ovos', 'Queijo'];
$first = reset($items); // 'Leite'$last = end($items); // 'Queijo'reset($items); // Retorna o ponteiro para o início!
foreach ($items as $key => $item) { if ($item === $first) { echo "Primeiro: $item"; } elseif ($item === $last) { echo "Último: $item"; } else { echo "$item"; }}Importante! As funções reset() e end() alteram o ponteiro interno do array. Após end(), é necessário chamar reset(), caso contrário, o foreach não começará do início.
Vantagens:
- Não precisa de contador.
- Funciona com arrays associativos.
Desvantagens:
- Perigo: se o array tiver valores duplicados (por exemplo, dois "Queijos"), o último elemento será identificado incorretamente.
- Não é adequado para objetos
Iterator. - Requer o reset manual do ponteiro.
Método 3: array_keys() — para arrays associativos e objetos
Se você precisa trabalhar com chaves (e não apenas com valores) ou com objetos Iterator, pode obter uma lista de todas as chaves e verificar o índice.
$items = ['a' => 'Lei