PHP break e continue

Um laço repete até a condição dele mandar parar. Mas às vezes você quer parar antes (achei o que procurava, não preciso olhar o resto) ou pular só esta volta (este item está estragado, passe para o próximo). Para isso o PHP tem duas palavras: break ("quebrar", sair do laço) e continue ("continuar", ir direto para a próxima volta).

Você vai aprender
  • break: sair do laço na hora, em foreach, for e while.
  • O padrão while (true) + break.
  • continue: pular o resto da volta atual.
  • break 2 e continue 2: controlar o laço de fora quando há um laço dentro de outro.
  • Por que, dentro de um switch, o break não sai do laço.
  • O laço infinito clássico do continue num while, e as mensagens de erro reais.

Você precisa conhecer os laços while, for e foreach. Os exemplos usam as frutas e as notas da Ana e rodam na pasta ~/site.


A ideia antes do código: a fruteira

A Ana pede: "pegue uma laranja na fruteira". Você olha as frutas uma por uma. Quando achar a laranja, você para; não faz sentido continuar olhando o resto. Isso é o break.

Agora ela pede: "lave todas as frutas, menos as que estiverem estragadas". Você pega uma por uma; quando uma está estragada, você a deixa de lado e pega a próxima, sem lavar. Você não parou o trabalho, só pulou aquela. Isso é o continue.

PalavraO que fazNa fruteira
breakSai do laço imediatamente. O programa segue na primeira linha depois do laço.Achei a laranja, parei de procurar.
continueAbandona o resto desta volta e começa a próxima.Esta está estragada, próxima.

break: parar quando achar

<?php
$frutas = ["banana", "maçã", "laranja", "banana", "uva", "maçã", "banana"];

foreach ($frutas as $posicao => $fruta) {
    echo "Olhando a posição $posicao: $fruta\n";
    if ($fruta === "laranja") {
        echo "Achei a laranja!\n";
        break;
    }
}
echo "Fim da busca.\n";
foreach ($frutas as $posicao => $fruta)
Percorre as frutas; $posicao é o índice (0, 1, 2…).
if ($fruta === "laranja")
A condição de parada: "é esta?"
break;
Sai do foreach na hora. As frutas das posições 3 a 6 nem são olhadas.
echo "Fim da busca.\n";
A primeira linha depois do laço: é aqui que o programa continua.
$ php procurar.php
Olhando a posição 0: banana
Olhando a posição 1: maçã
Olhando a posição 2: laranja
Achei a laranja!
Fim da busca.            « as posições 3 a 6 foram puladas

Sem o break, o laço olharia as 7 frutas. Com 7 itens não faz diferença, mas com uma lista de 100 mil clientes, parar no primeiro que interessa economiza muito trabalho.

Missão: o programa deveria mostrar a posição da primeira maçã, mas mostra a da última (5), porque continua olhando depois de achar. Acrescente um break no lugar certo para aparecer Primeira maçã na posição 1.

<?php
$frutas = ["banana", "maçã", "laranja", "banana", "uva", "maçã", "banana"];
$achou = null;

foreach ($frutas as $posicao => $fruta) {
    if ($fruta === "maçã") {
        $achou = $posicao;
    }
}
echo "Primeira maçã na posição $achou\n";
<?php
$frutas = ["banana", "maçã", "laranja", "banana", "uva", "maçã", "banana"];
$achou = null;

foreach ($frutas as $posicao => $fruta) {
    if ($fruta === "maçã") {
        $achou = $posicao;
        break;
    }
}
echo "Primeira maçã na posição $achou\n";

while (true) + break: quando a parada está no meio

Às vezes é mais fácil decidir se o laço deve parar no meio da volta do que no começo. Um jeito comum é escrever um laço que, sozinho, nunca pararia (while (true), "enquanto verdadeiro") e colocar o break onde a decisão acontece:

<?php
$valor = 1;
$dias = 0;
while (true) {
    $valor *= 2;
    $dias++;
    if ($valor > 1000) {
        break;
    }
}
echo "Dobrando todo dia, passa de 1000 em $dias dias (vale $valor).\n";
$ php dobrar.php
Dobrando todo dia, passa de 1000 em 10 dias (vale 1024).

O uso mais famoso desse padrão é ler o que o usuário digita até ele mandar parar. A Ana fez uma lista de compras no terminal (a leitura do teclado com fgets(STDIN) está em PHP na Linha de Comando):

<?php
$compras = [];
while (true) {
    echo "Item (ou 'fim'): ";
    $linha = fgets(STDIN);
    if ($linha === false) {
        break;                  // a entrada acabou
    }
    $item = trim($linha);
    if ($item === "fim") {
        break;
    }
    if ($item === "") {
        continue;               // linha vazia: pergunta de novo
    }
    $compras[] = $item;
}
echo "\nLista: ", implode(", ", $compras), "\n";

