PHP Projeto Calculadora Web
O primeiro projeto é uma calculadora que funciona no navegador: dois campos para os números, uma lista com as quatro operações e um botão Calcular. É pequeno, mas tem tudo o que um sistema web de verdade tem: um formulário, o PHP lendo o que chegou, a decisão sobre o que fazer, a checagem do que o usuário digitou e uma resposta bem apresentada.
- Montar uma página que mostra um formulário e também processa o envio dele (o mesmo arquivo faz as duas coisas).
- Saber se a página foi aberta normalmente (
GET) ou enviada pelo botão (POST). - Escolher a conta com
matche entender o erroUnhandledMatchError. - Validar números com
is_numeric, aceitar vírgula e impedir a divisão por zero. - Mostrar o resultado no jeito brasileiro (
1.234,50) comnumber_format. - Manter o que o usuário digitou nos campos depois de enviar, sem abrir brecha de segurança.
Antes de começar
Este projeto junta aulas dos níveis 1 e 2. Se algum termo parecer novo, a aula do assunto está ao lado:
| O que usamos | Para quê | Aula |
|---|---|---|
<form method="post"> | O formulário com os campos | Formulários HTML |
$_POST, $_SERVER['REQUEST_METHOD'] | Ler o que foi enviado | $_GET e $_POST |
?? | Valor padrão quando o campo não veio | ?? e ?-> |
match | Escolher a conta pela operação | match |
is_numeric, trim | Validar o que foi digitado | Validar Dados |
htmlspecialchars | Mostrar texto do usuário com segurança | Escapar a Saída (XSS) |
number_format | Formatar 2469 como 2.469,00 | Formatar Números |
<?= ?>, if: … endif; | Misturar PHP e HTML | PHP Dentro do HTML |
O projeto inteiro é um arquivo só, o index.php. Em cada etapa ele cresce um pouco. Você pode seguir de dois jeitos:
- No editor do guia: cada etapa tem uma caixa "Experimente" com o
index.phpdaquela etapa, no modo web: a aba Resultado mostra a página como no navegador, e o formulário funciona de verdade. - No seu computador: crie a pasta, salve o arquivo e suba o servidor embutido do PHP (veja O Servidor Embutido):
$ mkdir calculadora $ cd calculadora $ nano index.php « cole o código da etapa e salve $ php -S localhost:8000 [Fri Oct 16 20:44:15 2026] PHP 8.5.8 Development Server (http://localhost:8000) started
Deixe esse terminal aberto (ele é o servidor) e abra http://localhost:8000 no navegador. Para parar o servidor, volte a ele e aperte Ctrl+C. Neste guia, as saídas foram conferidas com o curl num segundo terminal: curl -s localhost:8000/ busca a página como o navegador faria, e curl -s -d 'a=10&b=4&op=dividir' localhost:8000/ simula um clique em Calcular com esses valores (o -d faz um POST).
As etapas
-
Etapa 1 · O formulário
Primeiro, a "cara" da calculadora. Por enquanto é quase só HTML: o PHP aparece em um lugar, para provar que o arquivo está passando pelo PHP.
<!DOCTYPE html> <html lang="pt-BR"> <head> <meta charset="utf-8"> <title>Calculadora</title> </head> <body> <h1>Calculadora da Ana</h1> <form method="post"> <input name="a" placeholder="Primeiro número"> <select name="op"> <option value="somar">+</option> <option value="subtrair">−</option> <option value="multiplicar">×</option> <option value="dividir">÷</option> </select> <input name="b" placeholder="Segundo número"> <button>Calcular</button> </form> <p>Hoje é <?= date('d/m/Y') ?>.</p> </body> </html>Trecho O que faz <form method="post">Quando o botão for clicado, o navegador envia os campos com o método POST (eles vão "dentro" da requisição, não na barra de endereço). Sem action, o formulário é enviado para a mesma página: o próprioindex.phpvai receber os dados.name="a",name="b"O nameé o nome com que o campo chega ao PHP. Semname, o campo nem é enviado.<select name="op">A lista de operações. O que chega ao PHP é o valueda opção escolhida (somar,dividir…), não o símbolo que aparece na tela.<button>Calcular</button>Um botão dentro de um formulário envia o formulário. <?= date('d/m/Y') ?>O único PHP da etapa: escreve a data de hoje. Se você vir 16/10/2026(ou a sua data), o PHP está funcionando; se vir o código<?=na página, você abriu o arquivo direto, sem servidor.Por quevalue="dividir"e nãovalue="/"?Símbolos como
+e/têm significado especial em endereços e formulários (o+, por exemplo, vira espaço). Palavras simples chegam sempre iguais. O símbolo bonito fica só no texto da opção.Com o servidor rodando, o
curlmostra o final da página que o navegador recebe:$ curl -s localhost:8000/ | tail -5 <button>Calcular</button> </form> <p>Hoje é 16/10/2026.</p> « o PHP trocou o date('d/m/Y') pela data </body> </html>Clicar em Calcular agora não faz nada visível: o formulário é enviado, mas o PHP ainda não lê os campos. A página volta igual (e vazia, porque o navegador não "lembra" o que estava nos campos). Isso muda nas próximas etapas.
Missão: acrescente uma quinta opção na lista, com o valor
potenciae o texto^, logo depois da divisão. Rode e abra a lista na aba Resultado.<!DOCTYPE html> <html lang="pt-BR"> <head> <meta charset="utf-8"> <title>Calculadora</title> </head> <body> <h1>Calculadora da Ana</h1> <form method="post"> <input name="a" placeholder="Primeiro número"> <select name="op"> <option value="somar">+</option> <option value="subtrair">−</option> <option value="multiplicar">×</option> <option value="dividir">÷</option> </select> <input name="b" placeholder="Segundo número"> <button>Calcular</button> </form> <p>Hoje é <?= date('d/m/Y') ?>.</p> </body> </html><!DOCTYPE html> <html lang="pt-BR"> <head> <meta charset="utf-8"> <title>Calculadora</title> </head> <body> <h1>Calculadora da Ana</h1> <form method="post"> <input name="a" placeholder="Primeiro número"> <select name="op"> <option value="somar">+</option> <option value="subtrair">−</option> <option value="multiplicar">×</option> <option value="dividir">÷</option> <option value="potencia">^</option> </select> <input name="b" placeholder="Segundo número"> <button>Calcular</button> </form> <p>Hoje é <?= date('d/m/Y') ?>.</p> </body> </html> -
Etapa 2 · Ler o que foi enviado
Agora o PHP vai pegar os três campos. A parte nova fica no topo do arquivo, antes de qualquer HTML: primeiro o PHP prepara os dados, depois o HTML mostra. Essa ordem vai acompanhar você em todos os projetos.
O começo do arquivo (novo):
<?php $a = $_POST['a'] ?? ''; $b = $_POST['b'] ?? ''; $op = $_POST['op'] ?? 'somar'; ?> <!DOCTYPE html> ...
E, logo depois do
</form>(novo):<?php if ($_SERVER['REQUEST_METHOD'] === 'POST'): ?> <p>Recebi: a = "<?= htmlspecialchars($a) ?>", b = "<?= htmlspecialchars($b) ?>", operação = "<?= htmlspecialchars($op) ?>"</p> <?php endif; ?>
O arquivo inteiro desta etapa está na caixa "Experimente" logo abaixo.
- $a
- A variável que vai guardar o primeiro número.
- =
- Recebe o valor da direita.
- $_POST['a']
- O campo
name="a"enviado por POST.$_POSTé um array associativo que o PHP enche sozinho a cada envio. - ??
- "Se não existir, use…". Quando a página é aberta pela primeira vez, ninguém enviou nada e
$_POST['a']não existe. - ''
- O valor padrão: texto vazio. Para a operação, o padrão é
'somar'.
No fim da página, um
ifno estilo "dois-pontos" (if (…): … endif;, mais fácil de ler no meio do HTML) só mostra a linha "Recebi" quando a página chegou por POST, isto é, quando o botão foi clicado.$_SERVER['REQUEST_METHOD']diz qual foi o método:GETquando você digita o endereço ou segue um link,POSTquando um formuláriomethod="post"é enviado.Semprehtmlspecialcharsao mostrar o que o usuário digitouOs valores vêm de fora: qualquer pessoa pode digitar
<script>no campo. Ohtmlspecialcharstroca<,>,&e as aspas pelas versões "inofensivas" (<…), e o navegador mostra o texto em vez de executá-lo. A aula Escapar a Saída (XSS) mostra o ataque que isso evita.$ curl -s -d 'a=10&b=4&op=dividir' localhost:8000/ | tail -3 <p>Recebi: a = "10", b = "4", operação = "dividir"</p> </body> </html> $ curl -s -d 'a=abc&b=2&op=somar' localhost:8000/ | tail -3 <p>Recebi: a = "abc", b = "2", operação = "somar"</p> « o PHP aceita qualquer texto </body> </html>
Repare: tudo chega como texto (string), até o
10. Um formulário não tem "campo de número" para o PHP; quem decide se aquilo é um número é você, e é isso que a etapa 4 faz.Missão: a caixa já começa com um envio (10, 4, dividir). Acrescente, logo abaixo da linha "Recebi", um parágrafo que mostre
Método:seguido de$_SERVER['REQUEST_METHOD']. Depois preencha o formulário na aba Resultado e envie de novo.<?php $a = $_POST['a'] ?? ''; $b = $_POST['b'] ?? ''; $op = $_POST['op'] ?? 'somar'; ?> <!DOCTYPE html> <html lang="pt-BR"> <head> <meta charset="utf-8"> <title>Calculadora</title> </head> <body> <h1>Calculadora da Ana</h1> <form method="post"> <input name="a" placeholder="Primeiro número"> <select name="op"> <option value="somar">+</option> <option value="subtrair">−</option> <option value="multiplicar">×</option> <option value="dividir">÷</option> </select> <input name="b" placeholder="Segundo número"> <button>Calcular</button> </form> <?php if ($_SERVER['REQUEST_METHOD'] === 'POST'): ?> <p>Recebi: a = "<?= htmlspecialchars($a) ?>", b = "<?= htmlspecialchars($b) ?>", operação = "<?= htmlspecialchars($op) ?>"</p> <?php endif; ?> </body> </html><?php $a = $_POST['a'] ?? ''; $b = $_POST['b'] ?? ''; $op = $_POST['op'] ?? 'somar'; ?> <!DOCTYPE html> <html lang="pt-BR"> <head> <meta charset="utf-8"> <title>Calculadora</title> </head> <body> <h1>Calculadora da Ana</h1> <form method="post"> <input name="a" placeholder="Primeiro número"> <select name="op"> <option value="somar">+</option> <option value="subtrair">−</option> <option value="multiplicar">×</option> <option value="dividir">÷</option> </select> <input name="b" placeholder="Segundo número"> <button>Calcular</button> </form> <?php if ($_SERVER['REQUEST_METHOD'] === 'POST'): ?> <p>Recebi: a = "<?= htmlspecialchars($a) ?>", b = "<?= htmlspecialchars($b) ?>", operação = "<?= htmlspecialchars($op) ?>"</p> <p>Método: <?= $_SERVER['REQUEST_METHOD'] ?></p> <?php endif; ?> </body> </html> -
Etapa 3 · Fazer a conta com
matchHora de calcular. Os textos viram números com
(float)e omatchescolhe a conta pela operação:<?php $a = $_POST['a'] ?? ''; $b = $_POST['b'] ?? ''; $op = $_POST['op'] ?? 'somar'; $resultado = null; if ($_SERVER['REQUEST_METHOD'] === 'POST') { $x = (float) $a; $y = (float) $b; $resultado = match ($op) { 'somar' => $x + $y, 'subtrair' => $x - $y, 'multiplicar' => $x * $y, 'dividir' => $x / $y, }; } ?> ... <?php if ($resultado !== null): ?> <p class="resultado">Resultado: <?= $resultado ?></p> <?php endif; ?>A linha "Recebi" da etapa 2 saiu: no lugar dela, o resultado. Ele só aparece quando existe (
!== null).- $resultado =
- O valor que o
matchdevolver vai para$resultado. Antes do envio, ele ficanull("ainda não calculei"). - match ($op)
- Compara
$opcom cada "braço", usando===(valor e tipo iguais). - 'somar' => $x + $y,
- Um braço: se
$opfor'somar', o resultado é$x + $y. - };
- O
matché uma expressão: devolve um valor, por isso termina com;depois da chave.
Por que
matche nãoswitch? Porque aqui queremos um valor para cada operação, sem precisar debreake sem o risco de "cair" no caso de baixo. A comparação entre os dois está na aula match.$ curl -s -d 'a=10&b=4&op=dividir' localhost:8000/ | tail -3 <p class="resultado">Resultado: 2.5</p> </body> </html>
Funciona! Mas esta versão é ingênua: ela confia no usuário. Veja o que acontece com três envios "maldosos":
$ curl -s -d 'a=abc&b=2&op=somar' localhost:8000/ | grep resultado <p class="resultado">Resultado: 2</p> « "abc" virou 0 em silêncio! $ curl -s -d 'a=7&b=0&op=dividir' localhost:8000/ | tail -6 <br /> <b>Fatal error</b>: Uncaught DivisionByZeroError: Division by zero in /home/ana/calculadora/index.php:14 Stack trace: #0 {main} thrown in <b>/home/ana/calculadora/index.php</b> on line <b>14</b><br /> $ curl -s -d 'a=7&b=2&op=resto' localhost:8000/ | tail -5 <br /> <b>Fatal error</b>: Uncaught UnhandledMatchError: Unhandled match case 'resto' in /home/ana/calculadora/index.php:10 Stack trace: #0 {main} thrown in <b>/home/ana/calculadora/index.php</b> on line <b>10</b><br />(float) 'abc'é0: a conversão forçada não reclama, só devolve zero. A calculadora deu uma resposta errada sem avisar ninguém. Esse é o pior tipo de erro.DivisionByZeroError: Division by zero(erro de divisão por zero): desde o PHP 8, dividir por zero com/para o programa. A página morre no meio, e o usuário vê uma mensagem técnica.UnhandledMatchError: Unhandled match case 'resto'(caso não tratado no match): omatchnão tinha braço para'resto'. "Mas a lista só tem quatro opções!" Na tela, sim; mas qualquer pessoa pode enviar o que quiser, como fizemos com ocurl. Nunca confie que o valor veio da sua lista.
No navegador, a mensagem aparece com
<b>porque o servidor embutido mostra os erros em HTML; no terminal (php index.php) ela sai em texto puro. As mensagens completas estão em Lendo Erros do PHP.Missão: a caixa começa enviando
op=potencia, que ainda não existe: rode e veja oUnhandledMatchError. Depois acrescente o braço'potencia' => $x ** $y,nomatch(e a opçãopotenciana lista). Rodando de novo, 2 elevado a 10 deve dar 1024.<?php $a = $_POST['a'] ?? ''; $b = $_POST['b'] ?? ''; $op = $_POST['op'] ?? 'somar'; $resultado = null; if ($_SERVER['REQUEST_METHOD'] === 'POST') { $x = (float) $a; $y = (float) $b; $resultado = match ($op) { 'somar' => $x + $y, 'subtrair' => $x - $y, 'multiplicar' => $x * $y, 'dividir' => $x / $y, }; } ?> <!DOCTYPE html> <html lang="pt-BR"> <head> <meta charset="utf-8"> <title>Calculadora</title> </head> <body> <h1>Calculadora da Ana</h1> <form method="post"> <input name="a" placeholder="Primeiro número"> <select name="op"> <option value="somar">+</option> <option value="subtrair">−</option> <option value="multiplicar">×</option> <option value="dividir">÷</option> </select> <input name="b" placeholder="Segundo número"> <button>Calcular</button> </form> <?php if ($resultado !== null): ?> <p class="resultado">Resultado: <?= $resultado ?></p> <?php endif; ?> </body> </html><?php $a = $_POST['a'] ?? ''; $b = $_POST['b'] ?? ''; $op = $_POST['op'] ?? 'somar'; $resultado = null; if ($_SERVER['REQUEST_METHOD'] === 'POST') { $x = (float) $a; $y = (float) $b; $resultado = match ($op) { 'somar' => $x + $y, 'subtrair' => $x - $y, 'multiplicar' => $x * $y, 'dividir' => $x / $y, 'potencia' => $x ** $y, }; } ?> <!DOCTYPE html> <html lang="pt-BR"> <head> <meta charset="utf-8"> <title>Calculadora</title> </head> <body> <h1>Calculadora da Ana</h1> <form method="post"> <input name="a" placeholder="Primeiro número"> <select name="op"> <option value="somar">+</option> <option value="subtrair">−</option> <option value="multiplicar">×</option> <option value="dividir">÷</option> <option value="potencia">^</option> </select> <input name="b" placeholder="Segundo número"> <button>Calcular</button> </form> <?php if ($resultado !== null): ?> <p class="resultado">Resultado: <?= $resultado ?></p> <?php endif; ?> </body> </html> -
Etapa 4 · Validar antes de calcular
A regra de ouro de todo formulário: primeiro confira, depois use. Vamos guardar os problemas encontrados numa lista
$errose só calcular se ela terminar vazia.O bloco do topo fica assim:
<?php $a = trim($_POST['a'] ?? ''); $b = trim($_POST['b'] ?? ''); $op = $_POST['op'] ?? 'somar'; $erros = []; $resultado = null; if ($_SERVER['REQUEST_METHOD'] === 'POST') { // aceita vírgula como separador decimal: "2,5" vira "2.5" $a = str_replace(',', '.', $a); $b = str_replace(',', '.', $b); if (!is_numeric($a)) { $erros[] = 'O primeiro número não é válido.'; } if (!is_numeric($b)) { $erros[] = 'O segundo número não é válido.'; } if (!in_array($op, ['somar', 'subtrair', 'multiplicar', 'dividir'], true)) { $erros[] = 'Operação desconhecida.'; } if (!$erros && $op === 'dividir' && (float) $b == 0) { $erros[] = 'Não dá para dividir por zero.'; } if (!$erros) { // ... o mesmo match da etapa 3 ... } } ?>E, antes do resultado, as mensagens de erro:
<?php foreach ($erros as $erro): ?> <p class="erro"><?= htmlspecialchars($erro) ?></p> <?php endforeach; ?>
Linha O que faz trim($_POST['a'] ?? '')Tira espaços das pontas: " 10 "vira"10". Quem cola um número de outro lugar costuma trazer espaços junto.$erros = [];Começa a lista de problemas vazia. str_replace(',', '.', $a)No Brasil, escrevemos 2,5; o PHP só entende2.5. Trocamos a vírgula pelo ponto antes de validar.!is_numeric($a)is_numeric("é numérico?") respondetruepara textos como"10","2.5","-8"e"1e3", efalsepara"abc",""e"2,5". O!inverte: "se não for número…".$erros[] = '…';Acrescenta uma mensagem no fim da lista. in_array($op, [...], true)A operação está na lista das permitidas? O truefaz a comparação estrita (tipo e valor).!$erros && $op === 'dividir' && (float) $b == 0Só se os números já estiverem bons: dividir por zero vira uma mensagem amigável, não um Fatal error. Uma lista vazia valefalse, então!$errosquer dizer "nenhum erro até agora".foreach ($erros as $erro)Mostra cada mensagem num parágrafo (com htmlspecialchars, por hábito: amanhã a mensagem pode incluir algo que o usuário digitou).$ curl -s -d 'a=abc&b=2&op=somar' localhost:8000/ | tail -4 </form> <p class="erro">O primeiro número não é válido.</p> </body> </html> $ curl -s -d 'a=7&b=0&op=dividir' localhost:8000/ | grep -E 'resultado|erro' <p class="erro">Não dá para dividir por zero.</p> $ curl -s -d 'a=7&b=2&op=resto' localhost:8000/ | grep -E 'resultado|erro' <p class="erro">Operação desconhecida.</p> $ curl -s -d 'a=&b=&op=somar' localhost:8000/ | grep -E 'resultado|erro' <p class="erro">O primeiro número não é válido.</p> <p class="erro">O segundo número não é válido.</p> « os dois problemas de uma vez $ curl -s -d 'a=10&b=4&op=dividir' localhost:8000/ | tail -3 <p class="resultado">Resultado: 2.5</p> </body> </html>
Os três envios que derrubavam ou enganavam a calculadora agora recebem uma resposta clara. Juntar todos os erros numa lista, em vez de parar no primeiro, é mais gentil com o usuário: ele corrige tudo de uma vez. Mais formas de validar estão em Validar Dados.
Missão: "não é válido" é uma mensagem estranha para um campo que ficou vazio. Mude a validação: se
$a === '', a mensagem deve serPreencha o primeiro número.; senão, se não for numérico, continua "não é válido" (useif…elseif). Faça o mesmo para o$b.<?php $a = trim($_POST['a'] ?? ''); $b = trim($_POST['b'] ?? ''); $op = $_POST['op'] ?? 'somar'; $erros = []; $resultado = null; if ($_SERVER['REQUEST_METHOD'] === 'POST') { // aceita vírgula como separador decimal: "2,5" vira "2.5" $a = str_replace(',', '.', $a); $b = str_replace(',', '.', $b); if (!is_numeric($a)) { $erros[] = 'O primeiro número não é válido.'; } if (!is_numeric($b)) { $erros[] = 'O segundo número não é válido.'; } if (!in_array($op, ['somar', 'subtrair', 'multiplicar', 'dividir'], true)) { $erros[] = 'Operação desconhecida.'; } if (!$erros && $op === 'dividir' && (float) $b == 0) { $erros[] = 'Não dá para dividir por zero.'; } if (!$erros) { $x = (float) $a; $y = (float) $b; $resultado = match ($op) { 'somar' => $x + $y, 'subtrair' => $x - $y, 'multiplicar' => $x * $y, 'dividir' => $x / $y, }; } } ?> <!DOCTYPE html> <html lang="pt-BR"> <head> <meta charset="utf-8"> <title>Calculadora</title> </head> <body> <h1>Calculadora da Ana</h1> <form method="post"> <input name="a" placeholder="Primeiro número"> <select name="op"> <option value="somar">+</option> <option value="subtrair">−</option> <option value="multiplicar">×</option> <option value="dividir">÷</option> </select> <input name="b" placeholder="Segundo número"> <button>Calcular</button> </form> <?php foreach ($erros as $erro): ?> <p class="erro"><?= htmlspecialchars($erro) ?></p> <?php endforeach; ?> <?php if ($resultado !== null): ?> <p class="resultado">Resultado: <?= $resultado ?></p> <?php endif; ?> </body> </html><?php $a = trim($_POST['a'] ?? ''); $b = trim($_POST['b'] ?? ''); $op = $_POST['op'] ?? 'somar'; $erros = []; $resultado = null; if ($_SERVER['REQUEST_METHOD'] === 'POST') { // aceita vírgula como separador decimal: "2,5" vira "2.5" $a = str_replace(',', '.', $a); $b = str_replace(',', '.', $b); if ($a === '') { $erros[] = 'Preencha o primeiro número.'; } elseif (!is_numeric($a)) { $erros[] = 'O primeiro número não é válido.'; } if ($b === '') { $erros[] = 'Preencha o segundo número.'; } elseif (!is_numeric($b)) { $erros[] = 'O segundo número não é válido.'; } if (!in_array($op, ['somar', 'subtrair', 'multiplicar', 'dividir'], true)) { $erros[] = 'Operação desconhecida.'; } if (!$erros && $op === 'dividir' && (float) $b == 0) { $erros[] = 'Não dá para dividir por zero.'; } if (!$erros) { $x = (float) $a; $y = (float) $b; $resultado = match ($op) { 'somar' => $x + $y, 'subtrair' => $x - $y, 'multiplicar' => $x * $y, 'dividir' => $x / $y, }; } } ?> <!DOCTYPE html> <html lang="pt-BR"> <head> <meta charset="utf-8"> <title>Calculadora</title> </head> <body> <h1>Calculadora da Ana</h1> <form method="post"> <input name="a" placeholder="Primeiro número"> <select name="op"> <option value="somar">+</option> <option value="subtrair">−</option> <option value="multiplicar">×</option> <option value="dividir">÷</option> </select> <input name="b" placeholder="Segundo número"> <button>Calcular</button> </form> <?php foreach ($erros as $erro): ?> <p class="erro"><?= htmlspecialchars($erro) ?></p> <?php endforeach; ?> <?php if ($resultado !== null): ?> <p class="resultado">Resultado: <?= $resultado ?></p> <?php endif; ?> </body> </html> -
Etapa 5 · Resultado formatado e campos que lembram
Dois acabamentos que fazem diferença para quem usa: o resultado no formato brasileiro, e os campos que continuam preenchidos depois do envio (ninguém gosta de redigitar tudo para trocar só a operação).
No topo, a lista de operações vira um array e aparece uma função de formatação:
<?php // Calculadora da Ana — versão final $operacoes = [ 'somar' => '+', 'subtrair' => '−', 'multiplicar' => '×', 'dividir' => '÷', ]; // ... leitura, validação e match como na etapa 4, mas a operação // agora é validada com: !array_key_exists($op, $operacoes) function br(float $n): string { return number_format($n, 2, ',', '.'); } ?>No HTML, os campos ganham
value, as opções são geradas pelo array e o resultado mostra a conta inteira:<form method="post"> <input name="a" value="<?= htmlspecialchars($a) ?>" placeholder="Primeiro número"> <select name="op"> <?php foreach ($operacoes as $valor => $simbolo): ?> <option value="<?= $valor ?>"<?= $valor === $op ? ' selected' : '' ?>><?= $simbolo ?></option> <?php endforeach; ?> </select> <input name="b" value="<?= htmlspecialchars($b) ?>" placeholder="Segundo número"> <button>Calcular</button> </form> ... <?php if ($resultado !== null): ?> <p class="resultado"><?= br((float) $a) ?> <?= $operacoes[$op] ?> <?= br((float) $b) ?> = <strong><?= br($resultado) ?></strong></p> <?php endif; ?>Novidade O que faz $operacoes = ['somar' => '+', …]Um array associativo com as operações: a chave é o valor enviado, o valor é o símbolo. Ele serve para três coisas: montar a lista do formulário, validar ( array_key_exists) e mostrar o símbolo no resultado. Uma operação nova entra num lugar só (mais o braço domatch).function br(float $n): stringUma função pequena para não repetir number_format($n, 2, ',', '.')três vezes: 2 casas decimais, vírgula nos decimais, ponto nos milhares (veja Funções e Formatar Números).value="<?= htmlspecialchars($a) ?>"Põe de volta no campo o que foi digitado. Aqui o htmlspecialcharsé obrigatório: uma aspa"no texto fecharia o atributovaluee deixaria o usuário escrever HTML na sua página.foreach ($operacoes as $valor => $simbolo)Gera as quatro <option>a partir do array.$valor === $op ? ' selected' : ''Marca como selected(selecionada) a opção que foi enviada. O operador ternário está em Operador Ternário.$ curl -s -d 'a=10&b=3&op=dividir' localhost:8000/ | grep -E 'resultado|erro' <p class="resultado">10,00 ÷ 3,00 = <strong>3,33</strong></p> $ curl -s -d 'a=1000000&b=3&op=multiplicar' localhost:8000/ | grep -E 'resultado|erro' <p class="resultado">1.000.000,00 × 3,00 = <strong>3.000.000,00</strong></p> $ curl -s -d 'a=2,5&b=3&op=multiplicar' localhost:8000/ | grep -E 'resultado|erro|value' <input name="a" value="2.5" placeholder="Primeiro número"> « o campo voltou preenchido <option value="somar">+</option> <option value="subtrair">−</option> <option value="multiplicar" selected>×</option> « a operação também <option value="dividir">÷</option> <input name="b" value="3" placeholder="Segundo número"> <p class="resultado">2,50 × 3,00 = <strong>7,50</strong></p>Repare que o campo voltou com
2.5(com ponto), porque a variável$ajá tinha passado pelostr_replace. Um dos desafios do fim pede para devolver o texto exatamente como foi digitado.E a prova de que o
htmlspecialcharsnovaluefunciona: um envio com aspas e tags aparece "desarmado":$ curl -s -d 'a="><b>oi</b>&b=2&op=somar' localhost:8000/ | grep 'name="a"' <input name="a" value=""><b>oi</b>" placeholder="Primeiro número">
Sem ele, o
">fecharia o campo e o<b>oi</b>viraria HTML de verdade na sua página. Com ele, o navegador mostra no campo exatamente o que foi digitado.Missão: abaixo do resultado, mostre também o valor arredondado para inteiro, no formato
(arredondado: 2.469). Dica:number_format($resultado, 0, ',', '.'), com zero casas decimais.<?php // Calculadora da Ana — versão final $operacoes = [ 'somar' => '+', 'subtrair' => '−', 'multiplicar' => '×', 'dividir' => '÷', ]; $a = trim($_POST['a'] ?? ''); $b = trim($_POST['b'] ?? ''); $op = $_POST['op'] ?? 'somar'; $erros = []; $resultado = null; if ($_SERVER['REQUEST_METHOD'] === 'POST') { $a = str_replace(',', '.', $a); $b = str_replace(',', '.', $b); if (!is_numeric($a)) { $erros[] = 'O primeiro número não é válido.'; } if (!is_numeric($b)) { $erros[] = 'O segundo número não é válido.'; } if (!array_key_exists($op, $operacoes)) { $erros[] = 'Operação desconhecida.'; } if (!$erros && $op === 'dividir' && (float) $b == 0) { $erros[] = 'Não dá para dividir por zero.'; } if (!$erros) { $x = (float) $a; $y = (float) $b; $resultado = match ($op) { 'somar' => $x + $y, 'subtrair' => $x - $y, 'multiplicar' => $x * $y, 'dividir' => $x / $y, }; } } function br(float $n): string { return number_format($n, 2, ',', '.'); } ?> <!DOCTYPE html> <html lang="pt-BR"> <head> <meta charset="utf-8"> <title>Calculadora</title> </head> <body> <h1>Calculadora da Ana</h1> <form method="post"> <input name="a" value="<?= htmlspecialchars($a) ?>" placeholder="Primeiro número"> <select name="op"> <?php foreach ($operacoes as $valor => $simbolo): ?> <option value="<?= $valor ?>"<?= $valor === $op ? ' selected' : '' ?>><?= $simbolo ?></option> <?php endforeach; ?> </select> <input name="b" value="<?= htmlspecialchars($b) ?>" placeholder="Segundo número"> <button>Calcular</button> </form> <?php foreach ($erros as $erro): ?> <p class="erro"><?= htmlspecialchars($erro) ?></p> <?php endforeach; ?> <?php if ($resultado !== null): ?> <p class="resultado"><?= br((float) $a) ?> <?= $operacoes[$op] ?> <?= br((float) $b) ?> = <strong><?= br($resultado) ?></strong></p> <?php endif; ?> </body> </html><?php // Calculadora da Ana — versão final $operacoes = [ 'somar' => '+', 'subtrair' => '−', 'multiplicar' => '×', 'dividir' => '÷', ]; $a = trim($_POST['a'] ?? ''); $b = trim($_POST['b'] ?? ''); $op = $_POST['op'] ?? 'somar'; $erros = []; $resultado = null; if ($_SERVER['REQUEST_METHOD'] === 'POST') { $a = str_replace(',', '.', $a); $b = str_replace(',', '.', $b); if (!is_numeric($a)) { $erros[] = 'O primeiro número não é válido.'; } if (!is_numeric($b)) { $erros[] = 'O segundo número não é válido.'; } if (!array_key_exists($op, $operacoes)) { $erros[] = 'Operação desconhecida.'; } if (!$erros && $op === 'dividir' && (float) $b == 0) { $erros[] = 'Não dá para dividir por zero.'; } if (!$erros) { $x = (float) $a; $y = (float) $b; $resultado = match ($op) { 'somar' => $x + $y, 'subtrair' => $x - $y, 'multiplicar' => $x * $y, 'dividir' => $x / $y, }; } } function br(float $n): string { return number_format($n, 2, ',', '.'); } ?> <!DOCTYPE html> <html lang="pt-BR"> <head> <meta charset="utf-8"> <title>Calculadora</title> </head> <body> <h1>Calculadora da Ana</h1> <form method="post"> <input name="a" value="<?= htmlspecialchars($a) ?>" placeholder="Primeiro número"> <select name="op"> <?php foreach ($operacoes as $valor => $simbolo): ?> <option value="<?= $valor ?>"<?= $valor === $op ? ' selected' : '' ?>><?= $simbolo ?></option> <?php endforeach; ?> </select> <input name="b" value="<?= htmlspecialchars($b) ?>" placeholder="Segundo número"> <button>Calcular</button> </form> <?php foreach ($erros as $erro): ?> <p class="erro"><?= htmlspecialchars($erro) ?></p> <?php endforeach; ?> <?php if ($resultado !== null): ?> <p class="resultado"><?= br((float) $a) ?> <?= $operacoes[$op] ?> <?= br((float) $b) ?> = <strong><?= br($resultado) ?></strong></p> <p>(arredondado: <?= number_format($resultado, 0, ',', '.') ?>)</p> <?php endif; ?> </body> </html>
Quando dá errado
Você escreveu $a = $_POST['a']; sem o ?? e abriu a página pela primeira vez (um GET, sem formulário enviado):
$ curl -s localhost:8000/ <br /> <b>Warning</b>: Undefined array key "a" in <b>/home/ana/calculadora/index.php</b> on line <b>2</b><br /> <br /> <b>Warning</b>: Undefined array key "b" in <b>/home/ana/calculadora/index.php</b> on line <b>3</b><br /> Recebi e
O que significa: "chave de array indefinida": não existe 'a' dentro de $_POST, porque nada foi enviado. Como consertar: $_POST['a'] ?? '', que dá um valor padrão quando a chave não existe.
Apareceu ao dividir por zero (etapa 3). Como consertar: confira o divisor antes da conta e transforme o caso numa mensagem para o usuário, como na etapa 4. O resto da divisão com % tem o seu próprio erro: DivisionByZeroError: Modulo by zero.
O match recebeu um valor que não tem braço. Como consertar: valide a operação antes (lista de permitidas) ou acrescente um braço default => …. Na calculadora preferimos validar, porque assim o usuário recebe "Operação desconhecida." em vez de um resultado inventado.
Por exemplo, abc + 2 dá 2. É o (float) transformando texto inválido em 0. Como consertar: is_numeric antes de converter. E se em vez de (float) você fizer a conta direto com os textos, o PHP 8.5 reclama de verdade: "abc" + 1 dá Fatal error: Uncaught TypeError: Unsupported operand types: string + int, e "5 maçãs" + 1 dá Warning: A non-numeric value encountered e o resultado 6 (veja Conversão de Tipos).
<?php à mostra
Você abriu o index.php com dois cliques (endereço file:///…). O navegador não sabe rodar PHP. Como consertar: suba o php -S localhost:8000 na pasta e abra http://localhost:8000.
O código completo
A versão final, com tudo das cinco etapas. São cerca de 70 linhas.
<?php
// Calculadora da Ana — versão final
$operacoes = [
'somar' => '+',
'subtrair' => '−',
'multiplicar' => '×',
'dividir' => '÷',
];
$a = trim($_POST['a'] ?? '');
$b = trim($_POST['b'] ?? '');
$op = $_POST['op'] ?? 'somar';
$erros = [];
$resultado = null;
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$a = str_replace(',', '.', $a);
$b = str_replace(',', '.', $b);
if (!is_numeric($a)) {
$erros[] = 'O primeiro número não é válido.';
}
if (!is_numeric($b)) {
$erros[] = 'O segundo número não é válido.';
}
if (!array_key_exists($op, $operacoes)) {
$erros[] = 'Operação desconhecida.';
}
if (!$erros && $op === 'dividir' && (float) $b == 0) {
$erros[] = 'Não dá para dividir por zero.';
}
if (!$erros) {
$x = (float) $a;
$y = (float) $b;
$resultado = match ($op) {
'somar' => $x + $y,
'subtrair' => $x - $y,
'multiplicar' => $x * $y,
'dividir' => $x / $y,
};
}
}
function br(float $n): string
{
return number_format($n, 2, ',', '.');
}
?>
<!DOCTYPE html>
<html lang="pt-BR">
<head>
<meta charset="utf-8">
<title>Calculadora</title>
</head>
<body>
<h1>Calculadora da Ana</h1>
<form method="post">
<input name="a" value="<?= htmlspecialchars($a) ?>" placeholder="Primeiro número">
<select name="op">
<?php foreach ($operacoes as $valor => $simbolo): ?>
<option value="<?= $valor ?>"<?= $valor === $op ? ' selected' : '' ?>><?= $simbolo ?></option>
<?php endforeach; ?>
</select>
<input name="b" value="<?= htmlspecialchars($b) ?>" placeholder="Segundo número">
<button>Calcular</button>
</form>
<?php foreach ($erros as $erro): ?>
<p class="erro"><?= htmlspecialchars($erro) ?></p>
<?php endforeach; ?>
<?php if ($resultado !== null): ?>
<p class="resultado"><?= br((float) $a) ?> <?= $operacoes[$op] ?> <?= br((float) $b) ?> = <strong><?= br($resultado) ?></strong></p>
<?php endif; ?>
</body>
</html>
Desafios extras
Sem resposta pronta; use a caixa livre abaixo ou o seu computador.
- Devolver o que foi digitado: guarde o texto original em
$aTextoantes dostr_replacee use-o novalue, para o campo voltar com2,5e não2.5. - Mais operações: potência (
**) e resto (fmod($x, $y), que funciona com decimais). Não esqueça de proteger o resto contra zero também. - Casas decimais à escolha: um terceiro campo (um
<select name="casas">com 0, 2 e 4) que decide quantas casas obr()usa. Valide comin_array. - Números que aparecem como digitados: mostre
10 ÷ 4 = 2,50(sem,00nos números inteiros). Dica:$n == (int) $n. - Histórico: guarde as últimas cinco contas numa sessão e mostre-as abaixo do formulário (veja Sessões; o projeto de login usa sessão do começo ao fim).
Missão final: acrescente a operação porcentagem: "a % de b". A chave é porcentagem, o símbolo é % de e a conta é $x * $y / 100. A caixa já envia "15 % de 200"; a resposta deve ser 15,00 % de 200,00 = 30,00. Repare em quantos lugares você precisou mexer (spoiler: dois).
<?php
// Calculadora da Ana — versão final
$operacoes = [
'somar' => '+',
'subtrair' => '−',
'multiplicar' => '×',
'dividir' => '÷',
];
$a = trim($_POST['a'] ?? '');
$b = trim($_POST['b'] ?? '');
$op = $_POST['op'] ?? 'somar';
$erros = [];
$resultado = null;
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$a = str_replace(',', '.', $a);
$b = str_replace(',', '.', $b);
if (!is_numeric($a)) {
$erros[] = 'O primeiro número não é válido.';
}
if (!is_numeric($b)) {
$erros[] = 'O segundo número não é válido.';
}
if (!array_key_exists($op, $operacoes)) {
$erros[] = 'Operação desconhecida.';
}
if (!$erros && $op === 'dividir' && (float) $b == 0) {
$erros[] = 'Não dá para dividir por zero.';
}
if (!$erros) {
$x = (float) $a;
$y = (float) $b;
$resultado = match ($op) {
'somar' => $x + $y,
'subtrair' => $x - $y,
'multiplicar' => $x * $y,
'dividir' => $x / $y,
};
}
}
function br(float $n): string
{
return number_format($n, 2, ',', '.');
}
?>
<!DOCTYPE html>
<html lang="pt-BR">
<head>
<meta charset="utf-8">
<title>Calculadora</title>
</head>
<body>
<h1>Calculadora da Ana</h1>
<form method="post">
<input name="a" value="<?= htmlspecialchars($a) ?>" placeholder="Primeiro número">
<select name="op">
<?php foreach ($operacoes as $valor => $simbolo): ?>
<option value="<?= $valor ?>"<?= $valor === $op ? ' selected' : '' ?>><?= $simbolo ?></option>
<?php endforeach; ?>
</select>
<input name="b" value="<?= htmlspecialchars($b) ?>" placeholder="Segundo número">
<button>Calcular</button>
</form>
<?php foreach ($erros as $erro): ?>
<p class="erro"><?= htmlspecialchars($erro) ?></p>
<?php endforeach; ?>
<?php if ($resultado !== null): ?>
<p class="resultado"><?= br((float) $a) ?> <?= $operacoes[$op] ?> <?= br((float) $b) ?> = <strong><?= br($resultado) ?></strong></p>
<?php endif; ?>
</body>
</html>
<?php
// Calculadora da Ana — versão final
$operacoes = [
'somar' => '+',
'subtrair' => '−',
'multiplicar' => '×',
'dividir' => '÷',
'porcentagem' => '% de',
];
$a = trim($_POST['a'] ?? '');
$b = trim($_POST['b'] ?? '');
$op = $_POST['op'] ?? 'somar';
$erros = [];
$resultado = null;
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$a = str_replace(',', '.', $a);
$b = str_replace(',', '.', $b);
if (!is_numeric($a)) {
$erros[] = 'O primeiro número não é válido.';
}
if (!is_numeric($b)) {
$erros[] = 'O segundo número não é válido.';
}
if (!array_key_exists($op, $operacoes)) {
$erros[] = 'Operação desconhecida.';
}
if (!$erros && $op === 'dividir' && (float) $b == 0) {
$erros[] = 'Não dá para dividir por zero.';
}
if (!$erros) {
$x = (float) $a;
$y = (float) $b;
$resultado = match ($op) {
'somar' => $x + $y,
'subtrair' => $x - $y,
'multiplicar' => $x * $y,
'dividir' => $x / $y,
'porcentagem' => $x * $y / 100,
};
}
}
function br(float $n): string
{
return number_format($n, 2, ',', '.');
}
?>
<!DOCTYPE html>
<html lang="pt-BR">
<head>
<meta charset="utf-8">
<title>Calculadora</title>
</head>
<body>
<h1>Calculadora da Ana</h1>
<form method="post">
<input name="a" value="<?= htmlspecialchars($a) ?>" placeholder="Primeiro número">
<select name="op">
<?php foreach ($operacoes as $valor => $simbolo): ?>
<option value="<?= $valor ?>"<?= $valor === $op ? ' selected' : '' ?>><?= $simbolo ?></option>
<?php endforeach; ?>
</select>
<input name="b" value="<?= htmlspecialchars($b) ?>" placeholder="Segundo número">
<button>Calcular</button>
</form>
<?php foreach ($erros as $erro): ?>
<p class="erro"><?= htmlspecialchars($erro) ?></p>
<?php endforeach; ?>
<?php if ($resultado !== null): ?>
<p class="resultado"><?= br((float) $a) ?> <?= $operacoes[$op] ?> <?= br((float) $b) ?> = <strong><?= br($resultado) ?></strong></p>
<?php endif; ?>
</body>
</html>
Exercícios
Complete para ler o campo op com o valor padrão 'somar' quando ele não vier:
Complete para formatar 2469 como 2.469,00:
Um usuário envia op=resto para a versão da etapa 3 (sem validação). O que acontece?
Por que o value="<?= htmlspecialchars($a) ?>" precisa do htmlspecialchars?
Quanto vale is_numeric("2,5")?
Resumo
- Um só
index.phpmostra o formulário (GET) e processa o envio (POST);$_SERVER['REQUEST_METHOD']diz qual dos dois aconteceu. - Todo campo chega como texto e pode não chegar:
$_POST['x'] ?? ''etrim. - Valide antes de usar:
is_numeric, lista de operações permitidas, divisor diferente de zero. Junte os erros numa lista. matchescolhe o valor; sem braço para o valor,UnhandledMatchError.number_format($n, 2, ',', '.')deixa o número no formato brasileiro.- Tudo que veio do usuário e volta para a página passa por
htmlspecialchars, inclusive dentro devalue="…".
Próximo projeto: o Formulário de Contato, que guarda as mensagens num arquivo e usa o padrão Post/Redirect/Get.