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).
break: sair do laço na hora, emforeach,forewhile.- O padrão
while (true)+break. continue: pular o resto da volta atual.break 2econtinue 2: controlar o laço de fora quando há um laço dentro de outro.- Por que, dentro de um
switch, obreaknão sai do laço. - O laço infinito clássico do
continuenumwhile, 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.
| Palavra | O que faz | Na fruteira |
|---|---|---|
break | Sai do laço imediatamente. O programa segue na primeira linha depois do laço. | Achei a laranja, parei de procurar. |
continue | Abandona 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
foreachna 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. Oforeachpassa 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.
| Escrita | Sai 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.
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
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
| Forma | O 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. |
return | Dentro 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:
2. Pular as bananas
Complete para mostrar todas as frutas, menos as bananas:
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
breaksai do laço na hora; o programa segue depois dele. Use para parar uma busca quando achar.continuepula o resto da volta atual e vai para a próxima. Use para ignorar itens inválidos.while (true)+breakserve quando a decisão de parar fica no meio do corpo; garanta que obreaksempre possa acontecer.- Em laços dentro de laços,
break 2econtinue 2agem no laço de fora. Umswitchconta como um nível. - Num
while, umcontinueantes do$i++cria um laço infinito. - Dentro de funções,
returncostuma ser mais claro quebreak 2; para buscas simples,in_arrayearray_search.