Para não precisar digitar, a Ana mandou as respostas pelo printf (uma por linha, com uma linha vazia no meio). As perguntas saem coladas porque as respostas não aparecem na tela, só são lidas:

$ printf "pão\n\nleite\ncafé\nfim\n" | php ate-fim.php
Item (ou 'fim'): Item (ou 'fim'): Item (ou 'fim'): Item (ou 'fim'): Item (ou 'fim'):
Lista: pão, leite, café

Repare nos dois break: um para quando a pessoa digita fim, outro para quando a entrada acaba (fgets devolve false). Sem o segundo, se a entrada acabasse sem um "fim", o laço nunca terminaria. Todo while (true) precisa de uma saída garantida.

while (true) sem break alcançável é um laço infinito

Se a condição do break nunca for verdadeira, o programa fica preso para sempre. No terminal, interrompa com Ctrl+C. Veja os detalhes em while e do-while.


continue: pular esta volta

A Ana digitou as notas de um aluno e errou duas: uma negativa e uma maior que 10. Para a média, ela quer ignorar as inválidas e usar as outras:

<?php
$notas = [9.5, -1, 8.0, 12, 7.5];

$soma = 0;
$validas = 0;
foreach ($notas as $nota) {
    if ($nota < 0 || $nota > 10) {
        echo "Ignorando nota inválida: $nota\n";
        continue;
    }
    $soma += $nota;
    $validas++;
}
echo "Notas válidas: $validas\n";
echo "Média: ", $soma / $validas, "\n";
if ($nota < 0 || $nota > 10)
"A nota é menor que zero ou maior que dez?" (veja Operadores Lógicos).
continue;
Pula o resto desta volta: as duas linhas de baixo ($soma += … e $validas++) não rodam para essa nota. O foreach passa para a próxima.
$ php pular.php
Ignorando nota inválida: -1
Ignorando nota inválida: 12
Notas válidas: 3
Média: 8.3333333333333

Repare que, diferente do break, o laço foi até o fim: a nota 7.5, que vem depois do 12, foi somada. (Para mostrar a média com uma casa, use number_format.)

O continue funciona igual no for. Aqui ele pula os números pares (o resto da divisão por 2 é zero; veja o operador %):

<?php
for ($i = 1; $i <= 10; $i++) {
    if ($i % 2 === 0) {
        continue;
    }
    echo $i, " ";
}
echo "\n";
$ php impares.php
1 3 5 7 9 

No for, o continue pula para o passo ($i++) e depois testa a condição. Isso é importante: no while a história é outra (veja Quando dá errado).

continue ou if/else?

Todo continue pode ser trocado por um if que envolve o resto do corpo. O continue é preferido quando o caso a pular é uma exceção e o resto do corpo é grande: você trata o caso estranho no começo ("se for inválida, pula") e o código principal fica sem recuo extra. Esse estilo se chama guard clause ("cláusula de guarda").


Laço dentro de laço: break 2 e continue 2

A Ana tem as notas da turma: cada aluno tem várias notas. Ela quer achar o primeiro aluno que tirou um 10. São dois laços: um pelos alunos, outro pelas notas de cada um. Veja o que o break simples faz:

<?php
$turma = [
    "Ana"   => [9.5, 10, 8.0],
    "Bruno" => [7.0, 6.5, 10],
    "Carla" => [10, 9.0, 8.5],
];

foreach ($turma as $aluno => $notas) {
    foreach ($notas as $nota) {
        if ($nota == 10) {
            echo "Primeiro 10 encontrado: $aluno\n";
            break;
        }
    }
}
$ php dez.php
Primeiro 10 encontrado: Ana
Primeiro 10 encontrado: Bruno      « ué? o laço de fora continuou
Primeiro 10 encontrado: Carla

O break sai só do laço em que está, o de dentro (as notas). O de fora (os alunos) continua, passa para o Bruno e acha outro 10. Para sair dos dois de uma vez, diga quantos níveis quebrar: break 2.

<?php
$turma = [
    "Ana"   => [9.5, 10, 8.0],
    "Bruno" => [7.0, 6.5, 10],
    "Carla" => [10, 9.0, 8.5],
];

foreach ($turma as $aluno => $notas) {
    foreach ($notas as $nota) {
        if ($nota == 10) {
            echo "Primeiro 10 encontrado: $aluno\n";
            break 2;
        }
    }
}
echo "Busca encerrada.\n";
$ php dez2.php
Primeiro 10 encontrado: Ana
Busca encerrada.
EscritaSai de
break; (o mesmo que break 1;)O laço onde ele está.
break 2;O laço onde está e o de fora dele.
break 3;Três níveis (se existirem três laços, um dentro do outro).

O continue 2 faz o mesmo raciocínio: abandona o laço de dentro e vai para a próxima volta do laço de fora. A Ana quer listar os alunos que não têm nenhuma nota vermelha (abaixo de 6). Assim que acha uma nota vermelha, não adianta olhar as outras notas daquele aluno: passa para o próximo.

<?php
$turma = [
    "Ana"   => [9.5, 10, 8.0],
    "Bruno" => [7.0, 4.5, 10],
    "Carla" => [10, 9.0, 8.5],
    "Diego" => [6.0, 5.5, 3.0],
];

foreach ($turma as $aluno => $notas) {
    foreach ($notas as $nota) {
        if ($nota < 6) {
            continue 2;     // tem nota vermelha: pula para o próximo aluno
        }
    }
    echo "$aluno não tem nenhuma nota abaixo de 6\n";
}
$ php continue2.php
Ana não tem nenhuma nota abaixo de 6
Carla não tem nenhuma nota abaixo de 6

Para o Bruno, o continue 2 aconteceu na nota 4.5: o laço das notas foi abandonado e a linha do echo (que fica no laço de fora) foi pulada.

Muitos break 2? Considere uma função

Dentro de uma função, return sai de todos os laços de uma vez (e da função). A busca do primeiro 10 fica mais clara assim:

<?php
function primeiraComDez(array $turma): ?string
{
    foreach ($turma as $aluno => $notas) {
        foreach ($notas as $nota) {
            if ($nota == 10) {
                return $aluno;
            }
        }
    }
    return null;
}

E, para buscas simples num array, o PHP já tem funções prontas: in_array("laranja", $frutas) responde true/false e array_search("laranja", $frutas) devolve a posição (2). Veja Funções de Array.


break dentro de switch

O switch também usa break, para terminar cada case. Para o PHP, um switch conta como um nível, como se fosse um laço. Por isso, um break dentro de um switch sai do switch, não do laço em volta:

<?php
$pedidos = ["bolo", "pudim", "sair", "torta"];

foreach ($pedidos as $pedido) {
    switch ($pedido) {
        case "sair":
            echo "Fechando a loja.\n";
            break;
        default:
            echo "Preparando $pedido\n";
    }
}
$ php switch.php
Preparando bolo
Preparando pudim
Fechando a loja.
Preparando torta          « a loja "fechou", mas o laço continuou

O break terminou só o case "sair". Para sair também do foreach, use break 2 (nível 1 = o switch, nível 2 = o laço):

$ php switch2.php          « o mesmo código, com break 2 no case "sair"
Preparando bolo
Preparando pudim
Fechando a loja.

Com o match, que não usa break, essa confusão não existe.


Quando dá errado

O programa travou: continue num while pulou o $i++
<?php
$i = 0;
while ($i < 5) {
    if ($i === 2) {
        continue;
    }
    echo $i, "\n";
    $i++;
}
echo "Fim\n";
$ timeout 3 php continua.php; echo "[código $?]"
0
1
[código 124]             « depois do 1, nada: o timeout matou o PHP após 3 segundos

O que aconteceu: quando $i chega a 2, o continue pula o resto da volta, inclusive o $i++. Na volta seguinte $i ainda é 2, o continue acontece de novo… para sempre. O processador fica ocupado sem mostrar nada; a Ana usou o comando timeout 3 para o terminal desistir sozinho depois de 3 segundos (o código 124 quer dizer "parado pelo timeout"). Sem ele, interrompa com Ctrl+C. No for isso não acontece, porque o $i++ fica no cabeçalho e roda mesmo depois de um continue.

Num site, o PHP desiste depois de um tempo (30 segundos, por padrão, na web). No terminal não há limite, mas dá para impor um com -d max_execution_time:

$ php -d max_execution_time=2 continua.php
0
1

Fatal error: Maximum execution time of 2 seconds exceeded in /home/ana/site/continua.php on line 3
Stack trace:
#0 {main}

Como consertar: atualize o contador antes do continue ($i++; continue;) ou troque o while por um for.

'break' not in the 'loop' or 'switch' context
<?php
$nota = 5;
if ($nota < 6) {
    echo "Recuperação\n";
    break;
}
echo "Fim\n";
$ php fora-laco.php

Fatal error: 'break' not in the 'loop' or 'switch' context in /home/ana/site/fora-laco.php on line 5
Stack trace:
#0 {main}

O que significa: "'break' fora de um laço ou switch". Um if não é laço: não há do que sair. Nenhuma linha rodou (nem o "Recuperação"), porque o PHP recusa o arquivo antes de começar.

Como consertar: para sair de um if, use else. Para parar uma função, return. Para encerrar o script inteiro, exit.

Cannot 'break' 2 levels
<?php
foreach ([1, 2, 3] as $n) {
    if ($n === 2) {
        break 2;
    }
    echo $n, "\n";
}
$ php niveis.php

Fatal error: Cannot 'break' 2 levels in /home/ana/site/niveis.php on line 4
Stack trace:
#0 {main}

O que significa: "não é possível quebrar 2 níveis": só existe um laço. Acontece muito depois de apagar o laço de fora e esquecer o 2.

Como consertar: use o número de laços que realmente existem em volta (aqui, break;).

'break' operator accepts only positive integers e non-integer operand
$ php zero.php            « break 0;

Fatal error: 'break' operator accepts only positive integers in /home/ana/site/zero.php on line 3
Stack trace:
#0 {main}
$ php variavel.php        « $niveis = 2; … break $niveis;

Fatal error: 'break' operator with non-integer operand is no longer supported in /home/ana/site/variavel.php on line 4
Stack trace:
#0 {main}

O que significa: o número depois de break (ou continue) precisa ser um inteiro escrito direto no código, 1 ou mais. Uma variável não serve: o PHP precisa saber o destino antes de rodar ("is no longer supported" = "não é mais aceito"; versões muito antigas aceitavam).

Como consertar: escreva o número: break 2;.

"continue" targeting switch is equivalent to "break"
<?php
$pedidos = ["bolo", "", "pudim"];

foreach ($pedidos as $pedido) {
    switch ($pedido) {
        case "":
            continue;
        default:
            echo "Preparando $pedido\n";
    }
}
$ php switchcont.php

Warning: "continue" targeting switch is equivalent to "break". Did you mean to use "continue 2"? in /home/ana/site/switchcont.php on line 7
Preparando bolo
Preparando pudim

O que significa: "um continue cujo alvo é o switch equivale a um break. Você quis usar continue 2?". Como o switch conta como um nível, o continue ali dentro apontaria para ele, e o PHP avisa que isso provavelmente não é o que você queria. O aviso aparece antes de qualquer saída, porque o PHP o detecta ao ler o arquivo.

Como consertar: como a própria mensagem sugere, continue 2; (pula para o próximo pedido do foreach).


Referência

FormaO que faz
break;Sai do laço (ou switch) atual. O programa segue depois dele.
break N;Sai de N níveis de laço/switch de uma vez. N é um inteiro fixo, 1 ou mais.
continue;Pula o resto desta volta e vai para a próxima (no for, passa pelo passo $i++).
continue N;Vai para a próxima volta do laço N níveis acima.
while (true) { … break; … }Laço cuja saída está no meio do corpo.
returnDentro de uma função: sai de todos os laços e da função.
in_array() / array_search()Buscas prontas, sem laço nem break.

Exercícios

1. Parar a busca

Complete para o laço parar assim que achar a uva:

if ($fruta === "uva") { echo "Achei!"; }

2. Pular as bananas

Complete para mostrar todas as frutas, menos as bananas:

foreach ($frutas as $f) { if ($f === "banana") { } echo $f, "\n"; }

3. Dois níveis

Dentro de um foreach que está dentro de outro foreach, complete para sair dos dois:

4. O que mostra? for ($i = 1; $i <= 5; $i++) { if ($i === 3) break; echo $i; }

5. O que mostra? for ($i = 1; $i <= 5; $i++) { if ($i === 3) continue; echo $i; }

6. Um foreach tem um switch dentro. Num case, você quer parar o foreach. O que escrever?

Missão final

Missão: a lista de pedidos da lojinha tem linhas vazias (pedidos apagados) e, no meio, a palavra fechar: o que vem depois dela fica para amanhã. Use continue para pular os pedidos vazios e break para parar no fechar. A saída deve ter só bolo, pudim e torta, e terminar com Total: 3 pedidos.

<?php
$pedidos = ["bolo", "", "pudim", "", "torta", "fechar", "brigadeiro"];
$feitos = 0;

foreach ($pedidos as $pedido) {
    echo "Preparando $pedido\n";
    $feitos++;
}
echo "Total: $feitos pedidos\n";
<?php
$pedidos = ["bolo", "", "pudim", "", "torta", "fechar", "brigadeiro"];
$feitos = 0;

foreach ($pedidos as $pedido) {
    if ($pedido === "fechar") {
        break;
    }
    if ($pedido === "") {
        continue;
    }
    echo "Preparando $pedido\n";
    $feitos++;
}
echo "Total: $feitos pedidos\n";

Resumo

  • break sai do laço na hora; o programa segue depois dele. Use para parar uma busca quando achar.
  • continue pula o resto da volta atual e vai para a próxima. Use para ignorar itens inválidos.
  • while (true) + break serve quando a decisão de parar fica no meio do corpo; garanta que o break sempre possa acontecer.
  • Em laços dentro de laços, break 2 e continue 2 agem no laço de fora. Um switch conta como um nível.
  • Num while, um continue antes do $i++ cria um laço infinito.
  • Dentro de funções, return costuma ser mais claro que break 2; para buscas simples, in_array e array_search.